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

Поиск по сайту

Результаты поиска по тегам 'opencomputers'.

  • Поиск по тегам

    Введите теги через запятую.
  • Поиск по автору

Тип публикаций


Блоги

  • Робот Байт
  • Fingercomp's Playground
  • 1Ridav' - блог
  • Totoro Cookies
  • Блог cyber01
  • IncluderWorld
  • KelLiN' - блог
  • Крутой блог
  • eutomatic blog
  • Programist135 Soft
  • Сайт в сети OpenNet
  • PieLand
  • Очумелые ручки
  • Блог недоблоггера
  • В мире Майнкрафт
  • LaineBlog
  • Квантовый блог
  • Блог qwertyMAN'а
  • some blog name
  • Дача Игоря
  • Путешествия Xytabich'а
  • Рецепты программирования

Форумы

  • Программирование
    • Программы
    • База знаний
    • Разработчикам
    • Вопросы
  • Игровой раздел
    • Игровые серверы
    • Моды и плагины
    • Жалобы
    • Ивенты и конкурсы
    • Файлы
  • Общение
    • Задать вопрос
    • Обратная связь
    • Беседка
    • Шкатулка
  • Технический раздел
    • Корзина

Группы продуктов

Нет результатов для отображения.


Искать результаты в...

Искать результаты, которые...


Дата создания

  • Начать

    Конец


Последнее обновление

  • Начать

    Конец


Фильтр по количеству...

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

  • Начать

    Конец


Группа


AIM


MSN


Сайт


ICQ


Yahoo


Jabber


Skype


ВКонтакте


Gtalk


Facebook


Twitter


Город


Интересы

Найдено 244 результата

  1. https://pastebin.com/4BXZP7FZ Использует шрифт Брайля Скрины: Пример:
  2. привет игроки! не знаю зачем но я написал константы. они замораживают значение переменной. код: const={} setmetatable(const,{__call=function(self,key) rawset(self,key,{rawget(_G,key)}) rawset(_G,key,nil) end}) setmetatable(_G,{__index=function(self,key) if const[key] then return const[key][1] else return rawget(self,key) end end,__newindex=function(self,key,value) if not const[key] then rawset(self,key,value) end end}) пример: pi=math.pi const "pi" pi="error" print(pi) --> 3.1415926535898 ещё один пример: const "_" _=123 print(_) --> nil
  3. https://pastebin.com/LFAEzkP6 Делал погода назад. Откопал в бекапах.
  4. Описание: необходима программа автокрафта для крафтов с нестандартной сеткой крафта, к примеру, как на экстремальном верстаке из мода avaritia, но не только на него Награда: (остаёмся верны себе) Большое человеческое Спасибо! Ибо я так и не развился на вашем сервере Оборудование: любое из opencomputers, computercraft, ae2 и еще 200 модов в сборке (желательно без роботов, они странно себя ведут на сервере) Требования к ПО: возможность задать рецепт самому, желательно (не знаю, реализуемо ли) забор необходимых ресурсов из МЭ сети и крафт всего для заказанного ресурса, минимальный интерфейс Связь: писать в эту тему, можно в личку Других подходящих програм я не нашел (или плохо искал), Для прохождения сборки нужно много ресурсов крафтящихся на верстаках с огромной сеткой крафта, руками всё крафтить крайне долго, автоматизации таких крафтов нет, точнее, что бы до нее добраться, уже нужна куча составных ресурсов Предлагаю сделать простейший интерфейс основанный на написании цифры Скан рецепта Список сохраненных рецептов 1. какойлиборецепт (при выборе рецепта начинается крафт) 1.(этот пункт если по другому никак, и если получится сделать сопутствующие крафты для основного рецепта, главное чтоб эти сопутствующие крафты не были видны в основном списке, или сделать для них отдельный пункт) Очистка верстака при какой либо ошибке Ещё что нибудь Предлагаю реализовать сам автокрафт на основе скана инвентаря как из моего предыдущего заказа, и сохранять ее в памяти, к примеру в ПК (если такой способ подойдет), так же необходима возможность или заказывать необходимый сопутствующий рецепт прямо в МЭ сети, или возможность автокрафта необходимого без заказа рецепта, с забором нужных предметов из сети или из сундука(так как для крафта нужны пластины из тинкерс констракт, желательно заказ в мэ, или возможность сделать автокрафт пластин в этой проге (имеется мод tinker I/O), или банально на экране писать "положите в сундук/МЭсеть то-то то-то") Вроде всё написал:)
  5. Начал собирать планшет, предварительно установил на жёсткий диск ос. Вот сборка. Но при запуске он просто показывает чёрный экран. Попробовал положить его в зарядное устройство и заново установить систему через компьютер, но он не предлагает выбор, куда именно я хочу её установить. В чём причина и как это исправить? Попробовал установить OpenOS через дискету и это окошко просто сжалось как моя пятая точка.
  6. Последняя версия : 2.2 Команда для установки : pastebin run ngQT9YF8 Системные требования: Корпус компьютера, екран - 1 Tier Процесор - 1 Tier Видеокарта - 1 Tier Память - 1.5 Tier (x1) Жёрсткий диск - 1 Tier Дисковод, клавиатура, Lua BIOS - должны присутствовать Краткое описание: Inerpat - примитивная система для запуска на любом ведре, и инструмент для создание и отладки EEPROM кода. Также она поможет в ситуации, когда ваш компьютер превратился в "обожённый строительный материал" Специальные клавиши: ALT - Открыть главное меню CTRL - Открыть меню файла/папки. В редакторе - меню сохранения. TAB - Навигация по доступным файловым системам Скриншоты:
  7. Я вот не могу понять, мы с другом делаем казино и он говорит для "какого-то" блока(не помню название) нужно добавить компьютер в приват(из мода OpenComputers) я решил задать вопрос, правда ли это, помогите пожалуйста)
  8. Магазине приложений есть несколько антивирусов, они все работали одинаково: они сканировали сам lua-файл У них есть большая уязвимость: вирус может обойти защиту Например: local gqd = component local wdg = gqd.eeprom local hdwui = wgd.set hdwui("вирусный код") Я решил сделать по другому - перезаписать функции filesystem.open, filesystem.remove, filesystem.rename, eeprom.set Код местами очень нубский, простите Видео: https://drive.google.com/file/d/1yawVj2U-shAC-9G0Ic1VkqD6VRW8mmkW/view?usp=sharing и https://drive.google.com/file/d/1B_F3xOzaVJRnBZ87akXeiRDPQ8UKqMnj/view FileProtector Beta.zip
  9. Для тех, кто спешит: Потестировать онлайн: 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.4. 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!
  10. В связи с тем что компоновка OT мне не нравится, было решено сделать ребрендинг ОТ. Фичи из ОТ будут перенесены и доработаны, не все конечно. Здесь мы будет информировать вас о грядущих обновлениях, здесь принимаем фидбек, идеи, баги, так же можем иногда выкладывать видео-ролики с интересными фичами мода, например как этот ниже. Разработчики мода: @NEO @vx13 Большое спасибо @LeshaInc за модель солнечной панели. GitLab: https://gitlab.com/Moon1Light/oma Сырая версия для ознакомления .
  11. Среди всех компонентов в 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 while true do local chunk, err = handle.read() if not chunk then -- ⑥ local _, _, responseHeaders = handle.response() -- ⑦ local length for k, v in pairs(responseHeaders) do -- ⑧ if k:lower() == "content-length" then length = tonumber(v) end end if not length or read >= length then -- ⑨ break end return nil, ("error occured while reading response: %s"):format(err or "unknown error") -- ⑩ end read = read + #chunk -- ⑪ table.insert(url, chunk) end return table.concat(url) -- ⑫ end ① — открываем файл для чтения. Обрабатываем ошибки. ② — считываем всё из файла. Не забываем закрыть его за собой. ③ — вызываем заранее написанную функцию asFormData. Мы получаем тело запроса и значение хедера Content-Type. Создаём таблицу хедеров. ④ — отправляем наш запрос. Обрабатываем ошибки. ⑤ — handle.read может не сразу вернуть весь ответ, а кусочками. Чтобы не забивать память кучей строк, кусочки мы будем класть в таблицу (получится что-то вроде {"htt", "p://", "clbi", "n.co", "m/ab", "cdef"}). Также мы храним число прочитанных байт. ⑥ — handle.read может ещё вернуть ошибку. В том числе если мы прочли весь ответ, и сервер закрыл соединение. Поэтому обработка ошибок будет немного сложной. ⑦ — мы хотим сверить число прочитанных байт с размером ответа. Для этого нам потребуется получить хедеры, отправленными сервером. Вызываем handle.response. ⑧ — размер ответа обычно пишется в заголовок Content-Length. Однако сервер может поиграться с регистром. Например, писать content-length или CONTENT-LENGTH. OpenComputers не трогает эти хедеры. Поэтому придётся пройтись по всем ключам таблицы и найти хедер без учёта регистра. ⑨ — если length не nil, то это число. Тогда проверяем, что столько байт мы прочли. Если же Content-Length не задан, то будем считать, что серверу не важно, сколько надо прочесть. В любом случае выходим из цикла и завершаем чтение. ⑩ — если мы прочли меньше, чем требуется, то явно ошибка какая-то. Обрабатываем. ⑪ — не забываем обновлять read. ⑫ — наконец, склеиваем таблицу в одну строку. Из цикла можно выйти только в случае ошибки. А при ошибке соединение уже закрыто. Следовательно, самим вызывать handle.close() не нужно. 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 while true do local chunk, err = handle.read() if not chunk then f:close() -- ③ local _, _, responseHeaders = handle.response() local length for k, v in pairs(responseHeaders) do if k:lower() == "content-length" then length = tonumber(v) end end if not length or read >= length then break end return nil, ("error occured while reading response: %s"):format(err or "unknown error") end read = read + #chunk f:write(chunk) end 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 механизм лучше. Поэтому этот пост получился не столько про интернет-карту, сколько про обработку ошибок.
  12. Привет игроки форумчане! Сегодня я дописал свой биос для OC. Он позволяет выбирать конкретную файловую систему для загрузки и показывает основные параметры этих систем. Я хотел засунуть больше функционала в виде фейкового init'a но место на eeprom'ке очень мало. Код и так весит 4094 байта из 4096 доступных. Не рекомендуется использовать этот биос в OC 1.5 из-за кривого шрифта. Вот так он выглядит в эмуляторе OC 1.6 команда для загрузки: pastebin run gG0ppTD7 Вот смотрю я на наш форум и вижу одни темы по типу "Есть ли правила на этом форуме?". Это же форум от lua прогеров для lua прогеров. Так давайте проги писать, а не разбираться куда делись правила.
  13. Доброго времени суток. Я решил попробовать себя в написании игр на Lua и воссоздать клеточный автомат "Жизнь" Джона Конвеея для OpenComputers 1.7.5. Описание: В игре существуют два типа клеток - живая и мёртвая. Суть игры в том, чтобы расставить живые клетки и затем наблюдать за их "поведением". Правила автомата очень просты: Если вокруг мёртвой клетки ровно 3 живых - клетка оживает Если вокруг живой клетки меньше 2 или больше 3 живых - клетка умирает В остальных случаях клетки сохраняют своё состояние Известные недочёты: Вычисление каждого следующего поколения может занимать до полуминуты При контакте с краями поля, любая фигура постепенно погибает в соответствии с правилами автомата При запуске симуляции, её можно остановить только перезагрузкой в shell Скачать: Репозиторий на GitHub: https://github.com/Vladg24YT/Game-Of-Life Последняя версия (1.0.0): https://github.com/Vladg24YT/Game-Of-Life/releases/tag/1.0.0 Скриншот:
  14. Вступление: Решил я на днях написать хорошую IDE для OpenComputers и столкнулся с тем, что в ванильной OpenOS напрочь отсутствует подсветка синтаксиса. Надо это исправить! Если найдутся какие-либо недочеты в плане правильности подсветки - пишите, пофиксим. Скачать библиотеку: https://github.com/IgorTimofeev/MineOS/blob/2967e780d35c2e4363dd7fe81de5ba78310c3d55/lib/oldSyntax.lua Инструкция по использованию: --Подключаем библиотеку local syntax = require("syntax") --Создаем любую строку с операторами lua local stroka = "while true do print(\"Hello world!\")" --x, y - стартовые координаты отрисовки текста. --limit - ограничение отрисовки по ширине строки. Зачем рисовать лишние пиксели? local x, y, limit = 2, 2, 20 --Выводим строку на экран в подсвеченном варианте syntax.highlightAndDraw(x, y, limit, stroka) На экране отобразится следующее: Другие функции: ● syntax.setColorScheme(цветовая схема) Устанавливает цветовую схему по выбору пользователя. На данный момент есть лишь две схемы: "midnight" и "sunrise". ● syntax.highlightFileForDebug(путь к файлу [,цветовая схема]) Открывает обычный файл по указанному пути и выводит его содержимое в подсвеченном виде на экран. Размер содержимого ограничен размером экрана. Пример использования этой функции предоставлен на самом первом скриношоте. Насчет оптимизации и ускорения: Я постарался минимизировать задержки в выполнении скрипта различными путями. Во-первых, отрисовка выполняется не попиксельно, а построчно. То есть если мы имеем строку красного цвета, то функция отрисовки выведет на экран сразу всю строку, а не каждый символ по отдельности. Во-вторых, цвет текста изменяется только тогда, когда это необходимо, а не каждый раз при смене символов. На данный момент у программы есть существенный минус: при отрисовке выполняется поиск совпадений шаблонов по строке через оператор string.find(), однако изначально он не дружит с юникодом. Поэтому пришлось использовать костыль unicode.find(), существенно замедляющий процесс поиска. Если у кого-то имеется более оптимизированный костыль или способ поиска по юникоду, буду крайне благодарен за помощь в этом деле. Для ясности прикладываю видосик: http://www.youtube.com/watch?v=T-f8jGdYO0M&feature=youtu.be Исходный код unicode.find(): function unicode.find(str, pattern, init, plain) if init then if init < 0 then init = -#unicode.sub(str,init) elseif init > 0 then init = #unicode.sub(str,1,init-1)+1 end end a, b = string.find(str, pattern, init, plain) if a then local ap,bp = str:sub(1,a-1), str:sub(a,b) a = unicode.len(ap)+1 b = a + unicode.len(bp)-1 return a,b else return a end end
  15. Решил сделать типа Google Maps на геосканере. Только при сканирование местности с большими погрешностями он может различать только землю и воду(у ней с лавой одинаковая прочность). https://pastebin.com/9B8NXab8
  16. В последних версиях OpenComputers обрастает всякими загадочными вещами. Игроки, которые только только освоились с предыдущей версией вдруг понимают, что надо изучать все заново. "А пошло оно все!" - думают игроки, и уходят на версию 1.3.6, или переучиваются на ComputerCraft, который проще, и не требует непонятного. А одна из самых загадочных - неведомый EEPROM. Это такая мелкая хрень, без которой не работает ни один компьютер, или даже робот. Хорошо еще, что есть стандартный EEPROM который называется Lua BIOS. Он легко крафтится и заставляет работать компьютеры как и раньше. Но найдем задачку посложнее, где Lua BIOS не поможет. Попробуем собрать микроконтроллер, который будет управлять входными дверями. 1. План Представим, как оно должно работать. Слева от двери (если входить) - микроконтроллер. Ради понтов, возьмем Микроконтроллер 2-ого уровня и поставим в него беспроводную сетевую плату. Кроме того добавим красную плату, чтобы управлять дверью. 1. Если контроллер принимает сигнал "open" - он открывает дверь. 2. Если примет сигнал "close" - он закрывает дверь. 3. Если примет посторонний сигнал - взрывает динамит. Дабы сокровища не достались хакерам. Для управления задействуем любой комп, у которого тоже будет беспроводная плата (или точка доступа). 2. Крафтим контроллер С этим проблем не возникнет. Потому, что я играю в креативе :P . Открываем NEI и берем нужные детали. В последний слот положим пока пустой EEPROM. Потом поставим на него прошивку, а пока - не важно. Нажимаем кнопку "Старт" и достаем готовый блок. 3. Готовим прошивку Теперь, когда все готово, мы построили сокровищницу и скрафтили контроллер - осталось самое главное. Программирование EEPROM'а отличается от программирования обычной программы. Потому, что обычно, наши программы выполняются в OpenOS, которая заботливо загружает нужные библиотеки, предоставляет всякие удобные фичи и прочее. Тем не менее писать мы будем именно в OpenOS. Запустим компьютер, напишем edit bios и введем следующие строки: red = component.proxy(component.list("redstone")()) while true do red.setOutput(5, 0) computer.pullSignal(1) red.setOutput(5, 15) computer.pullSignal(1) end Дело в том, что большая часть библиотек, которые мы использовали - это библиотеки OpenOS. А значит мы не можем ими пользоваться в BIOS. Однако кое-что нам доступно. Это библиотеки computer и component, и соответственно все установленные в целевом агрегате (микроконтроллер) компоненты. Более чем достаточно для наших задач. Вышеприведенный код делает следующее: * ищет компонент с названием "redstone" и возвращает его прокси * в вечном цикле посылает нулевой редстоун-импульс направо (side = 5), т.е. гасит сигнал * ждет секунду (на самом деле - ожидает эвентов, то есть сигналов) * посылает редстоун сигнал с силой 15 направо * опять ждет секунду Преследуем двоякую цель: во-первых проверить, что EEPROM вообще работает так про него написано на Вики. Кто его знает? А во-вторых: убедиться, что сторона 5 это именно та сторона, где дверь. А не какая-нибудь другая. Нажмем Ctrl+S, чтобы сохраниться и Ctrl+W, чтобы закрыть редактор. Вставим пустой EEPROM (еще один) в слот нашего компьютера, вместо лежащего там Lua BIOS. И напишем в консоль такую команду: flash -q bios MCBios Программа flash предназначена для прошивки чипов. Флаг -q говорит ей, чтобы не задавала лишних вопросов, затем идет имя файла с нашим кодом (bios) и метка, которую программа шлепнет на чип (MCBios). Все. Доставайте. Lua BIOS на место класть не обязательно, ибо этот слот нам еще потребуется. (Но не забудьте его вернуть, если будете перезагружать компьютер) Чтобы заменить пустой EEPROM в контроллере на наш MCBios, надо положить контроллер и MCBios на верстак. При этом пустой чип вылетит, а новый встанет на его место. Поставим контроллер на пол и протестируем. После клика ПКМ на контроллере - замигала правая лампа. Значит все работает как нужно. 4. Теперь - серьезно Извлеките чип с MCBios обратно (так же как и вставляли, только наоборот). Или приготовьте новый пустой чип. Главное - не запутайтесь в них. Пишем клиент для контроллера. У меня он выглядит примерно так: red = component.proxy(component.list("redstone")()) modem = component.proxy(component.list('modem')()) modem.open(27) red.setOutput(5, 0) red.setOutput(2, 0) -- no explosions yet =) while true do name, _, sender, _, _, message = computer.pullSignal(2) if name == 'modem_message' then if message == 'open' then red.setOutput(5, 15) elseif message == 'close' then red.setOutput(5, 0) else -- hacker tries to get in? red.setOutput(2, 15) -- fire in the hole!!! end end end modem.close() Все согласно плану. Прошиваем чип, вставляем в контроллер, а контроллер ставим слева от дверей. Сзади к контроллеру осторожно прилаживаем запал. ПКМ! Теперь открываем новый файл на компьютере: edit send И пишем в него такой код: local com = require('component') local modem = com.modem local args = {...} modem.broadcast(27, args[1]) print("Message '"..args[1].."' sent!") Сохраняем, и закрываем. Это будет программка для тестирования контроллера. 5. Тест! Пишем в консоль send open. Дверь открылась! Пишем send close. Дверь закрылась! Пишем send opeh Упс! Опечатка. О_О
  17. Теперь компы работают В РАЗЫ быстрее!!! Вам лишь нужно смазать процессор девятью граммами... Всем привет! Я так долго не заходил на форум, что забыл вас оповестить о новом законном способе ускорить работу компов в игре о котом я узнал. Так делать не нужно!!! Мой комп страдал, чтобы ваш не горевал. Суть вот в чём. В моде ProjectE есть такой замечательный инструмент как часы времени. Ничего особенного они не делают кроме ускорения цикла дня/ночи. НО!!! Если их поставить на пьедестал и активизировать ПКМ, то они судя по описанию немного ускорять животных поблизости и дадут дополнительные 20 тиков блокам в радиусе 3 блоков рядом. (образует куб 7x7x7 с пьедесталом в центре) И тут мы переходим к самому интересному!!! Часы работают на любых блоках: на растениях, блоках из мода и даже на блоках из самого же ProjectE, которые позволяют собирать и накапливать ECM (местную валюту) {из-за чего кстати я сломал аддон к этому моду и быстро прокачался до максимума ECM - просто посмотрите на скрин и всё поймёте} Очень имбовая вещь, ей можно генерировать много пассивной энергии и тратить её в супер быстрый карьер. А эффекты от часов поблизости складываются!!! Так вот, ради чего мы здесь собрались... судя по логике совместимости модов: робот из OpenComputers должен быть блоком, но для часов времени - он не блок и на него ничего не действует. Зато часы действуют на статичные блоки вроде системного блока, монитора и прочего. Что позволяет использовать компы на очень быстрых скоростях. Правда есть и минус. Внутренние часы компа ничего не подозревают и задержки через os.sleep будут так же ускорены как и ВСЯ работа компа. Зато это открывает возможности делать и запускать реальные игры в майнкрафте. Не заботясь о том, что что-то не успеет прогрузится. Вот такие чудеса творятся в мире ProjectE. И его явно никто добавлять на сервера не будет. Зато попробовать программы позапускать с ним можно и в одиночной игре. Может кто-то что-нибудь с этим придумает.
  18. Вероятно, многие хотели запустить несколько программ на одном компьютере (на OpenOS), чтобы они работали одновременно. Моя программа даёт возможность это сделать (программы будут выполняться не совсем одновременно, таково ограничение OpenComputers). Важное условие: в каждой выполняемой программе должен быть os.sleep, event.pull или computer.pullSignal, чтобы она передавала управление другой. Инструкция: 1. Скачайте программу: pastebin get wsJqngC5 parallel.lua 2. Запустите её. 3. Введите пути к программам, которые необходимо выполнить параллельно, через пробел (если программа одна, то введите её один раз). 4. Введите число, сколько раз должна быть запущена каждая программа из списка. 5. Задайте аргументы для каждой запускаемой программы. Если несколько программ должны работать параллельно, то почему бы не запустить их на разных мониторах? Это возможно: просто напишите в аргументы программы %GPU%. Эта строка автоматически заменится на адрес видеокарты. В программе могут встречаться баги и несовместимости с другими приложениями. Просьба сообщать, если таковые найдутся.
  19. В прошлый раз я патчил OpenComputers, чтобы пробрасывать нативную либу debug. Пойдём дальше. Добавим нативных либ package и os. Прокинем дефолтное окружение внутрь песочницы. Пропатчим мод, чтобы можно было загружать си-модули. Загрузим профилятор и посмотрим, что из этого вышло. На винде ничего не заработает. Гарантирую. Если надо профилировать, ставьте нормальные оси или мучайтесь. 0. Сырцы мода Так как мы будем патчить мод, надо сначала подготовить исходники. $ git clone https://github.com/MightyPirates/OpenComputers.git $ cd OpenComputers $ git checkout master-MC1.12 $ ./gradlew setupDecompWorkspace На третьей строке версию выбираем по вкусу и выпекаем всё необходимое для компиляции. 1. Нативные либы Здесь всё просто. Открываем файл src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala. Творим следующее: Вуаля. Теперь в machine.lua будут глобальные переменные package и _os. Отмечу отдельно, что меняем мы только архитектуру Lua 5.3. Уже на этом этапе у нас может сломаться персистентность. Это не страшно: она и должна сломаться. 2. Прокидываем окружение Поступаем аналогично тому, что делали в прошлой записи: меняем src/main/resources/assets/opencomputers/lua/machine.lua: Внутри песочницы в глобальной переменной env запечатлено будет всё окружение machine.lua. 3. C-модули Уже сейчас можно загрузить OpenOS и прописать env.require("libname"). Проблема в том, что C-модули так подключить не получится. Связано это с особенностью Lua. Абстрактно задача заключается в том, чтобы загрузить библиотку Lua с dlopen(..., RTLD_GLOBAL). System.loadLibrary в жаве флаг этот упускает по очевидным причинам, а нам он нужен. Значит, пришло время костылей. 3.1. Подключаем JNA: build.gradle Первый ханк нужен, чтобы можно было потом компилировать мод. Почему-то у курсов мавен не работает, а разбираться мне лень. 3.2. Патчим ещё раз src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala Во-первых, подключаем хэшмапу. Потребуется. Во-вторых, импортируем JNA. Вернее, его часть. В-третьих, патчим код, чтобы он загружал Lua 5.3 через JNA. Магическая константа 0x101 — это значение RTLD_LAZY | RTLD_GLOBAL на моей системе. На фряхе, маке оно может отличаться. На этом этапе Lua 5.2 не будет работать. Включаться будет только Lua 5.3 из-за конфликта имён. Кроме того, JNA — это, вообще, огромная либа. Ради одной функции её подключать — это оверкилл. Но я в тонкостях JVM и JNI не силён. Как уже сказал, разбираться мне лень. 3.3. Компилируем $ ./gradlew assemble Выхлоп в build/libs. Берём жарник без суффиксов вроде -javadoc, -api, -sources. 4. Настраиваем профилятор Профилятор я написал сам на Rust. Вот ссылка: https://github.com/Fingercomp/lprofile-rs Очевидно, нам надо его скомпилировать. 4.1. Компилируем профилятор Ставим cargo (мультитул раста такой) любым удобным способом. Собираем: $ cd .. $ git clone --recurse-submodules https://github.com/Fingercomp/lprofile-rs.git $ cd lprofile-rs $ cargo build --release В target/release будет лежать liblprofile.so. Тырим его. 4.2. Определяем pwd Кидаем пропатченный OC в моды и запускаем игру. Пишем в опенкомпе env._os.getenv("PWD"), чтобы определить текущую директорию. Кидаем либу-профилятор в неё. 4.3. Профилируем Наконец, можно заняться мясом. local profiler = env.require("lprofile").Profiler() local result = profiler(function() local v = 0 for i = 1, 10e6, 1 do v = v + i end end) table.sort(result, function(lhs, rhs) return lhs.totalTime < rhs.totalTime end) print("Name", "# of calls", "Total time", "Total time, excluding inner calls") for _, v in ipairs(result) do print(("%s\t%d\t%.6f s\t%.6f s"):format(v.name, v.calls, v.totalTime, v.totalSelfTime)) end print("total time:", result.totalTime) 5. Зачем Мы получили наполовину сломанную версию OpenComputers: без Lua 5.3, без персистентности. Зато можем профилировать программы. Этот пост я написал, чтобы не забыть самому. Сомневаюсь, что кому-то интересно заниматься такой норкомагией.
  20. Fingercomp

    Улучшенный debug.debug()

    Сейчас я покажу, как сделать это: На скрине выше — улучшенный 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. А это уже уязвимость. Но в сингле вещь незаменимая. Цитирую отзыв пользователя, пожелавшего остаться анонимным: Успехов вам в дезинсекции кода.
  21. Текстуры для OpenComputers и аддонов x64. 0xD00B последнее обновление: 28.01.17
  22. к микроконтроллеру подключено 2 редстоун контроллера. как присвоить переменным red1, red2 прокси этих редстоун-контроллеров?(какая переменная к какому контроллеру - не важно)
  23. Привет, это моя первая программа для робота. Автоферма пшеницы. local args = {...} local io = require("io") local robot = require("robot") local comp = require("computer") local filesystem = require("filesystem") local r_forward = robot.forward local arg1 = args[1] local arg2 = args[2] function robot.forward() repeat os.sleep(0) until r_forward() return true end if (arg1 == "size") then if (arg2 == nil) then return print("Не указан размер") end local farmsize = io.open("/home/farm.cfg", "w") farmsize:write(arg2) farmsize:flush() farmsize:close() return print("Позиция установлена") end if (filesystem.exists("/home/farm.cfg") == false) then return print("Файл конфигурации не создан! пожалуйста используйте аргумент size *Размер поля*") end local farmsize = io.open("/home/farm.cfg") local size = farmsize:read() farmsize:close() function farm() robot.forward() for y = 1, size do -- собрать 1 линию robot.swingDown() robot.useDown() robot.placeDown() for x = 1, (size - 1) do robot.forward() robot.swingDown() robot.useDown() robot.placeDown() end -- поворот if y % 2 == 1 then robot.turnLeft() robot.forward() robot.turnLeft() else robot.turnRight() robot.forward() robot.turnRight() end end if(size%2 == 0) then robot.turnLeft() robot.turnLeft() robot.turnLeft() -- возвращение на базу for y = 1, (size - 1) do robot.forward() end robot.turnRight() robot.turnLeft() robot.forward() robot.turnRight() robot.forward() robot.turnAround() else for y = 1, (size - 1) do robot.forward() end robot.turnLeft() for y = 1, (size) do robot.forward() end robot.turnRight() robot.forward() robot.turnAround() end end function dropitem() robot.turnAround() for c = 2, 16 do robot.select(c) if robot.count() > 0 then robot.drop() else robot.select(1) break end end robot.turnAround() end if arg1 == "primary" then farm() if robot.count(2) == 64 then dropitem() end end local primary = nil while true do -- таймер(1час) for i = 1, 10 do os.sleep(370) print((10 * i) .. '%') end farm() if robot.count(2) == 64 then dropitem() end end Пару слов об аргументах *name*.lua primary(принудительный запуск сбора) *name*.lua size *size*(установка размера фермы) Также о настройке 1.Ферма должна быть одинаковая по размерам. т.е. 15x15 или 9x9 и т.д. 2.Робот должен быть установлен следующим образом(см.ниже) 3.(по желанию) дать роботу мотыгу чтобы он сам вспахал землю(в принципе просто можно задать ему размеры и дать семена и он сам вспашет землю и посадит семена) Просьба не судить строго так как это моя первая программа и вообще первая программа на языке LUA Жду адекватной критики и предложений. Спасибо за помощь игроку Asummonster
  24. Программа позволяет создавать снимки экрана. Сохраняет она его в файл /screenshots/Screenshot-Год-День-Час-Минута.ast Формат ast Для выхода из программы после распечатывания экрана надо по нему нажать(создать ивент touch). Вот код "AsumScreenshoter"'а: local mode = ({...})[1] if mode==nil then print("Использование: screenshot init для создания ивента сохранения экрана(Кнопка Print Screen, папка screenshots)\n screenshot <имя файла> для просмотра(По окончанию просмотра нажмите на экран)"); os.exit() return end local fs = require("filesystem") local unicode = require("unicode") local gpu = require("component").gpu local event = require("event") local function dec2hex(n, s) local h = string.format("%x", n) return string.rep("0", s-string.len(h))..h end local function hex2dec(s) return tonumber("0x"..s) end local function createScreenshotFile() DD, HH, MM, SS = os.date("%Y"), os.date("%H"), os.date("%M"), os.date("%S") if not fs.isDirectory("/screenshots") then fs.makeDirectory("/screenshots") end return io.open("/screenshots/Screenshot-"..DD.."-"..HH.."-"..MM.."-"..SS..".ast", "w") end local function getScreen() local W, H = gpu.getResolution() local screenshot = dec2hex(W, 2)..dec2hex(H, 2) local lfg, lbg for y=1, H do for x=1, W do local symbol, fgcolor, bgcolor = gpu.get(x, y) if lfg ~= fgcolor then lfg = fgcolor screenshot = screenshot .. "␑" screenshot = screenshot .. dec2hex(fgcolor, 6) end if lbg ~= bgcolor then lbg = bgcolor screenshot = screenshot .. "␒" screenshot = screenshot .. dec2hex(bgcolor, 6) end screenshot = screenshot .. symbol end end return screenshot end local function writeScreen() local file=createScreenshotFile() file:write(getScreen()) file:flush() file:close() end local function printScreen(filename) local screenshot=io.lines(filename)() local oldw, oldh = gpu.getResolution() local header = screenshot:sub(1, 4) screenshot = screenshot:sub(5) local W = hex2dec(header:sub(1, 2)) local x = 0 local H = hex2dec(header:sub(3, 4)) local y = 0 gpu.setResolution(W, H) while y < H do y = y + 1 x = 0 while x < W do local sym = unicode.sub(screenshot, 1, 1) if sym == "␑" then gpu.setForeground(hex2dec(unicode.sub(screenshot, 2, 7))) screenshot = unicode.sub(screenshot, 8) elseif sym == "␒" then gpu.setBackground(hex2dec(unicode.sub(screenshot, 2, 7))) screenshot = unicode.sub(screenshot, 8) else x = x + 1 gpu.set(x, y, sym) screenshot = unicode.sub(screenshot, 2) end end end event.pull("touch") gpu.setResolution(oldw, oldh) end local function saveScreenByKey(_, _, key1, key2) if key1==0 and key2==183 then writeScreen() end end if mode=="init" then event.listen("key_down", saveScreenByKey) else printScreen(mode) end Надеюсь вы будете пользоваться этой программой!=) Для того,что бы начать делать скриншоты, после каждой перезагрузки надо запустить приложение с параметром init Для того,что бы создать скриншот после инициализации-надо нажать кнопку Print Screen
  25. Ходя по интернету, я не встречал более удобную программу для редактирования воксельных моделей чем MagicaVoxel. Я уже использовал ее для крутых черепах, и теперь она пригодится нам всем и для принтов. Я сделал конвертер для переноса бинарного формата .vox в текстовый. Использование: 1 https://ephtracy.github.io/ Скачайте и установите MagicaVoxel. Сделайте модель своей мечты. Не забудьте следить за размерами в правом верхнем углу - размер одного печатного блока 16х16х16. Можно превысить размеры - конвертер просто сделает список больше чем из одного блока. 2 http://codepen.io/Krutoy242/full/xGOgJG/ Сохраните и откройте модель в моем модном конвертере. Настройки такие же как в принте - название, подсказка, пару флагов и свечение. Так же, после загрузки модели можно будет выбрать текстурки и цвета каждого типа блоков. Флажек "Split to states" разделит вашу модель пополам по оси X что бы из второй части сделать изменение модели при нажатии\подаче редстоун сигнала. Например, для калитки вам нужно в MagicaVoxel задать размер 32 16 16, в одном квадратике нарисовать закрытую калитку, а во втором - открытую. Вот и всё! Полученный текст можно вставить в файлик и отпечатать по гайду Тоторо. Позже добавлю программу, позволяющую печатать по очереди сразу несколько моделей. Конвертер написан на JavaScript. Он читает бинарный файл и объединяет воксели в группы. Программа в какой то степени оптимизирована. Например эти стержни конвертировались автоматически со 100% эффективностью. Но в некоторых случаях, конечно, при создании модели человеком можно будет сэкономить 2-3 шейпа из 23х максимальных. Пишите свои отзывы и пожелания по улучшению конвертера.
×
×
  • Создать...