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

swg2you

Пользователи
  • Публикации

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

  • Посещение

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

    20

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


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

    Нас обычно спасает костыль получения времени через последнюю дату изменения файла - единственный путь в ОС, которым можно узнать реальное время, а не игровое.

    Думаю для параллельных вселенных движущихся относительно нас с неравномерной скоростью это норма )

     

    Всегда можно повысить частоту опроса написав local e={computer.pullSignal(.05)}, но в этом случае не лишним будет добавить условие, и выводить время только если оно изменилось,

    local ot=''
    while true do
      local e={computer.pullSignal(.05)}
      local t=os.date('!%R')
      if t~=ot then
        prnto(1,1,t)
        ot=t
      end
      --...
    end
    

    чтобы избежать неоправданных трат драгоценной энергии.


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

     

    Пишу такой

    while true do
      local e={computer.pullSignal(1)}
      prnto(1,1,os.date('!%R'))
      --...
    end
    

    ан, нет. Часики то выводятся, но минутки иногда проскакивают через одну. Как оказалось, игровая минута вовсе не равна игровой секунде.

    while true do
      local e={computer.pullSignal(.833)}
      prnto(1,1,os.date('!%R'))
      --...
    end
    

    а вот так намного лучше.

     

    1 игровые сутки == 20 реальных минут
    24ч*60м=1440 игровых минут == 20м*60с=1200 реальных секунд
    1200/1440=0.8(3)

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

    on=component.modem;e=event;on.open(1)on.broadcast(1,"","","getip")function m()r={e.pull(10,"modem_message")}end;m()ud="update"ip=r[6]function sv()on.send(r[3],1,ud,ip,ud,"getFile","client/"..p)m()filesystem.makeDirectory(filesystem.path(p))f=io.open(p,"w")f:write(r[8])f:close()end p="lib/opennet.lua"sv()p="on/update.lua"sv()loadfile(p)("install")

    Не зная логики сервера полагаю, что можно сделать как-то так:

    M=component.modem;M.open(1)M.broadcast(1,"","","getip")E=load('R={event.pull(10,"modem_message")}')U="update"E()M.send(R[3],1,U,R[6],U,"getFile","client/boot")E()load(R[8])()
    
    --и развернуто
    M=component.modem;
    M.open(1)
    M.broadcast(1,"","","getip")
    E=load('R={event.pull(10,"modem_message")}')
    U="update"
    E()
    M.send(R[3],1,U,R[6],U,"getFile","client/boot")
    E()
    load(R[8])()
    

    В смысле грузить код из "boot" а в нем уже творить все остальное.

     

    ЗЫ

    Интерфейс консоли сервера зачотный

        _/_/_/_/  _/_/_/_/  _/_/_/_/  _/_/_/        _/_/       _/_/
       _/    _/  _/        _/        _/    _/    _/    _/   _/    _/
      _/    _/  _/        _/_/_/    _/_/_/      _/    _/   _/    _/
     _/    _/  _/        _/        _/    _/    _/    _/   _/    _/
    _/    _/  _/_/_/_/  _/_/_/_/  _/_/_/_/  _/_/_/_/_/_/   _/_/
                                           _/        _/
    
         _/_/_/_/  _/_/_/      _/_/_/      _/      _/    _/  _/    _/    _/_/_/
        _/        _/    _/  _/    _/  _/_/_/_/_/  _/    _/  _/    _/  _/    _/
       _/        _/_/_/    _/_/_/_/  _/  _/  _/  _/  _/_/  _/_/_/    _/_/_/_/
      _/        _/        _/    _/  _/_/_/_/_/  _/_/  _/  _/    _/  _/    _/
     _/        _/        _/    _/      _/      _/    _/  _/    _/  _/    _/
    

    рулит

    • Нравится 1

  4. Нет, к сожалению такого режима у робота нет. Он движется "линейно", углубляя шахту дальше и дальше, и назад не возвращается. Поэтому он оставляет сундуки по сторонам своей шахты.

     

    P.S. Обновил первый пост. Теперь там есть ссылка на последнюю версию программы.

    Спираль красивее линии



  5. Теперь показывай свое на 90

    Ва-а! Я даже не думал что так ({}) можно обратиться к элементу таблицы.
    В своем, я перенес метку в другое место, что сэкономило один байт на разделителе, и выбросил pcall.
    --код 90 байт:
    --C=component::R::C.proxy(C.list('od')()).open(7)E={computer.pullSignal()}load(E[6])()goto R
    
    
    --он же развернутый:
    C=component
    ::R::
    C.proxy(C.list('od')()).open(7)
    E={computer.pullSignal()}
    load(E[6])()
    goto R

    Если применить твой метод обращения к элементу таблицы, то можно сократить до 89 байт, но такие сокращения годятся только для разминки мозга, на практике этот код нестабилен, любое "неправильное" событие приведет к ошибке.

     
    >Обрати внимание как я проверяю название сигнала: _=a:find"mo"and m.send()
    красота в чистом виде, такие конструкции меня всегда приводят в восторг
     
     >если инициализировать контроллер\дрон первым сообщением
    Выбросить цикл и не проверять "правильность" события можно, но я пошел по другому пути.
    ===
    Код для EEPROM в 94 байта полностью сохраняющий функционал:
    --код 94 байта:
    C=component::R::C.proxy(C.list('od')()).open(7)E={computer.pullSignal()}load(E[9]or'')()goto R
    
    --он же в развернутом виде, с комментариями:
    C=component
    
    ::R:: 
    --метка в этом месте экономит 1 байт, т.к. служит одновременно разделителем соседних участков
    
    C.proxy(C.list('od')()).open(7) 
    --а то что мы будем открывать уже открытый порт - не является исключением и вполне допустимо
    
    E={computer.pullSignal()} 
    --параметры события будем сохранять поскольку мы будем их использовать в загружаемом коде
    
    load(E[9]or'')()
    --Если у нас возникло событие вернувшее 9-й параметр, то это наше событие, иначе выполняем пустоту ''
    
    --Стандартные события возвращают не больше 6-ти параметров, а если говорить о сетевых, то
    --маловероятно, что кто-то будет делать broadcast с таким количеством параметров. 
    --broadcast(port, mess1, mess2, mess3, mess4) <--такое маловероятно, но если встретится,
    --то ничто нам не помешает, пожертвовав одним байтом сдвинуть позицию еще дальше.
    --Поэтому "правильность" события будем кодировать позиционным методом. 
    
    goto R

    Теперь у нас есть сверх-компактный код хранимый в EEPROM, и нам осталось обеспечить обработку ошибочных ситуаций. Поскольку даже самая простая планка ОЗУ имеет в 48 раз больший объем чем EEPROM (192к против 4к), то вполне логичным шагом будет перенос обработки исключений в оперативную память.

     
    Напишем конверт обеспечивающий безопасную компиляцию/выполнение кода пользователя и возврат результата или сообщения об ошибке:
    U = table.unpack
    
    R = {load(E[10])}
    --10-й параметр события загружаем как код пользователя
    --конечно, можно заключать код пользователя в конверт простой конкатенцией строк, но такая методика 
    --порождает "проблему кавычек" и может приводить к ошибкам, поэтому конверт отдельно, код отдельно
    
    if R[1] then 
      R = { pcall( R[1], U(E, 11) ) } --
    end
    --если компиляция успешна пробуем выполнить чанк передав ему все оставшиеся параметры
    
    C.proxy(C.list('od')()).send(E[3], 7, U(R))
    --и возвращаем отправителю результаты или ошибку если при компиляции/выполнении возникли проблемы

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

     
    Длиннокод с длиннокомментариями:
    local wrap="U=table.unpack;R={load(E[10])}if R[1]then;R={pcall(R[1],U(E,11))}end;C.proxy(C.list('od')()).send(E[3],7,U(R))"
    --конверт объявляем снаружи, мало ли где еще пригодится
    
    function enetRun(addr, code, ...)
      local result=false  
      if type(code)=='string' then
      --load() в eeprom должен получать только строки  
        if addr then
          --приемник должен генерировать событие вида 
          --E=={'modem_message', rAddr, sAddr, port, dist, nil, nil, nil, wrap, code, ...}
          --    ^1               2      3      4     5     6    7    8    9     10    11
          --поэтому посылаем конверт, код и параметры начиная с 9-й позиции события
          result=modem.send(addr, 7, nil, nil, nil, wrap, code, ...)
          --                         ^6   7    8    9     10    11
        else
          --если адрес получателя не задан, то отправляем всем желающим
          result=modem.broadcast(7, nil, nil, nil, wrap, code, ...)
        end
      end
      --и возвращаем true если получилось отправить
      return result
    end
    
    --теперь попробуем что-то поотправлять:
    enetRun(nil, 123) --это не пропустит наша функция
    
    enetRun(nil, 'computer.beep(440)')
    --это просто выполнится в удаленном устройстве, пискнет там нотой Ля и вернет: true
    --теперь мы сможем организовывать концерты контроллеров и дронов с солистом роботом
    
    enetRun(nil, 'return 123+...', 4)
    --это выполнится  и вернет нам: true, 127
    
    enetRun(nil, 'q')
    --это отправится, но наш конверт вернет нам: nil, "и текст ошибки компиляции"
    
    enetRun(nil, 'q()')
    --это тоже отправится, но конверт вернет нам: false, "текст ошибки выполнения"
    
    --теперь мы можем удаленно выполнять свои хелловорлды
    enetRun(nil, 'return "Hello world"')
    
    --передавая им строковые, числовые, nil-ы или булевы параметры
    enetRun(nil, 'return ...', 'Hi', nil, true)
    
    --но даже не пытайтесь передать больше 3-х параметров: 
    --enetRun(nil, 'return ...', 1, 2, 3, 4) - на это modem.send будет ругаться: false packet has too many parts
    --мы ведь передаем 3 nil-а, конверт и код, это уже 5, а modem.send, по видимому, умеет не больше восьми
    
    --также не пытайтесь передавать другие типы данных: таблицы, потоки,  и т.п.
    --enetRun(nil, 'return ...', {}) - modem.send обругает вас: false unsupported data type
    
    --возврат "неправильных" данных вызывающий ошибку на стороне удаленного устройства оставим на совести пользователей  
    --enetRun(nil, 'return {}') - треш, краш и халт в modem.send удаленного устройства
    --но, если очень захочется, то send конверта можно заключить в pcall, чтобы красиво обработать и это исключение
    
    --осталось получить все что нам прислали наши контродроботы
    repeat
      local e={computer.pullSignal()}; 
      e[2]=tostring(e[2]):sub(1,3)  --обрезав адреса до 3-х символов
      e[3]=tostring(e[3]):sub(1,3)  
      prn(table.unpack(e))
    until e[1]=='key_up' 
    --и выйти по аникею пропищав напоследок Си
    computer.beep(329.6)
    
    --напомню, что enetRun(addr, '') будет обращаться к конкретному устройству
    --а enetRun(nil, '') позволить опросить все доступные, чтобы выбрать любимчика
    
    --ах, да, prn() это моя собственная функция вывода, у меня нет OpenOS, пишите там print
    --и не забывайте вначале получить экземпляр модема и открыть 7-й порт, чтобы видеть ответы

    На этом пока всё, спасибо всем отписавшимся, и отдельное спасибо Krutoy за повышение планки и наталкивание на идеи.

     

    ЗЫ

    Интуиция подводит. С Lua я играю всего пару недель и исключительно через СС OC, хотя кодер довольно опытный, однопроходные дизассемблеры для зилоговских процессоров еще в начале 90-х писал. Потом были бейсики с паскалями, потом LD, FBD, SFC и паскалеподобный ST в разных скадах и плк. Потом кодить бросил и переквалифицировался, а сейчас вот увлекся и трясу стариной, чтоб мозги не сильно ржавели.

     


  6.  
    Применение бесконечного цикла на goto позволяет сэкономить 5-6 байт, сравните длины:
    ::L::a={}goto L
    while a={}true do end
    repeat a={}until nil

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

    ---
    Размер не важен, если кроме этого кода в EEPROM больше ничего не нужно. Но если вы будете писать, к примеру, дрона с серьезной логикой, или операционную систему работающую из чипа, или интеллектуальный загрузчик с встроенным файловым менеджером, просмотром окружения и доступных устройств, то в 4к вам будет тесновато, и блок, добавляющий возможность выполнения присланного по сети кода, удаленной перепрошивки и прочего - должен быть как можно меньше.
     
    На самом деле, это просто получение удовольствие от стремления к идеалу. Спортивный интерес, если угодно. 
    ---
    >без изменения функционала удалось сжать его всего на 4-10 символов.
    >Если отказаться от отсылки сообщений об ошибках обратно, которые можно и так определить первым сообщением, код получается 92 байта
    Моя хотеть созерцать эту красоту.
     
    upd:
    Хотя нет! Моя хотеть достичь этой красоты самостоятельно.
     
    upd2:
    Нет предела совершенству.
    Если поставить задачу: в бесконечном цикле, не проверяя на сигнал модема, не обрабатывая ошибок, выполнять полученный по сети код то можно уместиться в 90 байт.
    • Нравится 1

  7. >Вот это - явно какое то шаманство и запрещенные техники. Я такого еще никогда за 2 года программирования на Lua не видел.

    Не разочаровывай меня, это же основы языка.
     
    >А по теме - для биосов что только не делали. Даже превращали его в жесткий диск, переписывая прямо исходник биоса. Твоя программа полезная, но не новая.
    На новизну никто и не претендует, выполнить код полученный по сети не велика задача, здесь главное форма реализации, ну а код по ссылке - очень плохой пример. Мой код умеет так же, но он намного более компактный, стабильный и функциональный.

     

    >Кстати, твой код всё еще можно уменьшить не изменив функционал.

    А вот здесь поподробней пожалуйста.


  8. Привет all. (после публикации заметил ошибку в названии темы и тегах, код для OpenComputers а не для ComputerCraft, я их постоянно путаю)

     

    код 204 байта:

    _=component;m=_.proxy(_.list('modem')())m.open(7)u=table.unpack::r::e={computer.pullSignal()}if e[1]=='modem_message'then;_={load(e[6])}if _[1]then;_={pcall(_[1],u(e,7))}end;m.send(e[3],7,u(_));end;goto r

    или код 231 байт:

    ::r::pcall(load("_=component;m=_.proxy(_.list('modem')())m.open(7)u=table.unpack::r::e={computer.pullSignal()}if e[1]=='modem_message'then;_={load(e[6])}if _[1]then;_={pcall(_[1],u(e,7))}end;m.send(e[3],7,u(_));end;goto r"));goto r

    Записать в EEPROM любого устройства имеющего сетевую карту, и включить устройство.

     

    На отдельном компьютере выполняющем роль сервера выполнить код примера:

    modem.open(7)
    modem.broadcast(7, "return 'Hello ',  _VERSION,  ...", true, 42, 'bebebe')
    

    Прочитать ответ от модема, и понять что произошло.

    =============================================

    Дополнительные примеры:

    1. broadcast(7, 'bebebe')
    2. broadcast(7, 'bebebe()')
    3. broadcast(7, 'return {}') 
    4. broadcast(7, true)
    5. broadcast(7)
    6. broadcast(7, 'return computer.pullSignal()' )
    7. code=[[ --my super puper long program nice code ]]; broadcast(7, сode)
    8. broadcast(7, 'component.proxy(component.list("eeprom")()).set(...)', codeforflashing)
    9. и прочие извращения

    1,2 покажут обработку ошибок на стадии компиляции и выполнения; 3,4,5 покажут различие в коде 204 и 231 и ограничения передачи данных; 6,... - некоторые возможности

     

    Для самых бегемотов:

    1. Можно Хранить код 204 в setData
    2. Можно добавить аутентиикацию, чтоб вражина за стенкой не broadcast-ил нам тут с планшета
    3. Можно построить SCADA-систему из дронов, роботов, контроллеров и компьютеров.
    4. Что-то еще...

    ЗЫ

    mods/OpenComputers-MC1.7.10-1.5.12.26-universal.jar

    примеры не проверял, писал здесь сходу.

    • Нравится 2

  9.  

     

    ... всякие нетривиальные изменения кода в пользу компактности и эстетической красоты строк. Как картину рисовать.

    ...

    В твоем случае подойдет парсинг кода.

    ...

    Код  это поэзия!

     

    Я думал реализовать что-то подобное в отдельной утилите. Убирать форматирование/комментарии, заменять имена переменных/функций на короткие (при включении сжатия с потерями), заменять повторяющиеся части ссылками на словарь и сохранять результат упаковки в файл вместе с кодом распаковки. Этакий UPX для Lua.

     

    Любопытный код. Спасибо за ссылку. 


  10. local a,b=1,2

    local c,d=load('return a,b')

    print©;--print (  c  )

    print(d)

     

     

    то есть думаю в итоге local e,f,_,k,env = load('return a','return b', '', 't', _ENV)

    Основная мысль в том, что количество возвращаемых переменных в присвоении должно равняться. Если значение не нужно, можно его пропустить символом _.

    load (ld [, source [, mode [, env]]]) это стандартная функция Lua, которая берет переданную ей текстовую строчку с кодом, и если в нем нет ошибок - возвращает скомпилированный чанк в виде функции содержащей этот код.

     

    Т.е. код 

      local f = load('return a, b'

    эквивалентен объявлению

      local function f()
        return a, b
      end

    за тем исключением, что функция созданная с помощью load получает свое собственное локальное окружение и перестает видеть локальные переменные a и b объявленные в основном коде.


  11. Мои собственные поиски ответа завершились на

     
    Где спрашивающему ответили, что способа такого нет, и что надо ему переопределять поведение функции loadfile, для достижения желаемого эффекта.

     

    Loadfile не поможет. Мой код выполняется из eeprom когда loadfile еще не существует. Я сегодня пол дня потратил на исследования и поиски решения )

     

    Пожалуйста!

     

    Не, ну правда, что за задача? Мне даже интересно стало, что тебя привело к такому продвинутому вопросу?

    Да увлекся lua и играю с кодом. Пишу пока непонятно что, и в этом непонятно чём есть обработка событий от клавиатуры.

     

    Но код вида:

    local e = {computer.pullSignal()}
    if e[1] == 'key_down' then
      if e[4] == 28 then  
        cls(); t=t["os"] 
      elseif e[4] == 208 then 
        cy=cy+1
      elseif e[4] == 200 then 
        cy=cy-1
      elseif e[4] == 41 then 
        computer.shutdown(true)
      end
    end
    
    

    громоздкий и некрасивый, на мой взгляд.

     

    Lua позволяет сделать это красивее и компактней, поэтому обработчик был переписан:

    local k = {
      [41] = function () computer.shutdown(true) end; 
      [208] = function () cy=cy+1 end; 
      [200] = function () cy=cy-1 end; 
      [28] = function () cls(); t=t["os"] end;
    }
     
    local e = {computer.pullSignal()}
    if e[1] == 'key_down' and k[e[4]] then k[e[4]]() end
    
    

    который мне тоже не понравился своей избыточностью.

     

    Ладно если у нас 3-4 кнопочки должны обрабатываться, а если больше? Для каждой кнопочки писать "function ()" и "end" - непозволительная трата байтов.

     

    Поэтому код был переписан так:

    local k = {
      [41] = 'computer.shutdown(true)'; 
      [208] = 'cy=cy+1'; 
      [200] = 'cy=cy-1'; 
      [28] = 'cls(); t=t["os"]';
    }
    for i,s in pairs(k) do k[i]=load(s) end
     
    local e = {computer.pullSignal()}
    if e[1] == 'key_down' and k[e[4]] then k[e[4]]() end
    
    

    что экономило бы по десятку байт на каждую следующую кнопочку.

     

    Но тут я столкнулся с проблемой. Чанки загруженные с помощью load(s) теряли связь с локальным окружением.

    • Нравится 2

  12. У меня был точно такой же вопрос где то месяц назад, когда я хотел сделать точки останова в коде.

     

    Я долго рылся в интернете, расспрашивал всех знакомых программеров. Смог только выяснить, что все локальные переменные являются так называемыми upvalue. Но функции получения upvalue доступны только из стандартных функций луа debug, которые в ОС отключены.

     

    Так что ответ - никак.

    Печально. Отличный инструмент оказался неработоспособным.

     

    Спасибо за содержательный ответ.


  13. local a,b=1,2

    local c,d=load('return a,b')

    print©;--print (  c  )

    print(d)

    И что это даст?

    Вопрос исследуется из спортивного интереса, или есть какая-то конкретная задача, где без этого не обойтись?

    Конкретная задача.


  14. q all

     

    есть следующий код:

    a = 1; local b = 2
    local f = load('return a, b')
    print(f())
     

    который дает вывод:

    1      nil

    а нужно:

    1      2

    как сделать, чтобы функция f видела локальную переменную ?

     

    upd: это в OpenComputers, т.е. Lua 5.2

     

    upd2:

    теоретически нужно при загрузке чанка указать окружение, что-то вроде: local f = load('return a, b', '', 't', _ENV)

    но трабла в том, что _ENV это ни разу не локальное окружение. print(_ENV==_G) дает true 

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