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

Fingercomp

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

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

  • Посещение

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

    283

Записи блога, опубликованные пользователем Fingercomp

  1. Fingercomp
    Наткнулся на интересный эмулятор: https://git.io/vOJaq Написан на Lua. Эмулирует OpenComputers. В этой записи небольшой я расскажу немного о том, как варить пельмени использовать этот эмулятор.

    Использовать его так же, как и OpenOS!


    Установка:
    Linux git clone https://github.com/gamax92/OCEmu.git. Копируем содержимое репозитория. Устанавливаем зависимости: Lua 5.2 SDL2

    Далее нас потребуется версионированный luarocks. Т.е., под Lua 5.2 либы в одной директории, под 5.3 — в другой, ну и так далее. На Арче и Маках ничего сложного:
    Arch
    pacman -S lua52 luarocks5.2 lua52-filesystem lua52-sec lua52-socket
    Затем переходим к пункту Luarocks.
    Mac
    brew install luabrew install sdl2
    Затем переходим к пункту Luarocks.
     

    Ubuntu
    А вот тут уже чуть сложнее. По дефолту луарокс из репы швыряет всё в одну кучу, и потому нужно скомпилировать самому. Ничего особо сложного. Качаем пакет отсюда, распаковываем (tar xvf <имя файла>) и переходим в директорию. Зтем пишем:
    ./configure --lua-version=5.2 --lua-suffix=5.2 --versioned-rocks-dirmake buildsudo checkinstall
    Можно sudo make install, но я лично так не делаю, так как команда выше создаёт пакет сразу, а мейк-инстолл — тупо копирует.
    После этого — идём к Luarocks.
     

    Luarocks
    И, наконец, устанавливаем библиотеки через luarocks-5.2:
    luarocks-5.2 install luafilesystem luarocks-5.2 install luautf8 luarocks-5.2 install luasocket luarocks-5.2 install luasec luarocks-5.2 install --server=http://luarocks.org/dev luaffi[il]

    Если вы не страдаете ненавистью к SVN, устанавливаем [il]subversion и пишем make в директории с эмулятором для скачивания OpenOS.
    Иначе же открываем https://github.com/MightyPirates/OpenComputers и сами скачиваем src/main/resources/assets/opencomputers/loot в src/loot, src/main/resources/assets/opencomputers/lua в src/lua и src/main/resources/assets/opencomputers/unifont.hex в папку src/ эмулятора.
     

    Windows
    Бинарники: x32 / x64.
    Если же хочется самим скомпилировать:
    Устанавливаем MSYS2, запускаем. Пишем update-core. Закрываем окно и открываем снова. Обновляем остальное командой pacman -Su. Пишем pacman -S mingw-w64-i686-toolchain. Закрываем и в "Пуске" ищем MinGW Win32 Shell или MinGW Win64 Shell, в зависимости от разрядности системы. Открываем именно его. В терминале переходим в папку с эмулятором и пишем ./msys_setup_ocemu.sh. Это сделает всё, что нужно.

     

    Запуск:
    Чаще всего достаточно просто запустить lua5.2 boot.lua в директории src/. Это живенько запустит эмулятор с последней OpenOS. Можно указать путь для эмулируемой машины в аргументе: lua5.2 boot.lua ~/my/machine. BIOS машины хранится в $HOME/.ocemu или %APPDATA%\.ocemu, в зависимости от ОС. Можно изменить его код. Запустите emucfg в OpenOS — это конфигуратор эмулятора. Можно выбрать компоненты: [insert] для его добавления, [Delete] для удаления. Если запустить несколько сессий эмулятора, можно даже "общаться" между ними через эмулированные модемы.



    Сетевые соединения между эмуляторами (модемы)i
    Крутая штука. Так как ещё ни в одном эмуляторе не было модемов, надо акцентировать внимание на этом обязательно. Создаём две директории для эмулируемых машин (у меня: ~/.ocemu/machines/m1 и ~/.ocemu/machines/m2). В каждую из них копируем файл ~/.ocemu/ocemu.cfg. Открываем первый файл и меняем все адреса в настройке "components". Это необходимо для того чтобы эмулятор считал, что это два разных компьютера. Открываем второй файл и тоже меняем адреса. Достаточно сделать так, что бы они были не такие же, как в первой машине. Можно даже настройку по умолчанию оставить. Запускаем два эмулятора: ./boot.lua ~/.ocemu/machines/m1 & ./boot.lua ~/.ocemu/machines/m2 & (надо запустить их, очевидно, параллельно). И всё, теперь компьютеры могут общаться друг с другом!


     


     
    OpenOS 1.6
    И в бонус записи — предварительный обзор ключевых изменений OpenOS.
    PIPING. По стандартам. Я не буду объяснять смысл термина, предлагаю просто прописать cat /init.lua | head --lines=25. С grep это тоже работает. Стооооп. Новые программы: head, grep, touch — похожее на Лялеховские программы. Не забывайте про man: man grep. Спасает и помогает. Ещё проги: sleep (спит указанное время), source (считать файл и выполнить каждую строку как команду OpenOS). Тонны изменений в term, pipes, guid, transforms — в библиотеках OpenOS. Терминал теперь только для одного экрана, мультиэкранность хотят сделать отдельной библиотекой. И множество изменений в логике программ, ключах, аргументах.

     


     
    Огромное спасибо @Krutoy и @Strateg за помощь в установке на Windows.
    Если встретились с проблемой при установке на Win, посетите этот топик: http://computercraft.ru/topic/1512-%E2%9C%94-zakaz%E2%84%96-013-vypolneno-binarniki-emuliatora-ocemu-na-windows/
  2. Fingercomp
    Лого от Totoro
     
    Здрассьте!
    Несколько дней назад я прогуливался по всяким оплотам бюрократии и, не теряя времени, заодно размышлял о том, что форум наш наводит тоску и уныние: программок нет, ничего не обсуждается, дискуссии только разве что о лагах на сервере и сборочках с недосборочками.
    И появилась идея организовать конкурс программистский типа джема.
     
    Джем — это желеобразный пищевой продукт с равномерно распределёнными в нём целыми или измельчёнными плодами (ягодами), сваренными с сахаром с добавлением желирующих веществ... То есть, это такой конкурс, где даётся очень ограниченное время, которое надо умно потратить так, чтобы к кноцу срока предоставить готовый программный продукт. Игрушка под ведроид за два-три дня, как пример.
    Проекты в джемах, очевидно, совершенно недоработанные, борьба там идёт за идею. Но после конкурса никто не запрещает продолжить этот начатый проект.
     
    Так вот. До воскресенья, до 17 июля шеcтнадцатого года, будет по-тихому проходить тоже свой небольшой конкурсик. Он будет не столь серьёзным, чтобы вообще даже называться джемом: времени много, а проект не самый сложный. Есть время подумать, погуглить, поспрашивать на форуме.
     
    Итак, условия этого небольшого конкурсика:
    Дедлайн семнадцатого июля 2016 года (2016.07.17), воскресним вечером. За это время необходимо продумать и реализовать проект, написанный на языке MoonScript. Не пойдёт переписывание уже готовых программ на форуме на этот язык. Платформа абсолютно любая — хоть OpenComputers, хоть ComputerCraft, винда или лялех, Love2D, всякие микроконтроллеры — главное, основную часть должен играть код на MoonScript, оттранспиленный в Lua. Проект по завершении оформить нужно топиком на форуме, указав ссылку на pastebin, gist или github (последние два варианта предпочтительнее) с исходным кодом. На нашем IRC-канале, куда мы не устаём всех звать, мы будем обсуждать и выбирать интересные программы. После дедлайна тех, кто реализует самые интересные (по голосованию) проектики, объявим победителями конкурса.

    Если вообще будет какой-нибудь интерес к этому мероприятию, думаю, организуем награды в виде медальки на форуме, всяких поощрений в виде ююшек или денежек игровых. Может быть, что-нибудь особое вручим, кто знает.
     

    Ключевое условие: участие людей и интерес к конкурсу. Если есть интересные идейки для программы — самое время их реализовать. Заодно подучить новый язык программирования, что явно в пользу пойдёт.
     
    И пока что я довольно скептически настроен, в общем и в целом, так как не особой популярностью пользовались конкретные конкурсы и заказы. Но надо же как-то расшевелить форум.
    Так что дерзайте, и да прибудет удача. Во имя Луны!
     
    P. S. Слева вверху теперь прикручен обратный отчёт до конца джема.
    P. P. S. Добро пожаловать в Треллу! https://trello.com/b/ROncU99z/moonjam — вся та же информация, но в собранном и отклассифицированном виде.
    P. P. P. S. MoonJam завершился!
     
     
     
  3. Fingercomp
    С недавним (1.5.18) релизом OpenComputers появилась такая хрень — наноботы. Посмотрев ролик от автора мода (он будет внизу статьи), было ясно, что запутался или я, или автор. Скорее всего первое. Потому решил отложить их на потом.
     
    И вот, пришло то время. По-быстрому забежав на тестовый сервачок и поигравшись с ними, понял, насколько чудовищно... КРУТЫ эти мелкие штуки. Почему? А сейчас узнаем.
     


    I. Тварелогия.
    Вообще, я немного наврал со словом "сейчас". Обосновывать будем по ходу развития сюжета, а в конце (нет, в середине!) сделаем вывод.
    Итак, вот вам выдержка из статьи по нанороботиксам.
    Нанороботы, наноботы, наномашины, наноиды, наниты, наномиты и иже с ими (будем использовать несколько обозначений в этом гайде) — это такие мелкие штуки, которые попадают в организм кубического человека и жрать, мозги, хлам! помогают ему обрести новые способности.  


    II. Приборы и девайсы.
    Значит так, в процессе эксперемента нам нужно: желание умереть, мозги, больше мозгов, невероятное желание подохнуть, планшет, наноботы, грог (?!).  


    III. Ход эксперимента. Физ. часть.
    Итак, вы подготовили всё, что нужно. Давайте приступать.
    Я не указал, но возьмите ведро молока. Если, конечно, вы хотите избавиться от...
    В общем, съешьте нанитов. А теперь выпейте молока. После столь болезненной операции (что? Болезненной? Мы ж серьёзные люди — и кубизм явно то показывает. Не смешите) просто встаньте рядом с активной зарядкой.
    Ну, в общем, вот такая панорама (точней — её кусок) должна получиться.

    Как видно, слева от хотбара расположилась батарейка, которая показывает текущее состояние батареи в наните.
     

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


    IV. Ход эксперемента. Информационная часть.
    Всё управление происходит через беспроводную сеть, так что планшет нужен с беспроводной сетевой картой.
    "Протокол" общения с наномитами предельно прост. Используется функция modem.broadcast(). При этом наниты слушают эфир на всех частотах по умолчанию. Первым куском данных ВСЕГДА является строка "nanomachines". Команда уже такая: modem.broadcast(1337, "nanomachines"). Затем идёт сама функция и аргументами, так же отделёнными частями пакета. Итого: modem.broadcast(1337, "nanomachines", "команда", "первый аргумент, строковой", 2, "прошлый аргумент — числовой", "и т. д."). Не переживайте, я ещё вернусь к этому в перечне команд.  

    Итак, начнём наш перечень с не очень нужных игроку, но полезных команд.
    setResponsePort(port:Number):String,Number. ОБЯЗАТЕЛЬНАЯ КОМАНДА ПЕРЕД НАЧАЛОМ РАБОТЫ! Именно. Так как вещать на всех частотах — идея обломная, лучше поставить заданный порт. Обломная настолько, что без указания порта-канала не будут возвращаться данные! Вот так всё жестоко. Поэтому пропишите modem.broadcast(PORT, "nanomachines", "setResponsePort", PORT). Например: broadcast(1337, "nanomachines", "setResponsePort", 1337). Прописали? Можно продолжать.
    К слову, возвращает "port", port, собственно, где port и есть указанное значение. Бесполезная фишка, кажется. getHealth():String,Number,Number. Значит, возвращает количество здоровья (текущее и максимальное). Чтобы не отвлекаться в дальнейшем, скажу сразу, что взвращаются данные так же через модем, через тот самый указанный порт. Так что не забудьте открыть его (modem.open(PORT)) и указать получение данных через event.pull("modem_message"). Формат данных: "modem_message", "адрес сетевухи на текущем компе", "адрес наноботов", порт, расстояние, "nanomachines", возвращаемые данные.... В данной функции возвращаемые данные: "health", 15, 20, где 15 — текущее, а 20 — максимальное состояния здоровья. getPowerState():String,Number,Number. Возвращает состояние энергии в наномитах: "power", 5000, 10000, где 5000 — текущее, а 10000 — максимальное количества энергии. getHunger():String,Number,Number. Возвращает состояние шкалы голода: "hunger", 10, 20, где 10 — текущее, а 20 — максимальное показания шкалы. getAge():String,Number. Возвращает "возраст" — общее время пребывания игрока на сервере в секундах с момента первого захода на сервер: "age", 1896, где 1896 — этот самый "возраст". getName():String,String. Возвращает имя игрока, который переносит наноботов: "name", "Fingercomp", где "Fingercomp" — имя игрока.

    Вооооооот, теперь самое интересное.
     


    V. Ход эксперимента. Часть, в которой 18 кнопок.
    Тык-с, теперь основное предназначение нанитов — давать всякие эффекты! Начиная от простых частиц вокруг игрока, заканчивая смертью.
    Есть 18 переключателей (входов в терминологии ОС), каждый даёт свой эффект. Набор переключатель-эффект называется конфигурацией. При каждом поедании нанобота эта конфигурация обновляется рандомными значениями. Если в первый раз 9 вход убивал, то теперь он, например, хилит. И т. д.
    ^ Выдрано из комментариев и заменено предыдущей непонятной фигнёй.
    Эффектами, кстати, могут быть не только всякие regeneration, но и просто спаун частиц, а также раритетные эффекты вроде магнита, притягивающего предметы. getSafeActiveInputs():String,Number. Возвращает лимит безопасных активных входов: "safeActiveInputs", 2, где 2 — это установленное в конфиге значение. getMaxActiveInputs():String,Number. Возвращает второй лимит на количество входов всего: "maxActiveInputs", 4, где 4 — тот самый лимит. setInput(input:Number, active:Boolean). Активирует и деактивирует вход. Тут всё просто — первый аргумент является числом от 1 до 18, а второй — состояние активности (true — включить, false — выключить). getInput(inpt:Number):String,Number/String,Boolean. Возвращает состояние выбранного входа. Если неверно указать — ошибка ("input", "error"). Иначе — состояние. "input", 13, true, где 13 — номер входа, а true — состояние =) getActiveEffects():String,String. Возвращает активные эффекты. Формат: "effects", "{digSpeed}", к примеру.

     


    VI. Финал. Трагедия и выводы.
    Собственно, поигравшись так с нанитами, активировал 9 вход. Вот такой казус возник:

    Угадайте, что произошло, когда я отключил креативку?)

     
     

    Чего и вам желаю.
     
    Выводы:
    Наноботы — не плод фантазии больного ума, но полезная вещь. Наноботы — штука хорошая, но только в разумных пределах. В неразумных от них можно сдохнуть.

    Напоследок продемонстрирую обещанное видео от автора мода про нанитов =) И включите аннотации, там инфа полезная.

     
     
    Удачи :P
  4. Fingercomp
    Новая версия OpenComputers. Неожиданно.
     
    Из наиболее интересного:
    Видеобуферы у графической карточки. Помимо основного, нулевого буфера, который отображается на экране, теперь можно аллоцировать дополнительные буферы — 2D-массивы символов с заданным разрешением, с которыми можно проводить те же операции, что и раньше: set, fill, copy и т. д., — но без потребления бюджета вызовов (то бишь халявно). Добавлена операция bitblt (bit blit), которая копирует кусок одного буфера на другой. Копирование на основной буфер потребляет бюджет вызовов пропорционально разрешению исходного буфера (не размеру области копирования). Может занять несколько тиков. Если верить @ECS, последнее преимущества в производительности практически убивает. Впрочем, за несколько лет, пока буферы висели в дев-билдах, люди уже их заиспользовали для игрушек: вот платформер, например. Прямые вызовы методов компонентов (любых, не только GPU), не имеющих явных лимитов или использования бюджета вызовов, теперь абсолютно бесплатны с этой точки зрения. Раньше они потребляли одну тысячную единицы бюджета вызовов. Подробнее о них — в моей статье. Обновлён шрифт: покрытие значительно расширилось путём забития недостающих символов глифами из Unifont. Заблокирован диапазон 0.0.0.0/8 для интернет-карты. Запросы туда делают примерно то же, что запросы на localhost. Примечательно, что эту уязвимость использовали на CTF для обхода файрволла. Советую почитать. Метод media добавили и для дисководов в серверной стойке. Досадное упущение. Пофиксили отключение компов при перезагрузке чанка: в определённых случаях стейт компьютеров вовсе не сохранялся, из-за чего они рестартились при выгрузке и подгрузке чанка. Починили debug.sendToDebugCard (весьма полезная функция). Разобрались в ориентации редстоун-карт. В 1.7.3 карточки в компах и серверах почему-то использовали абсолютные направления (север/юг/запад/восток) вместо относительных. Беспроводные модемы первого уровня снова могут получать сообщения. А раньше не могли. Это была бага. Ретрансляторы потеряли возможность ретранслировать на неограниченно большие расстояния. В коде мода запутались в min и max. В результатах поиска вейпоинтов навигационным апгрейдом теперь пишутся их адреса.  
    Остальные изменения и ссылка на скачивание — на GitHub.
  5. Fingercomp
    Среди всех компонентов в 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 механизм лучше.
     
    Поэтому этот пост получился не столько про интернет-карту, сколько про обработку ошибок.
  6. Fingercomp
    ПОЛНЫЙ ОБЗОР



    Computronics версии 1.5.5



    Часть вторая: стандартные блоки.


    И снова я приветствую Вас, уважаемый читатель этого блога! Вашему вниманию представляю вторую часть полного обзора CX версии 1.5.5, в которой я поведаю Вам о:
    Cipher Block (шифратор) Advanced Cipher Block (продвинутый шифратор) Colorful Lamp (разноцветная лампочка) Tape Drive + Cassette tapes (кассетный привод и, собственно, кассеты))




    I. Cipher Block
    a.k.a. "Шифратор"
    Алгоритмов шифрования уже и так огороды, но суровые разрабы CX не сдаются) На самом деле, может быть очень полезным блоком для каких-нибудь там ивентов. Почему? Этот блок позволяет шифровать и дешифровать определённую строку, используя в качестве ключа... не что иное, как... предметы! Да, знаю, поворот весьма и весьма неожиданный, но так оно и есть на самом деле.  

    Функции. cipher.decrypt(encrypted_string:String):String — пытается расшифровать строку, используя как "ключ" предметы в инвентаре шифратора. В случае неудачи (неправильный код) выдаёт ошибку. cipher.encrypt(string_to_encrypt:String):String — шифрует данную строку, используя как "ключ" предметы в инвентаре шифратора. Возвращает зашифрованную строку. cipher.setLocked(lock:Boolean) — блокирует/открывает доступ к инвентарю шифратора. При попытке открыть заблокированный шифратор, игроку выдаётся предупреждение в чат. cipher.isLocked():Boolean — возвращает состояние блокировки шифратора на данный момент.


    Скриншоты:


     
     
     
     

    II. Advanced Cipher Block.
    a.k.a. "Продвинутый шифратор"
    Этот вариант шифратора отличается от предыдущего тем, что здесь нет необходимости задать предметы как ключ — для шифровки/расшифровки используется алгоритм RSA.  

    Функции. adv_cipher.createKeySet(prime1:Number, prime2:Number):Keygen — создаёт и запускает процесс генерации пары ключей на основе двух простых чисел. Возвращается структура, содержашая данные методы: key_set.finished():Boolean — так как процесс генерации ключей не мгновенный, использовать ключи сразу же после создания генератора нельзя. Данная функция возвращает готовность ключей: true при завершённом процессе генерации и false, если процесс генерации не завершён. key_set.getKeys():Table, Table — данная функция возвращает пару сгенерированных ключей или nil при незавершённом процессе генерации.
    [*]adv_cipher.createRandomKeySet():Keygen — идентичен полностью прошлой функции за исключением того, что числа простые указывать не нужно — они выберутся случайно. [*]adv_cipher.decrypt(encrypted_string:String, private_key:Table):String — дешифрует данную строку, используя приватный ключ RSA. [*]adv_cipher.encrypt(string_to_encrypt:String, public_key:Table):String — шифрует данную строку, используя публичный ключ RSA.


     
     
     

    III. Colorful Lamp.
    a.k.a "Разноцветная лампочка"
    Блок, испускающий свет, цвет которого можно менять программно (всего цветов 32768, включая чёрный).
    Функции. lamp.getLampColor():Number — возвращает текущий цвет лампочки. lamp.setLampColor(color:Number):Boolean — устанавливает текущий цвет лампочки. Если цвет равен нулю, то лампочка выключается.


    Скриншоты:
     
     
     
     

    IV. Кассеты и всё, что с ними связано.



    IV.1. Cassette Tapes.
    a.k.a. "Кассеты"
    Кассеты — мощное переносное хранилище информации, как текстовой, так и музыкальной. Всего видов кассет в CX — ни много, ни мало — 8 штук! И каждый тип различается требованиями по ресурсам и вместительностью музыки в минутах. Деревянная — 2 минуты Железная — 4 минуты Стальная — 6 минут Золотая дешёвая — 8 минут Золотая дорогая — 16 минут Алмазная — 32 минуты "Дешёвая" незерстарровская — 64 минуты И, наконец, самая ненажная и дорогущая одновременно вещь, которая когда-либо существовала в Майначе... Дорогущая незерстарровская — 128 минут. Это больше, чем на CD-диске!)) Но и качество хромает. Тем не менее, на такую кассету можно записать больше данных, чем на РЭЙД с 3 алмазными дисками)

     


    IV.2. Tape Drive.
    a.k.a. "Стример"
    Но одной кассетой Вы удолевтвориться не сможете... Для считывания и записывания информации необходим аналог CD-ROM'а, но для кассет — кассетный проигрыватель, называемый стримером.
    Функции. tape.stop():Boolean — останавливает проигрывание кассеты. tape.setSpeed(speed:Number):Boolean — устанавливает скорость воспроизведения (от 0.25 до 2). tape.getLabel():String — возвращает метку касссеты. Если не задано, равно "". tape.setLabel(label:String):String — устанавливает метку кассеты. Она видна в тултипе кассеты и в кассетном приводе, если там вставлена дискета. Возвращается новая установленная метка кассеты. tape.getState():String — возвращает текущий статус кассетного привода: "RUNNING", если проигрывается кассета, или "STOPPED", если нет дискеты, или воспроизведение было остановлено. tape.seek(amount:Number) — перематывает кассету на данное количество байтов вперёд/назад (при отрицательном значении). tape.setVolume(volume:Number) — устанавливает громкость кассеты от 0 до 1 (принимаются дробные значения). tape.getSize():Number — возвращает размер музыкального содержимого кассеты в байтах. Полезно вместе с функцией tape.seek() для перемотки в начало. tape.play() — начинает проигрывание музыкального содержимого кассеты. tape.isEnd():Boolean — возвращает true, если проигрывание содержимого завершилось. Полезно для зацикливания музыки на дискете в совокупности с tape.getSize() и tape.seek(). tape.isReady():Boolean — возвращает true, если в приводе присутствует дискета. tape.read([length:Number]):Number|String — читает всё (или определённое кол-во байтов), что записано на кассету. tape.write(data:Number|String) — записывает на кассету данные.


    Программа.
    При вставке кассеты в привод, появляется программа tape, которая позволяет совершать базовые операции над кассетой без необходимости использовать интерпретатор Lua. Доступно следующее: tape play — начать проигрывание. tape pause — приостановить проигрывание. tape stop — остановить проигрывание и перемотать в начало. tape rewind — перемотать кассету в начало. tape label — получить метку кассеты. tape label label — установить метку label кассете в приводе. tape volume volume — установить громкость volume музыке на кассете. tape speed speed — установить скорость speed проигрывания. tape write path/to/audio/file — записать на кассету файл с жёсткого диска компьютера. tape write URL — записать на кассету музыку с удалённого сервера, доступную по адресу URL.


    Скриншоты:


    IV.3. Формат звуковых файлов DFPWM.
    Прочитав рассказ о воспроизведении звука, наверняка, Вы уже начали искать тот самый файл с вашей любимой музыкой. Но не всё так просто! Дело в том, что используется странный и непонятный формат файлов — DFPWM... Но где его искать?

    Шаг первый. Конвертер.
    Первым делом, придётся открыть http://www.google.com/ в отдельной вкладке, ведь мороки с музыкой будет много. Сначала найдите в интернете конвертеры из формата Вашего файла в формат WAV (можно пропустить, если изначально в WAV). Для Линукса это ffmpeg, например.

    Шаг второй. Получение файла DFPWM.
    У вас должен быть на руках WAV-файл с музыкой. На всякий случай попробуйте открыть его в аудиопроигрывателе, дабы убедиться в "правильности" файла. Если всё ОК — закрываем Гугл и идём дальше. Загрузите данный файл на свой компьютер: https://dl.dropboxusercontent.com/u/93572794/LionRay.jar Это конвертер из WAV в DFMPW. Выставьте права на исполнение, если они не соблюдены, и запустите файл через Java. Укажите расположение исходного WAV-файла и запустите процесс конвертации.

    Шаг третий. Сохранение файла.
    Если у Вас есть доступ к папке сохранения, то всё просто — откройте папку ~/saves/<имя_мира>/opencomputers/<адрес_диска>/ и переместите туда Ваш получившийся аудиофайл. Затем вставьте кассету в стример, подключите последний к компьютеру и пропишите tape write <имя_аудиофайла>. Иначе Вам придётся искать хостинг, выдающий прямые ссылки на файл. Загрузите файл и скопируйте ссылку. Затем вставьте интернет-карту в компьютер и пропишите следующее: tape write <ссылка_на_аудиофайл>. Дождитесь окончания загрузки.  



    Фух, вот я, наконец, и закончил. В сумме на написание этого гайда ушло около шести часов ночного времени, так что думаю, что он Вам понравится) Оставляйте оценки, лайки, жду комментариев! А в следующей части я расскажу о четырёх новых картах, которые добавляет CX.  
    Ссылка на страницу мода: http://wiki.vex.tty.sh/wiki:computronics
  7. Fingercomp
    Здрассьте. Давно уже назревал план сменить к чертям оформление форума, ибо текущее положение дел вызывает тошноту. Да и ночью глаза болят потом.
    И вот, наконец, недавно я загорелся энтузиазмом, и было принято неотвратимое решение напилировать свою тему для форума. Так как станадртные средства форума по изменению мне неизвестны, а курить доки по этому было катастрофически лень, всё сделано в виде стиля к Stylish.
     
    Что меняется?
    Цвет всех элементов, расположение некоторых блоков-индикаторов, округление, картинки, кнопки и много чего другого.
     
    Как установить?
    Перейдите на эту страницу ( https://userstyles.org/styles/120122/dark-dark-robot) и перейдите по ссылке в заголовке. Вам предложат установить расширение для браузера "Stylish". Согласитесь на установку и дождитесь окончания. Затем снова перейдите на эту страницу и тыкните по большой зелёной кнопке "Install with Stylish". Во всплывающем окне нажмите "OK"... Всё! Теперь вы можете наслаждаться тёмным дизайном форума,
    Чтобы отключить тему, нажмите по значку Stylish в панели инструментов и снимите галку с темы "Dark-Dark Robot". Таким же образом её можно будет включить. Управление темой осуществляется на странице управления темами. Щёлкните по значку и выберите "Manage Styles...".
     
    А что нас ожидает?
    Скриншоты этого добра:

     
     

    Внимание!
    Начиная с версии 1.4, DDR требует наличия скрипта для GreaseMonkey под названием "Good Bad People".
    Ссылка: https://greasyfork.org/en/scripts/13471-good-bad-people
    Для работы требуется расширение браузера GreasyMonkey (Firefox) или TamperMonkey (Chrome)!
  8. Fingercomp
    О 1.6 было говорено ещё с очень давних пор — примерно год назад. И наконец-то первая бета OC 1.6 доступна для скачивания.
    Вообще, 1.6 всё это время можно было загрузить как дев-версию — достаточно перейти на Jenkins. Однако билды там не всегда блещут стабильностью.
     
    Что изменилось:
    Полный ченджлог Sangar предоставит, когда отрелизится 1.6.0.
    Но благодаря ГитХабу я могу сравнить 1.5.22 и 1.6. Так что краткое переложение >150 коммитов:
    Новые серверные стойки. Модульность, крутой GUI и многое другое. Дисковод — компонент. Геолайзеру теперь можно задать не только колонну, но и прямоугольник. Терминальный сервер. Lua 5.3.2. В internet.request можно задавать хедеры. Ачивки теперь даются не только за крафт, но и за поднимание предметов. Торговый апгрейд для роботов. playSoundAt для дебаг-карт. __gc-метаметод оказался опасным — им можно перегрузить сервер. Теперь в настройках есть опция для включения этого метаметода. По умолчанию __gc отключён. OpenOS 1.6. Там мегатонны всяких изменений. И, кстати, эта система грузится на планке памяти первого уровня. Даже несмотря на новые проги, либы и пр. Дронотапки можно красить. Новый шрифт. Логирование чанклоадеров. Ну и множество других изменений.

    Качайте новую версию тут и крушите её. А обо всех найденных багах, как обычно, сообщать сюда.
  9. Fingercomp
    Вот был у нас вот такой "простой" лончер от Квертика (тык), в котором без поллитра и ведра инструкций не разобраться, да.
    Естественно, это не вариант ни разу, особенно для меня. Потому представляю своё дитё — Крутой Графонистый Лончер Игр На Коленке!
     
    Интерфейс интуитивно понятен. Сейчас имеется рейтинг, лайки, статистика игр (по игрокам и всего), крутой поиск (чинит даже жестокие очепятки) и воз графонистости.
    Файлы хранятся в директории /games (создайте её). Для каждой игры необходима своя директория. Название директории должно быть коротким и простым (фиг знает, зачем). Например, /games/flappy-block/. В этой папке делаем файлец info и записываем следующую бурду:


    Перед запуском пропишите mkdir /var и mkdir /var/log. Это нужно для хранения лога лончера.
     
     
    Собственно, запишите сам лончер по ссылке ниже, поменяйте там настроечки по своему желанию (OWNER там, например, чтобы по вашему клику на "гамбургер" в главном меню программа завершалась). И всё, можно запускать!
     
    Gist ID: 684d2c72faaa941df857
     
    Скриншоты
     
     
  10. Fingercomp
    Приветствую Вас, уважаемый читатель! В самый ТРУДный день '15 года я, наконец, написал красивые часы, которые идеально подходят для декорирования, например, метро или аэропорта, а также как просто инфо-панель дома Не будем отрывать коту хвост, закончим здесь предисловие.
     
    Finger Clock v. ∞
    Простая прога для OpenComputers, которая будет показывать Вам текущее майновремя... и реальное время с поддержкой часовых поясов!
    Pastebin: http://pastebin.com/aKjh5SZL
    Command: pastebin get aKjh5SZL clock
     
    Минимальная комплектация:
    Монитор T1 x1
    Компьютер T1 x1
    Жёсткий диск T1 x1 + OpenOS
    Видеокарта T1 x1
    Редстоун-карта T1 x1 (требуется, если монитор не поддерживает клик мышкой, то есть если монитор первого уровня. В мониторах выше не обязателен).
    Процессор T1 x1
    ОЗУ T1.5 x2
    EEPROM — Lua BIOS
    Интернет-карта/OpenNet-интернет — только для установки

    Рекомендуемая комплектация:
    Монитор T2/T3 x6
    Компьютер T2/T3 x1
    Жёсткий диск T1 x1 + OpenOS
    Видеокарта T2/T3 x1
    Редстоун-карта T1 x1 (требуется, если монитор не поддерживает клик мышкой, то есть если монитор первого уровня. В мониторах выше не обязателен).
    Процессор T2/T3 x1
    ОЗУ T2/T2.5/T3/T3.5 x1/2
    EEPROM — Lua BIOS
    Интернет-карта/OpenNet-интернет — только для установки

    Функции:
    Позволяет показывать реальное время и внутриигровое время.
    Настройка цветов, разрешения и часового пояса в константах в начале программы.
    Не использует отдельные библиотеки — меньше возни с установкой.

    Настройка:
    В самом начале программы есть список констант для настройки программы.

    MT_BG = 0x000000 -- Цвет фона при режиме показа внутриигрового времени.MT_FG = 0xFFFFFF -- Цвет текста при режиме показа внутриигрового времени.DAY = 0xFFFF00 -- Цвет надписи "Day".EVENING = 0x202080 -- Цвет надписи "Evening".NIGHT = 0x000040 -- Цвет надписи "Night".MORNING = 0x404000 -- Цвет надписи "Morning".RT_BG = 0x000000 -- Цвет фона при режиме показа реального времени.RT_FG = 0xFFFFFF -- Цвет текста при режиме показа реального времени.TIMEZONE = 0 -- Часовой пояс (от -12 до 12).W, H = 80, 25 -- Разрешение экрана. Рекомендуется 40x8.REDSTONE = false --[[ Если параметр будет установлен в true, программа будет сменять режимы при изменении редстоун-сигнала. ]]--TOUCH = true --[[ Если параметр будет установлен в true, программа будет сменять режимы по клику мышкой по экрану. ]]--KEY1 = 13 -- Коды клавиши. По умолчанию — [Enter].KEY2 = 28 -- Подробнее в комментариях.AUTOMODE = true --[[ Режим, который активируется при запуске программы. true — режим внутриигрового времени, false — режим реального времени. ]]--SHOWSECS = true --[[ Позволяет скрывать секунды в режиме реального времени при значении, равном false. ]]--SWDATEMT = true -- Показывать внутриигровую дату.SWDATERT = true -- Показывать реальную дату.SWDTMMT = true -- Показывать внутриигровое время суток.SWDTMRT = true -- Показывать реальное время суток.
    Скриншоты:

    Режим показа внутриигрового времени
     
     

    Режим показа реального времени
  11. Fingercomp
    Первым делом, сохраняйте панику. Так интереснее.
    У нас есть группа в ВК, которая, в отличие от параллельных — тупиковых веток эволюции — жива и даёт продукт в виде постов. Очень интересных.
    Не любите ВК? Я тоже. Имеем мы канал в IRC: зайти можно даже с телнета. Но лучше с помощью какого-то клиента: HexChat, WeeChat, Quassel.
    Контактные данные для них:
    Сервер: irc.esper.net Порт: 6697 TLSv1.2 Канал: #cc.ru Впрочем, присутствует и веб-клиент для обитателей двумерного денежно-временного мира.
    Надо сказать, у нас даже дискорд-клуб где-то был.
    Однако не только флуждением занимается Хэш cc.ru. Мы завели организации вот тут:
    На GitHub. На GitLab. В Trello. Под кроватью. А ещё подпольные мудрости тщательно и бережно схороняются на свалку нашего цитатника, так что башорг вам больше не нужен.
    Не забудьте, по пути куда-нибудь находясь, записаться в читателей этого элитного клуба. Потому что здесь мы будем писать. Что-то.
    Отлично. Наши цепкие объятия уже ждут-поджидают вас.

  12. Fingercomp
    Здрассьте!
    Я тут прогуливался по StackExchange, и нашёл интересную штуку: Code Golf. В общем-то, это программистский конкурс, который цель ставит эффективно расходовать ресурсы... только жёсткго диска. Надо любыми судьбами на любом языке сделать программу с наименьшим числом даже не символов, а байт!
    Мне показалось это очень интересным занятием. Посмотрев на вопросы, которые по той ссылке доступны, у меня и идейка пришла тоже.
     
    Я всё расписал по идейке здесь: https://znc.hanvix.ru:1308/vori_zolota.htm — и правила, и задание, и полезные ресурсы вообще. Тут вкратце объясню.
     
    Слушали про BMP, что как BitMaP расшифровывается? Так вот это есть формат картиночек такой от Microsoft. Не то, что бы я как-то безудержно фанател от этой корпорации, просто формат картиночек простой, как бревно липовое. Никаких заморочек с компрессиями и прочей интересной очень дрянью! Немного метаданных — и набор пикселей, как он есть!
    Парсить там нечего совершенно, в общем.
    И, значит, берём такую картиночку. Задача: за минимальное число байт исходников написать работающую программу, которая будет рисовать различные символы в зависимости от цвета и прозрачности. Это не сложно, это просто.
     
    Итак, за неделю жду программочки, будем мерить байтики :P Я настоятельно рекомендую поучаствовать, хотя бы почитать в Wikipedia про формат: это достаточно интересная тема. Тем более, что язык программирования абсолютно любой, выбирайте любимый и дерзайте!
     
    Выбирать победителей будем по размеру программы и по количеству лайков. В комментариях опишите работу программы, как её использовать, какой язык, что для неё нужно, приложите саму программу. И можно будет надеяться на призы: от медальки на форуме до игрушки в Steam.
     
    Ещё раз советую заглянуть на https://znc.hanvix.ru:1308/vori_zolota.htm — там всё подробнейшим образом расписано, чтобы облегчить написание в разы. Если и там непонятно что-то — задавайте вопросы в нашей всеми любимой IRC Будем, как обычно, рады ответить и помочь.
     
    Удачи!
  13. Fingercomp
    Сегодня нашему каналу в IRC исполняется один годик, поэтому пришло время рассказать, что это, зачем это и как к нему подключиться.
     
    Начнём с понятий.
    IRC — это протокол обмена мгновенными сообщениями через интернет. Сделанный в далёком 1988 году, и по сей день он всё ещё юзается из-за удобности, простого масштабирования, простоты и доступности буквально отовсюду, где есть подключение к интернету — вплоть до холодильников.
    В общении участвуют клиент и сервер. Клиенты подключаются к серверу и общаются.
    Для разделения тем существуют каналы — на каждом отдельные сообщения, темы, люди и так далее. Так что на одном сервере могут быть сотни каналов, никак друг с другом не связанных.
     
    Главное, что нужно понять: IRC — это не чат в браузере, как здесь на форуме. Здесь отдельные серверы, отдельный протокол, и поэтому просто так через браузер не подключиться, набрав адрес сервера. Для подключения к IRC нужно воспользоваться специальной программой — клиентом. Здесь я покажу несколько клиентов и расскажу, как их настроить.
     


    Веб-клиент Iris IRC
    Для ситуаций, когда надо по-быстрому зайти на канал, но клиента нет под рукой или лень настраивать. Для полноценного сидения использовать проблематично, так как требуется грузить жирный браузер, и стабильность подключения так себе.
    Кроме того, веб-клиенты — поделки очень плохого качества, неконфигурируемые, отсутствуют банальнейшие фичи, например форматирование, или сделаны криво. Тем не менее.  

    Возьмём, например, Iris IRC. Ссылка на него (нацеленный на серверы Esper) находится вверху, в панели навигации (). Штука очень минималистичная.



    Сверху вводите свой ник, пишете название канала для подключения (по умолчанию стоит наш), если нужно, ставите галочку и вводите пароль и юзернейм (об этом позже). Однако ставить её необязательно. После этого тыкаете на кнопку. Через несколько секунд появится вот такой интерфейс:



    Что здесь видим?
    Во-первых, кнопка меню . Советую сразу перейти в Menu ‣ Options и поставить галочку напротив "Automatically colour nicknames", чтобы визуально различать людей на канале — по цвету.
    Во-вторых, переключалка каналов . Можно тыкать Alt и цифру от 1 до 9, чтобы быстро переключаться между каналами.
    Строка топика — небольшого сообщения с темой обсуждения или просто полезными ссылками.



    Ниже находится окно чата, в котором будут отображаться ваши сообщения и сообщения других людей, а так же другие оповещения (например, о заходе человека на канал).
    Правее — список ников, подключённых к каналу. Знак "@" перед ником означает операторские привилегии — т.е. админ канала, "+" же ничего не даёт (у нас он является неким знаком отличия для людей, которые часто находятся на канале и чего-то мыслят в программировании, но на других каналах может быть не так).
    И, наконец, поле внизу для набора сообщений и команд.
     

    Чтобы отключиться от сервера, просто закройте вкладку.
    Чтобы зайти на другой канал, пропишите /j #имя-канала. Например, /j #cc.ru-server1.
     


    HexChat
    Десктопный клиент IRC, конфигурируемый, довольно удобный и пригодный для повседневного общения. Однако он уже требует несколько более сложной настройки.  

    Скачав и установив HexChat, после запуска мы увидим вот такое окошко:



    Для начала введите 3 варианта ника (они будут пробоваться использовать поочерёдно в случае, если предыдущий ник занят на сервере). Обычно просто ставят "_" в конец. В поле "User name" введите юзернейм — это общий для всех ник (при этом проверка на занятость юзернейма не производится).
    В списке ниже найдите "EsperNet". Нажмите на кнопку , а затем поставьте галку , чтобы быстрее находить эту сеть. После этого можно нажать на кнопку .
    Произойдёт подключение к серверу. Используйте команду /j #имя-канала, чтобы зайти на нужные каналы. Например, /j #cc.ru. Появится вот такой интерфейс:



    Сверху находится меню. Ниже переключалка каналов. Крестик позволяет закрыть вкладку (и выйти с канала).
    Ещё ниже строка заголовка, режимы каналов. Справа список ников на канале, слева — сам чат, ниже — поле ввода сообщений и команд. Можно кликнуть правой кнопкой мыши по вкладке канала и отметить "Autojoin", чтобы автоматически заходить на канал после подключения к серверу.
    На данный момент HexChat — рекомендуемый нами клиент для Windows и Mac.
     
     
     

    KVIrc
    Объективно: вроде всё по стандарту, использовать можно. Субъективно: куча ненужных кнопок, прокладок, интерфейсов, всё запутано и намешано, выглядит ужасно. Поэтому настоятельно рекомендую не использовать этот клиент. В любом случае, рассказать о нём стоит.  

    После установки и запуска будет предложено выполнить около пяти простых шагов по настройке клиента. Следуйте инструкциям (тем более, что там есть русский язык). Появится вот такое окошко:




    Нажмите на иконку , введите в поле под списком "EsperNet". Затем нажмите на , в то же поле введите "irc.esper.net". Нажмите "Connect Now" и затем "OK".
    Появится вот такое диалоговое окно:
     




    Введите в верхнее текстовое поле имя канала (например, "#cc.ru") и нажмите "Join", а потом "Close".
    Наконец, можно использовать главное окно:
     




    Сверху меню, ниже ещё всякие кнопки для действий типа подключения к новому серверу. Ниже топик, режимы канала, потом список ников, сам чат и поле ввода. Ниже статусная строка.
     
     
     

    WeeChat
    Очень продвинутый, невероятно удобный клиент для Linux. Запускается и работает в терминале, использует ncurses, поэтому даже иксы не требуются. Для Linux однозначно рекомендую, настроив weechat, как нужно, больше другие клиенты использовать не захочется.  

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



    Пишем /set irc.server_default.nicks ник,ник_,ник__, чтобы выставить ник. Затем /server add esper irc.esper.net/6697 -ssl.
    После этого можно прописать /connect esper для подключения к серверу. И дальше уже /join #имя-канала.



    Общаться можно уже и так, а за дополнительными фишками обращаться нужно к мануалу.
     

    Для телефонов тоже есть свои клиенты, но тут я ничего посоветовать не могу.
     
    Это были клиенты. Но просто поставив их, особо толка не будет. Поэтому сейчас будет несколько штук IRC, общие для всех клиентов.
     
    Помимо каналов на сервере можно напрямую общаться с каким-либо человеком. Для этого нужно прописать /msg <ник> <сообщение> (например, /msg fingercomp привет). В большинстве клиентов можно открыть вкладку (или буфер) для общения с человеком, как для каналов, с помощью команды /query <ник> (например, /query fingercomp).
     
    Есть ещё команда /me. Если использовать её, то вместо <ник> сообщение будет показано что-то вроде * ник сообщение. Так можно отправить сообщения от третьего лица (вроде "fingercomp написал гайд").
     
    Команда /notice — это та же отправка сообщения. Она немного отличается видом в клиентах, но всё равно видна всем на канале или собеседнику, в зависимости от того, кому направить сообщение. Смысл команды — предотвратить вызов ботом команд других ботов.
     
    Чтобы уйти с канала, можно использовать /part <сообщение выхода>. Сообщение будет показано другим людям в оповещении, например так:
    Можно вообще от всего сервера отключиться с сообщением, как выше. Используйте команду /quit <сообщение выхода>.
     
    Авторизация на EsperNet.
    Зачем нужна авторизация? Прежде всего, чтобы автоматически получать какие-либо права. Например, на канале #cc.ru-server1 (туда транслируется чат с сервера) мы используем это, чтобы автоматически выдавать право отправлять сообщения на сервер.
     
    Чтобы зарегистрироваться, нужно зайти с нужного ника и прописать /msg nickserv register <пароль> <email>, например /msg nickserv register zxcvbnM1 fingercomp@example.com. На ящик придёт сообщение от Esper, в котором будет команда для подтверждения регистрации. Её нужно скопировать и выполнить (то есть написать в строку ввода).
    Чтобы затем залогиниться, используйте команду /msg nickserv identify <пароль>.
     
    А теперь последуют вещи, которые есть только на нашем канале #cc.ru.
    У нас есть правила, которые желательно соблюдать. Ссылочка на них в топике: https://git.io/vwLXq
    Основной бот на канале — brote. У него есть множество команд: от погоды до опросников. Список команд можно получить с помощью команды .help. Брот также обрабатывает команды в ЛС.
    В топике после даты я помещаю всякие интересные события, ссылки и прочее. Так что иногда лучше смотреть на топик.
    Ведётся статистика всего канала — анализируются логи с середины ноября, хотя канал существовал примерно полгода до этого. Вот ссылка: https://stats.fomalhaut.me/. Можно поизучать — достаточно интересная штука.
    Темы обсуждений могут быть абсолютно разными — от размеров очередей в больницах до новых фич в языке Rust. Но в любом случае я постараюсь ответить на все вопросы по программированию на Lua, отправленные на канал. Даже в середине обсуждения — тогда, может, не сразу, но обязательно отвечу.
     
    Кроме того, у нас есть канал #cc.ru-server1. Сюда бот пишет сообщения с чата сервера, сообщения о смерти игроков, а также пишет текущий онлайн в топик. Поэтому для модерирования очень удобная штука.
    Чтобы иметь возможность отправлять сообщения из канала на сервер, нужно иметь войс — знак "+", который выдаётся персонально зарегистрированным людям. Я использую несколько критериев для оценки, например активность и адекватность игрока. За любое нарушение правил сервера через IRC следует вечная блокировка возможности отправить сообщения.
    Но и без войса можно просто сидеть и читать чат.
     
    В целом, это всё, что я хотел сюда написать. Ждём на наших каналах — подключайтесь, у нас есть печеньки.
  14. Fingercomp
    О прошлой версии я умолчал, но исправляюсь. Вышла 1.7.5 с чаем и сладкими фичами.
     
    Новинки
    Анализатор, которым адреса компонентов получаем, теперь вставляется в планшет. Он займёт компонент barcode_reader, но методов у него нет. Зато он вернёт в ивенте tablet_use адреса и типы всех компонентов внутри блока, если планшетом нажать на него и удерживать до писка. Известно, что в компы вставлять можно любой объём текста не более 256 строк. Дело в том, что из-за ошибки каждая строка отсылает отдельное событие clipboard, а на компьютере есть лимит очереди необработанных сообщений. Равный 256. Поэтому остальные строки отбрасываются. Теперь этот лимит можно менять в конфиге. В internet.request разрешили использовать экзотические HTTP-методы вроде PUT. Ангельские апгрейды, которые позволяют ставить блоки без опоры, теперь вставляются в дронов. Наконец-то. [MC 1.12] Зарядники заряжают предметы в инвентаре игрока рядом с ним. Апгрейд опыта показывает уровень прокачки ещё и в тултипе. [MC 1.12] Если банки с эссенцией из Thaumcraft просканировать через контроллер инвентаря, то добавляется информация о том, что за ссенция в ней находится. [MC 1.12] Поддержка многожильных кабелей из SimpleLogic. [MC 1.12] Поддержка WE-CBE. [MC 1.12] Изменено
    Вокруг экрана не будет рамки, если не приседать. Добавлено ещё сколько-то имён для роботов. Обновлён китайский перевод мануала. Поменяны некоторые комментарии в конфиге. gpu.bind работает быстрее. В computer.pushSignal можно пихать таблицы! Кроме вложенных. Сделана более логичной нумерация уровней APU. Она соответствует теперь уровню процессора в нём. В RAID все диски переключаются в режим с ФС при вставке. Ну и форматируются, конечно. Починено
    Роботы научились черпать вёдрами. Очередной дюп жидкости. Два даже. Модемы T2 ловят и проводные сообщения, наконец-то. robot.swing правильно рапортует статус, даже если блок крушится очень быстро. Сообщения между серверами серверной стойки передавались медленно и неторопливо. Реле правильно показывают проходящий трафик. Всякие проблемы с передачей сообщений через реле. Если itemDamageRate поставить в 0, роботы теперь вообще не будут ломать инструмент. Краш из-за hologram.copy. Метод isSunVisible геолайзера на планшете теперь таки работает. Раньше true возвращал всегда. Краш из-за удалённых терминалов. Робот без инвентаря дропнутые предметы сжирал и не давился. TLWY не выкидывался, когда надо было. Можно было сервер положить. Теперь эту ошибку нельзя перехватить в pcall/xpcall. См. коммент об изменении в поведении xpcall. Краш при взрыве работающего компьютера. С включённым LuaJ не все архитектуры были доступны. Краши, баги, недочёты с AE2. Краш с IC2 Classic. Изменения в OpenOS
    Ошибки, связанные с установкой oppm. Таймеры не вызывались во время event.pull. Команда reset ставит максимальное разрешение экрана. Фиксы в либе vt100. Добротно падает, если принтить объекты с недоброкачественным метаметодом __tostring.  
    Стоит отметить, что OpenComputers больше не будет обновляться для версии MC 1.11.2. На 1.7.10, 1.10.2 и 1.12.2 всё останется по-прежнему. Впрочем, более половины новых фич только для 1.12.2.
     
    Качать можно отсюда.
  15. Fingercomp
    Да, дамы и господа, он тут! Тот самый автокрафт, который был на старом ИТ с АЕ, обновляется до новой версии, в которой полностью отказываемся от АЕ и переходим на сундуки.
    Для работы нужна "стенка" из сундуков, двойные использовать нельзя. Или ставить вплотную сундуки из IronChest, или же попеременно сундук обычный / сундук-ловушка. В левой позиции ставим робота-крафтера мордой в сторону других сундуков, под него ещё один сундук. Теперь считаем размеры массива: по ширине сколько (X) и по высоте (Y).
     
    Затем ставим комп/сервер/планшет с нет-картой беспроводной, пишем mkdir /usr/bin, mkdir /usr/share, затем wget https://gist.github.com/Fingercomp/64d811a08af2e7848c9d/raw/8bacd47b12d2f202fb2d8bb48ff4010534e29b96/pc-craft.lua /usr/bin/craft.lua, wget https://gist.github.com/Fingercomp/64d811a08af2e7848c9d/raw/8bacd47b12d2f202fb2d8bb48ff4010534e29b96/pc-recipes.lua /usr/bin/recipes.lua.
     
    Запускаем команду recipes, пишем 4 и 0. Всё. Компьютер готов.
     
    Включаем робота, пишем wget https://gist.github.com/Fingercomp/64d811a08af2e7848c9d/raw/8bacd47b12d2f202fb2d8bb48ff4010534e29b96/robot-autorun.lua /autorun.lua, wget https://gist.github.com/Fingercomp/64d811a08af2e7848c9d/raw/8bacd47b12d2f202fb2d8bb48ff4010534e29b96/robot-scan.lua /scan.lua.
     
    А ещё чуть выше мы считали размер массива сундуков. Пишем edit /usr/bin/craft.lua на компьютере, в начале самом файла находим local CHESTX, CHESTY = 1, 2 и меняем числа на свои. Если ширина = 4, а высота = 3, то будет 4, 3. И т. д.
     
    В принципе, автокрафт теперь готов к работе. Но чтобы скрафтить хоть что-либо, потребуется заполнить базу данных с помощью программы recipes. Я чуть ниже подробнее расскажу о том, как это делается, а пока можете просто прописать команду wget https://gist.github.com/Fingercomp/64d811a08af2e7848c9d/raw/8bacd47b12d2f202fb2d8bb48ff4010534e29b96/pc-db /usr/share/db, чтобы скачать готовую БД, которую составлял я сам. =)
     
    Давайте просканируем рецепт! Открываем прогу recipes:


    Жмём 7 и [Enter]:


    Идём к роботу и выкладываем рецепт так:


    Пишем в роботе scan:


    Всё, забираем предметы у робота и возворащаемся к компу. Там должна быть такая картинка:


    Вводим имя рецепта, количество предметов на выходе крафта. Жмём [Enter].


    Тыкаем по [y], энтерим... И всё! Рецепт сохранён в памяти... Программы. Это означает, что если сейчас выйти, рецепт будет утерян. Так что не забывайте писать 4 перед выходом!
     
    Остальные функции прог craft и recipes интуитивно понятны. Если нет — смотрите предыдущую запись про автокрафт, интерефейс тот же... =)
    Кроме одного. База данных предметов получается при первом запуске программы в сеансе, затем она кэшируется в _G. Это позволяет не ждать опять фигалион лет, если вы ошиблись номером рецепта или у вас не было какого-то ресурса. Но если что-то изменили в сундуках — всё, БДП не будет соответствовать действительности. В таком случае запустите программу так: craft update.
    Сундук под роботом (выход) проверяется каждый раз при старте проги. Если не хотите делать проверку (всего 5 секунд), запустите с аргументом noupd[/i]: [il]craft noupd.
     
    К слову, код там старый, гнилой и высохший. Самому не нравится. Но что поделать — переписывать лень.
     


    БАГИ В ПРОГРАММЕ CRAFT И ИХ РЕШЕНИЕ Программа некорректно просчитывает выходное количество, если запрашивается родитель в одном рецепте несколько раз, при этом выходных предметов нет в сундуках. То есть? А вот. Есть процессор, в нём 4 изумруда требуется. Допустим, изумрудов просто нет, но есть блоки. И во время построения рецепта изумруды буду запрошены 4 раза, каждый раз будет найдена нехватка, так что в итоге в плане окажутся 4 блока изумрудов. Проблема решается достаточно просто: вызовите тот крафт, родитель которого запрошен слишком большое количество раз. То есть, в нашем случае достаточно просто заказать крафт изумрудов.
    [*]Не баг, но всё же. Программа во время крафта выдаёт ошибку "в функции экспорта" и сбрасывает крафт.
    Случается из-за того, что БДП устарела, и когда робот пытается взять предмет в слоте из БДП, а там его не оказывается или там находится вообще другой предмет, вызывается ошибка. Решение банальное: перезапустите крафт, вызвав программу так: craft update.
    [*]Тоже ошибка в функции экспорта, но на 188 (или 118 ) строке и со стактрейсом.
    Это действительно баг, причину возникновения которого мне выяснить не удаётся. Однако, есть решение. Перезапустите программу, можно даже без update, и проблема решится.
    [*]И ещё один совсем не баг. Программа запускается и зависает на старте, даже не показав GUI.
    Всё просто: она ждёт ответа от робота, который недоступен Проверьте, работает ли робот вообще, запущена ли на нём программа и находится ли он в зоне дальности модема компьютера (а компьютер — в зоне радиуса модема робота). Прервите программу комбинацией [Ctrl] + [Alt] + [C] и запустите снова после устранения ошибки.



    Очень удобно становится, если вместо обычного сундука под роботом поставить якорный, программы с компа перенести на сервер и таскать с собою эндерсумку и удалённый терминал от сервера. Тогда вы можете заказывать предметы на расстоянии до 400 блоков от серверной стойки, и выход окажется в эндерсумке
  16. Fingercomp
    Здесь опишу такие штучки, которые могут потребоваться продвинутым OC-программистам (да и просто Луа-программистам).
     
    Busy Idle
    С помощью этого трюка можно делать довольно точные задержки, причём с длительностью менее тика.
    local lastSleep = os.clock() local function sleep(t) local begin = os.clock() while os.clock() - begin < t do if lastSleep - os.clock() >= 3.5 then -- В конфигурации дефолтное значение = 5 секунд, ставим на 1.5 меньше для безопасности. os.sleep(0.05) -- Вынужденная задержка. lastSleep = os.clock() t = t - 0.05 end end end
    Проверка по значению
    Очень часто в моих программах нужно найти ключ, значение которого соответствует данному. Для этого я написал простую функцию:
    local function isin(tbl, value) for k, v in pairs(tbl) do if v == value then return true, k end end return false end На огромных массивах может и затормозить — скорость работы прямо зависит от длины массива.
     
    Табличная магия
    Рассмотрим этот на первый взгляд обычный пример кода:
    local tbl1 = {"My", "super", "table", 42} local tbl2 = tbl1 tbl2[2] = "cool" for _, tbl in pairs({tbl1, tbl2}) do -- Напечатать значения таблиц for k, v in pairs(tbl) do print(k, v) end end Разумно ожидать такое:
    1 My 2 super 3 table 4 42 1 My 2 cool 3 table 4 42 Но вместо этого получаем: 1 My 2 cool 3 table 4 42 1 My 2 cool 3 table 4 42
    Как видно, изменив значение в одной таблице, изменилась и другая.
    Дело в том, что переменная хранит указатель на таблицу, а не саму таблицу. Соответственно, и tbl1, и tbl2 ссылаются на один и тот же массив.
    На первый взгляд это кажется ненормальным. Как скопировать-то таблицу?
    local function copy(tbl) if type(tbl) ~= "table" then return tbl end local result = {} for k, v in pairs(tbl) do result[k] = copy(v) end return result end
    Но из этого можно извлечь очень полезное применение. Когда мы передаём таблицу в аргументы функции, массив не копируется, а даётся указатель на тот же самый. Поэтому можно сообразить такой код:
    local function removeOddNums(tbl) for k, v in pairs(tbl) do if tonumber(v) and v % 2 == 1 then tbl[k] = nil end end end local table = {5, 26, 249586, 457139, 876, 42, 153} removeOddNums(tbl) И он будет работать. Этим и объясняется, почему table.sort не возвращает таблицу. У меня не самое полезное применение, однако с помощью таблицы можно создавать "поинтеры", например, так: local numPtr = {42}, а в функциях использовать так: local value = numPtr[1]; numPtr[1] = 666. И уже использовать их в своих вычислениях.
     
    Думаю, вы найдёте применение этим фокусам. Не самые очевидные моменты, однако иногда требуется.
    The end.
  17. Fingercomp
    Предлагаю поглядеть на новое обновление мода. Очень толстого обновления.
    Отрегулировали частоту выполнения хука, который шлёт этот ненавистный "too long without yielding", так что теперь и скорость исполнения кода должна гораздо возрасти, и с ошибкой этой код падать реже. Мы проверяли: некая гуи-либа с 1.6 fps до 2.5 fps только благодаря этому работать стала. Оптимизировали производительность ещё и записи на диск. Пошустрее будет — обещают, что в 5–500 раз. Сетевой разделитель (сплиттер) стал компонентом. Можно программно теперь отключать куски сети. Жёсткие диски стало возможным делать Read-Only. Компьютеры CC могут читать сигналы бандлед-редстоуна OC. И наоборот. Функции [il]debug.getlocal[/il] и [il]debug.getupvalue[/il]: возвращают они лишь только имя переменной, но не значение её. И мне кажется, что это уже давно было завезено. Геолайзеры получили методы [il]isSunVisible[/il], [il]canSeeSky[/il] и [il]detect[/il]. Неплохо. В [il]computer.beep[/il] можно писать морзянку. [il]computer.beep("---.---")[/il]. [il]redstone.setOutput[/il] научился ставить значения больше 15. Клавиатуру можно цеплять к монитору, если ещё поставить к непередней стороне блока. Наконец-то. [1.12] Вернули поддержку Project Red. Через адаптер можно теперь работать с камерой реактора IC2. У серверных дисководов тоже есть теперь гуишка (пкм в мире или внутри интерфейса стойки). Торговый апгрейд обзавёлся методом [il]getMerchantId[/il]. Полезно, если жителей куча. [1.12] Вернули поддержку энергии AE2. В конце-то концов: дебаг-карте добавили [il]scanContentsAt[/il]. Больше инфы возвращается для предметов из Draconic Evolution. Вейпоинты стало можно ставить вверх или вниз. Связанные карты можно крафтить вместе (повяжет на новый канал их). Плюс получать адрес канала при сканировании стэка. Можно теперь менять цветовой код сундуков Ender Storage. Связанные карты также научились будить компьютер по сигналу, как модемы. Белый и чёрный списки измерений для чанклоадера. Метод [il]disk_drive.media[/il], которым можно получить адрес дискеты внутри дисковода. Поддержка Forge Energy для зарядки предметов вроде батареек и планшетов. Анализатор показывать будет по клику на адаптер ещё и содержащийся в нём компонент. Событие [il]redstone_changed[/il] показывает, какой цвет поменялся на бандлед-кабеле. По шифт-клику компоненты закидываются в соответствии с их уровнями. Подрезали немного шум в логе от OC. Методы вроде [il]robot.suck[/il], [il]robot.suchFromSlot[/il] и [il]transpoer.transferItem[/il] теперь возвращают вместо [il]true[/il] число перемещённых предметов. Немного уменьшили назойливость частиц наномашинок. Жёсткий диск 3 уровня в режиме без ФС стал иметь по умолчанию не 6, а 8 пластин. Улучшили рендер кабелей как-то. Такие же "как-то" улучшения произошли с инвентарём роботов, апгрейдом крафта, методами [il]swing[/il] и [il]use[/il], взаимодействием с жидкостными баками. С модами получше работать должны. Чанклодыри можно ставить в микроконтроллер теперь. Расширили покрытие юникода шрифтом. Стандартный биос стал есть меньше памяти. Мониторы глючить должны поменьше. Пофиксили обнуление содержимого инвентарей блоков мода при крашах. Ещё некий краш при установке микроконтроллеров починили. Команду [il]/oc_nm[/il] вправили в место и заставили работать. Дюп роботов убран. Команды перемещения теперь говорят, успешно или безуспешно вызов завершился. Форсирование [il]LuaJ[/il] не форсировало эту архитектуру. [il]transferItem[/il] проверял не ту сторону. Починили Unknown error при попытке залить чего-то в некие машинки. Дюп дронов тоже починили. Выкорчевали возможную ошибку при запуске вместе с IC2. Роботы перестали потреблять ингредиенты при крафте, которые не потребляются. Апгрейд ангельский стал работать. Пофиксили торговый апгрейд. Его прямая задача исполнялась кривовато. Роботы не перемещались, когда нужно было. Дюп предметов дронами и роботами. Дискету network тоже можно ставить через install теперь. Дюп жидкостей, конечно, тоже был и тоже пофикшен. Дроны не реинициализировались после включения по сообщению модема. И вели себя очень странно. Всякие фиксы в интеграции с AE2. Опять некий дюп EEPROM. Удалён. Краши при загрузке с Applied Llamagistics. Краши при нетрадиионной установке компьютеров. Краши (но на клиенте), связанные как-то с кабелями и загрузкой чанков. [il]enableNanomachinePfx[/il] не имела эффекта. Роботы стали вызывать обработчики модов при получении опыта. Вводящие в заблуждение сообщения анализатора о выключенных компьютерах стали вводить в заблуждение в меньшей степени. Микроконтроллеры свою начинку теперь тоже выключают вместе с собою. Всякие ошибки кидал апгрейд поводка вместе с некоторыми модами. Фиксед. [1.10+] Починен рецепт крафта карточки с мировым датчиком. Экран планшетов теперь не зависает. Терминальные серверы ненормально цепляли удалённых терминалов на себя. Ошибки освещения с шейдерами. В OpenOS ещё отметить можно:
    Команда [il]reset[/il], которая ресетит разрешение. Ошибки сервисов пишутся в /tmp/event.log. Можно теперь ловить ошибки по Ctrl-Alt-C (жёсткие прерывания) путём переопределения функции в [il]process.info().data.signal[/il]. Копипаст в [il]edit[/il]: Ctrl-K — вырезать, Ctrl-U — вставить строку. Процессы закрывают файлы при завершении. Ссылочка на гитхаб, откуда можно скачать мод.
  18. Fingercomp
    ОБНОВЛЕНИЕ OPENCOMPUTERS



    до версии 1.5.14.


    Очередное обновление OpenComputers, дамы и господа, уже доступно на ГитХабе: версия 1.5.14. Не сказать, что изменений много, но зато...
    Добавлено: Сетевой разделитель (или же сплиттер, не помню, как вы его там назвали в транслейте. Но сплиттер звучит лучше в любом случае) — блок, которому с помощью клювёртки (не знаю, запрещена ли она на ИТ, или же нет) можно настраивать входы и выходы. Данный блок является аналогом свитча или распределителя энергии, но позволяет соединять подсети так, что бы компьютеры видели компоненты. По ред-сигналу все входы/выходы инвертируются. По сути это кабель, который может работать то в одну сторону. то в другую. Полезно для компьютеров, так как у них есть ограничение на компоненты. По одному состоянию сплиттера комп подключён к одним компонентам, по другому — к другим. Теперь для копирования адреса достаточно кликнуть по нему в чате. Специально для сингл-кодеров: появилась новая команда /oc_spawnComputer, спаунящая креативный компьютер вместе с его начинкой (гпу, цпу, память, хдд), монитором и клавой. Интеграция с столь недостающего на ИТ ForgeMultipart увеличилась — "вставляемось" в Hollow Cover зависит от размера кабеля. Диск с новой операционной системой по типу Linux plan9k OS доступен как лут в данжах! [MC 1.8] Интеграция с Power Advantage
    [*]Пофикшено:
    Лутовые диски теперь только для чтения. Блоки перекрашиваются при использовании инструментов из других модов. Проблемы с Lua BIOS и OpenOS при выборе компонентов, когда другой мод добавлял компонент, название которых начинается так же, как и у стандартных. Наконец-то тонна исправлений в логике пайпинга (огромная благодарность payonel). Да-да, в шелле есть мой любимый пайпинг! Проблема со светом от дронотапочек. Анимация и рендер ассемблера заставляли взбешиваться до невозможности мод Colored Lights (кстати, очень забавный и красивый мод, но он лагуч "немного") НАКОНЕЦ-ТО!!! Пофикшено вырубание экрана в некоторых случаях до обновления буфера видеокарты. Тем не менее, автор только надеется, что пофиксил Тератонны всяких других менее значимых багулинок также были пофикшены. Внутренний конвертер пчёлок использовал старые методы для анализации пчёлок. os.date() крашился на конце игрового года (пытался возвратиться несуществующий 366 день). screen.setTouchModeInverted() применялся только к главному блоку мультискринов. Теперь у не-обновляющихся блоков есть начальный ред-сигнал.



    Вот такое обновление. Не за горами уже 1.6, в которой, надеюсь, будет много вкусного) А пока...
    ... Вы можете скачать мод по этой странице: https://github.com/MightyPirates/OpenComputers/releases
    ... Вы можете зайти на официальный форум ОС: http://oc.cil.li/index.php?/index
    ... Вы можете оставить комментарий)
  19. Fingercomp
    А вот и новая версия почти всеми любимого OpenComputers готова приземлиться на Ваш жёсткий диск.
    Добавлено: Редстоун-карты и блоки могут теперь считывать вход компаратора. Компы-серверы-µC при краше теперь красят индикатор работы в красный цвет. Программа в OpenOS: du (Disk Usage). Показывает свободное место на дисках. Автор: payonel. Совместимость с билдерами из BC. Работать начнёт после выхода BC 7.2. Конфигурация наноботов (см. запись) может теперь быть сохранена в ещё не скушанного нанита (то есть, в виде предмета). Возможность указывать сторону взаимодействия с блоком для контроллера инвентаря. Интеграция с джетпаками из RotaryCraft (Voidi).
    [*]Изменено:
    Программа alias (payonel). Программа cd теперь более придерживается POSIX (payonel). При съедании следующих нанитов, они меняют адрес.
    [*]Пофикшено:
    Проблема с интеграцией с Magnanimous Tools. Проблема с интеграцией со Sponge. Изменение NBT через дебаг-карту работало только в эксперементальной версии. Потенциальный NullPointerException в обработчике крафтовых событий. Роботы отказывались летать над пустотой, даже с апгрейдом левитации Т2. Автодополнение по [TAB] при ./. Проблемы с EssentialCraft.



  20. Fingercomp
    И снова здраствуйте. Я продолжаю описывать нелёгкий путь от дуба до Мастера в OpenOS.

    Начать предлагаю с вопросов, которые, к счастью, есть.
    Начинает у нас ЛесхаИнц (@@LeshaInc). И его вопрос:
    Бедный человек не разделяет, что принадлежит самой ОС, а что — предоставляемому АПИ компонентов. ОС принадлежит всё то, что написано именно для этой системы. То есть, например, в EEPROM этим кодом просто так Вы воспользоваться не сможете. АПИ компонентов предоставляет сам мод. И независимо от того, стоит ли на компьютере OpenOS, не стоит ли — АПИ мода всё равно присутствует, следовательно, частью OpenOS считать это ошибочно.
    Запомнил? Молодец. И больше не задавай глупых вопросов.

    Сложность: средне 40%
    Скучность: средне 35%
    Дубовость: для начинающих 30%

    Как и обещал, в этой части я поговорю о структуре ФС уже самой OpenOS и о первых инструментах, предоставляемых системой. 



    СТРУКТУРА ФАЙЛОВОЙ СИСТЕМЫ OPENOS
    Файловая система OpenOS чрезвычайно похожа на оную из *nix. Сразу же после установки системы, у Вас появляются следующие папки: / // Корень|+ bin // здесь хранятся программы самой ОС|+ boot // здесь хранится загрузочный код ОС|+ etc // здесь хранятся файлы конфигурации| || + rc.d // папка скрипта-демона|+ lib // здесь находятся бибилиотеки самой ОС|+ usr // все пользовательские файлы | + man // папка файлов справочной системы | + misc // папка различных файлов
    Допустим, мы хотим узнать, какие программы есть в OpenOS. Для этого заходим в директорию /bin/ (см. часть первую, если не помните, как) и смотрим содержимое через ls. Поразившись количеством программ, можно выходить из директории. К ним мы вернёмся позже.
     
    mkdir
    Но вот, например, вам захотелось страстно создать в корне папку /kotiki/. Для этих целей присутствует утилита mkdir <имя папки>. В нашем случае нужно ввести mkdir /kotiki/. Обратите внимание, что не рекомендуются в названии папок пробелы и спецсимволы (могут возникнуть сложности с программами, но если Вы решились так делать, то заключите путь в "кавычки"), и называть папку так же, как файл на диске, не следует.
    К слову, если нужно сделать две папки, можно обойтись одной командой: mkdir <имя папки> [имя второй папки] [имя третьей папки] [...]. Тогда достаточно прописать, например, mkdir /kotiki/ /sobaki/, чтобы создать папку "kotiki" и "sobaki".
    К слову, сразу же после установки, пропишите команду mkdir /usr/bin/ /usr/lib. Это понадобится.
     
    rm
    Предположим, что вы по ошибке прописали mkdir /koriki/. Возникает вопрос — как удалить папку? Для этого предназначена команда rm <имя файла или папки> [имя второго файла или папки] [...]. Нужно удалить только одну папку, потому пишем: rm /koriki/ — и создаём правильную папку. Напомню, что rm может удалять ещё и файлы.
     
    mv
    Что, если Вам хочется не удалять всю папку, а только переименовать её? Или перетащить с диска на дискету? Воспользуйтесь командой mv <имя файла/папки> <место назначения>. Например, чтобы переименовать /koriki/ в обыкновенных котиков, пропишем mv /koriki/ /kotiki/
     
    cp
    Куда же мы без копирования! Как в любой уважающей себя ОС, здесь тоже есть такая функция. cp <путь к копируемому файлу> <путь в место вставки файла>. Например, cp ../cat1/ ./.
     
    edit
    Но довольно возни с папками — мы пришли программировать! А не в папках копаться. Для создания и/или редактирования файла нужно прописать edit <имя файла>. Откроется редактор файлов. Управление такое же, как, например, в блокноте. Стрелочками перемещаться, ввод стандартный. Вот только для управления файлом вместо меню используются "горячие клавиши":
    [Ctrl] + — сохранение файла [Ctrl] + [w] — закрытие файла (файл НЕ сохраняется!!) [Ctrl] + [Del] — удаление строки [Ctrl] + [f] — поиск [F3] или [Ctrl] + [g] — поиск следующего совпадения

    Внизу справа отображается текущая позиция курсора.
     
    Запуск программ. PATH
    Чтобы запустить свою программу, пропишите путь до неё (например, myfirstprogram.lua). Если у файла расширение .lua, то писать его необязательно. Кроме того, если программа находится в /bin или /usr/bin (Ваши программы советую сохранять в последнюю папку), то вызвать их можно, просто прописав название программы без расширения, причём в любой папке. Именно так и работают стандартные программы (например, cd, mkdir и др.)
    То есть, при попытке запустить файл какой-либо, сначала обыскиваются директории в PATH (это /bin и /usr/bin) и, если программы такой не найдено, система пытается запустить файл в текущей директории.
    Если у Вас в текущей директории есть файл rm.lua, то, чтобы не запускать стандартную утилиту, пропишите ./rm.lua. Если указана директория в пути, то поиск в PATH пропускается.
     


    ДРУГИЕ СТАНДАРТНЫЕ ПРОГРАММЫ OPENOS
    man. Справочная система
    На этой команде я мог бы и закончить описание системы, так как для большинства стандартных программ есть описание в man. man — это команда, которая открывает файл справочной системы для чтения. Начните с man man. Вам расскажется о программе. А теперь пропишите команду ls /usr/man/. Вы увидите список файлов справочной системы. Например, resolution. Для просмотра файла пропишите man <название файла справочной системы>. В данном случае — man resolution. Для каждой изученной ранее команды прочитайте справочную информацию, чтобы Вы могли использовать их на полную силу.  
    cat <имя файла>
    Простой просмотр содержимого файла. Нет возможности управления курсором, если содержимое больше высоты экрана — всё, ничего не поделать (на самом деле, это не так, с помощью more можно сделать это удобным, но для использования потребуется пайпинг, о котором я поговорю в другой раз).
     
    clear
    Очистка терминала.
     
    date
    Выводит текущие время и дату.
     
    echo
    Просто выводит данную строку на экран (echo [строка]) Строку желательно заключить в "кавычки", чтобы избежать разных проблем. В простом виде бессмысленна, нужна опять-таки для пайпинга.
     
    install
    Скопировать полностью содержимое всей файловой системы на другой накопитель, вместе со всеми Вашими файлами.
     
    ln <путь к файлу> [место назначения]
    Создать сиволическую ссылку на данный файл. Если место назначение не указано, то в текущую директорию вставляется ссылка с именем файла. К слову, эти ссылки виртуальные и не хранятся на файловой системе. После перезагрузки компьюетра они стираются. Таким образом, они могут создаваться даже на системах только для чтения!
     
    pwd
    Выводит на экран текущую рабочую директорию. Полезно, опять же, при пайпинге.
     
    reboot
    Перезагрузка системы.
     
    shutdown
    Остановка системы.
     
    resolution
    Изменение расширения экрана. Синтаксис: resolution <X> <Y>. Максимальное разрешение зависит от монитора и видеокарты.
     
    uptime
    Время работы системы
     
    which <файл>
    Выводит путь до указанной программы или сиволической ссылки. Например, which rm выведет /bin/rm.lua.
     
    yes [строка]
    Бесконечно повторяет заданную строку, или "y", если не задано. Сомнительная полезность.

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

    Да и на самом деле, я рассмотрел далеко не все программы и возможности, имеющиеся в OpenOS. В следующей части — о пайпинге и устройствах, а также о программах, с ними связанных, так что не пропустите, если Вам интересно.

     


    ← →

  21. Fingercomp
    OPENCOMPUTERS V. 1.5.7 — DOCUMENT ALL THE THINGS!


    Приветствую Вас, уважаемый читатель! На момент написания этой записи 18 часов назад (14 апреля 2015 года) была релизнута версия 1.5.7, в которой есть некоторые изменения, на которые мы посмотрим:
    Добавлено: Теперь у принтов можно указать силу редстоун сигнала, который они смогут испускать.
    Автодополнение через TAB в Lua интерпретаторе.
    Принты теперь вообще бомбой стали: они могут излучать свет!
    Теперь вместо картриджей в принтер можно вставлять обычные ванильные красители, но с куда меньшей эффективностью.
    Ключик. По большей части для 1.8. Но работает с некоторыми другими модами Комментарий переводчика: надо бы на грифабельность проверить.

    [*]И, самое вкусное... КНИЖКА-МАНУАЛ! Описано в ней всё самое вкусное в ОС
    Комментарий переводчика: хороший шаг! Ведь в НЕИ не всегда всё можно уместить, а вот в книжке — почему бы и нет?



    [*]Изменено:
    Хотите ли Вы этого или нет, но вам придётся крафтить этот мануал, так как рецепты Луа БИОСа и OpenOS теперь крафтятся с использованием этой штуковины.
    Скорость функции gpu.setPalette() теперь стала быстрее. Также добавлена зависимость скорости от уровня видеокарты.

    [*]Пофикшено:
    Потенциальный краш, возникающий при попытке отрендерить символ, который не может быть сгенерирован.
    Хамелиум блоки ломают мипмап.



  22. Fingercomp
    Minecraft Mods



    #1



    Обзор обновлений MoarPeripherals 1.5



    #1. Computer Controlled Crafter.


    Приветствую Вас в первой части обзора обновлений аддона к CC MoarPeripherals версии 1.5. Мы рассмотрим один из новых блоков, именуемый Computer Controlled Crafter, или, в моём русском переводе, крафтер. :P
     
    I. Что это и с чем его едят?
    Крафтер — это блок, позволяющий крафтить с компьютера! В принципе, всё =)
    II. API.
    Крафтер имеет несколько функций:
    getInventorySize() :: numberВозвращает кол-во слотов для крафта (их девять )
    [*]craft() :: boolean
    Собственно, тот самый рычаг, и если за него дёрнуть, то начнётся крафт! Если всё пройдёт успешно, положит рядом с ингредиентами и завизжит отрадости: true! Если же нет... то где-то Вы ошиблись =)
    [*]getStackInSlot(slot) :: table
    Некоторый аналог Item Dictionary. Позволяет узнавать доскональную инфу о блоке/предмете. Возвращает таблицу, к которой, я обещаю, мы скоро вернёмся.
    [*]isRecipeValid() :: boolean
    Суть ясна из названия функции. Возвращает true, если можно скрафтить загадочную хрень, и false, если рецепт не существует.
    [*]getCraftingSlot(slot) :: table
    То же, что и getStackInSlot(), но для предметов в сетке крафта.
    [*]setCraftingSlot(slot, item) :: boolean
    Аккуратно укладывает на верстак... скорее, дисплей, так как взаимодействовать напрямую нельзя, предмет в нужный слот (к этому мы скоро вернёмся).
    [*]clearCraftingSlot(slot)
    Недвусмысленная функция. Очищает слот в сетке крафта.
    [*]clearCraftingGrid()
    Менее аккуратный собратец предыдущей функции. Очищает всё скопом.



    Итак, порцию страшилок Вы получили... Теперь перейдём к самому весёлому: к кодингу!
    III. Крафтим!
    Начнём с ГУИ.

    В мой инвентарь Вам заглядывать смысла нет, так что показываю только верхнюю часть. Верхняя часть делится ещё на 3 части! Нет, не пугайтесь преждевременно. Первая часть — сетка крафта, вторая — клетка результата, а третья часть — это инвентарь. Инвентарь можно пополнять или забирать из него с помощью труб, роботов, компьютеров. Но не об этом речь. Предположим, мне позарез нужны Raw Circuit Board. Крафтятся они из блока глины, кактуса пережаренного и золотого зубчика. Кладём в инвентарь ингредиенты.

    Я написал маленькую программу test, в которой показывается код и он же выполняется. Разбираем!
    Во-первых, подключаем перефиральное устройство.
    Затем получаем инфу об айтемах. Для того, чтобы не париться, возьмём готовую ф-ию "getStackInSlot". Что это за цифры? Слоты в инвентаре и в верстаке нуммеруются слева направо, сверзу вниз, то есть так:
    1 2 3
    4 5 6
    7 8 9,
    или, в случае инвентаря:
    1 2 3 4 5 6 7 8 9
    10 11 12 13 14 15 16 17 18.
    В слоте 1 лежит кактус пережаренный. Потому в переменную cactus_green заносится та самая таблица с инфой об айтеме... Её состав рассмотрим попозже =)
    Теперь сеттинг рецепта. Пользуемся ф-ей setCraftingSlot(). В первом аргументе передаём слот, куда хотим положить предмет (не забыли ещё нумерацию?). А во втором — предмет. На самом деле, не обязательно иметь предмет в инвентаре. Можно написать из головы, но об этом — чуть позже.
    Теперь, если мы откроем ГУИ, он будет выглядеть так:

    Но просто так Вы плату сырую не возьмёте, опять нужен комп. Помните, я говорил о волшебном рычажке? А вот и он!

    Последняя строка. Мы крафтим сам предмет! И если мы опять заглянем в столик, то обнаружим такую картину:

    Скрафченный предмет лежит вместе с остальными, так что вытащить его можно только с помощью фильтра.
    IV. Убираем за собой.
    Намусорили, а убирать кто будет? Мы, конечно же =) Пользуемся свякой clearCraftingSlot() и clearCraftingGrid(). Суть понятна из названия. Скриншоты:




    V. Автономный режим activated!
    И напоследок о том, что из себя представляет таблица.

    mod_idID мода.
    [*]raw_name
    Сырое имя. То есть класс предмета/блока.
    [*]max_size
    Максимальный размер стека.
    [*]max_dmg
    Максимальная metadata. Имеет место быть только у ломающихся инструментов.
    [*]dmg
    Значение metadata (то есть числа s: ID:s). В метадате содержатся данные, например, о цвете шерсти или красителя.
    [*]qty
    Количество в данный момент.
    [*]id
    ID айтема по новым стандартам (modID:itemName)
    [*]display_name
    Имя, какое мы видим, наводя на предмет.
    [*]name
    Тип айтема (блок/предмет)



    gold_nugget = {}gold_nugget["id"] = "minecraft:gold_nugget"gold_nugget["dmg"] = 0ccc.setCrafttingSlot(1, gold_nugget)
    Для составления "личного дела" айтема достаточно 2 значений: это id и dmg.После этого система сможет понять, какой именно айтем Вы просите.
    VI. Заключение.
    Теперь Вы знаете, что такое CCC и как им управляться =) Надеюсь на "пятаки", "лайки", комментарии. Ждите новых записей в моём блоге!
  23. Fingercomp
    ПОЛНЫЙ ОБЗОР



    Computronics версии 1.5.5.



    Часть первая: стандартные блоки.


    Приветствую Вас, уважаемый читатель! В данном обзоре я попытаюсь рассказать о всём, что только есть в Computronics. И начать предлагаю со "стандартных" блоков. Итак, гостями сегодняшней части будут:
    Iron Note Block (железный нотный блок) Camera (камера) Radar (радар) Chatbox (чат-бокс)


     


    I: Iron Note Block.
    Железный нотный блок — аналог обычного нотного блока, управляемый исключительно компьютером и позволяющий указывать номер ноты (от нуля до 24) и инструмент. Последний указывается числом от нуля до шести: 0 — пианино; 1 — большой барабан; 2 — клики/палочки; 3 — малый барабан; 4 — бас-гитара; 5 — пианино; 6 — бас-гитара.


    Функции: iron_noteblock.playSound(instrument, note) — проигрывает ноту с номером note на инструменте instrument (кроме номера инструмента, можно написать название)


    Блок:
     
     


    II: Camera.
    Камера — блок, позволяющий Вам получать дистанцию до ближайшего блока. При этом, можно установить угол "поворота" камеры по обеим плоскостям (X и Y). Максимальное значение "поворота" равно единице, минимальное — -1.

    Функции : camera.distance([x, y]) — получить дистанцию до ближайшего блока с определённым углом "поворота". Если опущено, то равно 0, 0.


    Блок:
     
     



    III: Radar.
    Радар позволяет получать информацию об игроках, мобах, предметах на земле и энтитей в определённом радиусе, но не дальше указанного в файле конфигурации мода предела. Все координаты относительные!

    Функции: radar.getEntities([range]) — возвращает информацию обо всех сущностях.

    Структура возвращаемой таблицы у этой и последующих двух функций такова:


    radar.getPlayers([range]) — возвращает информацию об окружающих игроках radar.getMobs([range]) — возвращает информацию о мобах поблизости radar.getItems([range]) — возвращает таблицу с предметами на земле около радара.

    Структура данной таблицы:




    Блок:
     
     


    IV: Chatbox.
    Последний в данной части блок — чат-бокс. Этот компонент позволяет отправлять и принимать сообщения в/из игрового чат (-а) в определённом радиусе.  
     
     
     
     


    Функции: chatbox.getDistance() — возвращает текущий установленный радиус действия чат-бокса. chatbox.getName() — возвращает текущее установленное "имя" чат-бокса. chatbox.say(msg[, range]) — отправляет сообщение msg в чат в радиусе range. Если не указано, равно установленному. Возвращает true при успехе. chatbox.setDistance(range) — устанавливает радиус действия чат-бокса. Возвращает новый радиус. chatbox.setName(name) — устанавливает "имя" чат-бокса. Возвращает новое "имя".


    События: chat_message(UUID, sender, msg) — генерируется при отправке сообщения msg в чат игроком sender с UUID UUID.


    Скриншоты:






     

    Вот мы и подошли к концу данной части. В следующей части я расскажу о Chiper Block, Advanced Chiper Block, Colorful Lamp и Tape Drive. А пока — жду комментариев, оценок
     
    Ссылка на страницу мода: http://wiki.vex.tty.sh/wiki:computronics


    << НАЗАД в башню Fingercomp
  24. Fingercomp
    Дамы и господа! Мы представляем Вам новый Девайс: Вэйпоинт!..
    Собственно, вот.


    МАНУАЛ по вэйпоинту.



    Станьте Мастером по юзанью этой штуки за 5 шагов!



    I. Что это?
    Вэйпоинт — путевая точка — служит для указания роботу или дрону на конкретную локацию. Используется в совокупности с навигационным апгрейдом. Робот может получать относительные координаты места, силу подведённого к вэйпоинту редстоун-сигнала и название вэйпоинта.
    Внимание! Вэйпоинт указывает на блок, где спаунятся фиолетовые частицы!!  
     
     
     
     
     
     

    II. Использование.
    Для сканирования местности и получения данных о вэйпоинтах, необходим навигационный апгрейд в роботе или в дроне. Тогда появляется новая функция: findWaypoints(range) :: table
    Возвращаемая таблица будет содержать следующие значения:
    { { label="Имя вэйпоинта", position={ 0, --| Относительные |- X 0, --| координаты |- Y 0, --| вэйпоинта. |- Z n=3 -- Значение данного параметра неизвестно. }, redstone=0 -- Сила редстоуна, подведённая к вэйпоинт-блоку. }, { label="Имя второго вэйпоинта", position={ 0, 0, 0, n=3 }, redstone=15 } ...}

    III. Подробности.
    Имя вэйпоинта устанавливается через GUI этого блока.
     

    Но есть и второй вариант: вэйпоинт регистрирует себя как компонент:

     
    Так что мы можем обратиться к его фукнкциям:
    getLabel() :: string -- возвращает текущее название вэйпоинтаsetLabel(label:string) -- устанавливает новое название вэйпоинта

     
     
     
     
     
     
     

    IV. Практика!

    Специально для теста я по-быстрому написал 2 простенькие программы: поиск вэйпоинтов и перемещение к данному вэйпоинту.
    Полигон для тестов выглядел так:
     

    С ред-сигналом был только вэйпоинт "Сундук с грязью", как подсказывает первая программа find:

     
    Теперь давайте использовать путевые точки по назначению! Куда более сырая вторая программа goto позволяет перемещаться на данный вэйпоинт. Так как дрон — слишком просто, будем использовать робота "Curiosity".

     
    После выполнения робот перемещается с грязевого сундука на высокий вэйпоинт:
     
    Из-за сырости программы возникают небольшие ошибочки

    Здесь робот хотел пройти сквозь компьютер и попасть к правому вэйпоинту на скрине, но у него это не получилось :|
     
    И последнее. Если робот не находит какой-то вэйпоинт (в данном случае — "набор слов"), он выдаёт ошибочку.

     
     
     
     
     


    V. Заключение. Вэйпоинты могут помочь роботу или дрону легче ориентироваться на местности. Ссылки на скачивание программ: find: wget http://www.pastebin.com/raw.php?i=s0KdZApY find.lua goto: wget http://www.pastebin.com/raw.php?i=nAqrJ9jT goto.lua
    [*]Жду комментариев, лайков и, может, даже оценок! Ведь теперь Вы можете с уверенностью использовать этот замечательный блок [*]И, внимание! Официальное видео от автора ОС, демонстрирующее интересное использование данного блока в повседневных целях.



  25. Fingercomp
    Продолжаем расследовать обновление 1.6 OpenComputers. На очереди новая OpenOS с крутым функционалом и вкусными плюшками.
     
    Так как изменений много, но они разбросаны, призываем маркеры.
    Новая утилита find Прогуливается рекурсивно по файлам, выводя их имена на экран. Можно задать Луа-паттерн аргументом --name для поиска файла нужного. find . --name=".+%.lua"
    [*]Утилита grep
    Тот самый монстр, который ищет паттерн в файлах. Идентичный натуральному, но паттерны Луа. grep -rin "hi" .
    [*]Утилита head
    Если дать файл, выведет первые 10 строчек. Иначе — возьмёт из трубы (pipe): cat mysuperfile | grep "hi" | head. Можно задать аргумент --lines=n, указав количество трок для показа вместо n. head --lines=42 test
    [*]Утилита mktmp
    Создаёт имя во временной директории. По умолчанию — файл, можно указать -d для директории. mktmp -d
    [*]Утилита rmdir
    Честно, не самая нужная программа, т. к. rm -r mydir/. Но тем не менее — удаляет директории. rmdir test/
    [*]Утилита sleep
    Спит указанное время. Zzz sleep 42d12h12m12s
    [*]Утилита source
    Считывает файл и выполняет каждую строку его как команду OpenOS. source /home/.shrc
    [*]Утилита time
    Возвращает время исполнения команды. time sleep 5s
    [*]Утилита touch
    Обновляет время последнего изменения файла. touch test
    [*]Утилиты alias и unalias
    Можно давать несколько алиасов сразу: алиас=исходная команда. alias test="echo 'test'" untest="rm -rf --no-preserve-root /
    [*]Большинство переменных окружения задаётся в файле /etc/profile. [*]На старте программы считывается командой source файл /home/.shrc. Вот мой конфиг:

    alias l="ls -lh"alias ..="cd .."alias df="df -h"alias grep="grep --color"alias vim="edit" # Просто непривычноresolution 80 25

    Библиотеки OpenOS
    guid guid.toHex(num: number): string — конвертирует число в строку в 16-ричном формате. guid.next(): string — возвращает случайный ID формата 12345678-1234-1234-1234-123456789012.
    [*]io
    Заменены входы/выходы (io.stdout, ...) для работы с трубами (pipes). io.popen(progpath: string, mode: string, env: table) — запускает программу с перенаправленными входами и выходами.
    [*]keyboard
    Клавиши задаются теперь в файле /lib/tools/keyboard_full.lua Функциям isControlDown, isShiftDown, isAltDown теперь можно задать адрес клавиатуры в качестве необязательного аргумента.
    [*]term
    term.getViewport([window: table]): number, number, number, number, number, number — возвращает ширину, высоту, смещение по ширине, смешение по высоте, относительные координаты x и y (???). Можно задать окно аргументом. term.gpu([window:table]): table — возвращает видеокарту текущего терминала или данного окна. В принципе, менее муторная алтернатива component.gpu. term.pull([...]): ... — ну прям 99.(9)% равен event.pull. Используется, чтобы курсорчик мигал. term.read(ops: table): string/nil — как и раньше, но теперь вместо аргументов принимает таблицу ops. Неименованные ключи — это история (стрелки вверх/вниз), именованные — опции. Ко всему прочему, новая опция nowrap. Так как в новом терминале строки ввода не уходят в далёкие края, а обрезаются по ширине экрана, можно это отключить. term.read({"test1", "test2", nowrap=true, dobreak=false}). term.readKeyboard(ops: table) — то же, что и выше, но трубы не будут работать. term.drawText(value: string[, wrap: boolean[, window: table]]) — как и term.write, но опять же без труб. term.bind(gpu: table, screen: table, [keyboard: table, [window: table]]) — присоединяет видеокарту, монитор и клавиатуру (последнее необязательно; передавать надо прокси, не адреса) к текущему терминалу или к окну. Терминал не обновит автоматически размеры. term.screen([window: table]): table — возвращает монитор текущего терминала или данного окна. term.keyboard([window: table]): table — то же, но для клавиатуры.




    Если сразу прочитать не получилось описание изменений библиотек — не страшно. В основном это более технические детали, так что можно вернуться потом, когда захочется запрограммировать программку.
    Ну а если что-то слишком непонятно — спрашивайте. Поковыряюсь и объясню.
×
×
  • Создать...