Перейти к содержимому

Fingercomp

Гуру
  • Публикации

    1 629
  • Зарегистрирован

  • Посещение

  • Победитель дней

    283

Сообщения, опубликованные пользователем Fingercomp


  1. 1 час назад, Mihis сказал:

    Нет, это не оно. Тогда забыл упомянуть, что я имел ввиду ocelot-brain

    А зачем нужен собранный ocelot-brain? Это же тупо либа. Её надо подключать в другую прогу, которую так и так придётся компилировать. Но смысла тогда готовый брейн собирать поразительно мало, особенно с учётом малого объёма кода.

    • Нравится 1

  2. Внутри песочницы coroutine.yield переопределён:

    yield = function(...) -- custom yield part for bubbling sysyields
      return coroutine.yield(nil, ...)
    end

    Также переопределён и coroutine.resume.

    resume = function(co, ...) -- custom resume part for bubbling sysyields
      checkArg(1, co, "thread")
      local args = table.pack(...)
      while true do -- for consecutive sysyields
        debug.sethook(co, checkDeadline, "", hookInterval)
        local result = table.pack(
          coroutine.resume(co, table.unpack(args, 1, args.n)))
        debug.sethook(co) -- avoid gc issues
        checkDeadline()
        if result[1] then -- success: (true, sysval?, ...?)
          if coroutine.status(co) == "dead" then -- return: (true, ...)
            return true, table.unpack(result, 2, result.n)
          elseif result[2] ~= nil then -- yield: (true, sysval)
            args = table.pack(coroutine.yield(result[2]))
          else -- yield: (true, nil, ...)
            return true, table.unpack(result, 3, result.n)
          end
        else -- error: result = (false, string)
          return false, result[2]
        end
      end
    end

    Работает это так:

    • Если корутина вернула true, nil, ... — отдать true, ... (поэтому внутри песочницы разница между обычными и переопределёнными функциями не видна).
    • Если корутина йелднулась без нила в начале, то это "системный вызов". Потому что доступ к непереопределённой функции coroutine.yield есть только внутри machine.lua. В таком случае йелдится и текущая корутина вверх по цепочке.
    • EEPROM запускается в корутине. Если корутина эта йелдится, случается следующее:
      • yield(false) — это выключить компьютер;
      • yield(true) — ребутнуть его;
      • yield(n), где type(n) == "number", — спать n секунд;
      • yield(f), где type(f) == "function", — это вызвать непрямую функцию.
      • yield с любым другим аргументом — спать до скончания века.
    • Дополнительно обрабатывается умирание корутины: return посреди еепрома — это error("computer halted", 0); другие ошибки прокидываются.

    А теперь смотрим на наш вызов.

    • В EEPROM вызвана coroutine.yield(0).
    • Эта функция переопределена, чтобы вызвать нативный coroutine.yield(nil, 0).
    • Корутина тогда йелдится и отдаёт nil, 0.
    • В списке выше это пункт 3.5. Компьютер уйдёт в вечный сон.

    Ну, конечно, я преувеличиваю. Компьютер спит до момента получения сигнала и, получив, продолжит работу.

    Но вообще, незачем звать coroutine.yield напрямую. Есть computer.pullSignal, computer.shutdown, component.invoke(addr, nonDirectMethod, ...).

    • Нравится 5
    • Одобряю 1
    • Спасибо 1

  3. 11 минуту назад, ov3rwrite сказал:

    Ой-ой-ой,вы не видели сам код майноси на гитхабе)Я в году так 2018 смотрел,там были переменные по типу pizda, pizdaLength

    Как раз об этом я и пишу:

    23 часа назад, Fingercomp сказал:

    переименования всех переменных с непристойными выражениями

    Код видел, приходилось.


  4. registerListeners()
    
    repeat
    until event.pull("interrupted")
    
    disableListeners()

    Этот кусок кода кладём в конец программки. Вуаля.

     

    Если этот кусок кода ровно от начала и прямо до конца обернуть в поток, а затем сделать ему :join(), то disableListeners даже не потребуется. Всё само выключится при завершении потока. Жутко удобно, словами не передать.

     

    Программа закрываться будет по ^C. Если не нравится, репит заменить на это:

    while true do
      os.sleep(math.huge)
    end
    • Нравится 1

  5. Наконец-то мы смогли одолеть эту проказу демоническую! Сто лет не мог поставить: чуть что — ось меня посылала. Хоть теперь покойнее будет.

     

    Предлагаю не останавливаться на этом. Мир ждёт:

    • переписанной без матов истории коммитов;
    • переименования всех переменных с непристойными выражениями;
    • замены значков об ошибке рисунком бодрого розового единорога;
    • редакции вики в дореволюционной орфографии для истинных джентльменов;
    • перевода MineOS с Lua на китайский вариант Scratch;
    • кастомизации количества пробелов в отступах (и заменой на них табов);
    • патча OpenComputers, чтобы мониторы работали в кодировке KOI-8.
    • Нравится 1
    • Ха-ха 4

  6. Чтобы решить проблему, надо оформить код.

    entity = require("component").os_entdetector
    alarm = require("component").os_alarm
    door = require("component").os_rolldoorcontroller
    local e = entity.scanPlayers(3)
    local whitelist = {{}}
    
    alarm.setAlarm("klaxon2")
    alarm.setRange(15)
    whitelist[1] = "Arsean"
    whitelist[2] = "sherlock2202"
    
    function open()
      door.open()
      os.sleep(5)
      door.close()
      detect()
    end
    
    function detect()
      if type(q[1]) == type(nil) then
        print("Нету")
        os.sleep(2)
        q = e
        detect()
      else
        print("Есть")
        q = e
        check()
      end
    end
    function check()
      x = e[1].name
      if x == "Arsean" then
        open()
      else
        alarm.activate()
        os.sleep(5)
        alarm.deactivate()
      end
      detect()
    end
    q = e
    detect()

    Тяк, сначала починим обозначенную неполадку. Функция detect, согласно названию, должна при каждом вызове пересканировать игроков. Однако сканирование игроков это происходит в самом начале программы (4 строка) и один раз. Гм. Ещё раз, при каждом вызове функции надо выполнять определённый код. При каждом вызове... а почему бы не поместить сканирование внутрь функции? Разве это не тем и занимается?

    entity = require("component").os_entdetector
    alarm = require("component").os_alarm
    door = require("component").os_rolldoorcontroller
    local e = entity.scanPlayers(3)
    local whitelist = {{}}
    
    alarm.setAlarm("klaxon2")
    alarm.setRange(15)
    whitelist[1] = "Arsean"
    whitelist[2] = "sherlock2202"
    
    function open()
      door.open()
      os.sleep(5)
      door.close()
      detect()
    end
    
    function detect()
      e = entity.scanPlayers(3)
      q = e
      if type(q[1]) == type(nil) then
        print("Нету")
        os.sleep(2)
        q = e
        detect()
      else
        print("Есть")
        q = e
        check()
      end
    end
    function check()
      x = e[1].name
      if x == "Arsean" then
        open()
      else
        alarm.activate()
        os.sleep(5)
        alarm.deactivate()
      end
      detect()
    end
    q = e
    detect()

    Ну да, почти. Пришлось только ещё q = e перетащить туда же. Но теперь должно работать.

    ...

    Должно ли?

     

    Исправляем другие проблемы, которые здесь заютились.

    Проблема 1. Есть переменные q и e, которые во всех местах устанавливаются одинаковыми, при этом непонятно, чем они отличаются.

    Объединим их в одну переменную. Заодно назовём её по-человечески, а не для машины, то есть понятно:

    entity = require("component").os_entdetector
    alarm = require("component").os_alarm
    door = require("component").os_rolldoorcontroller
    local scan = entity.scanPlayers(3)
    local whitelist = {{}}
    
    alarm.setAlarm("klaxon2")
    alarm.setRange(15)
    whitelist[1] = "Arsean"
    whitelist[2] = "sherlock2202"
    
    function open()
      door.open()
      os.sleep(5)
      door.close()
      detect()
    end
    
    function detect()
      scan = entity.scanPlayers(3)
      if type(scan[1]) == type(nil) then
        print("Нету")
        os.sleep(2)
        detect()
      else
        print("Есть")
        check()
      end
    end
    function check()
      x = scan[1].name
      if x == "Arsean" then
        open()
      else
        alarm.activate()
        os.sleep(5)
        alarm.deactivate()
      end
      detect()
    end
    detect()

     

    Проблема 2. Куча переменных не локальны.

    Допустим такую ситуацию: в коде переименовали переменную, а в одном месте имя сменить забыли. Если эта переменная была локальной, то программа пропишет ошибку, стопнется, и проблему легко локализовать и починить. Если глобальной, то она никуда не денется до рестарта компьютера. Каково этого — поменял переменную, а в одном месте как будто одно и то же значение застряло? Чинить такие вещи — боль.

    Как правило, все переменные делать надо локальными. У глобальных есть несколько применений, но без них обходиться можно всегда. И лучше это делать.

    Чиним, короче:

    local entity = require("component").os_entdetector
    local alarm = require("component").os_alarm
    local door = require("component").os_rolldoorcontroller
    local scan = entity.scanPlayers(3)
    local whitelist = {{}}
    
    alarm.setAlarm("klaxon2")
    alarm.setRange(15)
    whitelist[1] = "Arsean"
    whitelist[2] = "sherlock2202"
    
    local function open()
      door.open()
      os.sleep(5)
      door.close()
      detect()
    end
    
    local function detect()
      scan = entity.scanPlayers(3)
      if type(scan[1]) == type(nil) then
        print("Нету")
        os.sleep(2)
        detect()
      else
        print("Есть")
        check()
      end
    end
    local function check()
      local x = scan[1].name
      if x == "Arsean" then
        open()
      else
        alarm.activate()
        os.sleep(5)
        alarm.deactivate()
      end
      detect()
    end
    detect()

     

    Проблема 3. Переполнение стэка из-за рекурсии.

    Во всех функциях в коде вызывается detect. В том числе внутри самой detect — когда функция саму себя зовёт, это зовут рекурсией.

     

    У меня есть подозрение, то функциональное программирование непрограммирующему обывателю концептуально проще, чем императивное. Вот взять этот код.

    Откуда тут рекурсия? Думаю, автор рассуждал так: после проверки на пробежчиков мы хотим снова отсканировать игроков. То есть перейти в detect. А как перейти в функцию? Вызовом же.

     

    Проблема в том, что Lua — язык императивный по большей части. А ещё оптимизаций делает мало. Поэтому не получится в луа сколь угодно много раз вызывать функции рекурсивно. Упрёмся в лимит и словим ошибку.

     

    Один способ починить — использовать хвостовую рекурсию, которую Lua оптимизировать умеет. Мы вместо этого воспользуемся циклами.

    local entity = require("component").os_entdetector
    local alarm = require("component").os_alarm
    local door = require("component").os_rolldoorcontroller
    local scan = entity.scanPlayers(3)
    local whitelist = {{}}
    
    alarm.setAlarm("klaxon2")
    alarm.setRange(15)
    whitelist[1] = "Arsean"
    whitelist[2] = "sherlock2202"
    
    local function open()
      door.open()
      os.sleep(5)
      door.close()
    end
    
    local function detect()
      while true do
        scan = entity.scanPlayers(3)
        if type(scan[1]) == type(nil) then
          print("Нету")
          os.sleep(2)
        else
          print("Есть")
          check()
        end
      end
    end
    
    local function check()
      local x = scan[1].name
      if x == "Arsean" then
        open()
      else
        alarm.activate()
        os.sleep(5)
        alarm.deactivate()
      end
    end
    
    detect()

    Если в первой функции вызвать вторую, то Луа будет выполнять код второй функции. Когды мы дойдём до конца кода её, Луа вернётся в первую функцию. То есть Lua не забывает, кто вызвал любую функцию. Таким образом, open вернётся в check, а check — в detect.

     

    В detect появился while true do ... end. Эта конструкция называется бесконечным циклом. Цикл — повторение одного и того же кода. Бесконечный — нет условия, при котором программа покинет цикл. (Формально есть ^[C, то есть цикл покинуть можно, но оставим это в стороне.)

     

    Проблема 4. Нелокальность переменных.

    Это не повторение проблемы #2, хотя на определённом уровне абстракции всё начинает казаться молотками и гвоздями они очень похожи.

    Переменная scan общая для всех функций. По сути, это тоже "глобальная" переменная, только живёт она не дольше, чем программа (статическая она, наверное, лучше сказать). Если нет веских причин, лучше всё-таки такие переменные передавать явно. То есть аргументом функций.

    local entity = require("component").os_entdetector
    local alarm = require("component").os_alarm
    local door = require("component").os_rolldoorcontroller
    local whitelist = {{}}
    
    alarm.setAlarm("klaxon2")
    alarm.setRange(15)
    whitelist[1] = "Arsean"
    whitelist[2] = "sherlock2202"
    
    local function open()
      door.open()
      os.sleep(5)
      door.close()
    end
    
    local function detect()
      while true do
        local scan = entity.scanPlayers(3)
    
        if type(scan[1]) == type(nil) then
          print("Нету")
          os.sleep(2)
        else
          print("Есть")
          check(scan)
        end
      end
    end
    
    local function check(scan)
      local x = scan[1].name
      if x == "Arsean" then
        open()
      else
        alarm.activate()
        os.sleep(5)
        alarm.deactivate()
      end
    end
    
    detect()

    Теперь scan определяется в detect и передаётся в check аргументом.

     

    Проблема 5. Программа страшно боится одиночества.

    Предлагаю взглянуть на эту строку пристально, можно без микроскопа:

    local x = scan[1].name

    Я утверждаю, что тут ошибка. Разберём строку.

    1. scan — это таблица с игроками. Она хранит внутри себя ещё таблицы. В последних инфа о конкретном игроке.
    2. scan[1] — это тогда таблица с инфой о первом игроке. А если вокруг радара пустыня, случайных прохожих нет, а последний местный подох под палящим пустынным солнцем где-то в лаве? scan будет пустой таблицей, и scan[1] будет nil.
    3. scan[1].name — эта штука безусловно полагает, что scan[1] можно индексировать, то есть это таблица (или что-то вроде неё). Нет, не scan — то, что это таблица, мы уже знаем безусловно. scan[1] — первый элемент таблицы scan. Выше мы определили, что этот элемент — или ещё одна таблица, или nil.

    Таблицу индексировать можно. Если проиндексировать nil, программа завершится с ошибкой. Напомню, nil у нас бывает только тогда, когда игроков вокруг нет. Следовательно, на необитаемом острове программа упадёт.

     

    Чтобы починить, подумаем, что надо делать на этом необитаемом острове. Визжать сиреной? Думаю, вряд ли. Лучше просто промолчать и ничего не делать. Добавим проверку.

    local entity = require("component").os_entdetector
    local alarm = require("component").os_alarm
    local door = require("component").os_rolldoorcontroller
    local whitelist = {{}}
    
    alarm.setAlarm("klaxon2")
    alarm.setRange(15)
    whitelist[1] = "Arsean"
    whitelist[2] = "sherlock2202"
    
    local function open()
      door.open()
      os.sleep(5)
      door.close()
    end
    
    local function detect()
      while true do
        local scan = entity.scanPlayers(3)
    
        if type(scan[1]) == type(nil) then
          print("Нету")
          os.sleep(2)
        else
          print("Есть")
          check(scan)
        end
      end
    end
    
    local function check(scan)
      if scan[1] then
        local name = scan[1].name
    
        if name == "Arsean" then
          open()
        else
          alarm.activate()
          os.sleep(5)
          alarm.deactivate()
        end
      end
    end
    
    detect()

    Не удержался, переменовал x и name, чтобы не потеряться.

     

    Проблема 6...

    Пожалуй, всё. Нет, код ещё можно улучшать и улучшать. Например, белый список не используется и задаётся странно. Но так и я не автор программы.

    В этом посте я попытался описать только "сложные" проблемы, то есть для определениях которых нужен какой-нибудь опыт программирования.

    Проблема 3, например, означает, что программа крашилась бы через час после включения. Стабильно. Со странной ошибкой.

    Проблему 5 вообще сложно было бы дебажить, так как игрока рядом с компьютером быть не должно, иначе и проблема не проявится.

    • Нравится 5
    • Спасибо 1
    • В шоке 1

  7. 5 минут назад, Totoro сказал:

    Вроде существует.

    Ну-ну. Твой sbt не пакует пустые директории. /home на дискете с опеносью нет. При установке она тоже не появлятся, соответственно.

    Только что, IS2511 сказал:

    У меня при создании компа почему-то без нее система и до, и после установки

    После установки пропиши поэтому mkdir /home.

    • Грусть 1

  8. Так как есть планы в самом биосе с интернетов грузиться, одной команды с wget мало. Я три дня назад как раз написал небольшой пост про то, как правильно использовать интернет-плату.

    Рекомендую ознакомиться с секцией номер 3 в записи. Скачивать с интернетов хоть что-либо — задача нетривиальная, занимает куда больше, чем 1 строчку.

    • Нравится 1

  9. Да, конечно, почему бы и нет. Сложность в том, что component.componentName возвращает прокси только одного компонента, а нам надо и других тоже получить. Здесь есть 2 варианта действий.

     

    1. component.invoke

    Эта функция первым аргументом принимает адрес компонента, а вторым — имя метода. Остальные параметры — это аргументы к этому методу. Например, если компонент по адресу "12345678-1234-1234-1234-123456789012" — видеокарточка, поменять разрешение у неё можно вот так:

    local com = require("component")
    local address = "12345678-1234-1234-1234-123456789012"
    
    com.invoke(address, "setResolution", 80, 25)

    В одиночестве функция выглядит страшно, если сравнивать с проксями. Обычно её используют, итерируя компоненты с помощью component.list, потому что итератор этот выдаёт адрес очередного компонента:

    local com = require("component")
    
    for addr in com.list("gpu", true) do
      local w, h = com.invoke(addr, "getResolution")
      com.invoke(addr, "fill", 1, 1, w, h, " ")
    end

    Я предпочитаю использовать этот способ, когда надо в цикле проходиться по всем компонентам и вызывать у них пару-тройку методов.

    local com = require("component")
    local event = require("event")
    
    local function hsv2rgb(h, s, v)
      local function f(n)
        local k = (n + h / 60) % 6
    
        return v - v * s * math.max(0, math.min(k, 4 - k, 1))
      end
      
      local r = math.floor(f(5) * 0x1f + 0.5)
      local g = math.floor(f(3) * 0x1f + 0.5)
      local b = math.floor(f(1) * 0x1f + 0.5)
      
      return (r << 10) | (g << 5) | b
    end
    
    repeat
      for addr in com.list("colorful_lamp", true) do
        local color = hsv2rgb(math.random(0, 360), math.random(.85, 1), math.random(.85, 1))
        com.invoke(addr, "setLampColor", color)
      end
    until event.pull(0.1, "interrupted")

    Здесь у компонента метод вызывается лишь один раз, поэтому проще использовать component.invoke. В противном случае лучше делать прокси.

     

    2. component.proxy

    Если список компонентов, с которыми работает программа, более-менее статичен, удобнее использовать component.proxy. Это функция, которая возвращает прокси компонента по данному адресу. С проксями мы уже знакомы: когда делаем в коде component.componentName, на самом деле вызывается component.proxy(component.getPrimary("componentName")).

     

    Когда компонентов несколько, обычный шаблон — это один раз напихать проксей в таблицу и использовать уже её.

    local com = require("component")
    local event = require("event")
    
    local gpus = {}
    
    for addr in com.list("gpu", true) do
      table.insert(gpus, com.proxy(addr))
    end
    
    assert(#gpus >= 4, "4 gpus required")
    
    gpu[1].set(1, 1, "first gpu")
    gpu[2].set(2, 2, "second gpu")
    gpu[3].set(3, 3, "third gpu")
    gpu[4].set(4, 4, "fourth gpu")

    Важно, что после заполнения таблицы компоненты эти отключаться не должны. В противном случае нужно ставить листнеры на component_added, component_removed.

     

    Прокси также можно использовать в цикле component.list, как в первом способе, чтобы упростить жизнь, если внутри цикла приходится трогать методы компонента по нескольку раз. Вот программка, которая чистит экран и принтит число почищенных символов.

    local com = require("component")
    local event = require("event")
    
    for addr in com.list("gpu", true) do
      local gpu = com.proxy(addr)  
      local litChars = 0
      local w, h = gpu.getResolution()
      
      local oldBg = gpu.getBackground()
      gpu.setBackground(0x000000)
      
      for x = 1, w, 1 do
        for y = 1, h, 1 do
          local char, _fg, bg = gpu.get(x, y)
          
          if char ~= " " or bg ~= 0x000000 then
            litChars = litChars + 1
          end
    
          gpu.set(x, y, " ")
        end
      end
      
      gpu.set(1, 1, ("%d lit characters"):format(litChars))
      gpu.setBackground(oldBg)
    end

    Как видно, я активно использую кучу методов гпу. Вместо того, чтобы каждый раз печатать component.invoke, я один раз взял прокси, а дальше работаю с ним.

    • Нравится 9

  10. 1 час назад, ProgramCrafter сказал:

    @BrightYC новый эмулятор почему-то медленнее: примерно в 2 раза меньше вычислений в секунду (600К вместо миллиона), дольше рендер случайных символов. Это ограничение сервера или какой-то баг?

    Проблема-то не эмуляторе, а в том, что запущен он на железке почти без памяти и с процессором, о котором лучше не говорить.

    • Грусть 4

  11. Оцелот хорош тем, что его мозги — это реорганизованный код из OpenComputers, поэтому вся логика работы компонентов сохранена и может быть легко обновлена. GPU также работает с теми же характеристиками, что и в моде. Задержки в отображении — это проблема исключительно онлайн-версии, которую Тотора год никак не может доработать. В незаконченной десктоп-версии проблем с GPU не наблюдалось.

     

    @Totoro так что давай доделывай оцелота. Нужен новый рендер и воркспайсы.

    • Нравится 3

  12. 12 часа назад, MrAbad сказал:

    потому что, это не ООП, а в чистом виде замыкание и функциональное программирование

    ООП — это парадигма. Программа манипулирует объектами, которые хранят состояние (какие-либо данные) и могут обрабатывать сообщения. Это ещё называется вызовом методов. В приведённом коде у нас есть объект, хранящий состояние (a = 3, b = 14) и обрабатывающий сообщения printAandB, GET. По всем признакам это чистое ООП.

     

    ООП — это не наследование, полиморфизм и инкапсуляция, и на этих трёх вещах ООП не покоится. Это просто удобные фичи, которые часто встречаются.

    • Нравится 1
    • Одобряю 2

  13. Я недавно выложил IRC-либу, которую я делал, чтобы собирать IRC-мост. Теперь я собрал и мост.

     

    Установка

    1. Соберите компьютер с интернет-платой, кучей памяти (на всякий случай), админ-чатбоксом из OpenTechnology и дебаг-картой (через неё онлайн получает прога).
    2. Поставьте на него OpenOS.
    3. Пропишите следующие команды:
      mkdir -p /home/bin
      wget https://gist.githubusercontent.com/Fingercomp/df483bc2cefa13e0422d656ae82495ac/raw/c8617e01b7baa0e47936300fd9e783afa36601cb/irc-bridge.lua /home/bin/irc-bridge.lua

       

    4. Скачайте и установите IRC-либу. Например, так:
      mkdir -p /home/lib/irc /home/lib/irc/client /home/lib/irc/event /home/lib/irc/protocol
      cd /home/lib/irc
      
      set ADDR=https://gitlab.com/cc-ru/irc-oc/-/raw/v1.1.0/irc
      wget $ADDR/init.lua init.lua
      wget $ADDR/enum.lua enum.lua
      wget $ADDR/state.lua state.lua
      wget $ADDR/throttlingScheduler.lua throttlingScheduler.lua
      
      wget $ADDR/client/init.lua client/init.lua
      wget $ADDR/client/handlers.lua client/handlers.lua
      
      wget $ADDR/event/init.lua event/init.lua
      wget $ADDR/event/bus.lua event/bus.lua
      
      wget $ADDR/protocol/init.lua protocol/init.lua
      wget $ADDR/protocol/isupport.lua protocol/isupport.lua
      wget $ADDR/protocol/capabilities.lua protocol/capabilities.lua
      wget $ADDR/protocol/splitter.lua protocol/splitter.lua

       

    5. Запустите мост, чтобы он создал конфиг-файл:
      irc-bridge
    6. Откройте файл /etc/conversationalist.cfg. Там будет сериализованная Lua-таблица (не самый лучший формат конфига, согласен). Найдите и поменяйте следующие настройки:
      • channel — канал, к которому подключаться
      • nickname — ник бота в ирке
      • account — имя аккаунта бота в ирке (можно в nil поставить, если нет)
      • accountPassword — пароль от акка (также в nil поставить, если нет)
      • gameAdmins или ircAdmins — в таблицу впишите себя, чтобы можно было конфигать

     

    Всё. Мост поставлен.

     

    Команды

    Мост воспринимает команды. Чтобы выполнить команду, например pm on:

    • Пропишите в игре: #IRC: pm on
    • Пропишите в ирке: /notice @<имя канала> pm on (например, /notice @#cc.ru-server1 pm on).

     

    Список команд:

    • online — показать онлайн на другом конце моста.
    • pm on — разрешить с другого конца моста слать вам ЛС.
    • pm off — запретить слать вам ЛС.
    • pm — показать, могут ли вам послать ЛС (в ирке включено по умолчанию, а в игре выключено и надо включать самому).
    • msg <имя> <сообщение>: отправить ЛС юзеру на другом конце моста.
    • pm ignore list — показать список игнорируемых юзеров.
    • pm ignore add <имя> — добавить кого-то в этот список.
    • pm ignore del <имя> — вытащить кого-то из него.

     

    Админы могут выполнять ещё такие команды:

    • irc admin list — показать список админов в ирке.
    • irc admin add <имя> — добавить кого-то в этот список.
    • irc admin del <имя> — убрать кого-то из него.
    • mc admin list, mc admin add <имя> и mc admin del <имя> — аналогично, но работает со списком админов в игре.
    • irc whitelist list — показать список юзеров, которые могут слать сообщения в игру.
    • irc whiltelist add <имя> — добавить кого-то в список.
    • irc whiltelist del <имя> — убрать кого-то из него.
    • mc blacklist list, mc blacklist add <имя>, mc blacklist del <имя> — аналогично, но работает со списоком тех, чьи сообщения не будут слаться в ирку.
    • irc alias set <имя> <алиас> — установить алиас юзеру. Когда он будет писать сообщения в игру, его имя будет заменено на алиас.
    • irc alias get <имя> — показать алиас для юзера.
    • debug on — включить режим дебага. Мост будет писать весь трафик с IRC на экран. Полезно, чтобы узнать, почему тупит мост. Пароли будут показаны плейнтекстом, поэтому лучше оставить выключенным, хотя бы во время подключения.
    • debug off — выключить этот режим.
    • debug — показать, включён ли дебаг.

     

    Как это выглядит

    На мониторе будет рисоваться вот такое:

    QywGWm8.png

     

    Как можно догадаться, чтобы мост остановить, нужно нажать Ctrl-C.

     

    Ссылки

    Код на гисте: https://gist.github.com/Fingercomp/df483bc2cefa13e0422d656ae82495ac/

    • Нравится 6

  14. Допилил версию 1.1.0. Теперь либа умеет трекать юзеров на канале, их префикс, аккаунты, ники, иные данные о них, режимы каналов. Досье целое собирает.

     

    Скачать можно через hpm. Только он не работает. Поэтому альтернативный вариант: тык.tar. Распаковывать можно программой tar (тырить из oppm).

     

    Документация для 1.1.0 лежит здесь.

     

    P. S. Совсем забыл. Ещё поддержку capabilities сделал. Если кто-то вообще понимает, что это такое.

    • Нравится 5
    • Спасибо 1

  15. 8 часов назад, whiskas сказал:

    Хмм я даже не думал что так можна сделать в ОС. Допилил чутку код фингера что б заранить можна было.

    Прога выводит в консольку все сообщения з логов в том числе и новые в риал тайме.

    [...]

    Понятно, что прожка писалась на скорую руку и должна быть доделана. Подскажу, как именно.

    1. Нет смысла качать мегабайт логов, которые всё равно не влезут в консоль. Нужно вытащить последние строки, например алгоритмом, который я описал в прошлом посте.
    2. Не нужно досить сервер запросами. Добавить хотя бы os.sleep(5).
    3. Проверять, изменились ли логи, можно через ETag. Его сервер тоже посылает.
    4. Проверять, что #chunk > 0, смысла не имеет. Там есть 3 случая: nil, когда оборвано соединение, "", если просто пока нет ответа, или же строка с данными.
    5. Прога такая работать будет не более суток. Затем она перестанет обновляться. Думаю, ясно почему.
    • Нравится 3

  16. Не-е, нельзя быть таким пессимистичным. Всё можно, и вопрос решается очень легко. В HTTP/1.1 есть хедер Range, который позволяет скачивать файлы кусками. Кроме того, OC умеет посылать и получать хедеры.

    local socket = component.internet.request("https://logs.s7.mcskill.ru/Hitechcraft_Public_Logs/public_logs/Hitechcraft_Public_Logs/14-02-2020.txt", nil, {
      Range = ("bytes=%d-"):format(start)
    })
    
    local data = ""
    
    while true do
      local chunk = socket.read()
      
      if not chunk then
        break
      end
      
      data = data .. chunk
    end
    
    local _, _, headers = socket.response()
    
    print("Got: " .. #data)
    print("Content-Length: " .. headers["Content-Length"][1])
    print("Content-Range: " .. headers["Content-Range"][1])

    Вместо start подставить количество байт, уже прочитанных. Там указывается начало диапазона номером байта (начиная с 0), от которого нужно выдать ответ. См. доки.

     

    Поэтому тактика такая:

    1. Посылаем запрос с методом HEAD (4 параметр к component.internet.request), чтобы получить только хедеры.
    2. Читаем в хедерах значение Content-Length.
    3. Начинаем запрашивать куски файла с конца, пока не наберём нужно кол-во строк.
    4. После получения начальных строк запомним позицию последнего байта и дальше запрашиваем инфу после него.
    • Нравится 9

  17. На тайпскрипте я, конечно, не писал, но пробовал MoonScript. Это такой язык, который транспилируется в Lua. У него тоже есть классы, сахара всякие. Но я на нём больше писать не хочу.

    Выхлопной код получается страшный. Что не сильно способствует дебагу. А ещё он неоптимален. В том числе по размеру получающегося скрипта, и минификатор не сильно помогает.

    Здесь, видимо, всё то же. Так что для опенкомпов будет проще всё же писать на Lua.

    • Нравится 1
    • Одобряю 2
×
×
  • Создать...