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

Лидеры


Популярный контент

Показан контент с высокой репутацией за 11.12.2018 во всех областях

  1. 25 баллов
    Для тех, кто спешит: Потестировать онлайн: https://ocelot.fomalhaut.me/ Скачать на комп и потестировать: Ocelot Desktop На форуме давно мелькают упоминания Ocelot. Это эмулятор OpenComputers, который находится в разработке примерно с 2015 года, был несколько раз переписан и наконец увидел свет в закрытом альфа-тесте зимой 2018. Я немного отвлекся на другие проекты (привет Stem), но теперь возвращаюсь к разработке Ocelot, и с гордостью предствляю вам тизер-анонс и, по совместительству, открытый альфа-тест Ocelot. Ещё один эмулятор? Да. Будем честны. Нормального эмулятора OpenComputers не существует. Те что есть - полны костылей, не совсем соответствуют реальному моду, сложны в установке, заброшены... и так далее. Ocelot - это решение всех этих проблем. Основная идея Ocelot - взять уже существующий код мода OpenComputers, тщательно отделить всё не нужное (Майнкрафт), затем осторожно переписать то что получилось с поправкой на реалии эмулятора. Благодаря этому, Ocelot эмулирует OpenComputers с ранее невиданной точностью. Вплоть до того, что в эмуляторе могут встречаться те же самые баги, что и в моде. Что он умеет? Практически всё. В перспективе. Ocelot позволяет воссоздать схему любой сложности из любого количества блоков - мониторов, компьютеров (любой конфигурации), проводов, модемов и прочих компонентов. Он позволяет управлять скоростью работы компьютеров, позволяет изменять "игровое" время, ставить его на паузу, сохранять состояние работы компьютеров и потом возобновлять работу с любого сохранения. Сейчас доступен базовый набор компонентов и блоков. Это кабель, корпус компьютера, APU/CPU, плашки памяти, видеокарты, дата-карты, EEPROM, дискеты, жесткие диски (managed и unmanaged режимов), интернет-карта, линкед-карта, сетевая карта (проводная и безпроводная), редстоун-карта / блок и монитор. Список будет расширяться. В перспективе будет эмуляция всех блоков и компонентов стандартного OC, роботов, дронов, микроконтроллеров, серверных стоек, плюс эмуляция адаптера и интеграции с ванильными блоками и блоками других модов. Что можно потрогать? Ocelot задуман как модульный проект. А именно: Ocelot Brain Основа эмулятора - это библиотека Ocelot Brain. Она написана на Scala и может быть подключена к любому другому проекта на Scala (и, может быть, Java). Ocelot Brain - это как раз переработанный код OpenComputers в компактной и удобной форме. Отвечает за всю эмуляцию кода и компонентов, а также сохранение / загрузку проектов. Вы можете использовать его для своих проектов, можете помочь с разработкой и патчами. Проект открыт и доступен по адресу: https://gitlab.com/cc-ru/ocelot/ocelot-brain На данный момент Ocelot Brain актуален версии OpenComputers 1.7.7. Ocelot Online На основе проекта Ocelot Brain, в качестве демонстрации его возможностей, создается проект Ocelot Online. Ocelot Online это эмулятор OpenComputers в виде сайта. Да. Всё что вам нужно для его запуска - это открыть сайт. Ссылка: https://ocelot.fomalhaut.me/ Исходный код тоже доступен: https://gitlab.com/cc-ru/ocelot/ocelot-online Поскольку проект пока находится в альфа-релизе, большая часть возможностей закрыта. Доступен только один монитор на всех, который позволяет взаимодействовать с уже настроенным демо-проектом. Конфигурация проекта: Креативный корпус, CPU T3, видеокарта T3, две планки памяти T3.5, managed жесткий диск T3, unmanaged жёсткий T3, интернет карта, редстоун карта T2, дисковод с дискетой Open OS, монитор T2, клавиатура и EEPROM с Advanced Loader от товарища Luca_S. Отличия от стандартного OpenComputers: * В OpenOS уже установлен HPM. Благодаря этому можно быстро ставить разные программы через hpm install. * Вставка текста заменена с Insert на Ctrl + V. Браузер не дает изменить этот хоткей. * В редакторе edit кнопка выхода заменена на Ctrl + E. Стандартная комбинация юзается браузером для закрытия вкладок - и переопределить её нельзя по соображениям безопасности. * Вместо OpenOS EEPROM используется Advanced Loader. Это сделано для удобства и наглядности. * Не работает лок на пользователя - по понятным причинам. Ocelot Online должен так же работать на смартфонах. Однако возможно придется отключить T9 - он портит эвенты клавиатуры. В разработке находится более сложная версия, где все получат возможность зарегистрировать аккаунт и создавать личные проекты любой конфигурации. Но это дело будущего. Ocelot Desktop Это классический вариант эмулятора Ocelot в виде программы, которую можно скачать и запустить на любой операционной системе, где есть Java. Построен на Ocelot Brain и библиотеке LWJGL (как и сам майнкрафт). Разработкой занимается товарищ @LeshaInc. Протестировать проект, сообщить о багах и поддержать разработчиков можно в топике Ocelot Desktop: Альфа-тест Итак, дорогие пользователи, пишите ваши хотелки, сообщайте о багах, обо всем что работает не так как должно, и как в оригинальном OC. Я, со своей стороны, постараюсь проект не забрасывать, развивать и своевременно (или не очень) обновлять. Благодарности Над проектом также работали: @LeshaInc, @Laine_prikol, @Fingercomp и @MeXaN1cK. За что им огромное спасибо и респект. Не забудем также всех, кто помогал с альфа-тестированием, Сангара - за чудесный мод, и мейнтейнеров OpenComputers за то что его не забросили. Enjoy!
  2. 22 балла
    Объяснять тут нечего. Качаем jarник (нужна как минимум Java 8), запускаем, пользуемся продвинутым эмулятором. Ссылка на jarник: https://cc-ru.gitlab.io/ocelot/ocelot-desktop/ocelot.jar О багах сообщать на https://gitlab.com/cc-ru/ocelot/ocelot-desktop/-/issues Фичи: поддержка всех карточек OC, полное сохранение луа стейта и конфигурации воркспейса, ограничения по памяти, вызовам компонентов, и т.д. и т.п. Все основано на реальном моде. В разработке участвовали: LeshaInc (фронтенд на LWJGL), Totoro (бекенд), rason (дал живительный толчок проекту), разработчики OpenComputers (стырили у них текстуры и код мода).
  3. 16 баллов
    Решил написать свой мультизагрузчик. Возможно, он похож на тот, что поставляется с комплектом MineOS, так что в целом является неплохой заменой стандартного биоса. Установка: Для OpenOS, просто запустите эту команду: wget -f https://raw.githubusercontent.com/BrightYC/Cyan/master/installer.lua /tmp/installer.lua && /tmp/installer.lua Для MineOS же есть приложение в местном AppMarket, под названием Cyan BIOS. Там тоже довольно элементарно. Исходники лежат тут: https://github.com/BrightYC/Cyan Что он может? Возможность загрузки/доступа к загрузчику по "белому" списку Lua-интерпретатор Возможность подключения компонентов "на лету" Возможность отформатировать/переименовать файловую систему Загрузка по интернету Доступ по белому списку: Ограничивает возможность управления биосом (на программном уровне), так же, если установить require user input, биос будет ждать нажатие от пользователя в белом списке. Shell: Позволяет выполнять некий код "на лету". Имплементированы следующие функции: print(...) proxy(componentName: string): component proxy or nil sleep([timeout: number]) Демонстрация: P.S Огромное спасибо @Fingercomp за лекцию по экранированию символов.
  4. 16 баллов
    quarry Для тех, кто спешит https://pastebin.com/1m7k9F01 или pastebin get 1m7k9F01 q Основное преимущество этой копалки - минимум телодвижений со стороны игрока. Всё что нужно это поставить робота, дать ему кирку и нажать q По умолчанию робот выкопает карьер 16/16 до бедрока. Можно указать произвольные размеры ширины (x) и длинны (y) не кратной 3 или 2, робот с координат не собьётся например q 31 17 Можно поставить сундук сзади робота, положить туда бур, уголь, робот в таком случае будет приносить руду в этот сундук, брать из него уголь для заправки, складывать разряженный инструмент и брать заряженный. Можно поставить робота на поверхности, а копать он будет глубоко под землей принося руду на поверхность, для этого нужно указать на сколько блоков ему опустится в низ например q 20 16 40 - - 20 (ширина) 16 (длинна) 40 (опустится в низ на 40 блоков) минимальные требования: корпус второго уровня (золотой) + процессор второго уровня улучшение инвентарь (лучше 2 или 3) контроллер инвентаря улучшение парение генератор контейнер для апгрейда, если хотите использовать чанклоадер пример сборки: основной алгоритм: Копает змейкой квадрат проходя три слоя блоков за один подход, пока не выкопает весь объём до бэдрока или не упрётся в не разрушаемый блок, при этом вернётся к старту и сообщит о проблеме, также предложит продолжить копать с последней позиции. Заправляется выкопанным углём из инвентаря и сундука на старте, если энергии не хватает, а уголь в генераторе есть, то возвращается к сундуку и заряжается. При наличии зарядчика на старте, робот ожидает полной зарядки батареи, не смотря на количество угля в генераторе. Сортирует выкопанный лут согласно чёрного списка, мусор выкидывает а инвентарь уплотняет. Умеет работать с электроинструментом, если он разрядился то сначала берёт запасной в инвентаре, а затем в сундуке на стартовой позиции, разряженный складывает в сундук. Если на старте слева от робота стоит зарядчик для электроинструмента, то робот будет его заряжать, включая запасной инструмент Если хотите, вместо обычного сундука можно поставить ender chest и у себя на базе сделать зарядчик для электроинструмента на другом роботе, с помощью этой программы https://pastebin.com/mtKbYn42 или pastebin get mtKbYn42 c список мусора можно настроить под ваши требования на строке 35 требуются только названия блоков, получить их можно тем же роботом с улучшением контроллер инвентаря, и этой программы https://pastebin.com/au9etcfF или pastebin get au9etcfF i Если добавить в робота улучшение опыт то у него будет огромный запас энергии. робот прокаченный на 10 уровней, имеет заряд батареи 70500 против 20500 по умолчанию. робот прокачивается сам когда копает, но если хотите вы сами можете его прокачать, скармливая ему зачарованный лут. прокачать его можно с помощью этой программы https://pastebin.com/ZKFw0Lst или pastebin get ZKFw0Lst e не знаете где взять много зачарованного лута, робот рыболов вам в помощь https://pastebin.com/RPLWqKTZ или pastebin get RPLWqKTZ r краткая статистика с разными инструментами: размер карьера 16/16, высота 70, уровней 22 по 3 слоя блоков, примерно 768 блоков на уровень. испытательный полигон: Программа доработана для нормальной работы на лагающем сервере p.s. отладка велась весьма продолжительное время, все возможные баги были отловлены, но только массовое использование исключит их полностью, так что пишите, не стесняйтесь. скриншоты приветствуются На основе этого карьера было создано несколько модификаций под различные условия Если сундук в привате, то у робота нет прав работать с инвентарём сундука и зарядчика инструмента
  5. 16 баллов
    HoverHelm — сетевые диски для тех, у кого нет жёсткого: дронов, микроконтроллеров. Вставляем диск в сервер — через модемы клиенты получают к нему доступ. И пару вкусных фич вдобавок. Фичи и преимущества по сравнению с голым EEPROM Сетевой жесткий диск: можно забыть про ограничение в 4кБ можно делать программы модульными легко обновить программу, не нужно перешивать каждого дрона за всю игру может понадобиться скрафтить только один жесткий диск Удаленный терминал: не нужны клава и моник, больше слотов в роботах централизованное управление всех ваших дронов Обратная совместимость: старые eeprom-программы работают без изменений Какие-то другие очевидные плюсы, про которые я забыл Прогресс разработки Система запускается и работает Виртуальный диск с доступом к папке на жестком диске сервера Удаленный терминал для запуска программ на устройствах Конфигурация Логирование Связь через сетевую и связанную карту Инсталлятор Виртуальный гпу Связь через интернет-карту (Stem) Сохранение имен устройств Удаленные терминалы Минимальные системные требования Конечное устройство Сервер Инструкция по установке Установка сервера: Установите OpenOS Выполнить команду pastebin run xh61Yx8a Отредактируйте открывшийся конфиг Добавьте желаемые к использования сетевые и связанные карты по образцу Можно настроить пути расположения пользовательских папок и папки клиентского ядра Установка клиента: Запустите HoverHelm server hoverhelm/main.lua Выполните команду prepare_eeprom <имя устройства> <адрес серверного модема> <порт> <адрес клиентского модема> Если на сервере одна сетевая карта, вместо адреса её можно вписать тип (modem или tunnel). Адрес клиентского модема можно убрать вообще, если на клиенте только одна сетевая карта. Инструкция по использованию Запустите сервер HoverHelm командой hoverhelm/main.lua Запустите устройства, просто включив их при первом запуске каждого устройства будет создана его пользовательская папка в /home/hoverhelm/devices/<deviceName>/ можно смонтировать по этому пути отдельный диск средствами OpenOS в терминале сервера и в файле лога устройства повится строка <deviceName> started, сигнализирующая о готовности устройства файлы, общие для всех устройств лежат в /home/hoverhelm/device_core/ (coreRootFolder в конфиге) файлы, специфичные для конкретного устройства лежат в /home/hoverhelm/devices/<deviceName>/ (userRootFolder в конфиге) В терминале сервера выполните deviceName>device-program-name args, чтобы выполнить на устройстве deviceName программу device-program-name с аргументом args программа с именем test будет искаться по пути /test.lua и /programs/test.lua, относительно виртуальной фс устройства Из коробки пока доступна только программы reboot и lua В терминале сервера выполните hide, чтобы свернуть HoverHelm server не прерывая его работу можно будет открыть его той же командой для продолжения работы с терминалом Ссылки Гитхаб: https://github.com/hohserg1/HoverHelm
  6. 16 баллов
    Некоторое время назад я публиковал программку - интернет-мост Stem. Он очень простой в использовании, но к сожалению пока не лишен некоторых глюков. А кроме того, у него есть недокументированные возможности. Предлагаю сейчас поиграться с одной такой тайной фичей. Это будет интересно тем, кто немного знаком с HTML/CSS/JavaScript. В чём суть? Всё просто. У Stem есть веб-клиент. То есть если вы зайдете по адресу https://stem.fomalhaut.me (например), вы увидите окно, которое пригласит вас ввести ID канала и початиться прямо с сайта. После ввода ID канала, вас перекинет на страничку с "чатом", где вы сможете посылать сообщения вашему компьютеру/роботу в майнкрафте. Адрес этой странички будет выглядеть примерно так: https://stem.fomalhaut.me/channel?id=test Где test это ID вашего канала. Когда робот будет вам отвечать, это будет видно в логе. Примерно так: А теперь - тайная фича. Находясь на страничке канала, как на скриншоте, вы можете приписать к адресу дополнительный флаг: &render=true. Полный адрес станет выглядеть примерно так: https://stem.fomalhaut.me/channel?id=test&render=true И начиная с этого момента, все входящие сообщения от компьютера в OpenComputers будут не отрисовываться в лог, а попадать в JS функцию eval(...). Что это значит? Те кто имел дело с JS уже наверное поняли все последствия. Но я поясню. Это значит, что компьютер OpenComputers может прислать сообщение с JS кодом, и сайт Stem этот код выполнит. А это значит, что вы можете творить на сайте вообще всё что угодно. Вы можете удалить интерфейс, показать вместо него новый, скачать что-то с инета, запустить игрушку... да хоть майнить крипту. Поиграем Ничего вредоносного мы делать конечно сейчас не будем. Вместо этого, традиционно, попробуем сделать так, что сайт Stem превратится в сайт для управления светом в нашем доме в Майнкрафте. Шаг первый. Подготовим дом. Тут всё просто - стандартный компьютер, с интернет картой, от него кабель к I/O блоку, а на блоке - подопытная лампа. На компьютер ставим Stem. Например, ставим HPM такой командой, pastebin run vf6upeAN И потом пишем: hpm install stem Нажимаем Enter и готово. Шаг второй. Нам нужен сайт. Усложнять не будем, и наш революционный дизайн будет выглядеть так: Что нам нужно сделать, чтобы сайт Stem превратился в то что нам надо? Методом тыка, через консоль браузера определяем, что достаточно выполнить такой код: document.body.innerHTML = " <style>button:active { background: #fac700; }</style> <button onclick=\"sendMessage(\'toggle\')\">Toggle Light</button> "; document.body.style.alignItems = "center"; Первой строкой мы просто заменяем всё содержимое тега <body> на то что нам надо. Я добавил еще немного косметики в виде стиля для кнопки. Вторая строка - тоже косметика, я просто поправил стиль на теге <body> чтобы кнопочка была посередине. Один важный ньюанс - на кнопке повешено свойство onclick с кодом sendMessage("toggle"). Таким образом, когда пользователь тыкнет по кнопке, мы отправим сообщение обратно в OpenComputers. Функцию sendMessage нам дает веб-клиент Stem. Ей можно безвозмездно пользоваться в своих целях. Шаг третий. Соединяем это все вместе. Теперь надо набросать программку для нашего компьютера в Майнкрафте, чтобы он выслал подготовленную JS-"бомбу" по нашему сигналу. Эники, беники... выходит что-то такое: local event = require('event') local stem = require('stem') local side = require('sides') local com = require('component') local redstone = com.isAvailable('redstone') and com.redstone or nil local channel = "test" local layout = [===[ document.body.innerHTML = "\ <style>button:active { background: #fac700; }</style>\ <button onclick=\"sendMessage(\'toggle\')\">Toggle Light</button>\ "; document.body.style.alignItems = "center"; ]===] local lampSide = side.top local lampTurnedOn = false print("Connecting to the #" .. channel .. " STEM channel...") local server = stem.connect('stem.fomalhaut.me') server:subscribe(channel) print("Done.") print("Press [q] to quit.") while true do local name, a, b = event.pull() if name == "stem_message" then local message = b if message == "connect" then print("Someone wants to connect - sending the layout...") server:send(channel, layout) elseif message == "toggle" then if redstone ~= nil then if not lampTurnedOn then redstone.setOutput(lampSide, 16) lampTurnedOn = true else redstone.setOutput(lampSide, 0) lampTurnedOn = false end end end elseif name == "key_down" then local char = b if char == 113 then break end end end server:disconnect() Я думаю тут большая часть очевидна и понятна. Мы подключаемся к серверу Stem, подписываемся на нужный канал и внимательно слушаем входящие сообщения. Когда пользователь присылает сообщение connect, мы отправляем ему подготовленный код, который мирно лежит в переменной layout. Этот код прилетит к пользователю, и, если пользователь смотрит страницу с включенной опцией render=true, код сработает и перерисует страницу. Если опция будет отключена - он просто увидит код в логе, как простое сообщение. Шаг четвертый. Тестируем. Откроем наш канал по ссылке: https://stem.fomalhaut.me/channel?id=test&render=true Появится пустой лог. Запустим программу в OpenComputers. Она отрисует наше приветствие, что-то вроде: Сonnecting to the #test STEM channel... Done. Press [q] to quit. Теперь пишем на сайте команду connect. Если мы все сделали правильно, и Stem не заглючил, интерфейс сайта исчез, и вместо него появилась наша кнопка. Нажимаем её. Вуаля! Дома зажегся свет. Постскриптум Это конечно же недокументированная хакерская фича, которую можно считать альфа-версией. Кроме того в Stem сейчас есть неуловимый баг, из за которого сообщения иногда не доходят. Не пугайтесь. Если кто-нибудь предоставит мне сценарий (прямо по шагам), при котором 100% срабатывает баг - буду очень рад и пофиксить его будет легче. С помощью описанной фишки можно придумать много хрени. Я показал самое простое что пришло в голову. Уверен, ваша фантазия будет покруче моей ) Пишите баг-репорты или пожелания по фиче, да и просто комменты в эту тему, либо мне в любой канал связи, где я онлайн.
  7. 16 баллов
    (проект в разработке) Цель данного проекта - создание нейросети и условий для ее самообучения. В идеале, это будет бомжовый робот минимальной комплектации (возможно даже без жесткого диска), который в зависимости от окружающих условий будет определять свою полезность. Также, он будет "программироваться" снаружи с помощью окружения оставленного другими роботами, формирующими "улей". Апогеем будет саморепликация. Кроме достижения цели, важен и процесс. Наблюдать за самообучением и выбором действий у нейросети очень интересно. Нейросеть изнутри: Мигающие точки - нейроны, линии - синапсы (связи), справа эмулятор мира майнкрафт на движке Love 2d. Нейросети устроены похожим образом. Есть входные значения, скрытые слои и выходные значения. В моей программе робот собирает информацию вокруг себя. На вход подается: Есть ли блоки над, под и перед ним Насколько много клеток он "разведовал" за последнее действие Его позиция в виде значений x,y,z от 0 до 1 Иногда я тестирую со значениями стороны куда он смотрит, возможностью двигаться, расстоянию до последней удачной копки Нейросеть "думает" и выдает 5 значений, которые соответствуют действиям движения 1) вперед, 2) вниз, 3) вверх, 4) вправо, 5) влево. Робот выполняет большее из этих значений. Далее, после действия, я вычисляю коэффициент полезности r [0..1] для этого действия. -- swingSucces: количество вскопанных блоков за действие [0..3] -- input.exploreSucces: коэффициент разведки [0..1], где 0 - уже разводовал все 3 блока, 1 - впервые проверил эти 3 блока -- logic(input.sweetsD < input.old.sweetsD): расстояние до последней удачной копки увеличилось 0, или уменьшилось 1 r = (swingSucces + input.exploreSucces + logic(input.sweetsD < input.old.sweetsD)) / 5 Нейросеь обучается, и в выбранное выходное значение приписывается r a остальные уменьшаются\увеличиваются на 1-r for i=1, #output do output[i] = switch i when actKey (output[i]+val^2)/2 when rndKey (output[i]+(1-val))/2 when oppositeKey (output[i]+(1-val))/4 else output[i] nn:propagate(output) Теперь матан. В начале я использовал обычный Персептрон, найденный готовый на ЛУА, но который мне пришлось править. Проблема в том, что он не может учитывать последние состояния. Нейросети с памятью называются "Long short-term memory" или LSTM, где каждый нейрон выглядит вот так: Найдя библиотеку synaptic.js, хитрыми путями я извлек из нее готовую LSTM сеть, которая получилась на 53 000 строк. Но это не проблема, если зайдет, я перепрограммирую ее на LUA. Текущее состояние сети - отстой. Я неправильно ее обучаю, так как не знаю какие значения выдавать для "обратного распространения ошибки". Круча разные коэффициенты, у меня получилось научить ее двигаться по спирали, как карьерный робот. Сейчас, когда я добавил во входные значения расстояние до последней удачной копки, он научился "кушать" как яблоко, но с огромный количеством лишних действий. В самом майне блоков намного больше и это выглядит вот так. На первом скрине работал 1 робот, на втором штук 6. Сейчас в их действиях слишком много шума. Я специально не даю им кирки, что бы они не унеслись в бесконечность. Фидбек Мне нужно помощь. Подскажите, что лучше подавать на вход, и как обучать сеть. Главная проблема, что для обучения я должен указать какие значения должны быть на выходах при текущих условиях, а я и сам не знаю. Обычно, робот теряется в пустом пространстве и я не знаю какое действие ему казать как "единственно правильное". А так, буду сюда отписываться и отчитываться о процессе разработки. Update 20.06.19, веб-версия эмулятора
  8. 15 баллов
    Предисловие: Недавно, по накурке, мне пришла идея написать браузер для OC. Браузер это конечно хорошо, но для его работы нужна сеть. Первое, что вспомнилось, это OpenNet. Но у него нашлось некоторое некоторое количество серьёзных недостатков(про них позже). Не найдя альтернативы, я пришёл к выводу, что нужно сделать новую сеть, которая будет похожа на OpenNet, но в которой не будет таких недостатков, какие есть в OpenNet. Собственно, о каких недостатках я говорю: 1) Ограниченность в рамках одного сервера. Для того что бы от сети был хоть какой то минимум пользы, ей нужны пользователи. Тяжело найти сервер, где хотя бы пяти людям будет интересна тематика OC. 2) Требования к структуре сети. Три уровня, не больше, не меньше. Первые два - роутеры, третий - клиенты. Роутеры первого уровня соединены с роутерами второго посредством спаренных карт, и с клиентами только через модемы. 3) Низкая надёжность. Стоит из роутера второго уровня вынуть спаренную карту, он сразу превратится в кирпич. 4) Сложность в развёртывании. Два вида роутеров, три вида библиотек. Отсутствие документации. Захотел сеть на даче - нужно участие владельца "коренных" роутеров. Ключевые особенности: Собственно сеть должна обладать такими свойствами: 1) Возможность бесшовно соединять несколько игровых серверов. 2) Иметь максимально простую структуру. 3) При разрыве связи между двумя частями сети, сеть должна "раскалываться" на две, а не ломаться. 4) Сеть должна иметь максимально простую структуру, и иметь хорошую документацию Процесс разработки: Обозначив основные моменты, я потихоньку начал писать сеть. За основу брал OpenNet. С кодом можно ознакомится на складе грязи(тут сама сеть, и ещё несколько программ, которые я использую при разработке). Что сделано: Собственно, сама библиотека, реализующая связь между узлами. Роутер. Библиотека, позволяющая работать в сети программам, заточенным под OpenNet. Браузер и HTTP-сервер, из за которого собственно, всё и началось. Вернее первая его версия. Теоретически(на практике мост, который я хочу использовать, себя странно ведёт) есть возможность устанавливать соединения между игровыми серверами. Инсталлер Что нужно сделать: DNS Интернет-сервер, и клиент под него Чат (клиент, сервер) Нормальный интерфейс. Шифрование пакетов Как это всё безобразие выглядит: И так, допустим, мне очень нужно развернуть сеть. Что мне для этого нужно: Ставим три компьютера. Один будет роутером, и два клиента. Пусть один клиент подключается к роутеру через модем, а второй через спаренную карту. Начинка роутера: И клиентов: Теперь ставим на все компьютеры OpenOS и файлы с репозитория. Можно воспользоваться инсталятором: wget https://raw.githubusercontent.com/AlexCatze/RacoonNet/master/installer.lua installer Начнём с настройки роутера. Запускаем "routconf", и первым делом конфигуратор попросит указать, через какую карту роутер будет подключатся к сети. Так как сети у нас пока нет, пропускаем этот момент. Теперь у нас спрашивают, какие карты будут использоваться для подключения клиентов к роутеру. Отвечаем: Теперь можно и запустить роутер. Пишем "router", и видим картину: Роутер ругнулся, что не имеет выхода в сеть, ну оно и понятно. Теперь настраиваем клиентов, вводим "rnconfig" на каждом из них: Сеть настроена, но что теперь с ней делать? Запускаем на одном компьютере "chat_server", на другом "chat <ip сервера>" . И можем поговорить сами с собой. Или можно посмотреть на браузер. Поднимаем сервер, "webserver", браузер "wr" и наслаждаемся. К стати, сеть называться RacoonNet(над названием я не заморачивался). Собственно, зачем эта тема: Т.к. изначально я собирался сделать только браузер, и вообще, это мой первый проект на Lua, я сам долго не выдержу. Понимаю что сеть сейчас практически является копией OpenNet`а. Я ищу людей, которые захотят помочь мне. Как то так.
  9. 15 баллов
    Проект больше не поддерживается.
  10. 15 баллов
    Среди всех компонентов в OC у интернет-платы самый ужасный API. Неудивительно, что правильно использовать его умеют немногие. Даже за Vexatos мне приходилось чинить tape.lua — программку для записи кассет. Плюс в ирке нередко спрашивают, как отправить HTTP-запрос на сервер. Значит, пришло время написать, как же всё-таки использовать интернет-плату. Гайд строится на следующих предположениях (сорри за педантизм): Вы умеете прогать на Lua, в том числе знаете о двух основных способах возвращать ошибку. Вы писали уже программы для OpenComputers, которые использовали API этого мода или OpenOS, особенно либу event. Вы как-то использовали (или пытались использовать) интернет-карточку в программах. Секции 1, 3: вы понимаете основные принципы HTTP. Секции 2, 4: вы понимаете, как пользоваться TCP-сокетами и зачем (не обязательно в Lua). Секция 4: вас не смущает setmetatable и вы понимаете, как делать ООП на прототипах. Секции 2, 4: у вас OC 1.6.0 или выше. Секции 1, 3, 5: у вас OC 1.7.5 или выше. Текущая версия мода — 1.7.5, а в новой ничего не изменилось. У инет-карты есть две разных фичи — HTTP-запросы и TCP-сокеты. Кратко пробежимся по API и затем разберём детальнее применение. Рассматривать я буду API компонента: часто используют require("internet") — это не компонент, а обёртка. 1. Отправка HTTP-запросов: component.internet.request У этого метода 4 параметра: URL, на который надо послать запрос. На всякий случай, URL начинается со схемы (http: или https:), после которого идёт адрес хоста (например: //localhost, //127.0.0.1, //[::1], //google.com:443), за которым следует путь (/my-file.html). Пример: https://computercraft.ru/blogs/entry/666-profiliruem-programmy-pod-oc/. Данные запроса. Оно же тело запроса. Если мы отправляем GET/HEAD-запрос, то этот аргумент надо установить в nil. Хедеры, которыми запрос сопровождать. Можно поставить nil, тогда там по минимуму дефолтные подтянутся. Иначе передавать надо таблицу. Её ключи — это названия хедеров. Например, {["Content-Type"] = "application/json"}. Метод запроса. Если же этот аргумент не передавать, то возьмётся по дефолту GET или POST: это зависит от того, пуст ли аргумент 2 или нет. Если возникла ошибка, метод вернёт nil и сообщение об ошибке. Если же всё нормально, то метод вернёт handle — табличку с функциями. Вот что это за функции: handle.finishConnect() — проверяет, подключены ли мы к серверу. Если да, то вернёт true. Если к серверу ещё не подключены, то вернёт false. Если же возникла ошибка (например, 404 вернул сервер или закрыл соединение), то вернёт nil и сообщение об ошибке. Например, nil, "connection lost". В доках написано, что функция ошибку пробрасывает. На самом деле нет: она вообще не бросает исключения. handle.response() — возвращает мета-данные ответа с сервера. Если соединение ещё не установлено, вернёт nil. Если возникла ошибка, вернёт nil и сообщение об ошибке. Например, nil, "connection lost". В противном случае возвращает 3 значения: Код ответа (например, 200). Статус (например, "OK"). Таблицу с хедерами, которые отправил сервер. Выглядит примерно так: {["Content-Type"] = {"application/json", n = 1}, ["X-My-Header"] = {"value 1", "value 2", n = 2}}. Выпишу отдельно, что значения таблицы — это не строки, а ещё одни таблицы. handle.read([n: number]) — читает n байт (если n не задано, то сколько сможет). Если компьютер ещё не успел получить данные, то отдаст "". Если возникла ошибка, то выдаст nil и сообщение об ошибке. Например, nil, "connection lost". Если сервер закрыл соединение, то вернёт nil. В противном случае отдаст строку с частью ответа. handle.close() — закрывает соединение. 2. TCP-сокеты: component.internet.connect У метода есть 2 параметра: Адрес хоста. Например, 127.0.0.1. Здесь также можно указать порт: google.com:80. Порт. Если в первом аргументе порта нет, то второй параметр обязателен. Если возникла ошибка, он также вернёт nil и сообщение. Иначе возвращает handle — табличку с функциями. Вот такими: handle.finishConnect() — то же, что и выше. handle.read([n: number]) — то же, что и выше. handle.write(data: string) — отправляет data по сокету на сервер. Возвращает число переданных байт. Если соединение не установлено, это число равно 0. handle.close() — то же, что и выше. handle.id() — возвращает id сокета. 3. Как правильно отправить HTTP-запрос на сервер и получить ответ Чтобы было интереснее, реальная задача: написать аналог pastebin, только вместо пастбина использовать https://clbin.com/. Особенности: Для взаимодействия с сайтом нужно отправлять HTTP-запросы: GET и POST. Это всё OC умеет. Чтобы скачать, достаточно простого GET по ссылке. Это можно сделать даже через wget. А вот чтобы отправить файл, надо использовать MIME-тип multipart/form-data. OC не умеет из коробки такие формы отправлять. Мы напишем минимальную реализацию, которая бы нас устроила. Не забываем, что этот MIME-тип нужно установить в хедер. При этом мы хотим красиво обработать все ошибки и не допустить ошибок сами. Таким образом, использовать будем практически все фичи. 3.1. multipart/form-data Порядок особенностей нам не важен, поэтому начинаем с самого скучного. Сделаем функцию, которая принимает данные и обрамляет их согласно формату multipart/form-data. local function generateBorder(str) local longestOccurence = nil for match in str:gmatch("%-*cldata") do if not longestOccurence or #match > #longestOccurence then longestOccurence = match end end return longestOccurence and ("-" .. longestOccurence) or "cldata" end local function asFormData(str, fieldName) local border = generateBorder(str) local contentType = "multipart/form-data; boundary=" .. border return ([[ --%s Content-Disposition: form-data; name="%s" %s --%s--]]):format( border, fieldName, str, border ), contentType end Так как это не туториал по интернет-стандартам, вдаваться в детали реализации не буду. С помощью asFormData можно содержимое файла превратить в тело HTTP-запроса. Мы будем вызывать asFormData(str, "clbin"), ибо этого требует сайт. Кроме того, эта функция нам передаст значение хедера Content-Type. Он нам понадобится. 3.2. Взаимодействие с сайтом Напишем теперь функцию — обёртку над component.internet.request. local function request(url, body, headers, timeout) local handle, err = inet.request(url, body, headers) -- ① if not handle then return nil, ("request failed: %s"):format(err or "unknown error") end local start = comp.uptime() -- ② while true do local status, err = handle.finishConnect() -- ③ if status then -- ④ break end if status == nil then -- ⑤ return nil, ("request failed: %s"):format(err or "unknown error") end if comp.uptime() >= start + timeout then -- ⑥ handle.close() return nil, "request failed: connection timed out" end os.sleep(0.05) -- ⑦ end return handle -- ⑧ end Эту функцию можно прямо брать и копипастить в свои программы. Что она делает: ① — отправляем запрос. Сразу обрабатываем ошибку. ② — запрос доходит до сервера не мгновенно. Нужно подождать. Чтобы не зависнуть слишком долго, мы засекаем время начала. ③ — вызываем finishConnect, чтобы узнать статус подключения. ④ — finishConnect вернул true. Значит, соединение установлено. Уходим из цикла. ⑤ — finishConnect вернул nil. Мы специально проверяем через status == nil, потому что не нужно путать его с false. nil — это ошибка. Поэтому оформляем его как ошибку. ⑥ — проверяем, висим ли в цикле мы слишком долго. Если да, то тоже возвращаем ошибку. Не забываем закрыть за собой соединение. ⑦ — нам не нужен бизи-луп. Спим. ⑧ — мы не читаем сразу всё в память, чтобы экономить память. Вместо этого отдаём наружу handle. Частая ошибка — отсутствие элементов ②–⑦. Они нужны. Если до установки соединения мы вызовем handle.read(), то получим nil. Многие программы в этом случае сразу отчаются получить ответ и вернут ошибку. А надо было просто подождать. 3.3. Отправка файла Функция для отправки файла должна сначала прочесть его содержимое, затем сделать запрос и прочесть ответ. В ответе будет находиться URL файла. local function sendFile(path) local f, err = io.open(path, "r") -- ① if not f then return nil, ("could not open file for reading: %s"):format(err or "unknown error") end local contents = f:read("*a") -- ② f:close() local data, contentType = asFormData(contents, "clbin") -- ③ local headers = {["Content-Type"] = contentType} local handle, err = request("https://clbin.com", data, headers, 10) -- ④ if not handle then return nil, err end local url = {} -- ⑤ local read = 0 local _, _, responseHeaders = handle.response() -- ⑥ local length for k, v in pairs(responseHeaders) do -- ⑦ if k:lower() == "content-length" then length = tonumber(v) end end while not length or read < length do -- ⑧ local chunk, err = handle.read() if not chunk then if length then -- ⑨ return nil, ("error occured while reading response: %s"):format(err or "unknown error") -- ⑩ end break -- ⑩ end read = read + #chunk -- ⑪ if length and read > length then chunk = chunk:sub(1, length - read - 1) -- ⑫ end table.insert(url, chunk) end handle.close() -- ⑬ return table.concat(url) -- ⑭ end ① — открываем файл для чтения. Обрабатываем ошибки. ② — считываем всё из файла. Не забываем закрыть его за собой. ③ — вызываем заранее написанную функцию asFormData. Мы получаем тело запроса и значение хедера Content-Type. Создаём таблицу хедеров. ④ — отправляем наш запрос. Обрабатываем ошибки. ⑤ — handle.read может не сразу вернуть весь ответ, а кусочками. Чтобы не забивать память кучей строк, кусочки мы будем класть в таблицу (получится что-то вроде {"htt", "p://", "clbi", "n.co", "m/ab", "cdef"}). Также мы храним число прочитанных байт. ⑥ — мы хотим сверять число прочитанных байт с ожидаемым размером ответа. Для этого нам потребуется получить хедеры, отправленными сервером. Вызываем handle.response. ⑦ — размер ответа обычно пишется в заголовок Content-Length. Однако сервер может поиграться с регистром. Например, писать content-length или CONTENT-LENGTH. OpenComputers не трогает эти хедеры. Поэтому придётся пройтись по всем ключам таблицы и найти хедер без учёта регистра. ⑧ — если length не nil, то это число. Тогда проверяем, что ещё столько байт мы не прочли, и заходим в цикл. Если же Content-Length не задан, то будем считать, что серверу не важно, сколько надо прочесть, и крутимся до упора. ⑨ — handle.read может ещё вернуть ошибку. Если нам известна длина, то в силу условия цикла мы прочли меньше, чем ожидали. Сигналим о неудаче. (Закрывать соединение в случае ошибки не требуется.) ⑩ — если же длина неизвестна, то считаем, что сервер отдал всё, что мог, ошибку игнорируем и покидаем цикл. ⑪ — не забываем обновлять read. ⑫ — если сервер случайно отослал нам больше данных, чем надо (а мы знаем, сколько надо: length определён), то излишки обрезаем. Код здесь отрежет с конца строки (read - length) байт. ⑬ — закрываем соединение за собой, когда оно больше не нужно. ⑭ — наконец, склеиваем таблицу в одну строку. 3.4. Скачивание файлов Код для скачивания похож на предыдущий. Только вот в память мы записывать ответ с сервера уже не будем. Вместо этого напрямую пишем в файл. local function getFile(url, path) local f, err = io.open(path, "w") -- ① if not f then return nil, ("could not open file for writing: %s"):format(err or "unknown error") end local handle, err = request(url, nil, nil, 10) -- ② if not handle then return nil, err end local read = 0 local _, _, responseHeaders = handle.response() local length for k, v in pairs(responseHeaders) do if k:lower() == "content-length" then length = tonumber(v) end end while not length or read < length do local chunk, err = handle.read() if not chunk then if length then f:close() -- ③ return nil, ("error occured while reading response: %s"):format(err or "unknown error") end break end read = read + #chunk if length and read > length then chunk = chunk:sub(1, length - read - 1) end f:write(chunk) end f:close() -- ④ handle.close() return true end ① — открываем файл, в этот раз для записи. Обрабатываем ошибки. ② — отправляем запрос без данных и с дефолтными хедерами. Обрабатываем ошибки. ③ — если мы сюда попали, то дальше сделаем ретурн. Поэтому не забываем закрывать за собой файл. (Сокет закрывать не нужно, так как при ошибке он это делает сам.) ④ — добропорядочно освобождаем ресурсы. Чтобы было удобнее копипастить, я оставил повторяющийся код в двух функциях. В своей программке можно sendFIle и getFile отрефакторить, выделить дублирующуюся часть в отдельную функцию. 3.5. UI Пришло время красивой каденции. Аккордом финальным в ней будет пользовательский интерфейс. Он к интернет-карте отношения уже не имеет, но для полноты приведу и его. local args, opts = shell.parse(...) local function printHelp() io.stderr:write([[ Usage: clbin { get [-f] <code> <path> | put <path> } clbin get [-f] <code> <path> Download a file from clbin to <path>. If the target file exists, -f overwrites it. clbin put <path> Upload a file to clbin. ]]) os.exit(1) end if args[1] == "get" then if #args < 3 then printHelp() end local code = args[2] local path = args[3] local url = ("https://clbin.com/%s"):format(code) path = fs.concat(shell.getWorkingDirectory(), path) if not (opts.f or opts.force) and fs.exists(path) then io.stderr:write("file already exists, pass -f to overwrite\n") os.exit(2) end local status, err = getFile(url, path) if status then print("Success! The file is written to " .. path) os.exit(0) else io.stderr:write(err .. "\n") os.exit(3) end elseif args[1] == "put" then if #args < 2 then printHelp() end local path = args[2] local url, err = sendFile(path) if url then url = url:gsub("[\r\n]", "") print("Success! The file is posted to " .. url) os.exit(0) else io.stderr:write(err .. "\n") os.exit(4) end else printHelp() end 3.6. Вуаля Осталось добавить реквайры, и мы получим полноценный клиент clbin. Результат — на гисте. 4. Как правильно установить соединение через TCP-сокет Прошлая секция была вроде интересной, поэтому здесь тоже запилим какую-нибудь программку. @Totoro вот сделал интернет-мост Stem. Напишем для него клиент. Правильно. Опять же, особенности: Работает через TCP-сокет. Протокол бинарный. И асинхронный. А ещё сессионный: у каждого TCP-соединения есть собственный стейт. Доки хранятся на вики. При разрыве соединения клиент должен переподключиться и восстановить стейт. Здесь снова придётся использовать все фичи интернет-карты. 4.1. Архитектура Мы разделим программу на 2 части — фронтенд и бэкенд. Фронт будет заниматься рисованием и приёмом данных от пользователя, и им займёмся в конце и без комментариев. Бэк — поддержанием соединения и коммуникации с сервером. Это куда больше имеет отношения к гайду, рассмотрим подробнее. Бэкенд реализуем через ООП. Создадим конструктор, напихаем методов, которые затем будет дёргать фронт. 4.2. Конструктор Привычно вбиваем ООП-шаблон в Lua. local newClient do local meta = { __index = {}, } function newClient(address, channels, connectionTimeout, readTimeout, maxReconnects) local obj = { __address = address, __channels = channels, __connectionTimeout = connectionTimeout, __readTimeout = readTimeout, __maxReconnects = maxReconnects; __socket = nil, __buffer = nil, __running = false, __reconnectCount = 0, } return setmetatable(obj, meta) end end Ну, тут всё мирно пока. Начнём боевые действия с протокола. 4.3. Протокол Для него наклепаем кучу методов, которые будут крафтить пакеты и писать их через write. Write сделаем позже. Также сразу сделаем персеры. local meta = { __index = { __opcodes = { message = 0, subscribe = 1, unsubscribe = 2, ping = 3, pong = 4, }, __craftPacket = function(self, opcode, data) return (">s2"):pack(string.char(opcode) .. data) end, __parsePacket = function(self, packet) local opcode, data = (">I1"):unpack(packet), packet:sub(2) return self.__parsers[opcode](data) end, send = function(self, channel, message) return self:write(self:__craftPacket(self.__opcodes.message, (">s1"):pack(channel) .. message)) end, subscribe = function(self, channel) return self:write(self:__craftPacket(self.__opcodes.subscribe, (">s1"):pack(channel))) end, unsubscribe = function(self, channel) return self:write(self:__craftPacket(self.__opcodes.unsubscribe, (">s1"):pack(channel))) end, ping = function(self, message) return self:write(self:__craftPacket(self.__opcodes.ping, message)) end, pong = function(self, message) return self:write(self:__craftPacket(self.__opcodes.pong, message)) end, }, } meta.__index.__parsers = { [meta.__index.__opcodes.message] = function(data) local channel, idx = (">s1"):unpack(data) return { type = "message", channel = channel, message = data:sub(idx), } end, [meta.__index.__opcodes.subscribe] = function(data) return { type = "subscribe", channel = (">s1"):unpack(data), } end, [meta.__index.__opcodes.unsubscribe] = function(data) return { type = "unsubscribe", channel = (">s1"):unpack(data), } end, [meta.__index.__opcodes.ping] = function(data) return { type = "ping", message = data, } end, [meta.__index.__opcodes.pong] = function(data) return { type = "pong", message = data, } end, } В коде я активно использую string.pack и string.unpack. Эти функции доступны только на Lua 5.3 и выше, но позволяют очень удобно работать с бинарными форматами. 4.4. Подключение к серверу Прежде чем реализуем write, нужно разобраться с подключением. Оно нетривиально. local meta = { __index = { ..., connect = function(self) local socketStream = assert(inet.socket(self.__address)) -- ① local socket = socketStream.socket -- ② local start = comp.uptime() -- ③ while true do local status, err = socket.finishConnect() if status then break end if status == nil then error(("connection failed: %s"):format(err or "unknown error")) -- ④ end if comp.uptime() >= start + self.__connectionTimeout then socket.close() error("connection failed: timed out") -- ④ end os.sleep(0.05) end self.__socket = socket -- ⑤ self.__buffer = buffer.new("rwb", socketStream) -- ⑥ self.__buffer:setTimeout(self.__readTimeout) -- ⑦ self.__buffer:setvbuf("no", 512) -- ⑧ for _, channel in ipairs(self.__channels) do -- ⑨ self:subscribe(channel) end end, }, } ① — я использую обёртку над component.internet. Она потом будет нужна, чтобы мы могли поместить сокет в буфер. Обращаю внимание, что вызов обёрнут в assert. Работает она так: если первое значение не nil и не false, то возвращает его, а иначе кидает ошибку, используя второе значение в качестве сообщения. Проще говоря, она превращает nil, "error message" в исключение. ② — а пока я вытягиваю из обёртки сокет... ③ — чтобы можно было проверить, установлено ли соединение. Код здесь аналогичен тому, что мы делали в прошлой секции. Не выдумываем. ④ — одно различие: вместо return nil, "error message" я сразу прокидываю исключение. Прежде всего потому, что ошибки мы прокидывать должны единообразно. Раз в ① кидаем исключение, и здесь делаем то же. Почему исключение, а не return nil, "error message"? Мы вызывать connect будем из всяких мест. Так как в случае ошибок бэкенд беспомощен, то лучше прокинуть ошибку до фронтенда и не усложнять код бэка проверками на nil. Кроме того, это громкая ошибка: если забыть где-то её обработать, она запринтится на экран, случайно пропустить её или подменить какой-нибудь непонятной "attempt to index a nil value" не получится. В конце концов, мне так проще. ⑤ — сокет я сохраняю в поле. socket.finishConnect нам ещё понадобится. ⑥ — пришло время обернуть сокет в буфер. Может показаться излишним, особенно учитывая ⑧. Причины станут ясны, когда будем делать чтение. rw — это буфер для чтения и записи. b — бинарный режим: buffer:read(2) вернёт 2 байта, а не 2 символа. Так как символы кодируются в UTF-8 и занимают 1 (латиница), 2 (кириллица, диакритика), 3 (BMP: куча письменностей, всякие графические символы, большая часть китайско-японско-корейских иероглифов) или 4 байта (всё, что не влезло в BMP, например emoji), то отсутствие этого режима может дать ощутимую разницу. В нашем случае протокол бинарный — ставим b. ⑦ — устанавливаем таймаут для чтения. Объясню подробнее, когда будем это чтение делать. ⑧ — отключаем буфер для записи. Он нам не нужен. ⑨ — здесь же подключаемся ко всем каналам. Итого мы получаем свойства __socket и __buffer. Сокет использовать будем, чтобы вызывать .finishConnect() и .id(). Буфер — для записи и чтения. 4.5. Запись Теперь, разобравшись с сокетами и буферами, мы можем запросто писать в сокет. Пилим write: local meta = { __index = { ..., write = function(self, data) return assert(self.__buffer:write(data)) end, }, } Здесь тоже оборачиваем write в assert, чтобы кидать исключения. Причины уже пояснял. 4.6. Чтение и обработка пакета Сначала делаем функцию readOne. Она будет пытаться читать ровно один пакет. Здесь требуется нестандартная обработка ошибок, поэтому код сложноват. local meta = { __index = { ..., readOne = function(self, callback) -- ⑥ self.__buffer:setTimeout(0) -- ① local status, head, err = pcall(self.__buffer.read, self.__buffer, 2) self.__buffer:setTimeout(self.__readTimeout) if not status and head:match("timeout$") then return end assert(status, head) -- ② local length = (">I2"):unpack(assert(head, err)) -- ③ local packet = self:__parsePacket(assert(self.__buffer:read(length))) -- ④ if packet.type == "ping" then -- ⑤ self:pong(packet.message) end callback(self, packet) -- ⑥ return true end, } } ① — рассмотрим эту мишуру по порядку: Любой пакет stem начинается с 2 байт, которыми кодируется длина остатка. Отсюда всплывает двойка. Автор buffer, к сожалению, не осилил реализовать адекватную обработку ошибок. Он использует и исключения, и тихие ошибки (nil, "error message"). В случае таймаута будет прокинуто исключение. Однако мы перед чтением поставили таймаут в 0. Если буфер не найдёт сразу 2 байта в сокете, то он сразу кинет ошибку. Мы хотим проверить, есть ли в сокете пакет, который бы можно было прочесть. Используем pcall. Сначала раскроем self.__buffer:read(2) как self.__buffer.read(self.__buffer, 2), а затем поместим функцию и её аргументы в pcall. pcall возвращать будет сразу 3 значения по следующему принципу: Если на сокете есть 2 непрочитанных байта, read вернёт их без ошибок. Тогда status будет равен true, в head сохранятся эти 2 байта, а в err запишется nil. Если на сокете этих байтов нет, то read прокинет исключение "timeout". status установится в false, head приравняется "/lib/buffer.lua:74: timeout", а err также будет nil. Если же при чтении с сокета возникла другая ошибка, то read вернёт её по-тихому: status будет true, head — nil, а сообщение об ошибке уйдёт в err. Не думаю, что этот случай возможен, однако read может кинуть исключение и не из-за таймаута. status установится в false, а ошибка сохранится в head. В if мы проверяем, был ли таймаут (ситуация 1.2). В таком случае мы не кидаем исключения, а тихо выходим. Наконец, не забываем вернуть прежнее значение таймаута. ② — обрабатываем случай 1.4. ③ — обрабатываем случай 1.3 с помощью assert. Последний оставшийся и единственный успешный случай (1.1) также покрывается: распаковываем 2 байта в целое беззнаковое число (uint16_t). ④ — в ③ мы получили длину оставшегося пакета. Очевидно, надо остаток дочитать, что и делаем. Здесь уже не надо отдельно обрабатывать таймаут, достаточно assert. Считанный пакет отдаём в __parsePacket. ⑤ — если сервер докопался до нас своим пингом, отправим ему понгу. ⑥ — функция readOne принимает коллбэк. Это функция, которая будет обрабатывать все пакеты. Коллбэк будет передавать фронтенд, а бэкенд займётся минимальной обработкой, чтобы в принципе работало. Как, например, ③. Отлично. Мы приготовили все примитивы, которые были нужны. Осталось собрать их воедино — в event loop. 4.7. Event loop и события Ивент луп — это цикл, который ждёт событий и что-то с ними делает. Пришло время разобраться, что за события есть в OC. Когда мы вызываем socket.read или socket.finishConnect, устанавливается "ловушка" (селектор). Она срабатывает, когда на сокет пришли новые байты. При этом компьютер получает событие internet_ready. После чего "ловушка" деактивируется до следующего вызова. internet_ready, таким образом, — это событие, извещающее нас о том, что на сокете валяются непрочитанные данные и пора вызвать socket.read, чтобы их собрать. У события два параметра. Первый — это адрес интернет-карты. Второй — id сокета. Тот id, который возвращает socket.id(). Поэтому мы сохранили сокет в поле __socket: сейчас будем использовать его. local meta = { __index = { ..., __run = function(self, callback) while self.__running do local e, _, id = event.pullMultiple(self.__readTimeout, "internet_ready", "stem%-client::stop") -- ① if e == "internet_ready" and id == self.__socket.id() then -- ② while self:readOne(callback) do self.__reconnectCount = 0 -- ③ end elseif e ~= "stem-client::stop" then self:ensureConnected() -- ④ end end end, stop = function(self) self.__running = false event.push("stem-client::stop") -- ⑤ end, } } ① — ждём события internet_ready или stem-client::stop. Так как в event.pullMultiple названия ивентов сверяются через string.match, дефис экранируем. Второй ивент нужен, чтобы принудительно прервать цикл из stop. ② — обрабатываем мы только internet_ready и только для нашего сокета. Проверяем. ③ — если поймался пакет или пакеты, то пытаемся обработать каждый в порядке прибытия. Когда мы закончили обрабатывать все пакеты, self:readOne вернёт nil, и цикл прервётся. Кстати говоря, если мы внутри цикла оказались, то соединение установилось. Не забываем отметить это. ④ — если же улов пуст, перепроверяем, подключены ли мы вообще. ⑤ — не забываем добавить метод, чтобы остановить наш цикл. Отсюда же отсылаем событие stem-client::stop. Отлично. Теперь пришло время ловить все наши прокидываемые исключения. 4.8. Обработка ошибок Последними 2 функциями, которые мы добавим, будут ensureConnected и run. С их помощью бэкенд будет автоматически переподключаться к серверу в случае проблем. local meta = { __index = { ..., ensureConnected = function(self) local status, err = self.__socket.finishConnect() -- ① if status == false then error("not yet connected") end return assert(status, err or "unknown error") end, run = function(self, callback) if self.__running then -- ② return end self:connect() -- ③ self.__running = true while self.__running do -- ④ local status, err = pcall(self.__run, self, callback) -- ⑤ if not status then if self.__reconnectCount == self.__maxReconnects then -- ⑥ return nil, ("connection lost: %s; reconnect limit is reached"):format(err or "unknown error") end self.__reconnectCount = self.__reconnectCount + 1 self.__buffer:close() -- ⑦ if not pcall(self.connect, self) then -- ⑧ if self.__socket then self.__socket:close() end if self.__buffer then self.__buffer:close() end os.sleep(1) end end end self.__buffer:close() end, }, } ① — ensureConnected просто прокинет ошибку, которую вернёт finishConnect(). ② — принимаем защитную позицию против дураков. Рекурсивно запускать циклы смысла нет. ③ — сначала подключаемся к серверу. Если всё отлично, то можно начинать. ④ — как и в __run, здесь мы оборачиваем код в цикл. Если вызван stop(), то сначала остановится self.__run, а затем и этот цикл. ⑤ — обработка исключений требует pcall. Потому что их надо словить. ⑥ — если мы старались-старались, но так и не смогли уложиться в self.__maxReconnects по реконнектам, кидаемся белым флагом. ⑦ — не забудем закрыть буфер. ⑧ — вспомним, что self.connect кидает исключение. Перехватываем. На всякий случае позакрываем то, что породил connect. 4.9. Фронтенд На этом наш бэкенд готов. Поздравляю. Остаётся лишь прицепить ввод-вывод. Опять же, даю готовый код без комментариев, ибо не об этом пост. local gpu = com.gpu local w, h = gpu.getResolution() local function writeLine(color, line) local oldFg if gpu.getForeground() ~= color then oldFg = gpu.setForeground(color) end local lines = 0 for line in text.wrappedLines(line, w + 1, w + 1) do lines = lines + 1 end gpu.copy(1, 1, w, h - 1, 0, -lines) local i = 0 for line in text.wrappedLines(line, w + 1, w + 1) do gpu.set(1, h - lines + i, (" "):rep(w)) gpu.set(1, h - lines + i, line) i = i + 1 end if oldFg then gpu.setForeground(oldFg) end end local channel = ... if not channel then io.stderr:write("Usage: stem <channel>\n") os.exit(1) end if #channel == 0 or #channel >= 256 then io.stderr:write("Invalid channel name\n") os.exit(2) end local client = newClient( "stem.fomalhaut.me:5733", {channel}, 10, 10, 5 ) require("thread").create(function() while true do term.setCursor(1, h) io.write("← ") local line = io.read() if not line then break end local status, err = pcall(client.send, client, channel, line) if not status then writeLine(0xff0000, ("Got error while sending: %s"):format(err or "unknown error")) break end end client:stop() end) client:run(function(client, evt) if evt.type == "message" then writeLine(0x66ff00, "→ " .. evt.message) elseif evt.type == "ping" or evt.type == "pong" then writeLine(0xa5a5a5, "Ping: " .. evt.message:gsub(".", function(c) return ("%02x"):format(c:byte()) end)) end end) os.exit(0) Здесь я упускаю одну вещь: обработку ошибок в client.send. Если мы попытаемся отправить сообщение, когда у нас потеряно соединение (или до того, как оно установлено), мы или словим ошибку, или потеряем сообщение. Починить это можно, добавив очередь отправляемых пакетов, но это в разы усложнит программу, поэтому оставим так. 4.10. Готово! Добавим реквайров... И у нас получился вполне рабочий клиент для Stem! Код программы — на гисте. 5. В чём различие между component.internet и require("internet") Первое — исходный компонент. Второе — обёртка над ним. У обёртки есть 3 функции: internet.request(url, data, headers, method) — обёртка над component.internet.request. Удобна тем, что все ошибки превращает в исключения за программиста. Кроме того, возвращаемое значение — итератор, и его можно поместить в цикл for. Тем не менее, код, который ждёт установки соединения, нужно писать самому. internet.socket(address, port) — промежуточная обёртка над component.internet.connect. Она используется для того, чтобы потом превратить её в буфер, как сделали мы. Сама по себе достаточно бесполезна. internet.open(address, port) — тоже обёртка над component.internet.connect. Она вызывает internet.socket(address, port) и сразу превращает результат в буфер. Проблема в том, что сам объект сокета использовать можно только через приватные свойства, которые могут ломаться между обновлениями OpenOS. Из-за этого функция исключительно ущербна. Для отправки HTTP-запросов я предпочитаю использовать API компонента. TCP-сокеты же проще создавать через обёртку (internet.socket), вручную проверять подключение и так же вручную укладывать обёртку в буфер, как показано выше. 6. Конец Самое сложное в использовании интернет-карты — это правильно обработать все ошибки. Они могут возникнуть на каждом шагу, при этом быть полноценными исключениями или тихими ошибками. Необработанные исключения крашат программу, из-за чего возникает желание весь код программы поместить в один большой pcall. Например, IRC-клиент, который на дискете поставляется, делает так. Тихие ошибки гораздо подлее. Необработанные, они тоже крашат программу, только вот сама ошибка теряется, подменяется другой (обычно "attempt to index a nil value"). В Lua обработать все ошибки — задача сложная, потому что механизм ошибок ужасен. В нормальных языках стэктрейс отделён от сообщения об ошибке, плюс каждая ошибка имеет свой тип, по которому можно безопасно определять вид ошибки. Lua этим не заморачивается: сообщение об ошибке включает позицию в коде, откуда ошибка прокинута. Есть или нет стэктрейс, зависит от выбора между pcall и xpcall. Если они находятся где-то в другой библиотеке, программист на выбор повлиять не может. В коде Stem-клиента единственный способ узнать, от таймаута ли ошибка прокинута, — матчить последние 7 символов на слово "timeout". Это эталонный костыль. Даже в JavaScript механизм лучше. Поэтому этот пост получился не столько про интернет-карту, сколько про обработку ошибок.
  11. 15 баллов
    Описание Данная программа позволяет видеть местоположение руды вокруг игрока. Работает следующим образом: Планшет с геосканером и сетевой картой сканирует область вокруг игрока и отправляет на стационарный компьютер информацию о найденных блоках Компьютер с терминалом OpenGlasses2 и сетевой картой принимает информацию о блоках и рисует этим блокам подсветку. Фичи и особенности Используется методика из исследования Дуба, которая позволяет весьма точно определять руду Настраиваемые параметры сканирования c валидацией и коррекцией сохраняются в конфиг при выходе из программы Понятный ui Подсветка границ области сканирования Отображение на очках с использованием всего 2 виджетов фпс снижается меньше Это программа работает на версии игры 1.12.2 с аддоном OpenGlasses2 теоретически можно портировать на 1.7.10 Скриншоты Видео Установка Требуемое железо Планшет, минимальное Планшет, рекомендуемое Стационарный компьютер Вместо системного блока 3 тира можно юзать серверную стойку с сервером 2 тира. Также вместо связанной карты можно юзать беспроводную сетевую, однако, радиус действия в этом случае будет ограничен. Все процессоры нужно переключить на Lua 5.3(шифт+пкм). На планшет и компьютер устанавливаем OpenOS. На планшет устанавливаем программу и ее либы wget https://raw.githubusercontent.com/hohserg1/OpenComputersPrograms/master/oresense/oresense_tablet.lua wget https://raw.githubusercontent.com/hohserg1/OpenComputersPrograms/master/simple_libs/bit_array.lua lib/bit_array.lua pastebin get iKzRve2g lib/forms.lua На комп устанавливаем программу и ее либы wget https://raw.githubusercontent.com/hohserg1/OpenComputersPrograms/master/oresense/oresense_server.lua wget https://raw.githubusercontent.com/hohserg1/OpenComputersPrograms/master/simple_libs/bit_array.lua lib/bit_array.lua Использование Привязываем очки к терминалу и надеваем. Запускаем на компе oresense_server. Программа будет работать в фоновом режиме, поэтому комп можно юзать для чего-нибудь еще. Запускаем на планшете oresense_tablet. Синие поля позволяют настроить параметры. Большая красная кнопка запускает сканирование. Во время этого процесса лучше не сдвигаться с места. По окончанию сканирования оглядитесь вокруг - руда будет подсвечена красными каркасными кубами. Благодарности Спасибо @Doob и всем, кто ему помогал, за исследование принципов работы геолайзера и статью об этом Спасибо @Zer0Galaxy за либу гуи forms Спасибо @BrightYC за реью гуи и идею мэйн-картинки Спасибо @Sainthozier за реью гуи Спасибо @Fingercomp за функцию индексации координат, к сожалению, она не понадобилась Спасибо людям из irc-чата за саппорт по ОС и Lua Ссылки Гитхаб: https://github.com/hohserg1/OpenComputersPrograms/tree/master/oresense
  12. 15 баллов
    Кому все еще нужно, в местном магазине теперь есть приложение OpenOS Имеется интеграция с майносью, а именно: Отображение os.getenv('_') в титлбаре Настоящий биос доступен из опеноси непосредственно computer.addUser/computer.removeUser/computer.uptime работают с настоящим компьютером Цветовая схема Настоящий диск смонтирован по пути /mnt/mineos
  13. 15 баллов
    Ну что ж. Буквально на днях майнось наконец релизнулась в виде полностью самостоятельной и независимой от OpenOS операционки с собственным набором библиотек и вики. Все родное, отечественное (звучит как диагноз). Большинство методов библиотек по типу filesystem или keyboard крайне схожи с таковыми в опеноси по поведению, так что особых проблем с переходом возникать не должно. Также существенно снизился расход оперативной памяти - примерно на 7% от 4 планок 3 уровня. Осталось, конечно, регулярно выискивать мелкие недочеты и допиливать их, а также более подробно наполнять инфу на вики, но в целом результатом я доволен. Среди ключевых нововведений стоит отметить следующие: Полностью переписанный инсталлер, запускающийся даже с EEPROM, имеющий возможность выбора тома для установки и его форматирования, а также систему конфигурации пользовательского профиля. Добавлено несколько системных языков, а заодно возможность установки лишь выбранного языкового пакета вместо всех сразу для экономии места на диске: Полная двойная буферизация графики, все приложения переписаны под библиотеку GUI. Кстати, местный картинко-редактор заимел оконный режим, а также Ёлочка @Totoro стала отлично работать в фоне и украшать хату (в моем случае - жалкий клочок земли в воздухе): Крайне полезный режим Internet Recovery, позволяющий мгновенно переустановить систему напрямую из EEPROM в случае возникновения каких-либо проблем: Возможность заливки файлов напрямую на Pastebin буквально парой кликов: Фича создания ассоциаций расширений файлов с возможностью назначения приложения для открытия того или иного расширения:
  14. 13 баллов
    Показывает основную информацию о системе и дисках, позволяет выбирать приоритетный и конкретные диски для загрузки. Из работающего: Информация о системе Краткая информация о дисках Загрузка через init.lua в корне диска Установка приоритетного загрузочного диска (при старте биос будет пытаться загрузится именно с него, а потом уже проверять другие диски) Выжигающий глаза интерфейс и код Форматирование дисков Загрузка в MineOS Из не работающего: Загрузка в Plan9k и SecureOS Вкладка настроек Частично работающее: Загрузка в MineOS (не будет работать если установлено значение приоритетного диска) Предстоит реализовать: Вкладку настроек с языком Включение/выключение авторана Функцию ремонта файла запуска системы (перекачка с репозиции) Модификатор запускаторов MineOS и OpenLoader Запуск файлов на уровне биоса Поддержку (хоть какую нибудь) дисков в блочном режиме Вырезать главный кусочек и сделать возможность работы без интернет карты Перелопатить интерфейс и код, дабы делать всё через ООП Для установки прошейте биос этим файлом: https://raw.githubusercontent.com/titan123023/OCBios/master/bios-starter.lua Сслыль на репозицию: https://github.com/titan123023/OCBios Для работы требуется: Интернет карта Монитор любого уровня Клавиатура (если хотите зайти в сам биос) Как минимум 1 палец (см описание выше)
  15. 13 баллов
    OpenPeripheral предоставляет мощный функционал по созданию собственных интерфейсов в виде очков дополненной реальности "Terminal Glasses". К сожалению, полного списка функционала и всех нюансов по работе с этими очками не найти. Присутствуют некоторые отрывки, небольшие видео с результатами работы и прочие поделки. Но! Спустя пару бессонных ночей, декомпилированного кода и трёх литров чая, удалось описать полный функционал этих очков. Основные возможности очков и периферии: Отображение геометрических объектов различной сложности и цветовой гаммы. Отображение текста. Отображение жидкостей и предметов. Взаимодействие с мышью/клавиатурой. Чтение сообщений чата. Специальные команды чата, не отображающиеся в нем. Возможность индивидуальной работы с каждым пользователем терминала. Установка очков в любой шлем. Возможности данных очков я опишу по разделам, в лучших традициях вики Для начала, определю некоторые понятия. Система терминальных очков состоит из трёх предметов: терминала, очков, беспроводной клавиатуры. Терминал является центром всей системы, через него происходит общение между компьютером и очками. Также он хранит все данные интерфейса и пользователей. На очки выводится вся графическая информация, а сами очки передают сообщения или команды чата на терминал. Беспроводная клавиатура позволяет дополнить всю систему, своей возможностью контроля элементов при помощи клавиатуры и мыши. Как выводить информацию на очки: local com = require("component") local opb = com.openperipheral_bridge -- Построение интерфейса происходит во внутреннем буффере терминала (он же мост). local text = opb.addText(10, 10, "", 0xffef7f) -- Создаем компонент "Текст". -- Его нужно создать только один раз, в остальное время можно обращаться по ссылке и изменять любой параметр. local counter = 0 while true do -- В качестве примера будет выводится счетчик секунд. text.setText(tostring(counter)) -- Обновляем текста компонента. opb.sync() -- Для отображения графики на экране, необходимо отправить буффер на очки. os.sleep(1) -- Ждем секунду и прибавляем счетчик. counter = counter+1 end API Перед тем как перейти к API, нужно знать: Color:number -- Число в формате TrueColor RGB (по умолчанию 0xffffff). Opacity:number -- Прозрачность, число от 0.0 до 1.0 (по умолчанию 1). Знак '?' -- Опциональный параметр/функция. События Строковые константы VerticalAlignment:[TOP, MIDDLE, BOTTOM] HorizontalAlignment:[LEFT, MIDDLE, RIGHT] GuiElement:[OVERLAY, PORTAL, HOTBAR, CROSSHAIRS, BOSS_HEALTH, HEALTH, ARMOR, FOOD, MOUNT_HEALTH, AIR, EXPERIENCE, JUMP_BAR, OBJECTIVES] Структуры данных SimpleBox, ColoredPoint, Coord, User Абстрактные объекты Drawable, BoundedShape, Box Графические объекты Управление графическими объектами DrawableFactory, DrawableContainer Управление терминалом Вот такая шпаргалка по очкам, надеюсь пригодится
  16. 13 баллов
    Всем привет. Представляю вам программу для почти полного управления дроном. Но сначала короткая предыстория: Из возможностей отмечу следующее: Перемещение по сторонам света без улучшения "Навигация"(Спасибо версии OC 1.7.3) Возвращение дрона к управляющему планшету(Компьютеру) Удалённый интерпретатор Привязка дрона к управляющему планшету(Почти полная невозможность угона) Красивый Динамичный интерфейс(Отображение заряда планшета, время, заряд дрона, уровень сигнала) Остальные функции можно найти в программе или в скриншотах. Системные требования: Планшет с OpenOS и видеокартой 2 уровня, беспроводной картой любого уровня и интернет-платой Дрон с беспроводной картой любого уровня (остальное не так важно, это опционально) Установка: На планшет (Для обладателей интернет-карт): wget https://raw.githubusercontent.com/BrightYC/DRC/master/tablet.lua drc.lua Для дрона (Для обладателей интернет карт): wget https://raw.githubusercontent.com/BrightYC/DRC/master/drone.lua drc-flash.lua && flash -q drc-flash.lua drc.lua (Код без минификации: https://raw.githubusercontent.com/BrightYC/DRC/master/non-minify-drone.lua) После вынимаем биос и вставляем в дрона. Первый запуск: Первый запуск сопровождается привязкой дрона к порту(Порт можно увидеть открыв дрона) и адресу беспроводной карты. Из-за этого возможность угнать дрона стремится к нулю, так как для первичной настройки нужно стоять не дальше 5 блоков от дрона. Пишем: drc <порт дрона> После этого дрона невозможно угнать, если рядом никого не было и он не мог подслушать сообщение(Перезагрузка привязку не ломает) Пара скриншотов: Видео: P.S Процент справа от шкалы сигнала обозначает не силу сигнала, а мощность передатчиков на планшете с дроном(поэтому для корректной работы нужны одинаковые карты беспроводной сети) На этом всё, дань уважения дронам выполнена, теперь могу спать спокойно.
  17. 13 баллов
    Я очень обожаю дронов из OC, довольно быстрые и манёвренные(и дешёвые!). Меня смущает насколько автофермы из разных модов дорогие, так что вот решение этой проблемы: В программе есть конфигурация, состоит она из: Размер фермы по X и Z Ожидание в секундах(Это нужно для того, чтобы культура успела вырасти) Ожидание при зарядке(Если дрон разрядился во время полёта) Процент заряда, при котором дрон вернётся домой Версия работы(лкм/пкм) Конфигурация дрона совсем простая, нужен лишь инвентарь, при желании можно запихнуть и 2 инвентаря, солнечную панель. Установка довольно простая: Пишем: wget https://raw.githubusercontent.com/BrightYC/Other/master/dronefarm.lua Делаем чистый EEPROM Вставляем в компьютер Пишем flash -q dronefarm.lua dronefarm.lua Вставляем в дрона ??? Профит! Расстановка фермы: Где жёлтый - граница фермы Красный - место стоянки Длина и ширина может быть любой, главное чтобы зарядки хватило(Если у дрона переполнится инвентарь или разрядится до 20% от общей зарядки единиц - он вернётся на базу)
  18. 12 баллов
    Сделал простенькую читалку для файлов GIF, поддерживает весь функционал со спецификации: слои/кадры, прозрачность, текст, комментарии, расширение NETSCAPE2.0 и прочее. Но не все доп. расширения были найдены, потому реализовал только описанные в интернете. Небольшая предыстория: В общем говоря, захотел я себе как-то анимированный "телек" для дома, в качестве украшения. С отображением были конечно проблемы - голо-проектор не вмещал больше 3х цветов в палитре, но это решалось количеством. Второй проблемой встала сама картинка - пнгшки кадрами собирать было лень, да ещё и в индексы надо переводить все цвета... Подождите-ка индексы, анимация, встроенная палитра... Это-же GIF! Эврика подумал я... Пока не увидел реализацию, а потом меня поглотила пучина гифа... При первом знакомстве GIF показался простым, как пробка, форматом (хотя, так и есть). Но сжатие изображения в LZW немного остудило мой пыл. Мне довольно трудно понимать работу хитроскрученных алгоритмов сжатия, но всё оказалось не так плачевно, и вооружившись методом проб и ошибок, я начал познавать это.. что-то. В конце концов, подняв несколько хабро-статеек, спецификацию, форумы покрывшиеся пылью, я начал открывать другую сторону GIF - она идеально подходит под реалии OpenComputers: 256 цветов, неплохое сжатие, встроенный текст. Всё это приправлено расширениями в виде анимаций, прозрачности слоёв, и разными дополнительными блоками. В конце-концов, этот формат может стать аналогом флеша в истории опенкомпов! Да здравствует новая эра, эра флеш-игр, когда-то покорившая интернет! В итоге всех скитаний по интернетам, мне удалось сделать более-менее полную читалку, хотя некоторые расширения потерялись в могилах истории, и я не решился пока их выкапывать. Саму читалку можно скачать для двух версий Lua: 5.2: wget -f https://github.com/Xytabich/GIF-Lua/raw/master/5.2/gif.lua /lib/gif.lua 5.3: wget -f https://github.com/Xytabich/GIF-Lua/raw/master/5.3/gif.lua /lib/gif.lua Различия между ними несущественны - Lua 5.3 использует встроенные битовые операции, а в Lua 5.2 используется библиотека bit32, соответственно. Функционал читалки: -- Сруктуры -- Заголовок GIF - базовая информация гифки. - width:number - ширина гифки - height:number - высота гифки - colorBits:number - кол-во бит, используемое для цветовой палитры. Устарело, но добавил на всякий. - bgIndex:number - индекс фонового цвета - aspectRatio:number - соотношение сторон, считается как: ширина/высота. Тоже устарело, но мало-ли. - colorsCount:number? - количество цветов в глобальной палитре - colors:number[]? - глобальная палитра цветов 0xRRGGBB, индексация начинается с 0 - extensions:table - список расширений гифки Блок изображения (image) - информация о слое/кадре гифки. Содерижит полное изображение, или только его часть. - x:number - отступ от левого края - y:number - отступ от верхнего края - width:number - ширина изображения - height:number - высота изображения - interlaced:bool - построчная или чересстрочная развертка - colorsCount:number? - количество цветов в локальной палитре - colors:number[]? - локальная палитра цветов 0xRRGGBB, индексация начинается с 0 - pixels:string - пиксели изображения, массив байт 0-255. Пиксель является индексом в локальной/глобальной палитре, и записаны в виде линий, слева-направо сверху-вниз. Порядок линий зависит от interlaced. - extension:table? - расширение изображения, параметры анимации и прозрачности - dispMethod:number - метод очистки экрана перед отображением: 1 - не очищается, 2 - закрасить фоновым цветом, 3 - восстановить состояние перед предыдущим кадром - delay:number - время до следующего кадра, в секундах - inputFlag:bool - ожидать ли ввода пользователя для продолжения отображения - transparentIndex:number - индекс цвета в палитре, использующегося в качестве прозрачного Блок текста (text) - отображает текст над изображением. По спецификации символы 7-bit ASCII, но ничто не мешает засунуть туда utf-8. - x:number - отступ от левого края - y:number - отступ от верхнего края - width:number - ширина текстовой области - height:number - высота текстовой области - charWidth:number - ширина символа - charHeight:number - высота символа - fgIndex:number - цвет символа - bgIndex:number - цвет фона символа - text:string - собственно, сам текст Блок комментария (comment) - произвольная текстовая запись, не влияющая на отображение. По спецификации так-же символы 7-bit ASCII, но и юникод спокойно помещается. Расширение гифки: NETSCAPE2.0 - информация о количестве циклов анимации. - iterations:number - количество итераций цикла (0 - бесконечное) - loop:bool - бесконечный ли цикл -- Функции -- gif.read(stream[, pos=0]):table -- читает весь файл. -- stream - поток данных, например - полученный через filesystem.open(...) -- возвращает заголовок с дополнительным полем - blocks:table {type:string, block:object}. В этом поле содержится список всех блоков в порядке чтения. gif.images(stream[, pos=0]):head, image -- итератор, последовательно считывает изображения. -- stream - поток данных, например - полученный через filesystem.open(...) gif.blocks(stream[, pos=0]):head, type, block -- итератор, последовательно считывает все блоки. -- stream - поток данных, например - полученный через filesystem.open(...) Особо сильно оптимизировать не стал, да и не знаю как, так что если есть предложения - выслушаю и внесу поправки. Один из вариантов ускорения чтения - сделать буферизированный поток данных. Код и тестовые изображения доступны в репозитории на github. В чересстрочной развёртке линии следуют не по прямому порядку, подробнее в выписке из спецификации: Пример приведу на тестовом коде, для проектора на картинке выше. Этот код просто рисует файл, так что настроить пересечение областей проекторов нужно вручную, или использовать один. Для OpenComputers этот формат имеет хорошие возможности, как для простого хранения сжатого изображения, или даже составного изображения из различных слоёв, так и для продвинутых действий: презентаций, анимаций, картинок с подписями. А если совсем заморочиться, то можно сделать свой Anone Gif Player, используя комментарии для записи кода, или собственные расширения приложения. По крайней мере, кинетические новелки возможно сделать и в стоковом варианте. История изменений:
  19. 12 баллов
    Приветствую вас. Этот пост я пишу, чтобы рассказать вам о своей разработке для мода OpenComputers: BetterBIOS Это специальная прошивка BIOS для EEPROM, позволяющая выбирать любое из подключенных устройств для загрузки. Для этого используется пользовательский псевдографический интерфейс, который поддерживается на всех уровнях видеокарт и экранов. Так же, в этой прошивке есть своя система отображения ошибок, показывающая весь "traceback" за исключением не нужных пунктов внутри самой прошивки. Это будет очень полезно для разработчиков собственных операционных систем, ведь данная прошивка подскажет места, в которых возникла ошибка, в то время как стандартная прошивка молчит как партизан. UPD. Начиная с версии 4, код ошибок можно пролистывать: Если доступных для загрузки устройств больше, чем одно - то при включении компьютера, пользователю предлагается выбрать одно из них. Если такое устройство только одно - загрузка начнётся незамедлительно. Подключение устройств для загрузки происходит динамически, "на лету", прямо во время выбора устройства. Так же, данная прошивка имеет приоритеты на выбор экрана: первым делом она пытается подключить тот экран, у которого есть клавиатуры поблизости. Если таковых не имеется - использует любой другой. Видеокарту прошивка использует максимального уровня. Прошивка защищена от критических сбоев при резком извлечении устройств, пока программа работает с файлами на них. Это делает данную прошивку практически не убиваемой, и позволяет избегать любых несчастных случаев при работе с компьютером. Я просто параноик, иначе сделать не мог... Данная прошивка поддерживает большое количество разных операционных систем, использующих разные файлы для загрузки: OpenOS Plank9 MineOS Работает это за счёт списка файлов, которые ищет прошивка. Вы можете добавить свои файлы, вписав их в переменную в начале файла. Ну или можете оставить комментарий под этой темой, с просьбой, какой файл стоит ещё добавить. Забавный факт: Если таких файлов будет очень много, то посмотрев их список нажатием клавиши F8 при выборе устройства - вы можете пролистывать список. Так же, если вы хотите, чтобы прошивка при запуске не перезаписывала данные EEPROM (setData), и не мусорила в оперативной памяти рудиментарными функциями типа "computer.getBootAdress", которые нужны для OpenOS - вы можете отключить всё это дело в меню через клавиши. А если вам не хочется каждый раз их нажимать - то просто измените комментарий в конце кода с 11 на 10 или 00. Несмотря на весь этот функционал и продуманность, в минимизированном виде данная прошивка помещается на EEPROM, стандартная ёмкость которого - 4096 символов. К сожалению, код не очень хорошо читаемый, но это обусловлено тем, что я писал заранее с расчётом на то, что подробный код будет минимизирован, а для них нужно ещё и грамотно написать код, который, увы - сложно читается. Исходный код данной прошивки вы можете посмотреть здесь. Спасибо за внимание. Если у вас есть какие-то идеи, как можно улучшить или ухудшить данную прошивку - пишите здесь, или как-нибудь вычислите меня, постараюсь подумать, и может быть, даже обновлю код, сделав его лучше для вас.
  20. 12 баллов
    Последняя версия : 2.2 Команда для установки : pastebin run ngQT9YF8 Системные требования: Корпус компьютера, екран - 1 Tier Процесор - 1 Tier Видеокарта - 1 Tier Память - 1.5 Tier (x1) Жёрсткий диск - 1 Tier Дисковод, клавиатура, Lua BIOS - должны присутствовать Краткое описание: Inerpat - примитивная система для запуска на любом ведре, и инструмент для создание и отладки EEPROM кода. Также она поможет в ситуации, когда ваш компьютер превратился в "обожённый строительный материал" Специальные клавиши: ALT - Открыть главное меню CTRL - Открыть меню файла/папки. В редакторе - меню сохранения. TAB - Навигация по доступным файловым системам Скриншоты:
  21. 12 баллов
    Мод Computronics существует уже более 5 лет. Однако почему-то и по сей день нет ни одной более-менее рабочей файловой системы для кассет из этого мода. Будем исправлять ситуацию))) На сей раз я и представляю вашему вниманию файловую систему для этих самых кассет. TapFAT (Tape File Allocation Table) является функционально полной ФС с возможностью фрагментированного хранения данных. Первые идеи о написании ФС для кассет меня посетили еще в октябре, но время появилось лишь к концу декабря, тогда же я начал обдумывать основу системы. К январю начал писать основную библиотеку, вспомогательные утилиты, отлавливал баги, оптимизировал и упорядочивал код. Наконец, к июню месяцу готова первая версия драйвера. ПРЕДУПРЕЖДАЮ! Файловая система - вещь сложная и довольно серьезная. Я постарался устранить все обнаруженные баги, но не исключено, что некоторые могли проскользнуть мимо глаз. Поэтому я не несу ответственности за порчу данных на кассете в следствии сбоя драйвера. Сохраняя данные на кассетах вы действуете на свой страх и риск! Скажу сразу, ФС довольно медленная (виной тому буферы в операционках, ибо запись на кассету больших и мелких кусков занимает, как ни странно, одинаковое количество времени), поэтому она больше подходит для архивации данных. Учитывая, что самая крутая кассета дает нам около 30МБ, туда можно заархивировать очень много информации (не знаю, нужно ли кому столько). Первые 8КБ кассеты зарезервированы под саму таблицу FAT. Она представляет собой обычную сериализоваyную таблицу Lua, которую при желании можно сжать двумя способами, поскольку она немного не экономична. Так при разных режимах в 8КБ таблицы может уместиться: Без сжатия - около 150 файлов Сжатие LZSS - около 580 файлов Сжатие Картой Данных - около 720 файлов Также можно сэкономить пространство таблицы, отключив хранение даты изменения файла, в таком случае все записанные файлы будут иметь дату изменения 0 (т.е. 00:00:00, 1 Января 1970 года). Краткое руководство: Загрузка ОС с кассет: Установка: pastebin run Tq3hbpaz Драйвер также доступен в MineOS App Market Репозиторий на GitHub Планы на будущее: Нормальное кодирование таблицы Шифрование таблицы. Сжатие и шифрование файлов. Ускорение работы файловой системы
  22. 12 баллов
    Каждый метод компонента в OpenComputers характеризуется его прямотой: есть прямые методы, а есть непрямые. Не раз в ирке разъяснял разницу. Сейчас расскажу и вам. Предположения: Текущая версия мода — 1.7.5. Вы собирали опенкомпьютер. Вы знаете, кто такой компонент, что у него за методы и как их вызвать. С непрямыми вызовами всё просто. Они уводят компьютер в сон на тик. Всегда, при любом условии, даже в случае ошибки, после вызова непрямого метода компьютер до следующего тика работать не будет. Прямые вызовы такого ограничения не имеют. Один такой вызов может занимать произвольное количество времени. Зависит это от бюджета вызовов. У компьютеров OC есть бюджет вызовов. Это скрытая безразмерная величина. Каждый тик она сбрасывается до определённого значения. Определено оно так: Сначала смотрим на процессор в компьютере. Точнее, на уровень: T1 соответствует 0.5, T2 — 1.0, T3 — 1.5. Затем на каждую из планок памяти. T1 или T1.5 — это 0.5, T2 или T2.5 — 1.0, T3 или T3.5 — 1.5. Получаем несколько чисел. Суммируем, делим на количество — находим тем самым среднее арифметическое, которое и будет максимальным бюджетом вызовов. Практикум: T2 процессор → 1.0 Планка T2.5 → 1.0 Планка T3.5 → 1.5 Бюджет вызовов: (1.0 + 1.0 + 1.5) / 3 ≈ 1.167. Пример второй: Т3 процессор → 1.5 Планки T3.5 → 1.5, 1.5, 1.5 Планка T2.5 → 1.0 Бюджет вызовов: (1.5 + 1.5 + 1.5 + 1.5 + 1.0) / 5 = 1.4. Достаточно. Каждый прямой вызов расходует этот бюджет. По умолчанию — ровно одну тысячную его. Самые последние дев-билды мода делают прямые вызовы по умолчанию абсолютно бесплатными. Когда бюджет уходит в минус, компьютер принудительно спит до следующего тика. Определяет прямоту метода разработчик. Для создания метода в коде мода используется аннотация li.cil.oc.api.machine.Callback. Примерно так (метод hologram.get): @Callback(direct = true, doc = """function(x:number, y:number, z:number):number -- Returns the value for the specified voxel.""") def get(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { ... } Если мы видим здесь direct = true, то это абсолютно точно прямой вызов. Если direct = false или отсутствует, то вызов непрямой. У метода может быть кастомная стоимость вызова. Не 0.001. У дата-карточки такое особенно. Пример (data.inflate): @Callback(direct = true, limit = 4, doc = """function(data:string):string -- Applies inflate decompression to the data.""") def inflate(context: Context, args: Arguments): Array[AnyRef] = { ... } limit = 4 читать как "потребляю 1/4 бюджета при вызове". Для сервера выше это 5 вызовов в тик. Недурно. У видеокарты всё сложно. Вообще, она тоже ограничивает ярость использования через бюджет вызовов. Потому на Т3-комплекте работать будет быстрее. Но количество потребляемого бюджета также зависит и от уровня видеокарточки. Для OC 1.7.5 распределение такое: Операция Стоимость T1 GPU T2 GPU T3 GPU setBackground 1/32 1/64 1/128 setForeground 1/32 1/64 1/128 setPaletteColor 1/2 1/8 1/16 set 1/64 1/128 1/256 copy 1/16 1/32 1/64 fill 1/32 1/64 1/128 Поэтому максимально можно вызвать 384 сета в тик. Чтобы программно определить прямоту методов, есть функция component.methods(addr). Отдаём ей полный адрес компонента, получаем таблицу. В ключах имена методов, в значениях их прямота. Или же можно воспользоваться этой таблицей. Она включает все методы всех компонентов, которые есть в OpenComputers. И наконец, размер бюджета можно сменить в конфиге. Опция budgetCosts занимается именно этим.
  23. 11 баллов
    Система предоставляет графическую оболочку для планшетов, имеющую минималистичный интерфейс и понятное только мне использование, а так же минимальное (надеюсь) потребление ОЗУ. Из фич оболочка дает: Возможность использования OpenOS частично без использования команд. Для особых случаев - используем контекстное меню -> "Выполнить команду" Возможность посылки уведомлений пользователю. Многозадачность не реализована, так что пассивную часть программы нужно активировать библиотекой thread из OpenOS Запуск программ-папок (*.pkg). Чисто для разграничения кода и возможности создания модулей Адаптивная отрисовка интерфейса. На экранах с разрешением по ширине, не кратной 20, могут возникать проблемы, однако без искусственного изменения разрешения такого не произойдет. Помощь в настройке при первом запуске. На случай проблем - на первом экране используется колёсико мыши. Блокировка экрана Горячие клавиши на главном экране (клик+delete - удалить, ctrl+e+клик - редактировать и подобное) QR-коды для быстрого доступа юзера к ссылкам В планах: Специальный фреймворк аля Zygote из андроида. Естественно абсолютно весь функционал переписывать не буду, однако основной останется. Этот фреймворк повлечет за собой полный рефакторинг кода (перевод системы на него), но полностью устранит все недостатки TabletOSNetwork - что бы было. Протокол сам в себе будет держать защиту от MITM (Сначала на DSA, потом переведу на ECDSA (реально сложно для меня пока)) и некоторую маршрутизацию с помощью специальных реле (что бы у юзеров планшеты не лагали). Установка - pastebin run 1xudmTa7 Выберите в установщике TabletOS и канал обновлений "Stable". В дальнейшем система будет уведомлять о обновлениях, при получении оного нужно будет зайти в настройки (контекстное меню в левом нижнем углу экрана) и там обновиться. В случае, когда при обновлении бросает ошибку - посмотрите изменения, там будут инструкции по ручному обновлению или переустановке системы. Если и это невозможно. переустановите систему. Данные должны сохраниться, а вот система - обновиться.
  24. 11 баллов
    В моде OpenComputers есть интересное устройство, которое позволяет определить плотность блока на расстоянии. Но вот беда, данные он выдает довольно шумные и чем больше расстояние, тем больше шума. Чтобы определить подлинную плотность блока, можно просканировать его несколько раз, а результат усреднить. Шум, мешающий сканированию, имеет вероятностную природу. И после нескольких сканирований можно статистически найти, какая вероятней всего плотность у блока. За один тик мы можем просканировать 64 блока. Чтобы проанализировать всю доступную область (65 x 65 x 64) сотней итераций, нам понадобится 422500 тиков, что равно 21125 секунд или 352 минуты, то есть без малого 6 часов. Но сколько раз надо сканировать? Сто? Тысячу? Нам открыто тайное знание и есть точный ответ. Один. Всего за одно сканирование мы можем найти руду среди любых других блоков. Если хочется абсолютной уверенности, придется сделать пару магических пассов и просканировать повторно. Начнем с теории. Для начала откроем код мода и найдем функцию geolyzer.scan, она располагается [здесь] src/main/scala/li/cil/oc/integration/vanilla/EventHandlerVanilla.scala и называется onGeolyzerScan() Просмотрев код, мы можем понять, что функция принимает параметры, по этим параметрам сканирует блоки в мире. Делает разные проверки вроде world.blockExists(x, y, z) && !world.isAirBlock(x, y, z), чтобы убедится, что блок есть. Потом получает информацию о блоке по координатам, делает еще несколько проверок (опять проверить, что блок все-таки есть block != null, проверяет дополнительные параметры: includeReplaceable, isFluid(block), block.isReplaceable(world, blockPos.x, blockPos.y, blockPos.z)) Потом происходит измерение расстояния до блока. И в конце берется плотность, смешивается с шумом и расстоянием. Результат добавляется к таблице блоков и отправляется игроку. Вроде-бы ничего необычного. Шум, расстояние, плотность. Нам и так известна зависимость силы шума от расстояния. И вот тут начинается волшебство. Рассмотрим поподробнее код вычисления итоговой плотности блока. e.data(index) = e.data(index) * distance * Settings.get.geolyzerNoise + block.getBlockHardness(world, x, y, z) Коротко можно это записать в виде формулы: R = G * D * N + H G - это сгенерированный шум. D - расстояние до блока. N - множитель шума из конфига (стандартно - 2). H - настоящая плотность. R - результат работы геосканера. Если мы попробуем в качестве эксперимента отнять от результата предполагаемую плотность, то ничего нового не узнаем. Если обратим все операции с известными значениями, то получим только шум. А можем ли мы так же разобрать формулу шума? Давайте попробуем. Несколькими строками выше [ссылка]. Можно наблюдать получение массива случайных байт. val noise = new Array[Byte](e.data.length) world.rand.nextBytes(noise) Далее следует нормализация значений. noise.map(_ / 128f / 33f).copyToArray(e.data) Хм. Так-так-так. Если мы это все обьеденим с предыдущей формулой, то получится что-то вроде такого: R = G(RANDOM_BYTE / 128 / 33) * D * N + H И что это нам дает? А то, что исходное псевдослучайное число имеет жесткую дискретность. ГПСЧ дает случайные числа типа byte, а это только 256 значений (-128, +127). Нам известны все значения, кроме H и RANDOM_BYTE, что нам это дает? Мы можем предположить значение H и обратить всю формулу. (R - H) / D / N * 128 * 33 Для стандартного конфига можно сократить до: 2112 * (R - H) / D А теперь тайное знание для тех, кто не понял самостоятельно. Мы взяли желаемую плотность блока (например 3 для руды). Подставили вместо H. Получили случайное значение. Можем легко определить, угадали ли плотность или нет. Из-за дискретности случайных значений генератора, распределение вероятностей для блоков с разной плотностью не одинаковое. Перейдем к практике. Вот код простого скрипта, который в заданном радиусе ищет блоки с нужной плотностью. Результат выводится на голопроектор. local sqrt = math.sqrt local component = require('component') local geolyzer = component.geolyzer local hologram = component.hologram local function distance(x, y, z) return sqrt(x^2 + y^2 + z^2) end local function magic(R, H, D) return 2112 * (R - H) / D % 1 end local function visualize(hardness, elevation, size) hologram.clear() hologram.setScale(9) local blocks, result for x = -size, size do for z = -size, size do blocks = geolyzer.scan(x, z, elevation, 1, 1, 32) for i_y = 1, 32 do result = magic(blocks[i_y], hardness, distance(x, i_y+elevation-1, z)) if blocks[i_y] ~= 0 and (result > 0.9998 or result < 0.00005) then hologram.set(x+24, i_y, z+24, true) end end end end end local hrd, ele, siz = table.unpack({...}) hrd = hrd or 3 ele = ele or -32 siz = siz or 16 visualize(hrd, ele, siz) А вот результат: При сканировании заметны артефакты. Когда разные плотности близки на целочисленных расстояниях, позникают коллизии. Это можно частично компенсировать, если есть блок кандидат на ошибку. На любом расстоянии можно рассчитать абсолютный минимальный и максимальный уровень шума. С расстоянием, у близких плотностей пересечение значений увеличивается, но если плотность блока не в области пересечений, то можно точно определить к какой области он относится. Пересечение плотностей руды (3) и камня (1.5), точками обозначены три сканирования блока руды. Результаты обратного вычисления для разных плотностей хорошо это демонстрируют. Для компенсации артефактов надо ввести дополнительное условие: полученный RANDOM_BYTE должен быть в диапазоне -128:127. Вот финальный скрипт и результат. local sqrt = math.sqrt local component = require('component') local geolyzer = component.geolyzer local hologram = component.hologram local function distance(x, y, z) return sqrt(x^2 + y^2 + z^2) end local function magic(R, H, D) return 2112 * (R - H) / D end local function visualize(hardness, elevation, size) hologram.clear() hologram.setScale(9) local blocks, result for x = -size, size do for z = -size, size do blocks = geolyzer.scan(x, z, elevation, 1, 1, 32) for i_y = 1, 32 do result = magic(blocks[i_y], hardness, distance(x, i_y+elevation-1, z)) if blocks[i_y] ~= 0 and result > -128 and result < 127 and (result%1 > 0.9998 or result%1 < 0.0002) then hologram.set(x+24, i_y, z+24, true) end end end end end local hrd, ele, siz = table.unpack({...}) hrd = hrd or 3 ele = ele or -32 siz = siz or 16 visualize(hrd, ele, siz) Для более точного определения плотности можно сделать два сканирования. Одно сместить относительно другого так, чтобы расстояния с артефактами не совпадали. Чтобы не выполнять тяжелую операцию sqrt, можно создать словарь, где [x^2 + y^2 + z^2] = sqrt(x^2 + y^2 + z^2), всего понадобится 1742 уникальных значений. P.S. Пост является компиляцией знаний из [этой] темы. Собрал, чтобы перевести и опубликовать на официальном форуме. Автор идеи хакнуть геосканер - @eu_tomat
  25. 11 баллов
    Почти с месяц назад я представил во флудилке пару скриншотов разрабатываемого мной браузера и приличному количеству людей оно тогда понравилось. Поэтому сегодня, спустя пол года кодинга, нескольких переходов с одного движка на другой и кучи потраченных нервов я могу наконец представить его вам. Знакомьтесь, Memphisto. Примите во внимание, что браузер находится в тестовой стадии, еще предстоит сделать много всего, поэтому возможно наличие неприятных багов (два глаза все не отловят). Основные характеристики: Используется движок NyaDraw, который является портом движка Screen из MineOS. Поэтому браузер весьма быстро (для таких ПК) отрисовывает и прокручивает страницы. Для страниц используется язык NFPL, специально разработанный с учетом особенностей OpenComputers. Поддержка гиперссылок, загрузочных ссылок, рисование векторной графикой и многое другое. Поддержка картинок формата OCIF5-8. А так же их использование в качестве графических ссылок. Возможность просматривать страницы оффлайн, то есть с жесткого диска (полезно при написании страниц). Для поднятия своего сайта достаточно обычного HTTP(S) сервера. Системные требования: Процессор: Уровень 1. Видеокарта: Уровень 3. ОЗУ: 1Мб или больше. Жесткий диск: не меньше 52Кб свободного места. Интернет карта Скачать: pastebin run EUZQRkwF Репозиторий на GitHub Полезное: Руководство по языку NFPL (Network Formatted Page Language). Первый NFPL сайт - bs0dd.github.io (запущен в тестовом режиме). Руководство по графическому движку NyaDraw. Увы, одного человека слишком мало для реализации задуманного, поэтому ищутся люди, хорошо понимающие в Lua, которые смогут помочь в устранении багов и расширении функционала как языка NFPL так и браузера для него.
  26. 11 баллов
    В 2015-2016 году тут разрабатывался файловый менеджер Midday Commander, который по неизвестным причинам оказался заброшен. Увы, он много чего не умеет, есть неприятные баги, однако других ФМ под компьютеры я не встречал (может плохо искал). Собственно, это и послужило появлением моего форка от оригинального MC. И имя ему Midday Commander Plus! Репозиторий на GitHub Основные нововведения: Поддержка видеорежима 160x50 Тени у окон (как у Norton-а) Поддержка манипулятора "Мышь" (в том числе колесика) Мультиязычность (языковые данные вынесены в отдельный .mcl файл) Поддержка тем (данные о цветах элементов вынесены в отдельный .mct файл) Система ассоциаций Параметры программы расположены в конфиг файле - /etc/mc.cfg Скачать: pastebin run pc73b8bB С программой поставляются: Файлы mcl для русского и английского языка (по умолчанию в конфиге стоит английский) Три mct темы - "Standart", "Redstone" и "Darkness" (увы, я не дизайнер, поэтому отсутствие вырвиглазности в темах не гарантировано) Краткая справка: Коротко про ассоциации:
  27. 11 баллов
    Coverett - библиотека для взаимодействия с HLAPI устройствами мода OpenComputers II. Что такое HLAPI устройство? Это новый тип устройств в OC2, к которому относятся большинство девайсов, вроде модулей для робота, красной карты, звуковой карты и прочих. Они управляются через "контроллер", который общается JSON сообщениями с компьютером через символьный файл-поток (по умолчанию "/dev/hvc0"). Для Lua автор уже добавил в систему библиотеку "devices", однако она не слишком быстра, как и Lua в целом. Для С (куда в первую очередь писалась библиотека) работа с JSON сообщениями - довольно утомительная задача. Coverett призван упростить общение с HLAPI устройствами в C и совместимых с ним языках. Разумеется, программы не станут от этого очень легкими в написании, C есть C. Однако, процесс разработки программ для OC2 заметно упростится. Сама по себе библиотека состоит только из "ядра" с набором стандартных функций. Наборы функций для устройств подключаются в виде отдельных "модулей" (папка devices) со своими заголовочными файлами (заголовки нужных устройств затем подключаются в исходнике программы). Поэтому, если вы очень хороший знаток C, то можете писать модули для своих устройств и подключать их к библиотеке при сборке. К сожалению на данный момент полноценная документация еще не готова (две руки - очень мало), но в заголовочных файлах библиотеки есть небольшие комментарии-описания (в формате doxygen), а также несколько программ-примеров, что немного поможет в работе с библиотекой. Также необходимо учесть, что библиотека находится в альфа-стадии. Поэтому не исключено наличие багов и (особенно) утечек памяти, поскольку ранее на C я практически не писал)). Репозиторий на GitHub. (Инструкция по сборке, а также собранная библиотека и программы находятся там) Немного про скорость:
  28. 11 баллов
    Иногда, особенно когда вы занимаетесь строительством реакторов, необходимо очень много укрепленного камня. Можно конечно и вручную залить нужную территорию, но согласитесь, лучше когда за вас это все сделает кто-то еще. Итак представляю вашему вниманию небольшую автоматическую систему по производству укрепленного камня. Вам всего то надо обеспечить эту ферму (генератор укрепленного камня) железными лесами, песком и энергией. Перейдем к постройке: Главным центром всей постройки является робот. Его устройство рассмотрим позднее. Справа от робота необходимо установить любую зарядку для бура/ваджары. Ну или если вы совсем бедные, и планируется что робот будет работать киркой, то установите сундук. Слева от робота устанавливайте Жидкостный/Твердотельный наполняющий механизм. Он заправляет распылитель сразу, за 1 прогон. Это будет наполнитель для зарядки распылителя. Рядом с зарядником распылителя рекомендую установить еще один Жидкостный/Твердотельный наполняющий механизм, который будет генерировать сжатую пену. В слоты улучшения обязательно установите "Выталкиватель жидкости". С подачей воды и смеси думаю разберетесь сами. Не забываем выставить настройки в механизмах как показано на скрине: За роботом нужна зарядка, чтоб он мог работать вечно! И не забываем её стукнуть ключиком, чтоб не заморачиваться с рычагами. Конвертор энергии не забудьте подключить к энергобуферу (или можете не устанавливать его, провода из ИС2 с ОС коннектятся) Над роботом установите сундук, и желательно побольше. В него необходимо засыпать железные леса и песок. Учтите, на 1 блок укрепленного камня необходимо 1 железные леса и 1 песок. Автоматизация засыпания расходников, уже на вас. Под роботом распологается сундук в который будет ссыпаться укрепленный камень. Так как наш робот будет нереально дешевым, не забываем сделать подставку на которой будет готовиться ваш камушек. Рекомендую вообще оградить это место, чтоб ни курица ни игрок не залезли перед роботом (иначе распылитель запенит все в округе). Собственно это все что необходимо вам знать при постройке этой фермы. Переходим к роботу. Программу я сделал для двух типов роботов, простейший Т1 и супердешевый на eeprom. Простой робот-генератор укрепленного камня: Супердешевый робот-генератор укрепленного камня: Настоятельно не рекомендуется лезть перед роботом иначе рискуете получить такое Не лезте своими шаловливыми ручками в слоты к работающему роботу. Можете забрать готовый камень, но расходники не трогайте, так как скорость смены инструмента огромная, и если робот что-то не найдет, вам грозит быть залитым в пену. (смотри скрин выше) Одной заправки распылителя хватает на 80 применений, потом робот автоматически перезаряжает инструмент и распылитель. А в остальном данная ферма работает довольно стабильно и исправно. А рассказал вам о ферме Asior, фармите укрепленный камень, гоняйте кур от фермы и удачи в тяжком труде реактостроителя! P.S. Укрепленный камень выдерживает ядерный взрыв, но радиацию сдержать увы не сможет. Осторожнее.
  29. 11 баллов
    Представляю вам опять программу для робота, которая позволяет добывать руду, не лазая по пещерам. Робот, используя геолизер, может самостоятельно находить и добывать руду. Реализованы еще не все возможности, поэтому прошу тестировать и сообщать мне о багах. Требования: Корпус компьютера (уровень II или III) Апгрейд инвентарь (больше - лучше) Апгрейд контроллер инвентаря Жесткий диск EEPROM с прошитым Lua BIOS Геосканер Память (уровень I или выше) Процессор (любой) Апгрейд полета (I уровень) Алмазная кирка или аналогичный инструмент. Опционально: Апгрейд верстак Беспроводная сетевая карта Апгрейд батарея Апгрейд опыта Апгрейд чанклоадер Апгрейд генератор Апгрейд солнечная панель Эндерсундук из мода EnderStorage Установка: Скачать и сохранить файл как init.lua wget https://raw.githubusercontent.com/DOOBW/geominer/master/miner.lua init.lua Закинуть этот файл в корень диска. Добавить диск при сборке робота. Установить робота на платформу из твердых блоков. Дать роботу кирку. Поставить возле робота контейнер и зарядник. Нажать кнопку питания и наслаждаться процессом.
  30. 11 баллов
    Когдато в далеком прошлом я и krovyaka написали прожки для казиношек. Но мы были слишком жадные что б выкладивать их в паблик и зажали ток себе. Но поже мы все перенесли на игт в паблик репозиторий и случилась бяка (их нашли и посливали друг другу). Теперь я думаю выкласть их здесь. https://github.com/lfreew1ndl/OpenComputers-Casino Валюта находится на отдельном сервере и вам что б запустить их нужно будет переписать durexdb.lua на свое хранилище валюты. (по назвах методов думаю поймете). Есть и деплоер который вам не поможет потому что вам нужно переписать durexdb.lua Какашками не бросайтесь мне просто лень переписивать для общего пользования но хочу выложить что б кто захочет сделал это и если ему не жалко заатачил переписаные файлики здесь или лучше даже кинул пул реквест на мою репу и я прийму его. Скринчики ниже. P.S. Для кого интерфейс вырвиглазный УХАДИ. Блек джек Больше меньше Видео покер Лабиринт Рулетка Терминал (на скрине не видно но игрок стоит на PIM)
  31. 11 баллов
  32. 11 баллов
    Предисловие Я думал на новый сервер запилить прогу — мост между чатом сервера и IRC. У меня уже были такие программки: я насчитал минимум 6 различных версий мостов — каждая была немного переделанным клиентом IRC, который на дискете встроенной есть. Понять, в чём разница, даже с вимдиффом было сложно. Потому я плюнул и решил запилить полноценную ирколибу с красивой апишкой. Как это выглядит Вот полный код бота — моста. Сто двадцать шесть строчек. Прокомментирую некоторые из них. ① Подключаем либу и для укорачивания имён ещё вытаскиваем events, в которых хранятся все ивенты и priority. ② Создаем клиент с помощью билдера. ③ Через :connection задаём настройки соединения. Самое важное — адрес иркосервера. Порт обязателен. ④ Ирколиба знает меру в флуде. Опасаться, что бота выкосит флуд-фильтром, можно гораздо меньше. Это опционально, конечно. ⑤ Задаём ник бота, юзернейм и реалнейм. Юзернейм виден в хосте (nickname!username@domain.name), а реалнейм пишется в /whois. ⑥ Ирколиба умеет авторизовываться на сервере. Тоже опционально. ⑦ Эта группа выделена для ботоводческих настроек. Но пока там единственная опция — в какие каналы автоматически заходить. ⑧ Здесь задаются настройки исполнения. Опция threaded, по дефолту включённая, запустит бота в отдельном треде. Опция reconnect, также включённая по умолчанию, заставит бота переподключиться к серверу, если отвалится от него. Опция catchErrors перехватит ошибки в пользовательких листнерах; она отключена по умолчанию, чтобы не смущать. ⑨ Бот генерит ивенты для каждого сообщения. Так мы задаём обработчик для ивента. К слову, вместо функции здесь может быть корутина. ⑩ Есть и другие события. Например, irc.events.client.connected означает, что клиент соединился с сервером. А irc.events.client.authenticated говорит, что теперь можно слать сообщения. ⑪ Когда мы закончили конфигурировать бота, собираем через :build(). Если вместо него вызвать :buildAndRun(), бот тут же ещё и запустится. ⑫ Для удобства создадим ещё один тред, где будем работать с чатбоксом и ждать ^C. ⑬ Запускаем бота. Затем ждём завершения любого из двух потоков. ⑭ Когда это произошло, мы выключаем клиент, если он ещё подключен: тот выйдет с сообщением "Quitting." ⑮ Наконец, принудительно останавливаем потоки. На всякий случай. Красота ведь. Репозиторий Репа либы — на нашем гитлабе. Там же есть примеры использования и документация с описанием всего. Наконец, версия 1.0.0 лежит на хеле. Из-за баги в OC хпм крашиться может (фиксить лень), но можно попробовать скачать: $ hpm install libirc
  33. 10 баллов
    Lost User - Самый Простой Робот Зачем? Роботы OC сложны в сборке и программировании. Эта программа BIOS помогает использовать роботов как "пользователей" и многими другими способами. Установка Сборка Соберите робота в минимальной конфигурации: Корпус ЦПУ ОЗУ E2E-E Если вы играете в Enigmatica 2: Expert - Extended, в модпаке есть предопределенный рецепт EEPROM. Найдите его в JEI и создайте. У него будет цветное свечение. Если вы его создали, можете пропустить следующий шаг Запись программы на EEPROM. Запись программы на EEPROM Скачайте файл из интернета (требуется ), запустите из командной строки: wget https://raw.githubusercontent.com/Krutoy242/lostuser/main/lostuser.min.lua Чтобы записать на существующий EEPROM, запустите: flash -q lostuser.min.lua LostUser Вставка в робота Возьмите EEPROM из корпуса компьютера и вставьте его в робота. Использование Программируйте робота, переименовывая его. Переименуйте робота на или с помощью . Назовите вашего робота robot.use(3), поставьте его на землю, включите и смотрите, как он кликает блоки перед собой. Синтаксис Кратко Если вы не хотите изучать Lua и вам нужно, чтобы робот кликал правой/левой кнопкой мыши, вот несколько простых имен для робота и результат: robot.use(3) - Робот будет кликать правой кнопкой мыши по блоку перед собой. robot.swing(3) - Робот будет махать мечом или ломать блок перед собой. Операторы и выражения Выполнение Робот будет выполнять свое имя как код Lua в цикле while true. Код может быть выполнен в любом варианте - оператор или выражение, но все равно должен следовать правилам потока кода Lua. Возврат Если выражение возвращает одну или несколько функций, они будут выполнены рекурсивно. Обратите внимание, что все возвращаемые значения рассчитываются сначала, и только потом будут вызваны функции. Глобальные переменные Все компоненты доступны как глобальные переменные. Компоненты сортируются естественным образом и добавляются в глобальные переменные по первой большой букве. C => computer E => eeprom I => inventory_controller R => robot T => trading ... Дополнительные глобальные переменные: i - текущий индекс цикла, начиная с 0. sleep(seconds: number = 1) write(...) - ошибка с сериализованным выводом. api(shortName: string, obj?: table) - написать полное имя сокращения. Сокращение Поскольку имя робота или дрона может содержать только 64 символа, указатели должны быть сокращены. Таким образом, вместо написания полного имени указателя, вы можете его сократить. Например, вместо написания robot.use(3), вы можете написать r.u(3) или даже Ru3. Правила сокращения: Если ключ имеет точное не-nil совпадение, оно будет возвращено. Сокращение должно содержать первую букву, а затем, по желанию, любое количество оставшихся букв. Если несколько имен имеют одинаковую первую букву, будет выбрано самое короткое, алфавитно отсортированное имя. Большая первая буква с точкой . может использоваться без точки. Число в конце сокращения будет вызывать сокращение как функцию с этим числом в качестве первого аргумента. В то же время, если это таблица, а не функция, все ключи таблицы будут естественно отсортированы и возвращен N-й элемент. Смотрите больше в Числовом Словаре. Локальные переменные не могут быть сокращены. Lodash _ Специальная вспомогательная функция нижнего подчеркивания _. Индексация _ Использование _ с числами _123 Вернет новый список-массив с длиной числа. Если первая цифра 0, таблица будет с нулевым индексом. Использование _ со словами _abc Создает функцию, которая будет записывать результат в переменную abc. Функция возвращает переданное значение. Обратите внимание, что _abc функциональна. Вызов _ Использование _ на строке Загрузит код внутри этой строки и вернет его как функцию. Вызов этой функции всегда безопасен для ошибок — если внутри произойдет исключение, функция просто вернет nil. Использование _ на таблице или функции Преобразует их в специальную таблицу _{} или функцию _'' для использования с Функциональным Программированием. Функциональное Программирование Любая таблица или функция, которую вы можете получить из глобальной переменной, будет преобразована в специальную таблицу _{}. Эта таблица улучшена дополнительными метаметодами операторов, которые помогают с функциональным стилем программирования. Любая итерация или вызовы pairs() на этих преобразованных таблицах будут выводить элементы в естественно отсортированном порядке. Операторы ведут себя по-разному в зависимости от левой и правой стороны оператора. Обратите внимание, что когда обнаруживается строка, она загружается и преобразуется в функцию по принципу _'fnc'. Приоритет Приоритет операторов в Lua следует таблице ниже, от высшего к низшему приоритету: ^ унарные not # - ~ * / // % + - .. << >> & ~ | < > <= >= ~= == and or Map ^, +, или & ^, +, и & операторы делают то же самое. Их три, только для управления приоритетом. Примечание¹: ^ ассоциируется справа. Это означает, что сначала будет вычислена правая сторона. Примечание²: Вы также можете вызывать невызываемые таблицы. t(x) то же самое, что t^x. Невызываемые таблицы — это таблицы без метатаблицы __call. Пример (map t^f): _{1,2,3}'0' -- _{0,0,0} Левая сторона Правая сторона Результат Таблица Функция Классическая карта _{4,5,6}^f -- {f(4),f(5),f(6)} Таблица Выбор индексов _{4,5,6}^{3,1} -- {6,4} Число, Булево значение Добавление значения в КОНЕЦ таблицы _{1,[3]=3,a=6,[4]=4}^5 -- _{1,3=3,4=4,5=5,a=6} Функция Функция Композиция f^g -- (...)=>f(g(...)) Таблица Распаковка как аргументы f^{1,2,3} -- f(1,2,3) Число, Булево значение Простой вызов f^1 -- f(1) Число, Булево значение Таблица Получение по числовому или булевому индексу 2^_{4,5,6} -- 5 Функция Не реализовано Лямбда - / | Левая сторона Правая сторона Результат Таблица Функция Фильтр, оставляет только значения, являющиеся Истинными _{4,5,6,7}/'v%2' -- {5,7} Таблица Не реализовано Число, Булево значение Удаление индекса _3/2 -- {1=1,3=3} Функция Функция Обратная композиция f/g -- (...)=>g(f(...)) Таблица Простой вызов f/R -- f(R) Число, Булево значение Композиция f/1 -- (...)=>f(1,...) Число, Булево значение Таблица Получение по модулю i/t -- t[i % #t + 1] Функция Вращающаяся композиция 2/f -- (...)=>f(..., 2) Цикл ~ или * Левая сторона Правая сторона Результат Таблица Функция Не реализовано Таблица Не реализовано Число, Булево значение Не реализовано Функция Функция Пока истинно, делай f~g -- пока истинно(g(j++)) делай f(j) Таблица Не реализовано Число, Булево значение Цикл for f~n -- for j=1,TONUMBER(n) do f(j) end Число, Булево значение Таблица Не реализовано Функция То же, что и f~n, но без передачи индекса n~f -- for j=1,TONUMBER(n) do f() end Унарные операторы Унарный оператор Объект Результат ~ Функция Пока истинно, делай ~f -- повторять пока не истинно(f()) Таблица Сглаживание таблицы, используя числовые индексы. ~_{1,{2,3},{4,a=5,b={6,c=7}}} -- {1,2,3,4,5,{6,c=7}} - Функция Создать функцию, результат которой будет инвертирован. Если результат истинный, возвращает 0. Возвращает 1 в противном случае. -- id здесь функция, возвращающая свой первый аргумент (-id)(0) -- 1 (-id)(4) -- 0 (- -id)(4) -- 1 Таблица Обмен ключей и значений -_{'a','b','c'} -- {a=1,b=2,c=3} # Функция Создать функцию, которая будет оборачивать свой результат в таблицу. Полезно для функций, возвращающих несколько значений. -- Предположим, `f(n)` возвращает три значения - 2,3,n f&4 -- 2 #f&4 -- _{2,3,4} Истинные значения Знач ение считается "истинным", если оно не является "ложным". "Ложные" значения: false или nil '' (пустая строка) 0 (ноль) nan (не число, n ~= n) inf или -inf (результат 1/0 или -1/0) Макросы В программе есть несколько предопределенных макросов - символов, которые будут заменены везде на другой текст. ! => '()' ⓐ => ' и ' ⓞ => ' или ' ⓝ => ' не ' ⓡ => ' вернуть ' ⒯ => '(истина)' ⒡ => '(ложь)' Примеры Перемещение между двумя точками и выполнение их метки Имя дрона: P=i/Nf300ⓡDm^Pp,s/1~'Dg0>1',_(Pl) Nf300: Выполнить navigation.findWaypoints(300). i/Nf300: i - индекс выполнения скрипта. i / table - "Получение по индексу модуля" t[i % #t + 1]. P=i/Nf300: Записать в глобальную переменную P различные точки каждый цикл скрипта. ⓡ: будет заменено на вернуть Dm^Pp: вызов drone.move(table.unpack(P.position)). s/1~'Dg0>1' => пока drone.getOffset() > 1 делать sleep(1). _(Pl): Загрузить P.label как код Lua. Эта загруженная функция будет возвращена и выполнена. Метки точек. Первая просто всасывает снизу, вторая итерирует по 4 слотам и сбрасывает вниз. _'Dsk0'~4 Dsel-'Dd0'~4 Зигзаг + Использование вниз, полезно для ферм Имя робота: m,t=_'Rm3,Ru0',Rtn/(i2>1)ⓡ~m,t!,_'m!,t!'!ⓞt/m m,t=_'Rm3,Ru0',Rtn/(i2>1): определить две функции для движения и поворота _'Rm3,Ru0': определить функцию Rm3,Ru0, которая будет двигаться вперед и использовать инструмент вниз Rtn/(i2>1): это создает функцию, которая будет вызывать Rtn (robot.turn) с аргументом i2>1. i2 - сокращение для i%2+1 ~m: Заставляет робота двигаться вперед, пока он не сможет двигаться. t!: просто поворачивает _'m!,t!'!ⓞt/m: Двигаться и поворачиваться. Если движение не удалось, повернуться и двигаться снова. Торговый бот Имя робота: Rsel-'Rd0'~RiS0,IsF/0~Igz0,Tg0'~tr' Rsel-'Rd0'~RiS0: Выбрать каждый слот и сбросить вниз IsF/0~Igz0: Для каждого слота инвентаря снизу inventory_controller.getInventorySize(0) вызвать inventory_controller.suckFromSlot(0, k) Tg0'~tr': Торговать всеми товарами. Есть другой вариант имени робота, более продвинутый. Он будет забирать только предметы, которые действительно требуются для торговли. Эта программа жестко запрограммирована для работы с внутренним и внешним инвентарем размером 16: -- Торговать всем a=-~Tg0"_{g!}'n',~tr"ⓡ_16&R16-'Rd0'&IgI/0&'a[n]ⓐI8/0&k' -- Не продавать изумруды [id==388] a=-~Tg0'388^-g0ⓞ{g0.n,~tr}'ⓡ_16&R16-'Rd0'&IgI/0&'a[n]ⓐI8/0&k' Создатель рун Поместите ингредиенты в первые 6 слотов робота. Живой камень на 7-м, палочку на 8-м. Имя робота: _8/'Rsel^v,v==7ⓐ{s3,Rm1,Rd(3,1),Rm0}ⓞ{Ie!,Ru3,Ie!}' Rsel^v: Выбрать слот по очереди v==7ⓐ{s3,Rm1,Rd(3,1),Rm0}: если это 7-й слот с Живым камнем, подождать 3 секунды до завершения крафта, затем сбросить Камень сверху. Ie!,Ru3,Ie!: Для других слотов - просто кликнуть правой кнопкой мыши с предметом Ферма одиночных деревьев Этот робот предназначен для использования с саженцами Forestry, которые обычно не могут быть поставлены как блоки, но требуют клика правой кнопкой мыши для посадки. Кроме того, роботу нужен неразрушимый Широкий Топор из TCon с чертой Глобальный Путешественник. Кроме того, мой топор имеет черту Удобрение - клик правой кнопкой мыши для удобрения. Поставьте робота на контейнер с саженцами. Имя робота: #(1|#Rdt&3)<6ⓐRsw/3-s/1-Rsk/0-Ie-Ru/3-IeⓞRu3,s (1|#Rdt&3): Обнаружить блок перед собой, выбрать второе возвращаемое значение - описание блока #()<6: хитрость, чтобы определить, является ли блок твердым Rsw/3-s/1: Срубить все дерево, подождать 1 секунду Rsk/0-Ie-Ru/3-Ie: Всосать саженец снизу, затем посадить его. Обратите внимание, что Rsk получает одно значение от sleep возврата Ru3,s: Удобрить саженец Другие примеры Круговой шахтер. Используя Молот с частью из Алумита (черта Глобальный Путешественник). Поставьте Робота под землю, поместите стак Блоков Угля в выбранный слот робота. Робот начнет кружить вокруг, добывая все на своем пути. Gi,_'Rm3,Rsw3'~i*2,Rtn⒯ Робот для сортировки дропа мобов. Берет снизу, предметы, подлежащие повреждению, вверх, остальные - вперед. Rd|3%2^(IsF(0,i%Igz0+1)ⓐIgSII!.mDⓞ2) Открыватель кошек. Берет 16 предметов перед собой, кликает их правой кнопкой мыши, затем сбрасывает инвентарь вверх. Rsk/3&16ⓐIe!,~_'Ru0',_16/Rc|Rsel/'Rd1' Компрессионный бот. Берет спереди, крафтит 3x3, затем сбрасывает назад. -(_16-Rc&12)|'Rd3'&Rsel,IsF/3/'_11/8/4&Rc!/9/RtT'|i81,Cc Бот для неразложимых предметов. Берет предмет перед собой только если они неразложимы и кладет их наверх. Если не может сбросить предмет наверх, толкает вверх и ставит блок. (IgSI/3&_a^i1728ⓞ{}).mS^_{_'IsF/3&a,Rd1ⓞ{Pps1,Rsel9,Rp1,Rsel1}'} Дополнительно Ссылки Репозиторий с исходным кодом и readme Модпак, для которого был запрограммирован этот робот: Enigmatica 2: Expert - Extended Старое описание: LiMWFlH.mp4
  34. 10 баллов
    https://pastebin.com/4BXZP7FZ Использует шрифт Брайля Скрины: Пример:
  35. 10 баллов
    Очередная сеть на OpenComputers модемах. Децентрализованная, одноранговая, как Zn, только лучше. Каждый узел сети видит все остальные и может отправить/принять сообщение на любом расстоянии. Отличия от сети Zn: Динамическая маршрутизация. Zn затапливает все узлы каждым сообщением. Moonlink же передает сообщение по самому короткому пути и никто, кроме промежуточных узлов, сообщение не видит. При изменении топологии сети, маршруты перестраиваются. Можно передавать больше одной строки за раз. Установка: wget https://raw.githubusercontent.com/OpenPrograms/zgyr-Programs/master/moonlink/moonlink.lua /usr/lib/moonlink.lua или через oppm oppm register OpenPrograms/zgyr-Programs oppm install moonlink Функции: connect([порт: number]) подключиться к сети, стандартный порт 8431, можно задать свой. disconnect() отключиться от сети. send(адрес: string, ...) послать сообщение узлу с указанным адресом. ping() принудительный пинг соседей (запрос маршрутов у соседних узлов). list():table получить список доступных узлов в сети. Событие только одно: moonlink_message(receiver_addr: string, ...) адрес получателя и сообщение. Пример использования: local event = require('event') local moonlink = require('moonlink') moonlink.connect() while true do local _, address, message = event.pull('moonlink_message') if message == 'bye' then break else print(message) end end moonlink.disconnect()
  36. 10 баллов
    Обновление 2.2: Исправлен баг: вместо удаления файла/папки вылетала ошибка Добавлена новая опция: "Flash" - теперь есть возможность прошить файл на EEPROM (конечно, если файл не больше 4 КБ) Кроме прочих мелких багов, которые я не розглашал, главным нововведением будет: MicroBT - бутлоадер, который позволяет многоразово перепрошивать микроконтроллер или дрон по воздуху! Установка: Вставляем чистый EEPROM в комп Заходим в ALT -> EEPROM -> Install MicroBT -> Yes и ждём Запоминаем адрес EEPROM, вынимаем и вставляем в Микроконтроллер или дрон (Беспроводная плата должна быть обязательно!) Запускаем свой девайс (должен пикнуть) Использование: Пишем прошивку (Должна быть не больше 3.5 КБ) Выбираем файл и переходим: CTRL -> Flash -> Throught the MicroBT (В компе тоже должна быть беспроводная плата!) Ищем адрес EEPROM и жмём Enter (должен снова пикнуть и выключиться) Запускаем девайс (должен пикнуть и выполнять вашу прошивку) Если нужно переписать прогу - выполняем всё с 1 пункта Требования по прошивке: Размер не больше 3.5 КБ Если в прошивке не используется computer.pullSignal() - Бутлоадер не будет работать! Утилита еще сырая, так что если нашли баги - смело пишите их сюда На этом всё, мне уже пора снова исчезнуть на несколько месяцев... пока.
  37. 10 баллов
    В связи с тем что компоновка OT мне не нравится, было решено сделать ребрендинг ОТ. Фичи из ОТ будут перенесены и доработаны, не все конечно. Здесь мы будет информировать вас о грядущих обновлениях, здесь принимаем фидбек, идеи, баги, так же можем иногда выкладывать видео-ролики с интересными фичами мода, например как этот ниже. Разработчики мода: @NEO @vx13 Большое спасибо @LeshaInc за модель солнечной панели. GitLab: https://gitlab.com/Moon1Light/oma Сырая версия для ознакомления .
  38. 10 баллов
    https://pastebin.com/jLkwQgHa Набор моделей символов, размером в 1 блок. Широкие, жирные, самое то для всяких вывесок.
  39. 10 баллов
    Сервер STEM был обновлён до 0.2.4 и перезагружен. (Прошу прощения у всех у кого посыпались коннекты. ) Был исправлен баг, который не позволял посылать пакеты длиной более 1.5 килобайт. Теперь можно без проблем слать пакеты до 64 кб размером (это уже ограничение протокола STEM - если нужны пакеты жирнее, просто разбивайте данные на чанки). Кроме того, я чуть чуть обновил OC клиент stem.lua - теперь можно делать просто stem.connect() не указывая никакого адреса, и библиотека законнектит на stem.fomalhaut.me. Спасибо @BrightYC за то что как обычно пинал и требовал фиксов, спасибо @Fingercomp и @NEO за помощь в установлении причин, и конечно же спасибо @Totoro за то что баг обнаружил и исправил несмотря на жестокую лень.
  40. 10 баллов
    Версия для OpenOS: HEL Repository Standalone версия (для хардкорщиков): (еще портируеться...) Системние требования: минимальные Программа представляет из себя простенький монофонический трекер (редактор мелодий) для стандартной пищалки из Opencomputers. Доступно 256 строков и где-то 5 октав (A0 - B6) из за ограниченого диапазона частот динамика (20 - 2000 Гц). Из недостатков могу упомянуть: Нельзя остановить мелодию во время проигрывания. Нельзя изменить темп Максимальная длительность трека - 16 секунд (хотя это можна исправить) Планы на будущее: Замутить мини-библиотеку, чтобы была возможность использовать мелодии в своих программах Начать грызть и изучать звуковые карты из CX и написать более продвинутый трекер Клавиши: Up, Down - навигация по дорожке Left, Right - уменьшить/увеличить октаву Space - Поставить паузу на дорожке Delete - Удалить ноту F1 - Помощь Расположение нот на клавиатуре:
  41. 10 баллов
    Упал, отжался, отозвался. Да, человечество уже давно мечтает о программе, которая позволит легко создавать lua скрипты. Но дальше разговоров дело не продвинулось до сих пор. Создавать lua скрипты стало даже сложнее, чем 20 лет назад. Обязательно! Но только если создавать lua скрипты в такой программе и вправду будет легко. В идеале нужно максимально упростить интерфейс. Если можно, оставить лишь две кнопки: "создать lua скрипт" и "создать графический интерфейс", а всё остальное программа должна сделать сама. Это очень бы упростило бы процесс создания lua скриптов и графических интерфейсов. Взаимно)
  42. 10 баллов
    По этой теме уже существует отлаженный софт, успешно используемый многими игроками. Однако на днях я заметил забавную особенность местных видеокарт, позволяющую выставлять разрешение большее, нежели получаемое через gpu.maxResolution(). К примеру, если maxResolution для видеокарты третьего уровня вернет числа 160 и 50, то никто не мешает установить разрешение, скажем, в 20x158 пикселей. При этом при проверке валидности устанавливаемого разрешения соблюдается два правила: Результирующее разрешение по числу пикселей не должно превышать результат умножения чисел, возвращаемых maxResolution. То есть для T3 GPU не более 160 * 50 = 8000. Каждый параметр разрешения, будь то ширина или высота, не должен численно превышать значение возвращаемой ширины. То есть для T3 GPU не более 160. Не знаю, баг это или фича, однако подобное грех не использовать в своих целях. К примеру, старая версия программы при вертикально-удлиненном расположении мониторов позволяла выставить разрешение лишь в 30х50 (1500 пикселей в итоге), заполняя тем самым "черные полосы". В то же время обновленная софтина, учитывающая описанные выше особенности, выдает 69x114 (7866 пикселей), максимально приближаясь к предельной отметке в 8000: Согласитесь, лишние пиксели на дороге не валяются, и, думаю, кому-то будет интересно узнать про реализацию подобного софта. Прежде всего нам требуется определить точную пропорцию мультиблочного монитора: то, как относится его ширина к высоте. Взглянем на текстуру блока: она явно состоит из мелких "квадратиков" в количестве 16 штук, причем лицевая часть монитора имеет рамку толщиной в 2 "квадратика" с каждой стороны: Эти "квадратики" мы и будем использовать для расчета пропорции, так как они позволяют идеально точно получить размеры отображающей части монитора в игровом мире. А размеры "в квадратиках" можно посчитать по формуле: число_блоков_монитора * 16 - 4 Следовательно, пропорция монитора будет считается следующим образом: local gpu = component.gpu local screen = component.proxy(gpu.getScreen()) local blockCountByWidth, blockCountByHeight = component.screen.getAspectRatio() local proportion = (blockCountByWidth * 16 - 4) / (blockCountByHeight * 16 - 4) Также не забываем, что высота каждого псевдографического пикселя при отображении в 2 раза больше его ширины, поэтому формула пропорции слегка меняется. Заодно произведем некоторые сокращения, чтобы избавиться от лишних математических операций. Все три варианта эквивалентны: local proportion = 2 * (blockCountByWidth * 16 - 4) / (blockCountByHeight * 16 - 4) local proportion = (blockCountByWidth * 16 - 4) / (blockCountByHeight * 8 - 2) local proportion = (blockCountByWidth * 2 - 0.5) / (blockCountByHeight - 0.25) После вычисления пропорции монитора можно приступить к написанию программной логики. Для начала получим максимальное разрешение видеокарты: local maxWidth, maxHeight = gpu.maxResolution() Обладая этими данными, а также взглянув на условия, описанные в начале поста, мы можем составить систему неравенств для получения итогового идеального разрешения: Помним также, что ширину мы вполне можем выразить через высоту и пропорцию монитора: width = proportion * height Подставим этот вариант в систему: Оставим высоту в левой части неравенств: Подставляем имеющиеся переменные в каждое неравенство, рисуем числовую прямую и выбираем высоту, удовлетворяющую условиям неравенства для конкретного случая. Разумеется, в программировании никаких числовых прямых нет, зато есть любимые math.min() и math.max(): local height = math.min( maxWidth / proportion, maxWidth, math.sqrt(maxWidth * maxHeight / proportion) ) -- Выражаем ширину через пропорцию local width = height * proportion Все. Разумеется, полученные значения ширины и высоты нужно округлить в меньшую сторону, дабы не кормить видеокарту дробями. Заодно добавим поддержку масштабирования, чтобы выставлять половинное или, скажем, четвертичное от идеального разрешение. Сократив код, избавившись от лишних переменных, мы получаем следующее: local component = require("component") -- Получаем масштаб в качестве первого аргумента скрипта и корректируем его значение local scale = tonumber(select(1, ...) or 1) if not scale or scale > 1 then scale = 1 elseif scale < 0.1 then scale = 0.1 end local gpu = component.gpu local blockCountByWidth, blockCountByHeight = component.proxy(gpu.getScreen()).getAspectRatio() local maxWidth, maxHeight = gpu.maxResolution() local proportion = (blockCountByWidth * 2 - 0.5) / (blockCountByHeight - 0.25) local height = scale * math.min( maxWidth / proportion, maxWidth, math.sqrt(maxWidth * maxHeight / proportion) ) -- Выставляем полученное разрешение gpu.setResolution(math.floor(height * proportion), math.floor(height)) Надеюсь, это микро-знание кому-то было полезно. Лично я очень доволен, что могу наконец запилить графонистый интерфейс для контроля реакторов на вертикальных мониках без осваивания профессии "глиномес", да и соответствующая либа для автопобора разрешения в оське пригодится.
  43. 9 баллов
    обменник руды с поддержкой словаря руд ( ore dictionary ) https://pastebin.com/jaYhuD0k или pastebin get jaYhuD0k exchange требуется мод OpenPeripheral после запуска обязательно снять клавиатуру с экрана Ложем в сундук руду (например медную индастриал крафт и получаем два медных слитка индастриал крафт или меди из других модов) множитель Х2 можно настроить под отдельные руды слитки будут в том же сундуке, обменник заберёт ровно столько руды сколько может обменять слитков из МЕ на экран выводится список принимаемой руды, множитель, а также сколько слитков доступно в МЕ сети также к МЕ можно поставить переработчик руды (дробилка + печка) чтоб сеть сама пополняла запас слитков Поскольку список руд ручками составить тяжело, написал прогу для формирования списка https://pastebin.com/0JSr91DQ или pastebin get 0JSr91DQ list Требования: пк второго уровня (золотой) видеокарта второго уровня адаптер 2 штуки база данных первого уровня МЕ интерфейс сундук Пример сборки:
  44. 9 баллов
    Продолжу рассказывать про знаки препинания. В этом посте — 3 разных истории про пару круглых скобок. 1. Вызовы функций Если функция вызывается с одним аргументом — строковым или табличным литералом, то скобочки необязательны. local function identity(x) return x end print(identity "test" == "test") print(table.unpack(identity {"test"}) == "test") Это чисто синтаксическая фишка, которая никак не влияет на исполнение кода. Очень удобно, чтобы вызвать функцию и передать ей таблицу с опциями. local logger = getLogger { name = "main", level = "info", output = {stdout}, } Если несколько литералов так разместить подряд, получится ряд последовательных вызовов: myFunc "hello" "world" {"how do you do"} -- myFunc("hello")("world")({"how do you do"}) Используя эту фичу, можно воплотить всякие норкоманские вещи. Как вам вот такой форматтер с интерполяцией? local myVar = 42 print(format "myVar = " {myVar} ", and I'm running " {_VERSION} ()) --> myVar = 42, and I'm running Lua 5.3 2. Ещё про литералы У всех строк есть метатаблица, у которой __index = string. Это значит, что можно вместо string.gsub(str, ...) писать str.gsub(str, ...), или str:gsub(...). Очень удобно, особенно последнее. Но вот просто так заменить str литералом нельзя. "test":gsub(...) — синтаксически неправильный код. Выручат скобки вокруг литерала: ("test"):gsub(...). Постоянно этим пользуюсь. Та же ситуация, если мы хотим проиндексировать табличный литерал: {foo = "bar"}.foo выдаст ошибку. Лечится аналогично: ({foo = "bar}).foo. Кроме индексации, скобочки нужны при вызове: вместо function() return 42 end() нужно писать (function() return 42 end)(). Наконец, есть ещё литералы численные: 42, например. В обычной Lua оборачивать их в скобки смысла, пожалуй, и не имеет, но с небольшим шаманством опять потребуются скобочки: debug.setmetatable(0, {__call = function(self) print(self) end}); (42)() --> 42 Правда, в OpenComputers отключён debug.setmetatable. 3. Функции с множественным выхлопом В Lua функция может вернуть несколько значений: local function test() return 1, 2, 3 end print(test()) --> 1 2 3 Однако бывает, что нужно достать только одно значение, а про остальные забыть. Для этого нужно обернуть в скобки вызов функции, вот так: print((test())) --> 1 Скобочки возьмут только первое значение и отбросят остальные. С помощью функции select можно выбрать и другое по счёту: local function identity(...) return ... end print((select(3, identity(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)))) --> 8
  45. 9 баллов
    Недавно возникла проблема с тем, что понадобилось быстро загружать репозитории с несколькими файлами + я хотел поиграться с реквестами. Решение воспроизвелось в виде Ziphyr (или зефира) - работающий на апи гитхаба (сори юзеры гитлаба) аналог git. Утилита нужна для обновления и скачивания целого репозитория, так как для остального уже существуют OPPM или wget сырых файлов с гитхаба. При клоне рекурсивная функция проверяет весь репозиторий и скачивает по очереди файлы, сохраняя их хэши. А при нужде пулла прога сверяет хэши и загружает последние версии файла. Работает с пробелами, так что люди, ставящие пробелы в названии .app тоже не помеха. # Текущие функции 1. Клонирование репозиториев по веткам/тегам/коммитам 2. Пулл репозиториев # Будущие функции (расположены по возрастанию даты реализации) 1. Пуш репозиториев 2. Диффы между версиями файла 3. Мультитрединг 4. Поддержка гитлаба # Установка: pastebin run 1BNSnN2X В бандле идет скрипт env, который упрощает создание переменных среды, а также библиотеки json и sha1 # Использование: ziphyr clone KoshakLoL/Ziphyr - клон репозитория в [ТЕКУЩАЯ_ПАПКА]/[НАЗВАНИЕ_РЕПОЗИТОРИЯ] ziphyr clone -d KoshakLoL/Ziphyr - клон реопозитория в [ТЕКУЩАЯ_ПАПКА] ziphyr clone --dir=[КАКАЯ-ТО ПАПКА] KoshakLoL/Ziphyr - клон реопозитория в [КАКАЯ-ТО ПАПКА] ziphyr clone --ver=[ТЭГ/РЕЛИЗ/КОММИТ] KoshakLoL/Ziphyr - клон репозитория в [ТЕКУЩАЯ_ПАПКА]/[НАЗВАНИЕ_РЕПОЗИТОРИЯ] с какой-либо версией ziphyr pull [КАКАЯ-ТО ПАПКА] - для пулла репозитория, содержащегося в [КАКАЯ-ТО ПАПКА] Все остальное (включая лицензию) можно найти на соответствующем репозитории (программа находится в еще очень ранней версии, с говнокодером в размере одного человека, так что... не судите строго) (за частичную реализацию директорий спасибо MrAbad)
  46. 9 баллов
    У многих новичков возникает вопрос - где писать код для OpenComputers? Кто-то советует VS, кто-то сторонник IDEA, кому-то по душе Atom, а кто-то вообще пишет код сразу в игре. Я решил пополнить этот список вариантов собственным решением. Вашему вниманию представляется интегрированная среда разработки (ИСР) под OpenComputers - OCLIDE. Проект находится на стадии публичного альфа-тестирования, поэтому критика приветствуется. На данный момент в ИСР присутствуют: - Подсветка Lua-синтаксиса - Система проектов - Интеграция с OCEmu (OpenOS 1.7.5) - Поддержка Windows 10 и Ubuntu 20.04 В планах на будущее: - Автодополнение кода - Добавление эмулятора на базе Ocelot Brain - Адаптация кода для работы на Mac - Опции настройки цветовой палитры редактора и используемого стандарта Lua - Поддержка сторонних переводов приложения Ссылка на проект: https://github.com/Vladg24YT/Oclide/ (ветка master - стабильная, ветка oclide-rc1 - indev сборки) Прога весит ~25 Мб, потребляет 167 Мб ОЗУ. Скриншот:
  47. 9 баллов
    Приветствую всех физиков-ядершиков и просто тех, кто мимо проходил! Наверняка многие из вас ставили в подвале дома несколько ядерных реакторов из мода IC2. И конечно-же на своей шкуре ощущали какого находиться в комнате с ними. Вечные пожары, радиация так и лезет из всех щелей, провода кусаются от перегрузок... Короче жуть да и только, хочется забетонировать их и забыть. Но возникает вопрос, а как-же управлять ими? Вот тут-то вам и пригодится моя программа по мультиконтролю. Для начала разберем что нам надо для её работы. Компьютер/сервер любой конфигурации (2 шт), с предустановленной OpenOS, беспроводной платой (Т2), и интернет платой для установки Реактор/ы (от 1 до 20) Адаптер и Контроллер красного камня (по 1 шт на реактор) Индикатор, лампа, что угодно, что умеет по редстоуну светиться Приступаем к установке оборудования на реактор. Ставить адаптеры можете как хотите, 1 адаптер на 1 реактор или 1 адаптер на 2 реактора, это неважно. Главное устанавливайте адаптеры так, чтоб он присоединялся к реактору только 1 стороной. Контроллер ставить можно как угодно, главное сами при настройке со сторонами не запутайтесь. Ну а где спрятать компьютер, сами думаю решите. Примеры расположения на картинке. Самые внимательные наверняка заметили лампы. Они нужны только для настройки порядкового номера реактора и не более. После настройки можно спокойно их демонтировать. Сделано так, чтоб не лазить по куче реакторов и не прислушиваться, какой же запустился. Переходим теперь к компьютеру - контроллеру, это тот который стоит у реактров и отслеживает их показатели. Запускаем и скачиваем следующие файлы: pastebin get iSSt1T59 setting_reactor.lua pastebin get Db76AbMg reactor_control.lua Естественно настройка реакторов начнется с файла setting_reactor. Запускаем его, и проходим все этапы настройки, там ничего сложного нет. Вырубаем все реакторы, указываем с какой стороны контроллеру подавать редстоун сигнал, и далее просто глядя на индикаторы выставляете порядковый номер реакторов. В конце укажите номер диапазона, чтоб программа могла связаться с управляющей программой. По окончании настройки запускаем программу reactor_control и бежим быстренько ко второму компьютеру. Тут все аналогично, но качать меньше: pastebin get FTgh6qRb reactor_desk.lua Запускаем, вас попросят при первом запуске указать номер диапазона для связи, надеюсь вы его помните. И на этом установка программы завершена. Экран автоматически подстроится под то количество реакторов, которое у вас есть. Как подгоняется экран можете видеть на gif Управлять реакторами просто, нажмите на тот, который вы хотите включить/выключить и через секунду он запустится/отключится. На кнопках показывается основная информация, номер реактора, его нагрев, и выход энергии. Надеюсь данная программа будет для вас полезна, а с вами был Asior. И большое спасибо за идею программы @Flays Для игроков minecraft 1.12.2 (Mihis) Убедитесь, что до выполнения setting_reactor реактор полностью охлажден (Heat: 0%). А так же при подаче редстоун сигнала реактор должен нагреваться или вырабатывать энергию. # Версия на minecraft 1.7.10 + OpenPeripheral pastebin get iSSt1T59 setting_reactor.lua pastebin get Db76AbMg reactor_control.lua # Версия на minecraft 1.12.2 pastebin get eXrfVEX9 setting_reactor.lua pastebin get QX1QXCYK reactor_control.lua P.S. Говорят если долго сидеть на реакторе, вырастет интересная мутация.
  48. 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, я один раз взял прокси, а дальше работаю с ним.
  49. 9 баллов
    Сейчас я покажу, как сделать это: На скрине выше — улучшенный debug.debug(). Он умеет: Бегать вверх-вниз по стэку вызовов независимо от того, где запущен. Показывать красивые стэктрейсы. Имитировать динамический скоуп: получать значения локальных переменных, редактировать их, не требуя возни с либой debug. При этом учитывает, на каком уровне в стэке вызовов он находится. Он не умеет: «Шагать» по коду, заходить внутрь функций, проскакивать над ними. Таким образом, это не совсем дебаггер. Но он может показать состояние всех доступных переменных. Чтобы заюзать в коде, нужно сделать так: require("dbg")() Впрочем, если в проге есть какой-то часто вызываемый сегмент, то безусловно падать в мини-дебаггер на каждой итерации очень печально. Поэтому можно задать условие, при котором его запускать. Например: require("dbg")(nonNegative < 0) У нас есть переменная nonNegative, которая семантически всегда неотрицательна. Если ж внезапно попалось что-то меньше нуля, есть смысл попросить программиста проверить, кто (и как) изобрёл свою алгебру. Команды: :bt — показать стэктрейс. :up — прыгнуть на уровень вверх. :down — спуститься на уровень вниз. :frame N — перейти на N-ый уровень. Выйти из интерпретатора можно, нажав Ctrl-D или Ctrl-C. Код: https://gist.github.com/Fingercomp/58388304f45bf6b2b8108e3b7a555315 (задумывался одноразовым, качество соответствующее). В обычной Lua надо просто кинуть содержимое куда-нибудь, откуда require тащит файлы. Чтобы это работало в OpenComputers, придётся пропатчить содержимое мода: Открываем jar-файл мода в архиваторе. Идём в /assets/opencomputers/lua. Открываем файл machine.lua и в районе 971 строки делаем как-то так: Сохраняемся и выходим. Если всё сделано правильно, в OpenComputers теперь доступна полная либа debug. Остаётся закинуть код мини-дебаггера, например, в /home/lib, дальше используем как обычно. Очевидно, что на серверах такое делать не надо. Ну, совсем не надо. Полной либой debug легко выудить нативную load. А это уже уязвимость. Но в сингле вещь незаменимая. Цитирую отзыв пользователя, пожелавшего остаться анонимным: Успехов вам в дезинсекции кода.
  50. 9 баллов
    Методика ускоренного вычисления константы шума геосканера Предлагаю ознакомиться с методикой, позволяющий максимально быстро и абсолютно точно вычислить константу шума геосканера (geolyzerNoise в config/OpenComputers.cfg). Методика и скрипты тестировались в среде OpenComputers-MC1.7.10-1.7.5.1290-universal.jar. Сохранит ли эта методика свою актуальность в будущем, вселцело зависит от авторов мода. Зачем знать константу шума геосканера? Не на всех серверах параметр geolyzerNoise сохраняет значение по умолчанию. Алгоритмы же поиска руд, использующие для своей работы геосканер, в большинстве своём эффективны лишь при определённых параметрах шума. При увеличении или уменьшении зашумлённости будут эффективны другие алгоритмы. Поэтому знание точных параметров шума геосканера поможет использовать наиболее эффективные алгоритмы поиска руд на конкретном сервере в конкретных условиях шума. Традиционный алгоритм Традиционный алгоритм использует выполнение многократного сканирования плотности блока, поиск минимального и максимального значений и вычисление разницы между ними. Плюсом алгоритма является его простота. Минусом является низкая точность. Для повышения точности используется увеличение кратности сканирования, но даже очень большая кратность не даёт гарантии абсолютной точности. Всегда есть небольшой шанс, что шум окажется немного больше найденного алгоритмом. Дискретная структура шума Как было обнаружено в недавнем обсуждении про вычисление погрешности геосканера, шум имеет дискретную природу. Согласно коду OpenComputers для каждого из сканируемых расстояний существует 256 возможных значений шума. Знание этого факта позволяет нам считать результат достигнутым, как только сканер выдаст 256 уникальных значений плотности одного и того же блока. Плюсом такого подхода будет абсолютная точность. Главный же минус заключается в том, что получение всех 256 значений может занять даже больше времени, чем получение реальных минимума и максимума плотности. В моих экспериментах на получение всех 256 значений уходит примерно 10-20k попыток сканирования. Вот скрипт для наглядности: -- демонстрация: шум геосканера имеет 256 фиксированных значений -- установка: любой блок, сверху блок геосканера, на него спавним комп /oc_sc local scan = require'component'.geolyzer.scan local uniqTbl = {} local uniqCnt = 0 local v for i=1,1e4 do v = scan(0,0,-1, 1,1,1)[1] if not uniqTbl[v] then uniqTbl[v] = true uniqCnt = uniqCnt + 1 print(("try: %d, uniqCnt: %d"):format( i, uniqCnt )) end os.sleep(0) -- ^C для останова end Наблюдая за скриптом, легко заметить, как медленно добираются последние из оставшихся уникальных значений. Алгоритм абсолютно точен, но неоптимален. Оптимизация поиска Оптимизировать вычисления нам поможет знание о том, что все 256 значений шума расположены равномерно, расстояния между смежными значениями одинаковы, и они ровно в 256 раз меньше расстояния между максимальным и минимальным из значений. В пределах погрешности вычислений, разумеется. Это значит, что для вычисления константы шума достаточно найти любые два смежных значения и вычислить разницу между ними. Но смежными эти значения должны быть в своей полной таблице из 256 значений, в то время как в целях оптимизации рабочая таблица должна быть заполнена как можно меньшим количеством данных, и поэтому соседство значений в рабочей таблице не означает обязательности их соседства в полной. Уменьшать количество значений в рабочей таблице следует ровно до того момента, пока это не мешает обнаружению двух смежных значений. Можно сказать и наоборот: пополнять таблицу новыми значениями следует ровно до тех пор, пока смежные значения не удаётся обнаружить с абсолютной точностью. Самое очевидное, что приходит в голову, это набрать ровно 129 уникальных значений, и найти среди них ближайших соседей. Почему 129, а не 128? Потому что при 128 уникальных значениях возможна редкая ситуация, когда эти найденные значения находятся в полной таблице ровно через одно. Это маловероятно, но возможно. Зачем рисковать? Тем более, 129 элементов почти всегда находятся примерно за 200 попыток сканирований, что можно увидеть, наблюдая за работой демонстрационного скрипта. Но внимательный читатель наверняка заметит, что может быть достаточно использовать и 128 значений при условии, что они размещены неравномерно относительно друг друга. Почти всегда 129-ое значение оказывается необязательным. И если развить эту мысль, то выяснится, что и 127 значений почти всегда будет достаточно при введении дополнительных условий. Тем же путём можно перейти к 126 значениям и т.д. В итоге окажется, что роль играет не равномерность расположения значений относительно друг друга, а расстояние между крайними элементами и между двумя ближайшими. А для этой цели может быть достаточно даже трёх найденных значений. В лучшем случае. Но в худшем могут потребоваться и все 129 значений. Оптимальный алгоритм В общем виде алгоритм сводится к циклическому получению очередного значения от геосканера и поиску разницы между всеми найденными значениями. И если соотношение максимальной разницы к минимальной превысит 128, то результат достигнут: минимальная разница между найденными значениями обязана быть равной разнице между смежными значениями в полной таблице. Результатом умножения минимальной разницы на 128*33 будет искомая нами константа geolyzerNoise из файла конфигурации. Результирующий код может выглядеть, например, так: -- измеритель шума геосканера (константы geolyzerNoise в config/OpenComputers.cfg) -- установка: любой блок, сверху блок геосканера, на него спавним комп /oc_sc local abs = require'math'.abs local scan = require'component'.geolyzer.scan -- округление до сотых долей для сокрытия погрешностей local function round( v ) return (v*1e2+0.5)//1/1e2 end -- множитель для восстановления значения шума до справочного local mul = 128*33 local uniqTbl = {} local uniqCnt = 0 -- текущее значение, разница с другим значением, минимальная и максимальная разница local val, dif, min, max -- флаг обновления экстремумов local fUpd for i=1,99 do os.sleep(0) val = scan(0,0,-1, 1,1,1)[1] fUpd = false -- обрабатывать только новые уникальные значения if not uniqTbl[val] then -- обновить информацию о минимальной и максимальной разнице for v,_ in pairs(uniqTbl) do dif = abs( val-v ) if not min or min>dif then min=dif fUpd=true end if not max or max<dif then max=dif fUpd=true end end -- пополнить таблицу уникальных значений uniqTbl[val] = true uniqCnt = uniqCnt + 1 end -- при обновлении вывести информацию на экран if fUpd then print(("uniq:%2d/%2d | min:%7.3f | max:%7.3f | max/min:%7.3f"):format( uniqCnt,i, round(min*mul), round(max*mul), round(max/min) )) end -- закончить по достижении результата if fUpd and max/min > 128 then break end end -- сообщить результат if fUpd and max/min > 128 then print(("Congratulations! geolyzerNoise = %f"):format( round(min*mul) )) else print("Failure! Tray again please!") end Результат Знание об особенностях генерации шума геосканера, позволило достичь абсолютно точных результатов при очень малом времени сканирования при вычислении константы шума геосканера. В лучшем случае результат можно получить за три сканирования. В типичном случае требуется около 20 сканирований, что требует около одной секунды времени. Самый худший случай не ограничен ничем, но использованное в моём скрипте ограничение в 99 сканирвоаний ни разу не было достигнуто за несколько сотен попыток. И это ещё не всё, что может дать знание о шуме геосканера. Пошумим, брат!
Эта таблица лидеров рассчитана в Москва/GMT+03:00
×
×
  • Создать...