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

Fingercomp

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

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

  • Посещение

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

    283

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

  1. 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.
     
    Качать можно отсюда.
  2. Fingercomp
    Продолжаем беседу об операционной системе Bolge OpenOS. В этой записи речь пойдёт про те самые оставшиеся утилиты, которые облегчат жизнь программисту.


    Сложность: средне 60%
    Скучность: высокая 80%
    Дубовость: для продвинутых 65%

    Операционная система OpenOS в первую очередь покрывает вопросы (относительно) удобного программирования для OpenComputers. Конечно, это не исключает сторонние редакторы типа Sublime Text или Notepad++, но иметь такие средства нужно и важно. Давайте же я расскажу о них. 


    УТИЛИТЫ ДЛЯ ПРОГРАММИРОВАНИЯ.
    Простой перечень утилит с пояснениями.  
    address
    Наипростейшая из наипростейших утилит, может соперничать даже с print("Hello, world!"). Просто выводит адрес компьютера. Интереса ради, пропишите view /bin/address.lua.
     
    components
    Ещё раз я расскажу об этой программке. Она выводит список компонентов, подключённых к системе. Можно задать фильтр, тогда выведутся только указанные компоненты. Если же указать ключ -l (о них в следующей части), то для каждого компонента будут указываться методы.
     
    dmesg
    Данная программа случает все события и выводит информацию о них на экран. Можно указать фильтры событий для просулшивания через пробел.
     
    flash
    Та самая программа, которая записывает код на EEPROM. Ключ -l выводит код о текущего EEPROM, -r записывает код в файл, а -q заставляет не задавать вопросов.
     
    hostname
    Данная программа бессмысленна без пакета network. Но, тем не менее, она может устанавливать и выводить текущее имя компьютера.
     
    lua
    Если запустить без аргумента или с ключом -i, то запустится интерактивный сеанс Lua-интерпретатора, где можно запускать программы. При этом все библиотеки автоматически переносятся в _G, так что дополнительно подключать их не требуется. Если же указать файл, то скрипт предпримет попытку запуститься и выдаст сообщение при обнаружении ошибки.
     
    primary
    Синтаксис: primary <компонент> [адрес]. Если аргумент [адрес] опущен, то выводится информация о первичном компоненте, иначе — предпринимается попытка сделать данное устройство первичным.
     
    rc
    Программа управления сервисами (о них — в следующих частях). Синтаксис: rc <сервис> [команда] [аргументы].
     
    redstone
    Предоставляет простой интерфейс управления редстоун-сигналами на первичной ред-карте/ред-блоке. По умолчанию синтаксис следующий: redstone <сторона> [значение]. Если [значение] не задано, выводится информация о текущем. Иначе — устанавливается.
    Для многожильных кабелей синтаксис следующий: redstone -b <сторона> <цвет> [значение]. Работа необязательного аргумента такая же.
    Для блоков, предоставляющих интерфейс беспроводной передачи ред-сигнала, команды следующие:
    redstone -w [сила]. Если [сила] задана, устанавливается значение на беспроводном передатчике. Иначе — выводится текущее значение силы. redstone -f [частота]. Устанавливает частоту, если задан аргумент, иначе — выводит текущую.

    sh
    Запускает сеанс командной строки, если не передано аргументов и io.stdin не перенаправлен. Иначе — читает команды из этого потока и выполняет их.
     
    umount
    Синтаксис: umount <точка монтирования>. Отмонтирует устройство по данной точке монтирования.

    Вот так. Следующая часть целиком и полностью посвящена шеллу, ему родимому. Это: переменные окружения, алиасы, ключи, аргументы, особенности работы шелла и прочее. Пока жду комментариев по этой части, пишите, если что-то непонятно.  



    ← →

  3. 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 блоков от серверной стойки, и выход окажется в эндерсумке
  4. 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)!
  5. 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 — то же, но для клавиатуры.




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


    Перед запуском пропишите mkdir /var и mkdir /var/log. Это нужно для хранения лога лончера.
     
     
    Собственно, запишите сам лончер по ссылке ниже, поменяйте там настроечки по своему желанию (OWNER там, например, чтобы по вашему клику на "гамбургер" в главном меню программа завершалась). И всё, можно запускать!
     
    Gist ID: 684d2c72faaa941df857
     
    Скриншоты
     
     
  8. Fingercomp
    Началось всё с пустого файла.
    Набросав костылей, ловисипедов и более-менее нормального кода... Рад представить вам свой ламповый репозиторий на ГитХабике с набором программок, написанных на Python. На текущий момент все они в какой-то мере взаимодействуют с Minecraft.
    По-порядку.
     

    Чат-клиент
    Так как блог так или иначе обязан подчиняться правилам, я упоминаю только один чат-клиент под именем "cc-chat". Кое-как подёргав API форума, смог сделать небольшую программу, которая сейчас умеет работать с чатом (отправлять и получать сообщения из/в чат (-а) нашего форума).

    Интерфейс простой. Внизу строка ввода, справа немного кнопок, лист онлайна и сам чат. К слову, клик по нику с контролом — вставить в строку его, а клик с альтом — открыть профиль в браузере.
    Но это так, плюшки.
    Если тыкнуть по большой кнопке [ⓘ], окно с тонной всякой информации.


    Здесь и ТОПы, и места в них, и баланс, и голоса. Особая благодарность @cyber01 за API.
     
    Установка
    Для начала установите Python 3, BeautifulSoup, PyGObject. Скачайте программу с Гитхаба и запустите её. Создастся файл конфигурации, путь к нему будет указан в диалоге.
    Теперь перейдите на страницу чата, откройте DevTools.
    Открыв вкладку "Network", нажмите на кнопку [Обновить] в чате. Появится запрос в списке. Откройте URL запроса в отдельной вкладке и скопируйте всё, что находится между secure_key= и &type. Это есть секретный ключ, который, естественно, никому не нужно давать.
    Откройте файл конфигурации в редакторе и в первую строку вставьте этот ключ.
    Затем в браузере найдите Cookies для сайта, скопируйте их и вставьте во вторую строку браузера.
    Всё это требуется из-за костыльного АПИ форума =\
    Если всё хорошо, при попытке запустить программу, она не завершится с ошибкой.
    К слову, в трее появится значок программы, левый клик по которой скрывает или показывает окно.
     
     
     

    Мониторилка
    Пропустив ту самую программу, идём к mc-monitor. Это небольшое приложение, которое полностью умещается в трее. Позволяет мониторить сервера Minecraft без захода на всякие сайты. Кроме того, имеется таймер, который "звенит" каждые 24 часа. Полезно, чтобы не забыть про голосование.
     
     
    Установка
    Установите, если ещё не сделали, Python 3, PyGObject.
    Просто запустите программу. Скопируйте путь к файлу кнфигурации и откройте его в редакторе.
    Там можно указать список серверов в следующем формате:
    адрес.сервера:порт=Имя сервера
    По строке на каждый сервер.
     
     
     

    Баги, глюки, пуллы, вопросы и сырцы
    Ищите на GitHub: https://github.com/Fingercomp/python-utils/
  9. 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
    [*]Жду комментариев, лайков и, может, даже оценок! Ведь теперь Вы можете с уверенностью использовать этот замечательный блок [*]И, внимание! Официальное видео от автора ОС, демонстрирующее интересное использование данного блока в повседневных целях.



  10. Fingercomp
    Lamp-o-mat! Это небольшая программулька, которая снова из категории "украшения" (предыдщую программу, часики, смотреть тут). В общем, это гирлянда такая. Для лампочек цветных из Computronics.
    Проста прога как дуб, но выглядит прикольно. В частности, это первая прога с принципом ООП.
    Писалась ночями двумя по заказу @Alex, итог меня радует.
     
    Скачивание в репозитории.
    Паста.
    Гист.
     
    Скриншот:

     
     
    Конфигурация:

     
  11. 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 Будем, как обычно, рады ответить и помочь.
     
    Удачи!
  12. Fingercomp
    Вкратце проедемся про изменениям с невероятно старой версии 1.5.5 до самой новой, 1.6.3.
     
    1.5.6 / 2015-07-24
    Большинство блоков из мода можно покрасить, тыкнув по ним красителем. Интеграция с модом Flamingo (самая нужная фича, конечно): через компы можно заставлять фламинго качаться. Интеграция с Armourer's Workshop. Улучшен улучшенный шифратор. Ключи быстрее генерируются.

    1.5.7 / 2015-09-12
    Разноцветный апгрейд добавлен, который делает роботов разноцветными. Можно вызвать component.colors.setColor(color: number) и покрасить корпус робота в желаемый цвет. Интересная штука.

    1.5.8 / 2015-10-11
    Эффект hive_mind для наномашинок. Часть интеграции с Forestry.
    Включив, можно тыкнуть пропитанной палкой из Forestry по желаемому улью с пчёлами - пчёлы станут летать над головой.
    Палкой же направлять можно пчёл на желаемую цель: пчёлы полетят к ней и станут жалить.
    Если отключить эффект, то пчёлы будут жалить игрока.

    1.5.9 / 2015-10-22
    Чатбоксики креативные стали писать во все измерения.

    1.6.0 / 2015-11-28
    Аудиокабели добавились. Их можно подключать к кассетным проигрывателям и к динамикам. Динамики будут воспроизводить звук вместо проигрывателя. Убрана поддержка NedoComputers, потому что этот мод сдох.



    Спустя год выпускается огромнейший апдейт мода, поэтому дальше расписываю фичи детальнее.  
    1.6.1 / 2016-11-12
    Мод портирован на 1.8.9, 1.9.4 и 1.10. На новых версиях майна используется улучшенный кодек DFPWM1a. Старые записи будут очень тихими, поэтому их нужно переконвертировать с помощью LionRay. С новым кодеком гораздо меньше шума будет. Добавлена шумовая карта. Отключается от пищащей карты тем, что можно для каждого из 8 каналов задать тип волны: синусоида, меандр, треугольная и пилообразная. Допольнительно появляется буфер операций: можно добавить ноты и затем вызвать component.noise.process(), чтобы проиграть. Добавлена звуковая карта. Как работает этот монстр, я описывал в предыдущей записи. Потом опишу функции покруче, а пока можно побикать. Интеграция с TIS-3D, модом от Sangar. Это и новые модули: разноцветный, считыватель дискет, самовыпиливатель - и документация в мануале TIS-3D. Интеграция с OC 1.6.
    Добавлены дополнительные штуки для серверов. Плата с лампочками. Ключом можно менять конфигурацию, изменяя положение и количество лампочек. Как компонент предоставляет функции для включения/отключения лампочек и изменения цвета (True color). SSD. Расшифровывается как Server Self-Destructor. Очередная версия самой нужной вещи - самовыпиливателя, но для серверов. Если взорвать через функцию специальную, в серверной стойке пропадут все вещи. Серверный вариант батарейки, которую можно пихнуть в стойку. Можно ещё считывать количество энергии. Плата с переключателями. Можно тыкать по кнопочкам, включая или выключая их. А сервер потом может считывать положение переключателей. По просьбам трудящихся.
    [*]Добавлена функция getPosition() в проигрыватель, чтобы узнать текущую позицию на кассете. Именно: её несколько лет не было. Ну что поделать, бывает. [*]Стандартная программка для работы с проигрывателем tape починена для работы с HTTPS. Можно менять размер чанка и таймаут ожидания. И tape wipe добавилась для стирания всей инфы на кассете. [*]1.7.10 Цифровый сигнальный контроллер (интеграция с RailCraft). Надеюсь, когда-нибудь дойдут руки рассказать, как она работает. Но не тут. [*]1.7.10 Цифровый сигнальный приёмник (интеграция с RailCraft). [*]1.7.10 Рецепты для Gregtech 6. [*]1.8+ Больше не требуется устаналивать Asielib: либа встроена в мод.


    1.6.2 / 2017-02-25
    Фиксы, фиксы и ещё фиксы. Фиксы крашей, фикс тихих звуков в звуковых карточках, дискеты можно теперь получить, наконец, с помощью ключа.

    1.6.3 / 2017-04-21
    Добавлен синтезатор речи. Так как это охрененная штука, без пинка её так просто не завести. Чтобы она работала, нужно поставить сам синтезатор речи (MaryTTS с поддержкой Forge), файл голоса и файл языка. Женский голос и английский язык, например. К счастью, эти файлы нужны только на сервере. На клиент тянуть их не нужно.
     
    Работать так:
    speech_box.say(text: string) -- что-то сказать.
    speech_box.stop() -- остановить воспроизведение речи.
    speech_box.isProcessing():boolean -- проверить, воспроизводится ли сейчас речь.
    speech_box.setVolume(volume: number) -- установить громкость (от 0 до 1). Блоки теперь крафтятся не из земли и палок, наконец-то, а из чего-то повеселее. Используются предметы из OpenComputers. Динамики могут смотреть в любую из шести сторон. Директория для кассет перемещена в папку мира. При обновлении нужно перетащить её из папки кубача в папку мира, соответственно. Модуляция из-за глупой ошибки не работала вообще. Ну и ADSR не работал, если время Attack (нарастания) равно было нулю.

    Как-то так. За мною остаётся ещё долг в виде продолжения обзора этого интересного модика, но всё как-то лень. Пока можно спрашивать меня, как работают вещи из CX, читать страницы в мануале и документацию к методам. Для большинства вещей этого более чем достаточно.
     
    Have fun :P
  13. Fingercomp
    BETTER THAN MINECON 2015


    В выходные (4 и 5 июля) проходило мероприятие Better Than Minecon 2015, где обсуждался модифицированный Майнкрафт. Из моддеров присутствовали:
    asie — BuildCraft BlayTheNinth — EiraMoticons, Cooking for Blockheads copygirl — Flamingo, BetterStorage Cricket — Chisel CyanideX — InfernalSkies Darkhax — WAWLA Drullkus — Thermal Smeltery Dynious, BlayTheNinth — Refined Relocation Jared — FluxedCrystals 2 ljfa — Glass Shards magik9k — PlankOS для OC marcin212 — Zetta Industries masa — Ender Utilities pixlepix — Aura Cascade Sangar — OpenComputers shadowfacts — Matter Overdrive Skyem123, Achati, Vec — Integrated Circuits tterrag — EnderIO 2.2.3 Vexatos — Computronics, BuildCraft Oil Tweak

    Подробнее о том, что там происходило, здесь: http://asie.pl/btm15.html. Там же ссылки на записи некоторых из событий на BTS15.
     
    В частности, живая презентация OpenComputers, о которой я и хотел рассказать:
     

     
    P. S. Кто-нибудь был там с нашего форума?)
  14. 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 и как им управляться =) Надеюсь на "пятаки", "лайки", комментарии. Ждите новых записей в моём блоге!
  15. 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.



  16. Fingercomp
    Он вышел раньше, чем я предполагал — ниже список нового.
    Сила овец и оцелотов в их пушистости. Теперь пушистость можно приложить к делу и питать компы — с помощью ковровых конденсаторов. От обычных конденсаторов они толком не отличаются, но могут генерировать энергию, если по ним ходят минимум 2 пушистых животных: овцы или оцелоты, которые генерируют больше энергии. Все новые процессоры, которые будут скрафчены, будут с Lua 5.3 по умолчанию. Сменить можно так же — шифт-пкм. К беспроводной карточке, которую мы все знали, теперь добавили урезанную версию T1, тоже беспроводную. Она может открывать только 1 порт и стрелять сигналом на 16 блоков, а не 400. Креативная компонентная шина (штука, пихабельная в серверы), которая добавляет 1024 компонента. Логичное дополнение. Роботов можно подключать к компьютерам как компоненты. И менять имя роботов: то, что раньше делалось в наковальне, теперь можно через setName и getName. Робот должен быть выключен, чтобы функции работали. Починены всякие проблемки с рендерингом всяких символов. Блоки-инвентари иногда не сохраняли содержимое при сохранении мира. Дроны с чанклодырями не всегда грузили чанки. Пофикшена интеграция с AE2. computer.addUser неправильно отдавал ошибку как-то. Хитбоксы у кабелей теперь обтягивают их форму. Раньше кабели-пересечения были с хитбоксом на весь блок. Апгрейд крафта не всегда крафтил, когда должен был. Апгрейд крафта крафтил один предмет и ломал рецепт — для всех, в том числе игроков. Весело. Датчик движения как-то коряво работал. Пофикшена работа роботов с предметами-инвентарями вроде жидкостных ячеек IC2. Устранена возможная утечка памяти в сетевом коде. В MC 1.10+: пофикшена getMetadata у дебаг-карты. В MC 1.10+: добавлена getBlockstate для дебаг-карты. В MC 1.12: нельзя было заменить EEPROM дрону. В MC 1.7.10: добавлены getAllStacks и inventoryName для транспозеров с инвентарных апгрейдов. Обновлён французский перевод. В OpenOS: Обновлён install.lua, чтобы работал более предсказуемо. uuid.lua возвращает правильные UUID 4 версии, как в RFC написано. Фиксы всякие поддержки vt100. Утечка памяти при загрузке процессов (есть и такая, даже в Луа). Более конкретные комбинации клавиш: Ctrl+Alt+Delete не будет считаться за Ctrl+Delete, например.



    Вайтлиста измерений для чанклоадера... ну, их пока нет.
     
    Скачать.
  17. Fingercomp
    Небольшое обновленьице OC вышло сегодня, в котором есть немного небольших изменений.

    1.5.16 Добавлено: Транспозер — управляемая компьютером мультисторонняя воронка. Позволяет перетаскивать жидкости и предметы между соседними блоками. Интеграция с читёрнейшим AgriCraft. LordJoda Интеграция с интересным модиком Better Records. Функция для программного изменения архитектуры процессора. Ребут при изменении. Аргумент, который не ресетит монитор при биндинге GPU. makkarpov
    [*]Изменено:
    Access Point + Switch = реле, блок, совмещающий функции составляющих. Изначально — свитч, беспроводная карта превращает в точку доступа. В режиме свитча может быть вставлена линкокарта. Обновление Plan9k
    [*]Пофикшено:
    Редкая проблема с интеграцией. Много другой мелкотни.




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

  18. Fingercomp
    Новая версия!
    Новое: Можно теперь отключать некоторые стороны адаптера с помощью ключа. Очень нужная фича, если требуется контроль в огромном лагодроме. От Vexatos. robot.compare умеет теперь сравнивать предметы, игнорируя метаданные. Например, сравнивать инструменты можно. Достаточно указать опцию. От Vexatos. Очень хорошая фича заключается в том, что теперь апгрейды табличек не игнорят приват просто, а посылают ивенты! От хорошего человека makkarpov. Можно указать белый список владельцев дебаг-карт. От makkarpov. В кубаче 1.8.9 и выше теперь взаимодействовать можно с инвентарями, имплементирующие интерфейс IItemHandler. Кастомные инвентари, то есть. От Vexatos. В кубаче 1.10 ещё можно менять теперь значения в scoreboard с помощью дебаг-карты. От RusselLong. В кубаче 1.10 вернулась интеграция с EnderIO и добавлена продвинутая поддержка проводочков редстоуновых из того же мода.
    [*]Изменения:
    Очень большое обновление OpenOS! От payonel, как ни странно. Перевод на русский усовершенствован был. От @Totoro.
    [*]Починено:
    Можно теперь всасывать полные текущие блоки жидкости. Функции обратной совместимости bit32.lrotate и bit32.rrotate тоже починены были (они очень некорректно работали при некоторых значениях). Это моё. Опции %c и %e для os.date() теперь возвращают более адекватные значения. От gamax92. Перед удалением дрона проверять, происходит ли это на сервере или на клиенте. От joserobjr. Теперь нельзя удалить файлы из devfs. От payonel. Апгрейд опыта не потребляет зачарованные предметы, если он уже наполнен опытом. Несовместимости со Sponge. От Vexatos. Именование ядер (какой-то фикс для LuaJ). От gamax92. Machine.signal теперь не настолько вредный по поводу типов списков. От Vexatos. NPE, наконец-то, который возникает, если какой-либо другой мод требует тултипы до загрузки рендерера шрифтов. os.time и os.date, как ни странно, зависели от часового пояса сервера. От gamax92. Теперь серверные стойки могут быть запитаны, если к ним подведён кабель, вне зависимости от подключения сервера. Очень прикольный баг был, когда кабель запитывал только компоненты, подключённые к этой стороне. Баг с чтением данных с проводов RedLogic. Патч от Vexatos. Теперь адреса файловых систем, хранящихся в NBT, проверяются. Раньше можно было выйти за пределы папки opencomputers выше по дереву. Очень весёлый баг, да. Фикс от gamax92. В кубаче 1.8.9 и выше только что поставленные сисблоки имели буфер энергии размером в 0 единиц. То есть, не имели совсем. В 1.6.1-hotfix.1: после апгрейда все стороны адаптеров в прежнем мире становились отключенными.



    Ссылки на скачивание.
  19. Fingercomp
    Тут полгода назад я описывал изменения в OpenOS 1.6 и среди прочего я упомянул какие-то окна в либе term. Пришло время описать всю либу term.
     
    Прежде всего, рассмотрим понятие окна. Окно — это таблица типа такой:
     

    {x = 1, y = 1, fullscreen = true, dx = 0, dy = 0, w = 0, h = 0, blink = true} По порядку.
    x, y — это позиция курсора. Ну тут всё предельно ясно. fullscreen — тоже достаточно очевидно. Находится ли окно в фулл-скрине или нет. dx, dy — это смещение окна отновительно левого верхнего края видеокарты. w, h — это всё ширина и высота окна. blink — опция, с помощью которой можно отрубить (и потом вернуть) мерцание курсора.
    Ещё одно понятие, которое нужно обязательно ввести, — это viewport (далее обзывать буду это как вьюпорт). Переводится как "окно просмотра", применительно к нашему контексту это слово означает пространство, в котором можно рисовать всякие символы.
     

    Вот есть монитор из OC. Какой у него вьюпорт? Вроде как очевидно, прямоугольник от левого верхнего символа с шириной и высотой, равный разрешению. Говоря проще, это то, что вы видите в интерфейсе монитора, когда по нему кликнете.
     
    Это было так до версии 1.6. В новой версии появилась функция setViewport, которая позволяет уменьшить видимую часть экрана, оставляя разрешение прежним.
    То есть, если вы на мониторе 3 уровня пропишете gpu.setViewport(80, 25), то всё, что было в пределах прямоугольника шириной в 80 и высотой в 25 символов, останется видимым. Остальное пропадёт. А для вас это будет выглядеть, будто просто сменили разрешение.
    Но при этом вы можете продолжать использовать оставшуюся, невидимую часть экрана. Сетить символы, рисовать квадраты. Как прежде. Только вот юзеры это не увидят.
    А потом можно будет скопировать область из невидимой части в видимую, чтобы показать готовую картинку.
     
    Вернёмся к либе term. Вот то самое "окно", о котором я говорил чуть ранее, — это же и есть самый что ни на есть вьюпорт. Поэтому, когда в либе будут функции с умонинанием window или viewport, нужно понимать, что речь там идёт именно про окно, описанное нами ранее.
     
    Итак, это всё была теория. Теперь будем, наконец, изучать функции либы term.
     
    Первая функция, которую изучим, — это term.internal.open([dx: number[, dy: number[, w: number[, h: number]]]]). Принимает 4, как видно, аргумента, которыми можно задать параметры окна. По умолчанию равны 0.
    Возвращает простую таблицу с установленными параметрами. Её я указывал выше. Ничего особенного.
     
    Этому окну следует присвоить gpu с прокси видеокарты для данного окна и keyboard с адресом клавиатуры для данного окна, опять же.
    Можно просто вызвать term.keyboard(window) — тогда автоматически выберется главный компонент.
    А ещё нужно к выбранной видеокарте прицепить нужный монитор.
     
    Как-то так:
     

    local com = require("component")local term = require("term")local screenAddr = "7d0180fd-541c-dacc-579f-683a3a3e2b67"local gpu = com.proxy("58fa8c35-60f9-d49a-5e14-4a57f3769463")local window = term.internal.open()window.gpu = gpugpu.bind(screenAddr)term.keyboard(window) Есть функция term.setViewport([w: number[, h: number[, dx: number[, dy: number[, x: number[, y: number[, window: table]]]]]]]). Если последним аргументом передать наше окно инициализированное, то значения для остальных аргументов подберутся автоматически, используя параметры видеокарты окна. Этой командой мы завершаем подготовку окна для работы.
     

    Но есть вариант попроще. term.bind([gpu: table[, window: table]]) — привязывает видеокарту к окну и вызывает функцию выше для установки стандартных значений. Очень удобно.
     
    Вот итоговый код:
     

    local com = require("component")local term = require("term")local screenAddr = "7d0180fd-541c-dacc-579f-683a3a3e2b67"local gpu = com.proxy("58fa8c35-60f9-d49a-5e14-4a57f3769463")local window = term.internal.open()gpu.bind(screenAddr)term.bind(gpu, window)term.keyboard(window) Давайте теперь использовать это окно во всю мощь. Чтобы потом не отвлекаться, сначала перечислю список скучных функций.
    term.gpu([window: table]) — возвращает прокси видеокарты для данного окна. term.isAvailable([window: table]) — говорит, готово ли окно к работе. term.keyboard([window: table]) — возвращает адрес клавиатуры для данного окна. term.screen([window: table]) — возвращает адрес монитора для данного окна. term.getGlobalArea([window: table]) — возвращает значения dx + 1, dy + 1, w и h для данного окна. term.getViewport([window: table]) — возвращает значения w, h, dx, dy, x, y для данного окна.
    А теперь настало время кое-чего поинтереснее. Например, term.drawText(text: string[, wrap: boolean[, window: table]]). Рисует текст, как io.write, но, во-первых, позволяет задать вторым аргументом true, а тогда текст будет переноситься на новую строку, если он длиннее ширины окна; во-вторых, можно задать окно для рисования — и писать текст, например, на другом мониторе, имея в распоряжении при этом обработку \t, \n.
     
    А ещё есть term.scroll(n: number[, window: table]). Он копирует область внутри окна и вставляет её — ниже на n строк, если n > 0, или выше на -n строк, если n < 0. Остальное очищается.
     
    Можно очистить строку, на которой находится в данный момент курсор, с помощью term.clearLine([window: table]). К слову, в прошлой версии первым аргументом был номер строки, которую нужно очистить. Теперь этого нет.
     
    Как видно, во всех функциях аргумент window опционален. Если его не указывать, возьмётся стандартное окно, которое используется системой. Ничего особенного.
     
    Собственно, это всё по окнам. В функциях, которые я перечислю ниже, нет возможности, к сожалению, указать окно аргументом — будет использоваться стандартное. Вот эти функции:
    term.read([options: table]) — функция, с помощью которой можно получить значение от пользователя. Создаёт строку ввода, её обрабатывает и возвращает результат. Можно передать таблицу с опциями: dobreak — если равен false, то после нажатия Enter курсор не переместится на новую строку. hintHandler — функция (принимает текущее значение поля ввода и номер подсказки, переключаемый Tab/Shift-Tab, и возвращает строку с подсказкой) или таблица с подсказками, которые будут предлагаться юзеру по нажатию Tab (или Shift-Tab для возврата назад). pwchar ­— символ, который будет показываться вместо введённых пользователем. Этим можно воспользоваться, чтобы, например, писать пароль. filter — функция, которая принимает значение поля ввода и возвращает true при валидном вводе и false при невалидном, или строка с паттерном Lua, с помощью которых будет проверяться валидность введённых данных. Например, можно разрешить вводить только цифры. Если попытаться ввести невалидные данные, то комьютер радостно пропищит. nowrap — если не задан или равен false, то при достижении конца строки, последующие символы переходят на следующую строку. Иначе будет вести себя, как в версиях до 1.6 — скроллить горизонтально. И, наконец, под числовыми индексами ([1], [2], [3], ...) история ввода — строки, между которыми можно переключаться с помощью стрелочек вверх и вниз.
    [*]term.clear() — вообще 0 идей, с чего вдруг здесь нельзя задавать окно, но тем не менее. Очищает экран. [*]term.pull() — ожидает ивентов, рисуя мерцающий курсор. [*]term.write(value: string[, wrap: boolean]) — то же, что и io.write. [*]term.getCursor() — возвращает позицию курсора. [*]term.setCursor(x: number, y: number) — устанавливает позицию курсора. [*]term.setCursorBlink(enabled: boolean) — включает/выключает мерцание курсора. [*]term.getCursorBlink() — проверяет, включено ли мерцание курсора.

    И вот здесь я предлагаю закончить, наконец, этот туториал. Я описал все функции публичного API этой интересной либы, которая пополнилась очень забавными и прикольными фичами. Теперь думайте сами, что будете делать со всем этим добром :P
  20. Fingercomp
    ОБНОВЛЕНИЕ OPENCOMPUTERS ДО ВЕРСИИ 1.5.8!


    Дата релиза: 21-04-2015
    Версия: 1.5.8
    Приветствую Вас, господа и дамы, перед Вами новая версия 1.5.8, о котором я спешу Вас оповестить!
    Взглянем на список изменений:
    Добавлено: Теперь, если бедный робот пытается стукнуться со стеной (он что, в коллайдере живёт?), появляются частицы! Дабы избежать спама частицами, поставлена небольшая задержка... Так что robot.detect() теперь куда нужнее.
    Если Вы игрете в мирном режиме, то наверняка замечали, что та же интерпаутина (а не интерсеть, кстати) требует эндержемчуг. Но где ж его достать-то в мирном режиме? О_о В общем, данное недоразумение поправлено: добавлен файл рецептов для мирного режима.
    Спасибо, Кибер! Ведь у нас русский перевод мануала для чайников в Инглише!

    [*]Изменено:
    Как и было заказано на багтрекере ОС, голограммы отрисовываются куда быстрее. Также на очереди метод для помещения сырого набора инфы (data) для быстрой изменении картинки.

    [*]Пофикшено:
    Опять-таки с багтрекера: теперь дроны умеет ходить сквозь Порталы и не выключаться. Доставщик пиццы между измерениями дождался своего часа!
    Пути теперь в мануале абсолютные. Немцы теперь тоже смогут вполне пользоваться мануалом.
    OpenOS был продублирован в НЕИ (на что я обратил внимание, кстати).
    Дроны! Отставить кушать! Что? Память! Потенциальные утечки при использовании дронов.
    Чуждое мне состояние гонки. Великий Рандом был недоволен этим и отрубал иногда компы.
    Память кушается меньше при таймауте __gc (то бишь __сборщикамусора).
    В OpenOS стандартные потоки I/O были способны закрываться. THNX TO: mpmxyz.
    При фоллбэке в LuaJ компы, которые отрубались при отгрузке чанка, не стартовали снова.
    1.8:
    Цветной текст на мониторе красил другие цветные блоки. А также много всякой рендерной ерунды.
    Поршневой апгрейд. Фиксед.



    На этом всё!
    Следите за багтрекером ОС, там ОЧЕНЬ много интересного! https://github.com/MightyPirates/OpenComputers/issues
    Следите за целями в ОС 1.6! https://github.com/MightyPirates/OpenComputers/milestones/v1.6.0
    Следите за релизами ОС! https://github.com/MightyPirates/OpenComputers/releases/
    Заходите на форум ОС! http://oc.cil.li/index.php?/index
    Предлагайте, о чём мне ещё написать, оставляйте комментарии, оценки — этому всему я буду рад :P
    А также прямые ссылки на ОС 1.5.8:
    1.7.10: https://github.com/MightyPirates/OpenComputers/releases/download/v1.5.8/OpenComputers-MC1.7.10-1.5.8.17-universal.jar
    1.8: https://github.com/MightyPirates/OpenComputers/releases/download/v1.5.8/OpenComputers-MC1.8-1.5.8.19-universal.jar
  21. Fingercomp
    Потоки — очень полезные штуки, позволяющие исполнять несколько кусков кода. Раньше для их использования приходилось скачивать отдельную библиотеку, работающую через костыли. Начиная с OpenOS 1.6.4, они есть в стандартной поставке ОС — в модуле thread. Давайте посмотрим, из чего она состоит — и в чём её преимущество перед любыми другим библиотеками.
     
    Начнём с версий. OpenOS 1.6.4 — версия, включённая в OpenComputers 1.7.0. Если не хотите возиться с обновлением системы вручную, требуется иметь версию выше или равную 1.7.0.
     
    Сразу обращаю внимание на самую важную вещь: потоки не могут исполняться одновременно. В один момент времени только один поток может работать.
     
    В чём тогда красота тредов?
     
    Они автономны, то есть:
    Начинают исполнение сразу же после создания. Передают исполнение в другие потоки в местах, указанных использователем, — при том или ином вызове computer.pullSignal (os.sleep, event.pull и т. д.). Автоматически продолжают своё исполнение без необходимости самостоятельно их стартовать. Потоки можно убить и приостановить.
    Они неблокирующие:
    Вызов computer.pullSignal не блокирует исполнение других потоков.
    Они отцепляемые:
    Процесс, в котором был создан поток, называется родительским. При завершении родительского процесса все потоки останавливаются. Поток может отсоединиться от родительского процесса и работать полностью автономно — например, как слушатели событий. Поток может сменить родителя на другого. Поток сам является процессом и потому может создавать дочерние потоки. Работающий поток не даёт завершиться своему родителю.
    Они независимы при обработке событий:
    Потоки не наследуют и не передают дочерним свой набор слушателей событий. Все слушатели событий и таймеры принадлежат только конкретному потоку. Как следствие, поток не может изменять их набор в другом. Слушатели и таймеры автоматически удаляются при завершении потока, даже если завершение вызвано ошибкой. Приостановленные потоки игнорирует события. Если несколько потоков вызвали event.pull на одно и то же событие, они оба его получат.
    Этот набор фич в таком объёме присутствует только в этой библиотеке, и ни одна другая и не даёт столько простоты в работе с ними.
     
    Пожалуй, приступим к использованию. Потоки создаются функцией thread.create: первым аргументом передаётся функция, дальше идут аргументы к ней.
    local thread = require("thread") local t = thread.create(function(a, b) print("В потоке получены аргументы:", a, b) end, 21, 42)
    Функция возвращает объект потока. Его же может получить сам поток вызовом thread.current() — однако если вызвана не в потоке, то возвращает nil. На всякий случай, основной процесс не является потоком.
     
    Объект потока позволяет чудить различные вещи с потоком.
     
    t:suspend() приостанавливает поток. Как уже сказано, такой поток не будет получать события и обрабатывать тики таймера. Забавно, что если приостановить поток, когда он ждёт события, то неизвестно, что он получит после его возобновления.
     
    t:resume() возобновляет работу ранее приостановленного потока. Так как созданные потоки сразу начинают работу, то обычно этот метод вызывать не придётся.
     
    t:kill() убивает поток, то есть завершает его, удаляя всех слушателей и таймеры. Возобновить работу потока после того, как он убит, нельзя.
     
    t:status() возвращает строку со статусом потока:
    "running" — поток работает или блокирован другим. Такой поток не даёт завершиться своему родителю. "suspended" — поток приостановлен. Его дочерние потоки также будут приостановлены. Когда родительский процесс завершается, такой поток автоматически убивается. "dead" — поток мёртв.  
    t:attach() позволяет сменить родителя у потока. Без аргумента поток будет присоединён к текущему процессу. Переданное как аргумент число позволяет указать, к кому присоединить: 0 — текущий процесс, 1 — родитель текущего и т. д.
     
    t:detach() отцепляет поток от родителя. Такой поток будет работать до его остановки или перезагрузки компьютера.
     
    t:join() останавливает процесс, в котором была вызвана это функция, до завершения потока t.
    local thread = require("thread") local t = thread.create(function() os.sleep(10) end) t:join() -- остановится на 10 секунд
    Можно передать первым аргументом этой функции число, которое будет служит таймаутом (в секундах). Тогда, если не успеет завершиться поток за это время, join завершится досрочно.
     
    t:join ждёт только одного потока. Для групп потоков есть функции thread.waitForAny и thread.waitForAll — обратите внимание, что это функции библиотеки, а не методы объекта потока.
     
    Обе функции первым аргументом требуют таблицу с потоками, а вторым опционально можно задать таймаут.
     
    thread.waitForAll ждёт, пока завершатся все потоки из списка.
    local thread = require("thread") local t1 = thread.create(function() os.sleep(10) end) local t2 = thread.create(function() os.sleep(15) end) thread.waitForAll({t1, t2}) print("Это сообщение будет написано через 15 секунд")   thread.waitForAny ждёт, пока завершится хотя бы один поток из списка.
    local thread = require("thread") local t1 = thread.create(function() os.sleep(10) end) local t2 = thread.create(function() os.sleep(15) end) thread.waitForAny({t1, t2}) print("Это сообщение будет написано через 10 секунд")
    Что будет, если поток бросает ошибку? При ошибке в потоке она не будет проброшена в родительский процесс. Как и со слушателями, она будет записана в файл /tmp/event.log, но родитель не сможет узнать причину ошибки — и, вообще, успешно ли завершился поток.
    local thread = require("thread") local t = thread.create(function() os.sleep(3) error("test") end) print(t:status()) --> running t:join() print(t:status()) --> dead
    Кроме того, событие жёстокого прерывания (Ctrl+Alt+C) не передаётся всем процессам — только одному; причём неизвестно, какому именно: родителю или одному из его потоков. Если вы используете потоки, первым делом сделайте один, который будет ждать события interrupted и подчищать ресурсы.
    local thread = require("thread") local cleanupThread = thread.create(function() event.pull("interrupted") print("Принял ^C, чищу всякие ресурсы") end) local mainThread = thread.create(function() while true do local input = io.read() if input == "exit" then break end end end) thread.waitForAny({cleanupThread, mainThread}) os.exit(0) Обратите внимание, что в конце программы стоит os.exit. Я уже упоминал не раз, что родительский процесс, достигнув конца программы, не завершится до тех пор, пока работает хотя бы один из его дочерних потоков. Вызов os.exit() позволяет выйти из программы, закрыв все дочерние потоки. Что, безусловно, достаточно удобно.

    Есть ещё один момент. Допустим, данная программа запускается в роботе:
    local robot = require("robot") local thread = require("thread") local moveThread = thread.create(function() while true do robot.forward() end end) local inputThread = thread.create(function() while true do local input = io.read() if input == "exit" then break end end end) thread.waitForAny({inputThread, moveThread}) os.exit(0) Если вы запустите эту программу, то должны заметить, что вы ничего не сможете написать в роботе, хотя работает io.read. Дело в том, что функция robot.forward вызывает метод компонента, который блокирует исполнение компьютера. Пока робот двигается, на компьютере не может выполняться ни одна команда.
     
    Чтобы хоть что-то можно было вставить в строку, то поставьте после robot.forward какой-нибудь os.sleep(0) — он позволит соседнему потоку принять и обработать события. Тем не менее, строка ввода всё равно будет работать с тормозами.
     
    В подобном случае задумайтесь над тем, чтобы использовать вместо строки ввода иное средство коммуникации: редстоун, сеть, интернет-сокет.
     
    Несмотря на всё, библиотека действительно облегчает работу с потоками в OpenOS. Кроме того, очень удобно поместить все слушатели событий в один поток, чтобы они все автоматически были удалены после убийства потока.
    local event = require("event") thread = require("thread") local mainThread = thread.create(function() event.listen("key_down", function(evt, addr, key, code, user) print("A key has been pressed!") end) while true do print("do something") os.sleep(0.5) end end) -- событие interrupted не ловится обработчиками local intThread = thread.create(function() event.pull("interrupted") end) thread.waitForAny({mainThread, intThread}) os.exit(0) Не нужно функции сохранять в переменные и помнить, что нужно ставить event.ignore в конце программы; не требуется ребутать компьютер, если программа завершилась с ошибкой, а до отключения слушателей дело не дошло.
     
    В общем, красота.
  22. Fingercomp
    Выключен
    Хост XMPP-сервера был отключён, ищу другой по возможности для настройки.
     
    Итак, хост, на котором был XMPP-сервер, окончательно ушёл куда-то, а вернуться так и не пообещал. Так что пока сервер, который популярностью и не пользовался, будет отключён до нахождения другого бесплатного хоста (буду рад помощи). Планирую запилить на него IRC, плюс ещё парочку других серверов при необходимости. Тем не менее, зарегистрировалось там 8 человек, сообщений написало (вместе с ЛС): 42, а продержался он 3.5 месяца.
     
    Старая запись:

     
  23. Fingercomp
    Приветствую Вас, уважаемый читатель! Рад сообщить о том, что, спустя целых 24 дня, наконец, отрелизилась версия 1.5.15. Изменений маловато, но перечислить, думаю, не помешает.
    Добавлено: Режим доступа к накопителям низкого уровня. То есть, это является, по сути, одним "файлом", в который можно писать и из которого можно читать. Может быть полезным. Что? Мало карт данных? Не проблема. Теперь они различаются по уровням (бесполезная и даже неприятная фича ) Фича из разряда "НАКОНЕЦ-ТО!!!" Две функции для контроллера инвентаря: isEquivalentTo() и areStacksEquivalent(). Они позволяют определять, есть ли общие категории OreDict у обоих предметов. Всё больше мы переезжаем на Луа 5.3. "Новая"-старая либа bit32 для обратной совместимости. Перехватывание и возврат значений чисел различных типов в сигналах. Срочно бежим дизассемблировать кольчужную броню! Ибо добавлен чёрный список для него, и кольчуга там по умолчанию. Что? Только в 1.8 редактирвоание NBT? Да? Уже нет. Для дебаги добавлена функция управления NBT (спасибо gamax92).
    [*]Изменено:
    Использование правильного дефолтного шаблонного премета в лутогенераторе, для помощи работе другим модам.
    [*]Пофикшено:
    Сообщение computer.stopped. При отключении компьюетра, например, не закрывались сокеты. Переборщили с правкой времени копания. Как, что — не указывается. Если компьютеры остановились неожиданно, крашилось всё (бежим крашить сервер, пока не поздно!) Дедлок потенциальный на работающих компьютерах. Нестабильный мануал: деление на нуль. В интеграции с АЕ2 была небольшая проблема. Наконец-то. Утечки памяти в серверных стойках (кажется, именно это и происходило) были устранены. Состояние гонки возникало между ServerThread и ClientShutdownThread. Всякие очепятки и опечатки в мануале (спасибо Shuudoushi).



  24. Fingercomp
    ОБНОВЛЕНИЕ OPENCOMPUTERS ДО ВЕРСИИ 1.5.9 RELEASE CANDIDATE 1!


    Приветствую Вас, уважаемый читатель. Автор OpenComputers Sangar продолжает работать над своим модом, поэтому встречаем новую версию 1.5.9! Точнее, первого кандидата на релиз.
    Взглянем на изменения.
    Добавлено: Charger теперь может использовать для зарядки батарейных апгрейдов! И, кроме них, все другие предметы, хранящие и содержащие энергию RF и EU!
    Интеграция с IngameWikiMod. Большая часть страничек из мануала там доступны.
    То, чего мне иногда не хватало. Стандартный шелл теперь поддерживает пайпинг!cat < f1 | cat >> f2

    Поддержка многожильного кабеля из BluePower.
    Больше, БОЛЬШЕ интеграции с GregTech! В рецептах можно использовать машины из Грега.
    Waypoint. Может использоваться с навигационными апгрейдами.

    [*]Изменено:
    Логика и механика рендеринга апгрейдов на роботе была изменена. Аддонам проще ставить свои апгрейды, а на MC1.8 всё работает, как и должно.

    [*]Пофикшено:
    Дроны теряли своё имя при замене EEPROM.
    Потенциальный клиентский лог-спам при открытия ГУИ Dissaembler'а, если функция disassembleAllThings была включена.
    Роботы продолжать анимироваться даже при паузе.
    MC1.8:
    Несколько потенциальных NPE



    Вот и всё. Комментарии как всегда приветствуются, равно как и лайки и даже оценки!
    А ещё...
    Следим за интересными предложениями, багами на баг-трекере OpenComputers! https://github.com/MightyPirates/OpenComputers/issues
    Проверяем обновления ОС! https://github.com/MightyPirates/OpenComputers/releases
    Заходим на официальный сайт... http://oc.cil.li/ ...и форум! http://oc.cil.li/index.php?/index
    И прямые ссылки для скачивания:
    [MC1.7.10] https://github.com/MightyPirates/OpenComputers/releases/download/v1.5.9-rc.1/OpenComputers-MC1.7.10-1.5.9.20-rc.1-universal.jar
    [MC1.8] https://github.com/MightyPirates/OpenComputers/releases/download/v1.5.9-rc.1/OpenComputers-MC1.8-1.5.9.22-rc.1-universal.jar
  25. 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.
×
×
  • Создать...