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

Fingercomp

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

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

  • Посещение

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

    283

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

  1. 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
  2. Fingercomp
    Перед тем, как я начну, хочу сразу обратиться к забугорным ребятам, читающим эту запись.
     
    Hey! If you are reading this, please tell Vexatos to document the sound card. The in-game manual page is a meaningless piece of text that explains nothing.
    Documentation is written to help others to get an idea of how something works. The sound card is a complex thing that definitely needs a how-to guide to be used in programs.
    So if he wants the sound card to be used in programs, he should write the proper documentation with the detailed description of each concept, feature, and component method.
    The following is the result of efforts put to understand how the sound card works. It took me several months until I realized how to make a simple beep. How am I supposed to create more complex waves? >_>
     
    Ну да ладно.
     

    Хей! Разбирался я недавно, как работает звуковая карта из CX, и поэтому хочу предоставить результаты своих трудов.
    Как и написано сверху, доков нет, автор мода бездействует, везде уныние и отчаяние, поэтому пришлось действовать по-хардкорному. То есть чтением исходников и научнейшим тыком.
     
    Итак, есть компьютер со звуковой картой. Нужен звук. Звучит просто, м?
     
     
    Звуковая карта даёт нам замечательные функции вроде установки ADSR-огибающей, типа волны, шума простого или из LFSR, амплитудной и частотной модуляции. Но мы тут решили заняться простым звуком, поэтому всякие LFSR, AM, FM безжалостно выкидываем. Получаем такой план:
    Открыть канал. Поставить тип волны. Задать частоту волны. Установить громкость. Обработать.  
    1. Открыть канал
    Э, извините, а что за каналы?
    ...
    В звуковой карте звук генерируется с помощью инструкций, которые выполняются друг за другом. Почти все работают с определённым каналом. Если бы канал был один, мы бы смогли играть в один момент времени только одну, условно, ноту. Чтобы параллельно шло сразу несколько звуковых волн, нужно использовать несколько каналов. На каждом можно задать свои тип и частоту волны, громкость, ADSR-огибающую. По умолчанию таких каналов в звуковой карте 8 штук.
     
    С каналами всё довольно просто: открытый канал генерирует звук, закрытый канал не генерирует звук (правда, нужно будет здесь кое-что поправить, когда будет изучать ADSR).
     
    Открыть канал можно с помощью функции sound.open(channel: number). Закрыть: sound.close(channel: number).
     
    2. Поставить тип волны
    Всего типов звуковая карта поддерживает пять: шум (noise), меандр (square), синусоида (sine), треугольная (triangle) и пилообразная (sawtooth).
     
     

    Waveforms ru [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0) или GFDL (http://www.gnu.org/copyleft/fdl.html)], автор Omegatron (original work)
    Kirill Borisenko (Russian translation; see File:Waveforms for Cyrillic Segment.png)
    Incnis Mrsi (ultimate work), с Викисклада
    Очевидно, что разные типы волн звучат по-разному.
     
    Установить тип волны можно с помощью функции sound.setWave(channel: number, type: number). Видно, что тип волны должен быть числом. Соответствующее типу волны число можно получить с помощью таблицы sound.modes. Например, sound.modes.sine или sound.modes.noise.
     
    3. Задать частоту волны
    Чем больше частота, тем выше звук. Частота 440 Hz соответствует ноте ля в первой октаве. Другие частоты можно найти по табличке на Википедии.
     
    Используем функцию sound.setFrequency(channel: number, freq: number).
     
    4. Установить громкость
    Громкость может быть в пределах от 0 до 1 включительно. Половина максимальной громкости - 0.5. И так далее.
     
    Функция: sound.setVolume(channel: number, volume: number).
     
    5. Обработать
    Чтобы обработать все инструкции, нужно вызвать sound.process(). Но если вы просто возьмёте и вызовете функцию, то ничего не услышите. Дело в том, что, достигнув конца инструкций, звуковая карта прекращает генерировать звук.
     
    Поэтому нужно выставить некоторую задержку в конце всех других инструкций, чтобы звуковая карта подождала перед прекращением воспроизведения звука. Для этого служит sound.delay(delay: number). Задержка указывается в миллисекундах. Например, sound.delay(2500). При этом эта функция тоже является инструкцией, поэтому её можно выставить несколько раз. Однако если задержка превышает 5000 (5 секунд), то звуковая карта выдаст ошибку.
     
     
    Проиграв несколько типов волн, мы всё равно останемся неудовлетворёнными. Ну где это видано, что звук мгновенно нарастает и затухает?
    Чтобы как-то это контроллировать, мы воспользуемся ADSR-огибающей. На Википедии довольно хорошо написано, в чём смысл этого.
     
    Скопируем сюда оттуда пикчу:

    И вкратце распишем суть.
    Если вы хоть когда-либо слышали музыкальные инструменты, вы могли заметить, что громкость звука со временем меняется. У гитары громкость мгновенно нарастает и постепенно затухает, а у флейты нарастает не сразу, например.
    Чтобы очень-очень грубо моделировать эти инструменты, мы используем ADSR.
    Attack - это время нарастания громкости звука до максимальной (последняя устанавливается через sound.volume).
    Decay - время затухания сигнала сразу после достижения максимальной отметки до следующего параметра.
    Sustain - отметка громкости, к которой стремится Decay. Значение 1 - это максимальная громкость звука на канале (установленная с помощью sound.setVolume), 0 - отсутствие звука вовсе.
    При достижении отметки Sustain звук остаётся на этом уровне до "отпускания клавиши" (т.е. закрытия канала).
    Release - время затухания сигнала после закрытия канала, от отметки Sustain до нуля.
     
    Время везде в миллисекундах.
     
    Попробуем? Вызываем функцию sound.setADSR(channel: number, attack: number, decay: number, sustain: number, release: number) до пятого шага. Например sound.setADSR(1, 1000, 500, 0.33, 1000).
    И замечаем, что громкость меняется со временем. Yay!
     
     
    На этом счастливом моменте повествование обрывается. Итоговый код, который можно запустить и послушать примитивный бип:
    local sound = require("component").sound sound.close(1) sound.close(2) sound.open(1) sound.setWave(1, sound.modes.sine) sound.setFrequency(1, 440) sound.setVolume(1, 1) sound.setADSR(1, 1000, 500, 0.33, 1000) sound.delay(1000) sound.open(2) sound.setWave(2, sound.modes.noise) sound.setFrequency(2, 440) sound.setVolume(2, 0.6) sound.setADSR(2, 1, 250, 0, 1) sound.delay(1500) sound.process()
    К остальному, надеюсь, я ещё вернусь в другой записи. Сначала нужно самому понять, как оно работает в версии для CX.
    Поэтому наслаждаться пока приходится только бибикалкой.
  3. Fingercomp
    Немногие знают, как работают палитры в OpenComputers. Расскажу здесь, как избавиться от необходимости прописывать гектары цветов в палитре, покажу, как упаковываются цвета в OpenComputers и дам пару алгоритмов для работы с индексами.
     
    Сразу условимся, что индексы палитр у нас будут начинаться с нуля.
     
    На каждой из трёх уровней видеокарт и мониторов своя поддерживаемая палитра цветов. Будем двигаться снизу вверх.

    Первый уровень
    Палитра состоит из двух цветов: чёрного и заданного в конфиге (по умолчанию белого). Конвертация цвета в индекс палитры тривиальна:
    цвет нулевой — и индекс нулевой (чёрный цвет); цвет ненулевой — индекс единичный. Цвет в индекс (deflate) и обратно (inflate) превращать — одно удовольствие:
    local palette = { 0x000000, CONFIG.monochromeColor } local function t1deflate(index) if index == 0 then return 0 else return 1 end end local function t1inflate(index) return palette[index + 1] end Как и говорил.

    Второй уровень
    В палитре второго уровня имеется 16 закреплённых цветов:
    local palette = {0xFFFFFF, 0xFFCC33, 0xCC66CC, 0x6699FF, 0xFFFF33, 0x33CC33, 0xFF6699, 0x333333, 0xCCCCCC, 0x336699, 0x9933CC, 0x333399, 0x663300, 0x336600, 0xFF3333, 0x000000}
    При конвертации цвета в индекс палитры вернётся ближайший к данному цвет из палитры. Насколько цвета друг к другу близки, рассчитывается по специальной формуле, которая учитывает, что человеческий глаз лучше воспринимает зелёный, нежели красный и синий. В коде этим занимается функция delta. Вот как она выглядит (вместе с функций extract, выделяющей из числа вида 0xABCDEF числа 0xAB, 0xCD, 0xEF):
    local function extract(color) color = color % 0x1000000 local r = math.floor(color / 0x10000) local g = math.floor((color - r * 0x10000) / 0x100) local b = color - r * 0x10000 - g * 0x100 return r, g, b end local function delta(color1, color2) local r1, g1, b1 = extract(color1) local r2, g2, b2 = extract(color2) local dr = r1 - r2 local dg = g1 - g2 local db = b1 - b2 return (0.2126 * dr^2 + 0.7152 * dg^2 + 0.0722 * db^2) end   Теперь можно конвертировать цвет в индекс палитры. Суть такова: выбираем из двух цветов ближайший и возвращаем его.
    local function t2deflate(color) -- Сначала проверяем, совпадает ли данный цвет -- с каким-либо из палитры for idx, v in pairs(palette) do if v == color then return idx end end -- Составляем таблицу разниц между цветами local deltas = {} for idx, v in pairs(palette) do table.append(deltas, {idx, delta(v, color)}) end -- Сортируем по увеличению разницы table.sort(deltas, function(a, b) return a[2] < b[2] end) -- Первый элемент будет с наименьшей разницей, -- то есть искомый. Возвращаем индекс. return deltas[1][1] - 1 end  
    Обратная же процедура — превращение индекса палитры в цвет — неизменна.
    local t2inflate = t1inflate
    Третий уровень
    Палитра третьего уровня содержит уже 256 цветов: первые 16 цветов изменяются, а остальные соответствуют цветам палитры RGB-6-8-5. Это означает, что можно смешивать 6 оттенков красного, 8 оттенков зелёного и 5 оттенков синего. В общем-то, довольно очевидна причина такого выбора: человеческий глаз лучше всего различает оттенки зелёного и хуже всего — оттенки синего.

    В любом случае, здесь алгоритмец будет посложнее. Сначала нужно сгенерировать палитру.
    Начнём с первых 16 цветов. Они не включаются в палитру RGB-6-8-5, поэтому их заполнять нужно отдельно. В OpenComputers по умолчанию они содержат оттенки серого. Так как чёрный и белый уже включены в основную, зафиксированную палитру, то заново их дублировать смысла нет.
    local palette = {} -- grayscale for i = 1, 16, 1 do palette[i] = 0xFF * i / (16 + 1) * 0x10101 end
    Таким образом в таблице получаются следующие оттенки серого:
    0x0F, 0x1E, 0x2D, 0x3C, 0x4B, 0x5A, 0x69, 0x78, 0x87, 0x96, 0xA5, 0xB4, 0xC3, 0xD2, 0xE1, 0xF0 Эти цвета мы записываем в индексы от 0 до 15. Теперь нужно сгенерировать остальные цвета — они не изменяются. Здесь будет посложнее.
    Посмотрим на картинку с палитрой:

    В OpenComputers левая верхняя ячейка палитры (0x000000) имеет индекс 16, а правая нижняя (0xFFFFFF) имеет индекс 255. Индексы распределяются слева направо, сверху вниз. То есть правая верхняя ячейка (0x00FFFF) имеет индекс 55, а вторая сверху и левая (0x330000) — это номер 56. Отсюда вытекает следующий алгоритм нахождения цвета: сначала найти индексы отдельно по R, G, B, затем для каждого из этих трёх индексов найти соответствующий ему оттенок цвета, а затем всё сложить.
    for idx = 16, 255, 1 do local i = idx - 16 local iB = i % 5 local iG = (i / 5) % 8 local iR = (i / 5 / 8) % 6 local r = math.floor(iR * 0xFF / (6 - 1) + 0.5) local g = math.floor(iG * 0xFF / (8 - 1) + 0.5) local b = math.floor(iB * 0xFF / (5 - 1) + 0.5) palette[idx + 1] = r * 0x10000 + g * 0x100 + b end Идея следующая. Каждый из трёх каналов принимает значение от 0 до 255 (0xFF). Разбиваем их на определённое число ступеней (по-умному — квантуем): 6 для красного, 8 для зелёного и 5 для синего. Например, синий канал мы разобьём так:
    0/4 · 255 = 0 1/4 · 255 = 63.75 2/4 · 255 = 127.5 3/4 · 255 = 191.25 4/4 · 255 = 255 В знаменателе тут 4, а не 5, потому что считаем с нуля. Затем округляем до ближайшего целого конструкцией math.floor(x + 0.5). Перебрав все комбинации, мы получим все 6 × 5 × 8 = 240 цветов неизменяемой части палитры.
     
    Всё. Палитра есть, теперь можно, наконец-то, конвертировать индексы между цветами.
    Из индексов получить цвет довольно просто. Достаточно использовать ту же функцию, что и для предыдущих уровней:
    t3inflate = t2inflate
    С обратной же конвертацией всё несколько сложнее. Функция, используемая в OC, подбирает ближайший цвет хитрым алгоритмом, который я привожу ниже.
    local function t3deflate(color) local paletteIndex = t2deflate(color) -- Если цвет из палитры, то используем значение выше for k, v in pairs(palette) do if v == color then return paletteIndex end end -- Иначе используем хитромудрый код local r, g, b = extract(color) local idxR = math.floor(r * (6 - 1) / 0xFF + 0.5) local idxG = math.floor(g * (8 - 1) / 0xFF + 0.5) local idxB = math.floor(b * (5 - 1) / 0xFF + 0.5) local deflated = 16 + idxR * 8 * 5 + idxG * 5 + idxB if (delta(t3inflate(deflated % 0x100), color) < delta(t3inflate(paletteIndex & 0x100), color)) then return deflated else return paletteIndex end end Хитромудрость здесь не сильно сложная, на самом деле. Мы сначала находим индекс самого близкого цвета из изменяемой части палитры (paletteIndex). Дальше высчитываем индекс цвета из неизменяемой части (deflated), для чего в каждом канале отдельно ищем номер ближайшей ступени, на которые ранее квантовали. Последний if сравнивает 2 варианта и возвращает самый похожий (с точки зрения человека) на исходный.   В общем-то, это всё. Показал портированный со Scala на Lua код, который используется в OpenComputers. С помощью этого можно оптимизировать операции с экраном, выбирая поддерживаемые монитором цвета. И заодно избавиться от таблиц цветов, которые некоторые буквально берут и копипастят в файл, даже не задумываясь об изменяемых цветах палитры.
    Особенно это важно, когда берётся значение цвета через gpu.get, потому что следующий код всегда вернёт false:
    local gpu = require("component").gpu gpu.setForeground(0x20AFFF) gpu.setBackground(0x20AFFF) gpu.set(1, 1, "HI") return select(2, gpu.get(1, 1)) == 0x20AFFF И всё потому, что gpu.get возвращает уже приведённый к индексу из палитры цвет. А 0x20AFFF в палитре, если не менять первые 16 цветов, не имеется.

    Enjoy :P
  4. 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 следует вечная блокировка возможности отправить сообщения.
    Но и без войса можно просто сидеть и читать чат.
     
    В целом, это всё, что я хотел сюда написать. Ждём на наших каналах — подключайтесь, у нас есть печеньки.
  5. Fingercomp
    Вышла новая версия OpenComputers, в основном с самыми разными фиксами. Что изменилось:
    Моды теперь могут добавлять свои кастомные дискеты, которые можно получить перекрафтом их с ключом. (Vexatos) Две функции дебаг-карты: для отправки сообщения на другую дебаг-карту и отправки текста в буфер обмена какого-либо игрока. Опасная штука. (Vexatos) Когда роботы/дроны выкидывают что-либо в мир, они теперь посылают ивент. (Sangar) Добавлен новый предмет — MFU. Позволяет подключить к адаптеру любой блок, находящийся на удалении нескольких блоков. Это позволяет небольшие билды сделать красивее, а сложные схемы — удобнее. (Vexatos) Обновлён перевод на немецкий. (Vexatos) Обновлены LuaJ и JNLua — заявляется, что теперь всё лучше с UTF-8. (gamax92, Sangar) Обновление OpenOS. Как обычно, много изменений, но здесь опять уменьшение потребление памяти и увеличение производительности. (payonel) Дискета oppm теперь использует современные способы установки в OpenOS. А ещё программу обновили. (Vexatos) Обновление Plan9k. (magik6k) В планшетах не работал слот для карт, если было вставлено улучшение. (Vexatos) Очень труднонаходимый баг с экранами, которые после перезагрузки чанка (причём особенного) зависали, был ликвидирован. (Sangar) Больше не получится много раз потыкать ключом по дрону, чтобы получить большее их количество. (Vexatos) Устранены проблемы с вайлой (очень странная логика у неё) (Sangar) [MC 1.8.9+] При выкидывании предметов роботами или дронами некоторые уничтожались. (Vexatos) [MC 1.8.9+] Краш при использовании функции getName на нанонашинах. (Vexatos) [MC 1.9.4+] Компоненты могли не деинициализироваться, когда чанк с ними выгружался. Это приводило, например, к смене UUID компонентов. (Sangar) [MC 1.9.4+] Починили рендер клавиатуры. (Vexatos) [MC 1.9.4+] Команды ломали /help. (Vexatos) [MC 1.9.4+] В наномашинах не было эффектов зелий. (Vexatos) [MC 1.10.2+] Улучшена интеграция с JEI. (Vexatos)

    Релиз на GitHub.
  6. Fingercomp
    Если до версии 1.6 все использовали файл /autorun.lua и были довольны, то теперь ситуация несколько изменилась. Поэтому я опишу все варианты автозапуска программ в этой небольшой заметке.
     
    С версии OpenOS 1.6 файл autorun.lua больше не запускается на rootfs (то есть на файловой системе работающей операционной системы). Вот все пять способов, которые можно использовать для автозапуска программ.
    Модифицировать /init.lua.
    Это самый плохой и ужасный вариант из всех. Во-первых, программа будет запускаться до запуска шелла и инициализации библиотек, поэтому возможны краши системы. Во-вторых, если сделать ошибку в файле, то придётся переустанавливать этот файл, что не очень удобно.
    Добавить скрипт в /boot.
    Это не такой плохой вариант, но здесь также возможны ошибки при использовании стандартных библиотек, так как бутскрипты запускаются не в самом конце загрузки.
    Модифицировать /etc/profile.
    Это файл, каждая строка которого последовательно исполнаяется при запуске программ. Проблема в том, что при переустановке системы этот файл будет перезаписываться. Поэтому не вариант.
    Модифицировать /home/.shrc.
    Это самый оптимальный вариант. Но программа будет запускаться при каждом запуске шелла. Если прописать exit в шелле, то программа запустится ещё раз. Если для графических всяких программ это самый лучший вариант, то для одноразовых демонов, которые регистрируют листнеры на ивенты и выходят, вариант не очень хороший, так как тогда листнеры зарегистрируются дважды.
    Использовать систему rc.
    Подробно о ней рассказывал @LeshaInc: http://computercraft.ru/topic/1679-rc-chto-za-zver-takoi/
    Это система, которая позволяет писать своих "демонов" — программ, исполняемых в фоне — и контролировать их из шелла с помощью команд. Графические утилиты так запускать проблематично, потому что возможны всякие артефакты отображения.

    Поэтому используйте варианты 4 или 5 в зависимости от программы, которую требуется запустить.
  7. 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: после апгрейда все стороны адаптеров в прежнем мире становились отключенными.



    Ссылки на скачивание.
  8. 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
  9. Fingercomp
    Добавлено Версия дисковода гибких дисков (дискет, если что) для серверов. Возможность взаимодействовать с некоторыми хранилищами предметов с помощью контроллера инвентаря (не особо понял, что тут нового. Видимо, новые инвентари или черех адаптер). Поддержка энергии RotaryCraft. Возможность задать границы вывода (я про viewport, да) на GPU, так что теперь можно химичить с производительностью всякими нестандартными путями. Кабели запоминают цвета, в которых их красили, при срубании. Можно их теперь ещё и в сетке крафта красить. Интеграция с IC2 на 1.8.9. Можно переключаться между всеми лут-дискетами, перекрафчивая их с ключом. computer.getDeviceInfo() — метод, который возвращает базовую инфу об устройствах (от планок памяти до всяких шифраторов CX). computer.getProgramLocations() — функция, которая возвращает, на каких лут-дискетах какие лут-программы лежат. Торговый апгрейд для роботов. Торговля с жителями, об этом я уже писал. Можно задать свои HTTP-хедеры вместе с, ммм, HTTP-запросом. debug.playSoundAt, которая, как ни странно, играет звуки. Возможность задать используемый CPU из AE2 при запросе на автокрафт. Интеграция с ThaumicEnergistics. Цветные дронотапки (hover boots). Индикатор сетевой активности на серверах. Перевод на бразильский язык.
    [*]Изменено
    Мажорнейшее и вообще самое основное — серверные стойки. Я о них писал, да. Удалённые терминалы (Remote Terminals) подключаться должны к серверу удалённых терминалов (Remote Terminal Servers), штучке для серверной стойки. Компонент дисководиков. Можно теперь программно выкидывать диски из дисководов. Нёрф геолайзера — учитывается теперь дистанция до блока, а не колонны. Зато область сканирования можно задавать не только в виде колонны, а в виде кубоидов объёмом до 64 блоков. Упрощены рецепты. Новый шрифт поставлен. Можно сменить ещё с помощью ресурспаков. Один солидный книжный том изменений в OpenOS.
    [*]Починено
    Зависания при крафте, возвращающем тот же предмет, что и данный на входе. Всякие проблемы с рецептами режима грега. Обработка userdata в LuaJ. Конвертация энергии некоторых модов. Проблемы производительности из-за слишком усердной компресси данных, передаваемых клиенту. Тоже проблемы производительности, связанные с отправкой пакетов с дескрипторами компьютеров на клиенты. Обновление LuaJ с фиксами багов. Интеграция с Mystcarft. Напомню, категория "починено". Интеграция с ключом BuildCraft. Роботы могли черпать блоки текучих жидкостей, не источников. Генератор поедал нещадно предметы, если они не были вовремя оттуда вынуты. Контейнеры апгрейдов никогда не выпадали из планшетов при разборке. Сломанный код сохранения информации о блоках OC в версиях MC выше 1.8. Микроконтроллеры ловили только сигналы с сетевой карты.



    Вот это всё и есть OC 1.6. Вот чем он так крут по сравнению с прошлой версией. Ченджлог на страницу!
     
    Со времени первого коммита OC 1.6 до текущего момента прошло 465 дней. Это год и 100 дней. И ведь всё это время я ошибочно думал, что вот-вот, немного подождать, и будет 1.6.0, пару изменений ещё только.
     
    Здесь оригинал списка гигантского и ссылки на скачивание.
    У меня есть несколько записей, посвящённых обновлениям в этой версии. Если ещё их не читали, рекомендую ознакомиться.
  10. Fingercomp
    CSV идёт от Comma-Separated Values, что, в общем, довольно точно описывает этот формат хранения таблиц. Вот типичная таблица:
    aaa,bbb,ccc,dddeee,fff,ggg,hhh
    Как видно, строки отделяются \n, а ячейки ­— запятой. Последняя строка может иметь или не иметь \n.
     
    Формат очень простой. Описывается он в RFC 4180. Там всего 7 пунктов. Ну а раз простой, давайте соорудим парсер.
     
    Вот у нас есть строка aaa,bbb,ccc,ddd\neee,fff,ggg,hhh. Задача: сделать из неё
    [ [ "aaa", "bbb", "ccc", "ddd" ], [ "eee", "fff", "ggg", "hhh" ]]
     
    Так как позже я немного усложню парсер, очевидный вариант со split, которая делит строку, опустим. Сделаем так:
    def parse_csv(s): # Сюда идёт результат result = [] # Текущая строка row = [] # Текущая ячейка cell = "" # Проходимся по строке for i in range(len(s)): # Текущий символ c = s[i] if c == ",": # Если символ — запятая, закрываем ячейку row.append(cell) cell = "" elif c == "\n": # Если это перевод строки, то закрываем ячейку и строку row.append(cell) cell = "" result.append(row) row = [] else: # Любой другой символ добавляем в ячейку cell += c # Возвращаем результат return result
    Запускаем:
    >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh\n")[['aaa', 'bbb', 'ccc', 'ddd'], ['eee', 'fff', 'ggg', 'hhh']] >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh")[['aaa', 'bbb', 'ccc', 'ddd']]
     
    Действительно, в конце может и не быть \n. Давайте поправим:
    def parse_csv(s): result = [] row = [] cell = "" for i in range(len(s)): c = s[i] if c == ",": row.append(cell) cell = "" elif c == "\n": row.append(cell) cell = "" result.append(row) row = [] else: cell += c # Если ячейка не пуста if cell: # Закрываем ячейку и строку row.append(cell) result.append(row) return result
    Проверяем:
    >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh\n")[['aaa', 'bbb', 'ccc', 'ddd'], ['eee', 'fff', 'ggg', 'hhh']] >>> parse_csv("aaa,bbb,ccc,ddd\neee,fff,ggg,hhh")[['aaa', 'bbb', 'ccc', 'ddd'], ['eee', 'fff', 'ggg', 'hhh']]
    Замечательно.
     
    Почему я проверяю только ячейку, а не строку ещё? Просто пустая ячейка и непустая строка может быть только тогда, когда на конце строки висит запятая. aaa,bbb,. А это явно запрещено по RFC.
     

    В текущем виде в ячейке у нас не получится хранить \n и ,. Если первый символ ещё кое-как, то без запятой как-то совсем не весело, верно?
    На наше счастье, в спецификации есть и это. Ячейку можно поместить в двойные кавычки (", кто не понял), тогда до следующей кавычки обрабатываться \n и , не будут.
     
    Давайте улучшим наш парсер, добавив поддержку этих самых кавычек. Так как у нас посимвольный парсинг, сделать это гораздо проще. Вот так:
    def parse_csv(s): result = [] row = [] cell = "" # Начиналась ли текущая ячейка с кавычки quoted = False for i in range(len(s)): c = s[i] if quoted: if c == '"': # Закрывающая кавычка quoted = False else: cell += c else: if c == '"': if not cell: # Открывающая кавычка в начале ячейки quoted = True else: # Кавычка в середине строки: запрещено return False elif c == ",": row.append(cell) cell = "" elif c == "\n": row.append(cell) cell = "" result.append(row) row = [] else: cell += c if cell: if quoted: # Где-то не закрыли кавычки return False row.append(cell) result.append(row) return result
     
    Проверяем:

     
     
    Всё верно, кроме последнего. В середине строки в закавыченных строках эти самые кавычки должны быть экранированы вот так: "". Например: "aaa""bbb,ccc",ddd,eee. Давайте починим и это.

    def parse_csv(s): result = [] row = [] cell = "" quoted = False # Является ли предыдущий символ кавычкой prevQuote = False for i in range(len(s)): c = s[i] if quoted: if c == '"': # Помечаем, что у нас есть кавычка в середине строки. # Она может быть экранированной. prevQuote = True quoted = False else: cell += c else: if c == '"': if not cell: quoted = True else: if prevQuote: # Если у нас прошлый символ был кавычкой, # то получаем экранированную кавычку. cell += '"' quoted = True prevQuote = False else: return False elif c == ",": row.append(cell) cell = "" # Кавычка была закрывающей prevQuote = False elif c == "\n": row.append(cell) cell = "" result.append(row) row = [] # Кавычка была закрывающей prevQuote = False else: if prevQuote: # Мы ждали кавычку или закрытие ячейки. return False cell += c if cell: if quoted: return False row.append(cell) result.append(row) return result
     
    Опять тестируем:

     
     
    Вот и всё. 44 строки кода на Python — и мы можем парсить CSV.
    Я также переписал парсер на Lua, опубликовал его в OPPM под libcsv. Можете качать и радоваться. Вот сырцы.
     
    Ну и надеюсь, это было менее сложно, чем мои записи про пакетные менеджеры до этого, и вы смогли прочитать это .
  11. Fingercomp
    В прошлой части:

    Вы-то прогу скопировать/разархировать и сами можете, вот только если программа зависит от другой, а та — от двух других, и т. д., вам это надоест. Людям надоело. Создали пакетные менеджеры.  
     
     

    Итак, давайте сделаем программу для установки пакетов. Очевидно, что просто так в рандомном порядке пакеты поставить нельзя: нам надо сначала брать пакеты без неразрешённых зависимостей и подниматься вверх.  
     
     

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

    Зачем нужны версии? Нуууу, наверное, чтобы отмечать разные варианты кода.
    Зачем ПМ нужны версии? Хм, чтобы быть уверенным, что при установке пакета, зависимости будут именно такие, которые были при написании кода.
     
    Ну, то есть. Вчера у нас была одна функция сделатьХорошо() и попала в релиз 1.0.0, а сегодня она переименована в сделатьПлохо(), сменили код этой функции, да ещё впридачу накинули ничегоНеСделать(). Всё это в релизе 2.0.0. Но если новый код будет использовать ничегоНеСделать(), то его версия 1.0.0 не устроит — там ведь функции нет. А если нужно будет сделатьХорошо(), то в версии 2.0.0 её уже не будет.
     
    Люди — существа чертовски изобретательные, так что форматы версий у нас тоже всяко-разно изобретательны. Начиная от даты релиза (20161023) и номером билда (1243, она ещё ревизией зовётся), заканчивая полноценным семантическим версионированием. Первые потуги нас интересовать будут не сильно, а вот про SemVer можно поговорить.
     
    Спецификация SemVer прямо говорит: версия задаётся тремя числами, разделённые точкой. Первое число — мажорная версия, увеличивается, когда происходят "ломающие" изменения типа удаления старых функций, второе — минорная версия, которая на новых фичах обычно увеличивается, а третье — патчи, это всякие багфиксы.
     
    Вот сферическая версия в вакууме: 1.2.3.
     
    На самом деле, в описании semver задано несколько правил, например, место пререлиза (например, 1.2.3-dev), метаданных (например, 1.2.3+build-15+20161023+amd64), ну и т.д. Если интересно — можете почитать, ссылочка в конце.
     
    Так вот, здесь попробуем организовать разрешение зависимостей с версиями. Не будем брать пока очень мудрёную систему конфликтов.
     
    Начнём с манифеста. Дополним его указанием версий:
    { "name": "pkg1", "versions": [ { "number": "1.0.0" , "files": [ { "url": "http://example.com/pkg1/1.0.0/file1", "path": "/opt/pkg1/file1" } , { "url": "http://example.com/pkg1/1.0.0file2", "path": "/opt/pkg1/file2" } ] , "depends": [ { "name": "pkg11", "version": "^1" } , { "name": "pkg12", "version": "1.6.2" } ] } ]}
    Ну и по аналогии с другими пакетами. Вот такое чудо у нас должно получиться.



     

    Напомню, чем у нас закончилась прошлая часть:
    def resolveDeps(name, resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] if name in unresolved: raise ValueError("circular dependencies detected") if name in resolved: return resolved unresolved.append(name) if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: resolveDeps(dep, resolved, unresolved) resolved.append(name) del unresolved[unresoved.index(name)] return resolved
    Давайте перепишем эту функцию так, чтобы она работала после наших изменений в манифесты:
    def resolveDeps(name, resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] if name in unresolved: raise ValueError("circular dependencies detected") if name in resolved: return resolved unresolved.append(name) if not isInstalled(name): manifest = getManifest(name) version, data = getLatestVersion(manifest) # получаем последнюю версию for dep in latest["deps"]: resolveDeps(dep["name"], resolved, unresolved) resolved.append({"name": name, "version": version) del unresolved[unresoved.index(name)] return resolved
    Всё хорошо и замечательно, вот только толку от того, что мы ввели версии, как-то нет совсем.
     

    А вот далее нам потребуется очень серьёзная либа-парсер семверов. На Python есть semantic_version, которую я ещё портировал на MoonScript — очень доволен. Но это так, будем пока плавать на более высоком уровне абстракции.
     
    Итак, версии. Тот самый граф, ещё раз:



     

    Около стрелочек висят какие-то штуки, ^1, например. Эти штуки, которые мы ещё вписываем в манифесты пакетов, ограничивают варианты версий пакетов, которые можно поставить. ^1 говорит, что можно брать любую версию не менее 1.0.0 и не более следующего мажорного релиза (2.0.0). * говорит, что пофигу абсолютно, какая версия встанет. А точное указание версии, как, например, в случае с 1.6.2, не даёт установиться какой-либо другой версии.
     
    Ну а так как они ограничивают, то и называются они ограничениями (или constriants). Пакетный менеджер — скажем так, классическая задача о соблюдении ограничений. Отнюдь не простая.
     
    Раз есть версии, есть ограничения, нужно эти ограничения, значит, включить в функцию разрешателя. Например, дополнительным аргументом. Давайте так и поступим:
    def resolveDeps(name, vconstraint="*" resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] if name in unresolved: raise ValueError("circular dependencies detected") if name in resolved: return resolved unresolved.append(name) # Создаём объект ограничения из строки vconstraint = createSemVerConstraint(vconstraint) if not isInstalled(name): manifest = getManifest(name) version, data = vconstraint.match(manifest) # получаем версию, соответствующую ограничению for dep in data["deps"]: resolveDeps(dep["name"], dep["version"], resolved, unresolved) # и не забываем передавать версию требуемую resolved.append({"name": name, "version": version) del unresolved[unresoved.index(name)] return resolved
    При запуске resolveDeps("pkg1") мы теперь получим
    [ { "name": "pkg1-1-1" , "version": "1.0.1" }, { "name": "pkg1-1" , "version": "1.2.4" }, { "name": "pkg1-2" , "version": "1.6.2" }, { "name": "pkg1" , "version": "1.2.3" }]
    Вот как это на графе будет:



     

    Давайте теперь приспособим функцию install к установке:
    def install(name): depList = resolveDeps(name) for pkg in depList: manifest = getManifest(pkg["name"]) data = getVersion(manifest, pkg["version"]) for file in data["files"]: download(file["url"], file["path"]
    В общем-то, это всё. У нас есть вполне рабочая функция установки пакетов с учётом версии по Semantic Versioning. Однако у неё есть некоторые проблемы.
     

    Ну, во-первых, мы ошибочно считаем, что если пакет установлен, то у него нужная версия. Если у нас уже будет пакет версии 1.12.53, а мы потребуем ^2, то новая версия не поставится. Рискуем попасть на глюки, баги. Кажется, надо просто обновить пакет!..
     
    Но при таком решении невозможно организовать обновление пакетов.
    Вообще. Никак. А почему?
     
    У нас зависимости задаются в версиях. Каждая версия может сменить зависимости. При этом каждая новая версия может не соблюдать ограничения, которые дают зависимые от данного пакеты.
     
    А вот вам адская ситуация:



    Так вот, чтобы обновить выделенный пакет без нарушения всех зависимостей, потребовалось разрешать конфликты версий. Это достаточно сложный алгоритм, и о нём мы поговорим как-нибудь потом. Тем более, мне только предстоит ввести в свой ПМ резолвер конфликтов.
     

    А пока можете почитать спецификацию SemVer.
     
    .
  12. Fingercomp
    Раз уж я тут пишу понемногу свой крутой пакетный манагёр, расскажу о пакетных менеджерах немного.
     
    Пакетный менеджер — штука сложная. Потому что, хотя задача у него, в общем-то, одна — менеджировать пакеты — сюда включается и установка, и удаление, и обновление, и, вообще, много всякого. Но а так как пока сам не напишешь, ПМ не поймёшь, здесь расскажу об установке пакетов и зависимостей с кодом.
     
    Ещё немного предисловий, о зависимостях. Это ключевая фича ПМ: вы-то прогу скопировать/разархировать и сами можете, вот только если программа зависит от другой, а та — от двух других, и т. д., вам это надоест. Людям надоело. Создали пакетные менеджеры. Теперь программы пакуются в пакеты — а рядом со скомпилированными бинарниками лежит ещё кусок информации: имя пакета, версии, зависимости, авторы, изменения и много-много всяких других полей. При установке данные считываются и далее уже делается, что сказано. Зависимости ли ставятся, ещё ли что-нибудь.
     
    А затем пакетов становится много, появляются репозитории полноценные, ну и так далее.
     
    Итак, давайте сделаем программу для установки пакетов. Ну, почти. Именно полезной нагрузки как таковой не будет, будем использовать такую структуру информации о пакете (назовём это манифестом пакета):
    { "name": "имя пакета", "files": [ { "url": "http://example.com/bin-1", "path": "/usr/bin/program1" } , { "url": "http://example.com/library-1.so", "path": "/usr/lib/library1.so" } ]}
    Пока без зависимостей, всё просто.
     

    Вот такой код получим:
    def install(name): # получаем манифест пакета с данным именем manifest = getManifest(name) # проходимся по файлам... for file in manifest["files"]: # ...скачиваем и ставим их в нужные места download(url=file["url"], path=file["path"])
    Ничего примечательного, на самом деле. Получаем манифест, скачиваем файлы и пишем "тадаам".
     

    Давайте сделаем вот такие манифесты:
    { "name": "pkg1" # имя пакета, "deps": # зависимости [ { "name": "pkg1-1" } , { "name": "pkg1-2" } ], "files": [ { "url": "http://example.com/pkg1/file", "path": "/opt/pkg1/file" } ]} { "name": "pkg1-1", "deps": [ { "name": "pkg1-1-1" } ], "files": [ { "url": "http://example.com/pkg1-1/file1", "path": "/opt/pkg1-1/file1" } , { "url": "http://example.com/pkg1-1/file2", "path": "/opt/pkg1-1/file2" } ]} { "name": "pkg1-1-1", "deps": [], "files": [ { "url": "http://example.com/pkg1-1-1/file", "path": "/opt/pkg1-1-1/file" } ] { "name": "pkg1-2", "deps": [], "files": [ { "url": "http://example.com/pkg1-2/file1", "path": "/opt/pkg1-2/file1" } , { "url": "http://example.com/pkg1-2/file2", "path": "/opt/pkg1-2/file2" } ]}
    У нас есть 4 пакета: pkg1, pkg1-1, pkg1-1-1, pkg1-2. Вот граф зависимостей:
     
     
     
     
     
     
     
     
     



     

    Очевидно, что просто так теперь тут в рандомном порядке пакеты поставить нельзя. Так как при установке пакета, например, pkg1-1, он совершенно справедливо считает, что его зависимость, pkg1-1-1, уже установлена.
     
    То есть, по-хорошему, нам надо сначала брать пакеты без неразрешённых зависимостей, и подниматься вверх. Однако, есть идея покруче.
     
    Я сейчас наваяю рекурсивную функцию resolveDeps, которая будет, как ни странно, разрешать зависимости:
    def resolveDeps(name): result = [] # результатирующая последовательность установки пакетов manifest = getManifest(name) for dep in manifest["deps"]: # В Python справедливен код типа `[1, 2, 3] + [4, 5, 6] == [1, 2, 3, 4, 5, 6]`, т.е. склеиваение списков. result = resolveDeps(dep["name"]) + result return result
    Если мы дадим ей манифесты, она выдаст вот такой список: ["pkg1-1-1", "pkg1-1", "pkg1-2", "pkg1"] — от менее сложного к более сложному. То, что нужно. Затем мы ставим просто их:
    for pkg in resolveDeps("pkg1"): install(pkg)
    Давайте улучшим алгоритм. Сделаем проверку на установленность: нам ведь не надо повторно скачивать файлы, которые уже есть.
    def resolveDeps(name): result = [] # Проверяем, установлен ли пакет if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: result = resolveDeps(dep["name"]) + result return result
    Если у нас уже поставлен pkg1-1, то получим всего лишь ["pkg1-2", "pkg1"]. Круто!
     

    Возьмём другой граф:
     
     
     




    Как видно, от pkg1-1-1 зависят сразу 2 пакета: pkg1-1 и pkg1-2. Проблема в том, что на выходе у нас будет ["pkg1-1-1", "pkg1-1-1", "pkg1-1", "pkg1-2", "pkg1"] — ни разу не то, что мы хотели. Давайте это исправим:
    def resolveDeps(name, resolved=None): resolved = resolved or [] # список уже разрешённых пакетов if name in resolved: # Пакет уже был разрешён, ничего больше не требуется return resolved if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: resolveDeps(dep["name"], resolved) # Теперь список один на всю рекурсию resolved.append(name) # Без рекурсии сюда попасть можно, если пакет не имеет неустановленных зависимостей return resolved
    Теперь выхлоп у нас ["pkg1-1-1", "pkg1-1", "pkg1-2", "pkg1"] — как и предписывали.
     

    А вот вам ещё граф:
     
     
     




     

    Какая тут засада? А у нас дерево циркулярное — не руки циркуляркой отрезает, а в бесконечную рекурсию вводит. Вот как можно этого избежать:
    def resolveDeps(name, resolved=None, unresolved=None): resolved = resolved or [] unresolved = unresolved or [] # список ещё не разрешённых пакетов if name in unresolved: # Мы попали сюда через рекурсию. Когда-то пакет уже был добавлен в список unresolved, # после чего функция ушла в рекурсивное разрешение зависимостей этого пакета. # Какой-то из зависимостей в итоге опять имеет данный пакет как зависимость. # Это ошибка, такого быть не должно, паникуем. raise ValueError("circular dependencies detected") if name in resolved: # Пакет уже был разрешён, ничего больше не требуется return resolved unresolved.append(name) if not isInstalled(name): manifest = getManifest(name) for dep in manifest["deps"]: resolveDeps(dep["name"], resolved, unresolved) # даём unresolved resolved.append(name) # Не забываем убирать из списка del unresolved[unresoved.index(name)] return resolved
    Теперь у нас в данном графе будет сгенерировано исключение, а потому рекурсии бесконечной не произойдёт.
     

    Итак, у нас есть простая функция, которая составляет список пакетов для последовательной установки без ломаний. Это уже круто, но время шло, и появилось такое явление как версии. Впрочем, об этом поговорим в другой раз. Там есть свои заморочки, с которыми нужно разобраться.
     
    Вот похожая статья, но на английском. Рекомендую ознакомиться.
     
    Лицензия: CreativeCommons Attribution-NonCommercial 4.0 International License

  13. 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 Будем, как обычно, рады ответить и помочь.
     
    Удачи!
  14. 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 завершился!
     
     
     
  15. Fingercomp
    Имеется один проект на Питоне, который потребовал для себя парсер семантического версионирования, семвера, в общем. Ну там такие штуки как парсинг версий человеческий, обработка ввода юзверёв, умение сравнить все эти версии и выбрать из списка по указателю типа ">=1.5.17", ну, думаю, об этом вы слышали.
    К слову, вот тута лежит спецификация SemVer: http://semver.org/, можете почитать.
     
    Ну и нашёл я либу интересную, semantic_version зовётся. Подключил и нарадоваться не мог фичам всяким. И там пирожки, и здесь пэнкейки, в общем, использовалась весело и радостно.
     
    В рамках того же проекта ещё умещался репозиторий на мункрипте, который мункриптил очень важные дела, и ему тоже нужен был парсер. Без промедления открыл файлик того парсера, и спустя два вечера этот парсер был успешно портирован на Мункрипт, при всём при этом он даже работал! D:
    Ну, как минимум, насколько хватило желания протестить.
     
    Так что вот как-то так у нас всё получилось, я по-быстрому ознакомился с лицензией тех ребят, оформил всё более-менее и пихнул в свою репу на OpenPrograms.
     
    Потому если интересно будет когда-нибудь решить вопрос с версиями, то милости просим использовать либу libsemver, для OC её можно скачать с помощью OPPM: oppm install libsemver.
     
    Документации для либы этой именно я не писал, ибо она работает на 99% так же, как и оригинал. Потому вся инфа здесь: https://python-semanticversion.readthedocs.io/en/latest/
     
    А, ну и мой отзыв по поводу мункрипта: язык этот сумасшедший, но норм, писать можно. Ознакомиться с ним лишним явно не будет, так что осаждайте референс языка.
    Удачи!
  16. Fingercomp
    Недавно вышло мелкое обновление OC, которое я пропустил из-за некоторых проблем, и в нём:
    ДобавленоИнтернет-карты посылают событие, когда они получили данные по сокету и можно использовать :read.
    [*]Пофикшено
    Несколько мелких багов.
    [*]OpenOS
    Небольшие фиксы install, /lib/buffer.lua, ls, /lib/package.lua, rc, /lib/sh.lua.



    Вот и всё действительно небольшое обновление. Скачать можно по ссылкам в блоке справа сверху.
  17. Fingercomp
    Обновление OpenComputers до третьей беты 1.6. Сегодня в гостях у нас следующие изменения:
    Добавлено В русский мануал добавлена информация про saveConfiguration (PR #1855 от cyber01). Функция computer.getProgramLocations, которая используется для программы install из OpenOS.
    [*]Пофикшено
    Неожиданный баг с сохранением мира в версиях MC выше 1.8.
    [*]OpenOS
    Команда cat теперь будет читать stdin, если не указан файл. cp: поддержка путей вида /. и более мелкие фиксы. df теперь поддерживает относительные пути. Функция next либы /lib/guid.lua теперь возвращает GUID в корректном формате. head теперь закрывает stdin. install был сильно переработан и теперь с помощью этой программы можно устанавливать файлы с loot-дискет (и не только), мануал в man install. less теперь поддерживает скроллинг, в кои-то веки! Ну и алиас в /etc/profile был, конечно, убран. В mv добавлено несколько проверок: команда будет выдавать теперь ошибку, если путь назначения в режиме только для чтения или путь имени для копирования — точка монтирования. У rm была пофикшена проблема, при которой ссылки на директории не могли быть удалены. Шелл поддерживает экранировку пробелов и может отдавать сигнал SIGPIPE. А ещё позволяет использовать getWorkingDirectory до установки переменной окружения PWD.



    На момент писания этого текста в install был уже обнаружен один баг, который даже пофикшен, остаётся ждать принятия PR.
     
    Github
  18. Fingercomp
    Начну со слов автора мода: "давайте будем считать, что кандидата к релизу не было. Не потому, что он был сломан, нет. Просто я добавил несколько вещей, которые требуют тестирования, поэтому у нас снова будет бета".
     
    Изменения
    ДобавленоНовая функция computer.getDeviceInfo() теперь возвращает список всех компонентов, имеющихся у устройства, включая планки памяти, процессоры и пр. Для показа их в OpenOS есть теперь команда lshw.
    [*]Изменено
    Большинство "магических файловых систем" у компонентов было перенесено в дискеты. То есть, теперь, чтобы иметь либу lib/internet.lua, например, придётся любую из уже имеющихся стандартных дискет OC (дискету OpenOS, например) в сетке крафта объединять с ключом OC (Scrench), пока не получится нужная дискета, а потом скопировать файлы с дискеты на устройство. Но есть и положительная сторона изменения: те диски стандартные, которые можно было найти только в данжах, теперь могут быть спокойно получены через тот же самый ключ. Теперь игрок не будет в AFK для сервера, если он что-то пишет в мониторах, например. Команда /oc_dn будет теперь выводить дебаг-инфу и в чат выполнившего эту команду. Та самая команда saveConfiguration, которую я внезапно обнаружил некоторое время назад, теперь таки добавлена в мануал.
    [*]Пофикшено
    Контейнеры с жидкостью могли пропадать в апгрейде-генераторе (например, cells из ИК2 с лавой). Роботы могли всасывать вёдра жидкости не из источника её в мире, а из прилегающих "текущих" блоков. Всякие внутренние функции были тоже пофикшены. Потенциальный фикс какого-то бага с серверной стойкой. gpu.setResolution возвращала false, даже если разрешение было изменено успешно. При разборке планшета теперь будет, как и положено, возвращаться с нормальным шансом контейнер апгрейдов.
    [*]OpenOS
    Добавлены devfs. Те самые магические штуки внутри /dev. /dev/null, /dev/zero. Перенаправление I/O. Это не так страшно: myprogram > stdout.log 2> stderr.log. Но объяснять, что это, не буду — кто знает, тот поймёт. Более тысячи (ТЫСЯЧИ) юнит-тестов для OpenOS. Множество мелких фиксов.



    Напомню, что разработка OC 1.6 уже заняла более 1 года и ещё 2-3 месяцев. Список изменений на релизе обещает быть огромнейшим.
     
    Скачать новую версию можно, как обычно, на билд-сервере:
    1.7.10: http://ci.cil.li/view/OpenComputers/job/OpenComputers-1.6-MC1.7.10/lastSuccessfulBuild/artifact/build/libs/OpenComputers-MC1.7.10-1.6.0.4-beta.2-universal.jar 1.8.9: http://ci.cil.li/view/OpenComputers/job/OpenComputers-1.6-MC1.8.9/lastSuccessfulBuild/artifact/build/libs/OpenComputers-MC1.8.9-1.6.0.5-beta.2.jar 1.9.4: http://ci.cil.li/view/OpenComputers/job/OpenComputers-1.6-MC1.9.4/lastSuccessfulBuild/artifact/build/libs/OpenComputers-MC1.9.4-1.6.0.1-beta.2.jar

    Или же на GitHub, если угодно.
  19. Fingercomp
    И лучше поздно, чем никогда. Недели две-три назад вышла версия OC 1.6-RC1 (Release Candidate). В основном ничего особенного, только баг-фиксы.
     
    Изменения:
    Фиксы LuaJ с помощью мастера по починке LuaJ. gamax92, если быть точнее. Plan9k теперь будет хотя бы запускаться. Ну замечательный прогресс уже, хотя крашиться всё так же любит. Switch и Access Point были скрыты в NEI. Поддержка энергии RotaryCraft. Обновление мануала под 1.6. Облегчённые рецепты. Из алмазов делаются алмазные кусочки, которые нельзя потом собрать воедино. Опция для отключения эффектов частиц у нанытов. Работа с жидкостными реакторами IC2 через редстоун-порт. Кабели можно красить в инвентаре, они выпадают крашенными. Краска теперь тратится. Обновлён китайский перевод. Обновлён русский перевод. Фикс ачивки про свитч. Свитча-то нет. Команда /oc_sc спаунит компьютеры лицом к игроку. Баг-фиксы в OpenOS. Фикс краша при подключении нескольких серверных компонентов к одной стороне, когда при этом к ней не подключён сервер. install теперь не будет копировать ещё и файлы по символическим ссылкам. Юнит-тесты.

    В целом OC выглядит уже достаточно стабильным для игры. Новую версию можно скачать только здесь. На GitHub её нет.
  20. 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.
  21. 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 — то же, но для клавиатуры.




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

    Ну тут всё интересно. Пугающая штука теперь — интерфейс стойки.


    А на хотбаре у меня лежат орудия пыток.  

    Думаю, предпоследний предмет опознали — это сервер T3. По нажатию ПКМ этим предметом всё так же открывается интерфейс подобный компьютерному, куда можно вставить компоненты. Заменил я его на креативный, так как я играю в креативе, но уровень не так важен.  

    Кладём три предпоследних предмета в стойку. Видим эту страшную картину.


    Но у нас же вроде гайд, поэтому добавим стрелочек.


    (2) — это сервер креативного уровня. В нём стандартный набор компонентов + инет- и беспроводная сетевая карты.
    (1) — это Server Terminal. Об его функции я расскажу позже.
    (3) — специальный дисководик для серверов. Вместо отдельного чукчёмного блока. Функции абсолютно те же.  

    Сразу скажу, что (6) — это та же кнопка, что и [internal/External] в прошлых версиях, а так как её практическое использование нулевое, я промолчу про её функцию.  

    Справа от слотов для серверов и модулей есть 6 линий разноцветных (7). Под каждой линией есть изображение стороны игральной кости (4), символически обозначающее эту линию. Их расшифровка — (5). Получается, для каждой из пяти сторон стойки (передняя не считается) в интерфейсе отдельная линия.  

    Напротив слотов с предметами на линиях образуются точки (9), (10), .... Они требуются для соединения компонентов для серверов . То есть, подключив сервер (2) и компоненты к нижней стороне в интерфейсе, кликнув по большим точкам на линиях, для сервера (2) становятся доступны Server Terminal (1), Rack Disk Drive (3) и компоненты с нижней стороны. Неожиданно просто.  

    А что же за маленькая точечка (8) напротив сервера? Оказывается, она служит для подключения сетевой карты в сервере к какой-либо стороне. Действует так же, как и в прошлых версиях.  

    Теперь про (1), как и обещал. Если раньше всё было очень просто — берём Remote Terminal, подключаем и просто работаем, то теперь всё плохо.
    Эта штука позволяет подключённому к этой же стороне сервер у работать с удалёнными терминалами. Для этого берём Remote Terminal и делаем им ПКМ по компоненту в серверной стойке. Думаю, опознаете. Если загорится лампочка на компоненте в стойке — всё ОК.
    Если же тратить ресурсы на эту штуку не хочется, достаточно просто от указанной стороны компонентов провести кабель к монитору и клавиатуре.  

    "Эм, а как включить сервер?" Теперь всё управление ими ведётся через ПКМ по серверу в стойке. Щёлкаем и можем включить сервер, потушить его и даже сменить компоненты во время работы!  

    Кстати, о дисководах. В него и в дисковод обычный можно вставлять и изымать дискеты через Шифт-ПКМ. Очень удобно.  
     
    Теперь сходим к роботу, так как в OpenComputers появился новый апгрейд: торговый.

    Торговый апгрейд для робота — апгрейд второго уровня, при подключении предоставляет компонент "trading" .
    У него всего одна функция — trading.getTrades() , возвращающая таблицу предложений жителей в радиусе 8 блоков от робота. Каждый элемент представляет собою одну сделку одного из жителей. Структура: {getInput = function():table, table, getOutput = function():table, isEnabled = function():boolean, trade = function():boolean[, string]}

    Функция getInput() возвращает таблицы с описанием необходимых предметов. По сути, это то же описание, что возвращает контроллер инвентаря — метаданные, имеет ли нбт-теги, имя предмета, его айдишник, максимальное повреждение, размер стэка и количество предметов, необходимых для торговли. Если второй предмет не требуется для торговли — вторая таблица будет равняться nil .
     

    Функция getOutput() действует по схожему с предыдущим принципу, только возвращает таблицу с описанием выходного предмета.
     

    Функция isEnabled() возвращает, интересна ли эта сделка на текущий момент жителю. Как известно, после 7 сделок она блокируется. Для разблокирования надо совершить другую сделку с этим же торговцем.
     

    Функция trade() , наконец, совершает сделку. Её условия: в инвентаре робота должно быть достаточное количество предметов для сделки, а предложение должно быть активно. Если всё верно, предметы обмениваются в инвентарь робота.
    Ошибки:
    false, "not enough items to trade" — в инвентаре робота недостаточно предметов для торговли.
    false, "trade is disabled" — житель более не заинтересован в этом предложении (было совершено 7 сделок).
     
    Кроме того, ещё одно мелкое изменение — для дисковода появился собственный компонент "disk_drive". Он есть только у Rack Disk Drive и Disk Drive, но не во встроенных в компьютер.

    Функция isEmpty() возвратит статус дисковода — есть ли в нём диск.  

    Функция eject([velocity]) выплюнет диск из дисковода. Если дать как аргумент число (числа более 1 смысла не имеют, так как эффект тот же), диску передастся определённая скорость.
    Вот пример для максимальной скорости:
     
    Ещё из изменений — интернет-карта.

    Функция request() принимает третьим опциональным аргументом таблицу хедеров. Например, {["Accept-Encoding"] = "application/json"} . Это очень крутое изменение — так, для работы с чатом форума с OpenComputers теперь нет никаких технических преград. А ещё можно наконец-то запилить логин на сайты... Ах, применений много.  
    Для модняков. Если дронотапки совместить с красителями, как кожанку, то неон на них покрасится.


    Для смытия краски достаточно кинуть тапки в ванильный котёл с водой, как кожаную броню.


    Если у меня хватит духу написать вторую часть, то, скорее всего, я начну рассказывать об изменениях в OpenOS 1.6. Ибо материала там тонны.
    Пока что не забудьте проголосовать в опросике сверху. Порадуйте диванных аналитиков.
  23. 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. Там мегатонны всяких изменений. И, кстати, эта система грузится на планке памяти первого уровня. Даже несмотря на новые проги, либы и пр. Дронотапки можно красить. Новый шрифт. Логирование чанклоадеров. Ну и множество других изменений.

    Качайте новую версию тут и крушите её. А обо всех найденных багах, как обычно, сообщать сюда.
  24. 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/
  25. Fingercomp
    Что-то особо нового в последнее время не виднеется, а программы не пишутся. На первый взгляд.
    Но, как известно, перед революцией обычно образуется что-то вроде застоя. Предлагаю окунуться в это болото и глянуть, что интересного происходит с OpenComputers и аддонами.
     
    OpenComputers
    Сэнгар немного прихворал, так что коммитов в последнее время немного. Идёт работа над портом 1.6 под версии MC1.8, делают OpenOS 1.6 (псст, эта игрушка обешает быть очень крутой; больше POSIX и пр.). Низкая активность на баг-трекере, в основном, всякие фич-реквесты.
    В скором будущем расскажу, что именно изменилось в 1.6.
    А пока что здесь можно загрузить на поиграться последнюю версию OC. GitHub.
     
    Computronics
    MOAR FEATURES
    Кхм. Ну, значит, бип-карту на прокачку взяли.
    1 tier: простая beep card; 2 tier: возможность указать одну из четырёх волн (синусоидную, квадратную, треугольную или пилообразную); 3 tier: в разработке, в финальном виде должна позволить создавать собственную волну. В общем, ADSR. Вот это обещает быть бомбочкой.

    GitHub. Dev-версии.
     

    OpenSecurity
    Починены турельки, очередь кейпадов. Добавлены небольшие (7 символов) экранчики на них и возможность менять символы на кнопочках.
    GitHub.
     
    Форумы
    Очень интересная программа: ServerFS. Вкратце: сетевые диски. Подробнее: имеем серв, подключаем к нему RAID и ставим tunnel/modem. На другом же компе свободно подключаемся к серверу. В /srv будут примонтированы все эти диски.
×
×
  • Создать...