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

Лидеры


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

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

  1. 5 баллов
    Думаю будет полезно https://gitspartv.github.io/lua-patterns/
  2. 4 балла
    PlayerMonitoring - программа мониторинга посещения игроками некой области. Это может быть ваша база или какое-то другое интересующее место. Позволяет сканировать область на наличие игроков раз в N секунд выводить список игроков на монитор выводить список игроков на гист уничтожает gist id и токен авторизации гитхаба при приближении опасных игроков ближе чем на 20 блоков - чтобы сохранить логи поддерживаются радар из Computronics и детектор сущностей из OpenSecurity Github: https://github.com/hohserg1/OpenComputersPrograms/tree/master/player-monitoring Видео-демонтрация Текстовая инструкция по установке 1. Это программа для HoverHelm, поэтому сначала нужно установить HoverHelm-сервер (pastebin run xh61Yx8a) 2. Далее установите сканирующее устройство(их можно сделать несколько) Его минимальная конфигурация(на основе сервера 1 тира): 3. Подготовьте для него eeprom (командой prepare_eeprom <device name> modem <port>) и убедитесь, что устройство коннектится к серверу 4. После скачайте radar.lua (wget https://raw.githubusercontent.com/hohserg1/OpenComputersPrograms/master/player-monitoring/radar.lua radar.lua) radar.lua должен лежать в папке programs конкретного устройства 5. В начале файла есть секция конфигурациии, настройте в ней gistId и githubToken, если хотите выгружать логи на гист. Подробнее про конфигурацию гиста: 6. Запустите программу из терминала HoverHelm командой your device name>radar Также можно настроить автозапуск. Для этого: 7. Скопируйте дефолтный конфиг из /home/hoverhelm/device_core/lib/config.lua в /home/hoverhelm/devices/<device name>/lib/config.lua 8. Установите опцию autorun в конфиге устройства на значение "radar"
  3. 3 балла
    Если не ошибаюсь, существует шаблон для шестнадцатеричных символов - %x
  4. 3 балла
    Так а чо, пройдись регуляркой по этой стринге, и всё: str = "Meow u0422u0435" str = str:gsub("u(%d+)", function(code) return utf8.char(tonumber(code, 16)) end) -- Meow Te Если дискордыч всегда выдаёт 4 знака после "u", то разумнее будет воспользоваться паттерном "u(%d%d%d%d)" для более корректного декодирования ситуаций, когда после экранируемой последовательности следует обычная цифра, не входящая в неё. Например, "Meow u042215" (где 15 - просто текст)
  5. 3 балла
    Банк сейчас вживую не пощупать, хотя его и можно построить по этому гайду Одной из уникальной фитч был прогресс бар для покупки энергии созданный @Totoro Тут и тонкие линии за счёт юникода + фон и тень на прогрессе, также мышкой можно менять значение позиции Хочу поделится прогресс баром @Totoro отдельно от банка https://pastebin.com/DzJvr7xF Добавил скролл мышкой и стрелками клавиатуры + мелкие правки при отрисовке мин и макс значениях Также можно указать шаг заполнения линии за одно нажатие стрелки или поворот колёсика мыши
  6. 2 балла
    Решил я вдруг написать дискорд-клиент на основе интернет карты, гуи библиотеки @Zer0Galaxy и json либы на гитхабе. Спустя долгое время разработки я понял, что совсем ничего не умею, а проблемы появились даже на стадии вывода. Вообщем я решил выложить сюда свои наработки, так как то, что уже написано работает нереально медленно и через раз, а сохранить и показать задумку хочется. Вкратце - это дискорд клиент, работающий на реквестах и без веб-сокетов(что и стало основной причиной того, что я это забросил), который умеет авторизовываться, выводить сервера, каналы и криво-косо выводить последнее сообщение в них Также возникла проблема с русскими символами, вместо них оно выводит Unicode Escape Sequence в виде "u0422u0435" Ссылку оставляю тут, делайте что хотите, надеюсь кто-нибудь поддержит задумку и я возьмусь за написание веб-сокетов https://pastebin.com/Zfn7YCqT Использовалось: https://gist.github.com/tylerneylon/59f4bcf316be525b30ab
  7. 2 балла
    Что мы имеем по результатам обсуждения в дискорде: • 16 TPS это именно 16 TPS. Тикрейт на сервере не повышенный, а пониженный. И не специально, а по причине лагодромов. Соответственно, тикрейт может только замедлять чудо-схему с предполагаемой скоростью переработки в 20 блок/сек. • Предполагается, что чудо-схема отличается от описанной в теме лишь программным кодом, что и делает того программиста "афигенным". Но что там за волшебный код, мы не знаем. • Лагодромщики, пытаясь экономить на дорогих инструментах, используют асинхронно работающих роботов в своих схемах и творят прочую дичь. В результате администрация сервера ограничила количество зарядников до одного на чанк. Этот подход не гарантирует отсутствия лагодромов, но усложняет их масштабирование. • Из предыдущего пункта следует вывод: какие бы способы масштабирования схемы мы в этой теме ни реализовали, они окажутся публичными, и рано или поздно администрация сервера начнёт борьбу уже с ними. Поэтому отметаем такой способ увеличения производительности, или делаем это втихаря среди узкого круга посвящённых. Я до конца не уверен, является ли заявление автора розыгрышем, или же он сам стал жертвой розыгрыша. Но я попытался исследовать тему настолько глубоко, насколько хватило опыта: Самым узким местом обсуждаемой схемы является установка блоков. Я знаю только два способа ускорения этой операции: правка конфигов или увеличение тикрейта. Правка конфигов исключена, т.к. она ускорила бы операцию установки блоков роботами на всём сервере для всех игроков, чего не наблюдалось. Глобальный тикрейт оказался даже ниже стандартного, но не исключена возможность локального увеличения тикрейта с помощью модов, например, ProjectE. Аргейд опыта также не может ускорить установку блоков роботом. Поэтому при стандартных настройках теоретически максимально возможная скорость установки блоков составляет 2.5 блок/сек. Скорость добычи блока можно повышать чарами на эффективность и апгрейдом опыта. Но пределом является скорость 6.66 блок/сек. На добычу блока роботом тратится минимум три тика (0.15 сек), даже если добывается блок грязи максимально эффективной киркой и с полностью прокачанным апгрейдом опыта. Для того, чтобы скорость установки блоков превзошла скорость их добычи, необходимы три робота на установку блоков. Три робота могут устанавливать блоки со скоростью 7.5 блок/сек. Но эта скорость будет ограничена добывающим роботом. Выше 6.66 она не поднимется. Исходя из этого простой вариант схемы мне видится таким: Вокруг зарядника стоят 4 робота лицом к заряднику. Под одним из роботов расположен энергохранитель для заряда инструмента. За спинами роботов стоят интерфейсы для загрузки руды и выгрузки полученных материалов. В этом положении роботы подзаряжаются, заряжают инструмент и ожидают пополнения запаса руды в своих инвентарях. По команде роботы поднимаются на один блок вверх, трое из них выставляют блоки руды, а четвёртый рубит. По завершении переработки порции руды роботы возвращаются на исходную позицию, делая шаг на один блок вниз, и весь цикл повторяется с начала, если остались ресурсы для переработки. Плюсом этой схемы является экономия на дорогом инструменте. Плюс сомнительный, т.к. при наличии апргрейда опыта иридиевый бур выполняет работу не хуже вадржы. Апргдейд опыта даже алмазную кирку с эффективностью 4 уровня делает не хуже ваджры. Теперь попробую оценить эффективность расходования ресурсов сервера этой схемой. В схеме с двумя роботами робот-установщик выполняет одно действие каждые 8 тиков. Робот-добытчик на протяжении этих 8 тиков делает один успешный взмах киркой за 3 тика, а оставшиеся 5 тиков тратит на бесполезные взмахи киркой. Получается 5 бесполезных действий на каждые 8 тиков или на каждый обработанный блок руды. В схеме с тремя роботами-установщиками непрерывно работает только робот-добытчик. Если сервер не лагает, то каждый взмах кирки успешен. Роботы-установщики рано или поздно самосинхронизируются, но для поддержания синхронизации роботы выполняют избыточные действия. Для непрерывного обеспечения робота-добытчика блоками каждый из трёх роботов-установщиков должен устанавливать блоки со скоростью 2.22 блок/сек (с интервалом 9 тиков). На успешную установку блока робот тратит 8 тиков. Оставшийся тик тратится на неудачную установку. Это значит, что робот выполняет одно бесполезное действие на каждый блок руды. Эффективность новой схемы также будет плюсом в сравнении с изначальным вариантом. Казалось бы, это успех, но нет. Схема с четырьмя независимыми роботами всё равно более эффективно расходует ресурсы сервера. Каждое выполненное действие ведёт к успеху. На каждый обработанный блок руды тратится одна установка блока и одна добыча. Заодно и роботов никуда не надо двигать. Вместо одного MFSU потребуется установить 4 MFE для зарядки инструмента. А для экономии на инструменте роботам вместо ваджры можно выдать иридиевые буры и апгрейды опыта, раскачанные до 25 уровня. И ещё важный момент: я ошибся в порядке чисел. robot.select требует на выполнение не 10 тиков, а 1 тик. Поэтому, оптимизировав её использование, мы сможем получить ускорение не в 2.2 раза, а всего в 1.12 раз. Впрочем, и 12% это тоже хорошо. Но разумнее будет получить выигрыш в 44%, перейдя на схему с независимыми друг от друга роботами.
  8. 2 балла
    Поступила жалоба на оффтоп в этой теме. Да, оффтоп уже развился, но я не знаю, как с ним правильно поступить. Пусть пока остаётся как есть. У автора темы очень специфические заказы. Их сложно обсуждать в отрыве от темы лагодромов. 16 TPS на сервере это ненормально. Каков вклад в лаги сервера со стороны автора темы, это вопрос дискуссионный, но обсуждение желательно вести с доказательствами и какими-нибудь методиками измерения. Надо выяснять, что именно там сильнее всего нагружает сервер. Сообщество computercraft.ru не заинтересовано помогать строительству лагодромов. Чем больше лагодромов будет создано с помощью компьютеров, тем меньше будет шансов встретить OpenComputers и ComputerCraft на серверах Майнкрафта. Такая перспектива не радует. Но сообщество заинтересовано помогать лагодромщикам в поиске схем, более эффективно расходующих ресурсы сервера. Если на производство какой-нибудь лепёшки можно затратить 2 действия вместо 10, то пусть лагодромщик выберет более экономный вариант. Тогда компьютерные моды будут меньше раздражать администраторов серверов. Предлагаю сосредоточиться на оптимизации программ, если кому интересно. А судьбу 300 спартанцев реакторов пусть решает администрация сервера.
  9. 2 балла
    Программа умеет управлять одним транспозером, для перемещения определенных предметов между инвентарями. Предметы можно перемещать/не перемещать проверяя их unlocalized name и meta. При запуске программы откроется графический интерфейс, в котором отобразится ID компонента транспозера, доступные инвентари, их названия, стороны, количество слотов ... Управление в программе: Для работы программы необходимо создать один или несколько правил (В программе они называются фильтрами. filters). Новые правила создаются нажатием клавиши INSERT на клавиатуре. При нажатии на эту клавишу вы попадете в меню настроек только-что созданного фильтра, в котором вы сможете настроить как именно, что и куда надо перемещать, используя данный фильтр. Виды полей и их описание: Алгоритм сортировки: Программа последовательно проходится по всем фильтрам в порядке их ID и пытается вытащить предмет из инвентаря источника (input side, input slot) и положить его в инвентарь приемник (output side, output slot), попутно проверяя совпадает-ли unlocalized name предмета с фильтром, meta с фильтром, если совпадает, то необходимое количество предмета перекладывается в инвентарь-приемник. Программа переходит к следующему фильтру. Ссылка на pastebin: https://pastebin.com/YQdYSa77 При корректном выходе из программы (С помощью BACKSPACE) программа сохранит конфигурацию в файл в корне файловой системы /filter.txt. Удаление этого файла сотрет конфигурацию. Буду благодарен за конструктивную критику по поводу качества кода. Скрины:
  10. 2 балла
    Конкретно на этом участке быстродействие кода не критично, а поле для оптимизаций в программе пока ещё не исчерпано. Но пусть будет. Это зависит от того, что именно для нас хорошо. С точки зрения быстродействия лучше вообще не создавать и не вызывать функции. И локальные переменные внутри часто вызываемых функций тоже лучше не создавать. Но читать и дорабатывать такой код будет не просто. Хороший код всегда является результатом компромисса между быстродействием, потреблением памяти и простотой обслуживания кода. Баланс определяется условиями задачи. Поэтому говорить о качестве кода в отрыве от условий задачи бессмысленно. Да, смысл есть, это полезный приём, позволяющий упростить код. А долго ли... Зависит от выбора единицы измерения, из которых наиболее адекватной в большинстве случаев является процент времени, затрачиваемый на выполнение участка кода по отношению к содержащей его функции – либо непосредственно функции, содержащей этот код, либо вызывающими её функциями вплоть до всей программы целиком. Скорее всего, нет. Это надо проверять в каждом конкретном случае. В общем же это выглядит так: Обращение к локальным переменным требует меньше времени, чем к глобальным переменным или же полям таблицы. Поэтому локальные переменные позволяют увеличить быстродействие в большинстве случаев. Но локальные переменные требуют времени на их создание и уборку мусора. И если функция, содержащая локальную переменную, вызывается слишком часто, то эти накладные расходы могут превысить затраты на обращение к глобальной переменной или полю таблицы.
  11. 1 балл
    @hohserg Вот, компьютер собирал самый мощный
  12. 1 балл
    До тестирования я не добрался, но почитал код. Есть замечания и вопросы: Функцию сканирования инвентаря можно немного ускорить, отказавшись от выполнения getStackInInternalSlot(slot) для заведомо пустых ячеек. Наличие предмета в слоте помогает определить функция robot.count(slot), и работает она очень быстро. И только уже зная, что слот не пуст, появляется смысл запрашивать getStackInInternalSlot(slot). В остальных случаях получаем экономию в один тик на каждый пустой слот. robot.inventorySize() вызывается больше одного раза, каждый раз тратя на это один тик времени. Такое решение полезно для случая съёмных апргейдов инвентаря. Но удобнее всё же сделать несъемные. Тем более, автор заказа не ограничен в материалах. Ему под силу сразу скрафтить нужного робота. При поступлении сигнала inventory_changed программа заново сканирует инвентарь, тратя на это 3.2 сек при 64 слотах в инвентаре робота, хотя достаточно обработать лишь те слоты, для которых поступил сигнал изменения. При обнаружении отсутствующего компонента в инвентаре робота необязательно тратить время только лишь на ожидание его появления. Достаточно сообщить игроку список отсутствующих компонентов и продолжать заполнение, возвращаясь к пропущенным слотам позже. У на же есть контроллер инвентаря, поэтому вместо robot.drop() можно использовать component.inventory_controller.dropIntoSlot(side,slot,count), помещая предмет строго в нужный слот. Или на сервере какие-то особенности приватов не позволяют так обращаться с внешними инвентарями? Если использовать dropIntoSlot допустимо, то заранее сканировать инвентарь робота имеет смысл только для того, чтобы сообщить об отсутствующих игроку компонентах. А если сообщение игроку поступает ровно в момент попытки поместить компонент в реактор, то и сканировать слот инвентаря можно непосредственно перед этим. Обнаружение изменения инвентаря сейчас реализовано проверкой на 0 значения robot.count(slot). Это работает только для случая извлечения предмета из слота. Но игрок может произвести замену предмета. В этом случае программа рискует поместить в реактор не тот компонент. Зачем функция set сделана рекурсивной? В чём удобство именно рекурсивной функции?
  13. 1 балл
    Для полной уверенности придётся проверять и статус операции отправки в реактор, а сразу после отправки вдобавок ещё проверить, не поступили ли за это время какие-то лишние события по этому слоту. Или можно тупо проверить нужный слот в инвентаре реактора. Это, конечно, немного замедлит работу, но обеспечит полную устойчивость алгоритма без его сильного усложнения.
  14. 1 балл
    Для начала рекомендую почитать об отличиях прямых вызовов от непрямых. Статья полезная, но даже если не знать теорию и названия, всегда можно обнаружить закономерности экспериментальным путём. И я сейчас попробую это продемонстрировать. Есть функция computer.uptime(), она возвращает время работы компьютера в секундах. Но время всегда пропорционально одному тику. Вычисляя разницу во времени, прошедшем от начала и до конца измерений, можно измерить скорость выполнения операций. Грубо, в тиках, но можно. Выполняя операции массово, в цикле, можно обнаружить, что некоторые операции всегда требуют целого количества тиков. Количество затрачиваемых тиков на некоторую определённую операцию всегда одинаково, и может задаваться в файле конфигурации. Это непрямые вызовы, и они замедлены намеренно. Также можно заметить, что во время лагов не только сервер снижает TPS ниже стандартных 20, но и вызовы периферии могут выполняться за большее количество тиков в сравнении с заданными значениями. Также существуют вызовы прямые. Они не отличаются от вызова обычных функций, и время затрачиваемое на их выполнение, очень мало. Какого-то фиксированного времени для их выполнения не существует. Время определяется быстродействием сервера и его текущей нагрузкой. А ещё есть не особо прямые вызовы. По терминологии, наверное, может проконсультировать @Fingercomp. Время, затрачиваемое на эти вызовы, не кратно размеру тика, но количество таких вызовах на протяжении тика ограничено некоторыми предельными значениями. Теперь практика. По счастливому стечению обстоятельств я не закрыл документ с текущими записями по этой теме. Записи такого вида я обычно не сохраняю в файлы, а веду их только для удобства вставки скриптов в консоль робота. Текущее содержимое документа (комментарии добавлены сейчас): -- Ссылка на получение времени time=computer.uptime -- Тестирование затрат времени на установку блока и его рубку -- Я уже знаю, что это непрямые вызовы, поэтому не использую сложных измерений -- Для исклчюения влияния случайных лагов повторяю выполнение скрипта раза три t0=time() robot.place() print(time()-t0) t0=time() robot.swing() print(time()-t0) -- Это пробная версия скрипта, рубящего руду -- Именно этот скрипт я выкладывал где-то в ранних постах этой темы t0=time() robot.select(1)while robot.suckUp() do for i=1,64 do robot.place()robot.swing()end for slot=4,1,-1 do robot.select(slot)robot.dropDown()end end print(time()-t0) -- Тут я проверял алгоритм выгрузки результатов переработки for slot=4,1,-1 do robot.select(slot)robot.dropDown()end -- Так я обычно пишу несложные программы -- Ничего не сохраняю в файлы, пишу скрипт в текстовом редакторе, -- а потом сворачиваю до одной строки для удобства копирования в консоль робота -- Самые простые скрипты сразу пишутся в одну строку ---------- рубщик руды time = computer.uptime while not robot.detect() do end t0=time() for i=1,64 do while not robot.swing() do end end print( time()-t0 ) time = computer.uptime while not robot.detect() do end t0=time() for i=1,64 do while not robot.swing() do end end print( time()-t0 ) ---------- установщик руды time = computer.uptime t0=time() for i=1,64 do while not robot.place() do end end print( time()-t0 ) time = computer.uptime t0=time() for i=1,64 do while not robot.place() do end end print( time()-t0 ) ---------- -- Похоже, вызов robot.count непрямой, операция заняла 0 тиков t0=time() robot.count(1) print(time()-t0) -- И сейчас 0 тиков t0=time() for i=1,1e2 do robot.count(1) end print(time()-t0) -- А сейчас 1 тик t0=time() for i=1,1e3 do robot.count(1) end print(time()-t0) -- Ну, ясно, быстрая операция. -- А как быстро выполняется выбор слота в роботе? t0=time() robot.select(1) print(time()-t0) -- И т.д... t0=time() robot.drop() print(time()-t0) t0=time() component.inventory_controller.getStackInInternalSlot(1) print(time()-t0) Полученная разница во времени не всегда кратна ровно размеру тика по причине погрешности вычислений. Но округление решает эту проблему. Для полной ясности можно сразу считать целые тики: print( ((time()-t0)/0.05+0.5)//1 ) Но мне обычно лень писать такой код во время разовых исследований. Да и есть шанс пропустить какое-то значимое расхождение с размером тика, превышающее погрешность вычислений. В программах я обычно использую что-то вроде такого: (dt*1e6+0.5)//1/1e6
  15. 1 балл
    Тогда это не заказ, а квест: пойди туда, не знаю куда, принеси то, не знаю что.
  16. 1 балл
    Для тех, кто решится повторить подвиг, преобразование шифра в текст работает вот так local function decode(text) local text, line = text:gsub("u(%x+)", function(code) return unicode.char(tonumber(code, 16)) end) return text end Много вариантов фильтров перепробовал, но это самое адекватное, правда там символы перехода отображаются, но это уже ерунда. Наверное
  17. 1 балл
    жуть какая, а потом жалуются что сервак лагает ( по поводу улучшения проги можно избавится от сотни операторов or используя цикл и таблицу с шаблонами, (возможно это немного поможет) также добавит универсальности, используя шаблон можно будет расставлять различные схемы одной прогой одной из проблем является то, что робот не может произвольно ложить компоненты в реактор, только последовательно
  18. 1 балл
    высокий тикрейт и возможность крайне быстро выборочно выгружать ресы из чего либо
  19. 1 балл
    Насколько очень? Какие имеются ресурсы для построения схемы? Сколько роботов допустимо задействовать в этой схеме? Насколько дорог инструмент для рубки руды в сравнении с роботами? Какова доступная для реализации схемы площадь или объём? Схема перерубки руды на двух роботах имеет смысл лишь в случаях использования очень дорогих или редких кирок. В противном случае предложенная схема оказывается неэффективной, и появляется смысл реализовать схему на одном роботе. Или на двух, но с одинаковой программой. Или даже на сотне или тысяче одинаковых роботов, если сервер справляется с нагрузкой, и другие игроки не возражают. За какое время требуется перерабатывать такое количество руды? Также возникает вопрос, зачем копить такое количество руды, и затем стоять AFK, если руду можно перерабатывать постепенно, по мере её накопления, параллельно занимаясь другими задачами? Или даже по мере добычи руды, поставив такую схему рядом с буровой установкой.
  20. 1 балл
    Ради чего? Если новая тема будет посвящена очередному этапу разработки, то нет, создавать новую тему не стоит. Если же в новая тема будет содержать описание готовой программы и ссылку на рабочий код, то это имеет смысл. Предлагаю пока что продолжить обсуждение в этой теме. А новую тему создать, когда уже появится что-то пригодное к использованию.
  21. 1 балл
    Порылся в коде и обнаружил, что событие onChange принимает три параметра: 1)выбранная строка списка(string), 2)элемент, соответствующий этой строке(любой lua-тип) и 3)имя пользователя, выполнившего клик(string). Поэтому элемент можно достать, например, так: function onListChange(line,item,user) local selectedItem = item end List1=Form1:addList(left,top,onListChange) Кроме того, выбранный элемент списка всегда (не только из onChange) доступен как List1.items[List1.index]
  22. 1 балл
    Теперь я предлагаю разобрать таблицу inventories и код, работающий с ней. Сейчас эта таблица строится при запуске программы таким образом: local function initInventories() for i = 0, 5, 1 do local name = transposer.getInventoryName(i) local size = transposer.getInventorySize(i) if name then table.insert(inventories, newInventory(i, name, size)) end end end Здесь я бы предпочёл избавился от вызова функции newInventory. Хотя быстродействие этого участка кода и не критично, но оно немного увеличится. А главное, упростится обслуживание кода спустя месяц-другой: не потребуется искать функцию newInventory, чтобы вспомнить детали её работы. Необходимые детали окажутся в месте непосредственного их использования: table.insert(inventories, { side = side, name = name, size = size, stacks = {} }) Есть и не столь очевидная оптимизация. Бегло изучив код, я предполагаю, что более эффективно себя покажет другой формат таблицы. И строить её следует так: local function initInventories() for side = 0, 5 do local name = transposer.getInventoryName(side) if name then inventories[side] = { name = name, size = transposer.getInventorySize(side), stacks = {} }) end end end Быстродействие этого участка кода увеличивается незначительно, но для сохранения работоспособности программы вслед за ним потребуется переписать и другие участки, новые варианты которых могут исполняться как более, так и менее эффективно. Для оценки общего изменения эффективности требуется более подробный анализ. В дальнейшем таблица inventories используется в следующих функциях: В функции drawInfo текущий код for i = 1, #inventories do local inventory = inventories[i] gpu.set(1, i + 9, inventory.side .. " : " .. inventory.size .. " : " .. inventory.name) end будет немного усложнён и замедлен: local i = 0 for side = 0, 5 do if inventories[side] then i = i+1 gpu.set(1, i + 9, side .. " : " .. inventories[side].size .. " : " .. inventories[side].name) end end Но содержащая его функция drawInfo вызывается один раз при запуске программы, а также используется в функции drawAdditional, которая вызывается лишь во время редактирования фильтров пользователем, что должно происходить редко. В функции getStacks текущий код for i = 1, #inventories do local inventory = inventories[i] inventory.stacks = transposer.getAllStacks(inventory.side) end также будет усложнён и замедлен: for side = 0, 5 do if inventories[side] then inventories[i].stacks = transposer.getAllStacks(inventory.side) end end Вызывается эта функция один раз при запуске программы, и далее каждый раз в главном цикле программы, то есть часто. Это уже неприятно и заставляет сомневаться в правильности смены формата таблицы inventories. В функции getInventoryBySide текущий код for i = 1, #inventories, 1 do local inventory = inventories[i] if inventory.side == side then return inventory end end будет сильно упрощён и ускорен: return inventory[side] Благодаря этому от вызова функции getInventoryBySide можно будет полностью отказаться. Количество вызовов функции getInventoryBySide в основном цикле равно удвоенному количеству фильтров. Даже если фильтр всего один, такая оптимизация уже позволит снизить общую нагрузку, создаваемую программой. По совокупному снижению нагрузки я считаю данную оптимизацию полезной. Также, если есть желание максимально повысить быстродействие программы, пожертвовав потреблением памяти, можно сформировать сразу две таблицы. Одну с номерами рабочих сторон транспозера для упрощения их перечисления, где это требуется, и вторую таблицу с ассоциативным доступом к нужной стороне транспозера без необходимости использовать перечисление.
  23. 1 балл
    Начну, с loadProperities, функции чтения конфигурации. В ней несколько раз встречаются строки вида string.byte(bytes, ...) и string.sub(bytes, ...). Я предлагаю использовать синтаксический сахар, предоставляемый Lua для более компактной записи: bytes:byte(...) и bytes:sub(...). На поведении программы это не отразится, но код станет светлее, что благоприятно скажется на его чтении. Там же встречается такая строчка: (enabled == 1 or true and false). В использованном контексте брать это выражение в скобки не обязательно, а с точки зрения замусоривания кода лишними символами – вредно. А кроме того, результатом выполнения выражения enabled == 1 уже является true или false, поэтому вся эта длинная запись приводит ещё и к бесполезному дублированию вычислений. С тем же успехом для булевой переменной var можно было бы написать длинное if var then return true else return false end вместо лаконичного return var Такой подход приводит к раздуванию кода на пустом месте. Поэтому, учитывая сказанное выше, строчку сокращаем до enabled == 1. Зато в функции saveProperities эта конструкция облегчит код: local enbd = 0 if filter.enabled then enbd = 1 end до local enbd = filter.enabled and 1 or 0 Да и к слову, зачем нужна переменная enbd, если её значение используется лишь один раз, и можно было сразу писать такой код: local enabled = string.char(filter.enabled and 1 or 0) local input_side = string.char(filter.input_side) ... Также я бы предпочёл избавиться от длинной последовательности string.char, осветлив код, заодно уменьшив количество вызовов функции char и количество переменных, заменив этот код local enabled = string.char(enbd) local input_side = string.char(filter.input_side) local output_side = string.char(filter.output_side) local input_slot = string.char(filter.input_slot) local output_slot = string.char(filter.output_slot) local name_length = string.char(#filter.name) local name = filter.name local metadata = string.char(filter.metadata) local count = string.char(filter.count) bytes = bytes .. enabled .. input_side .. output_side .. input_slot .. output_slot .. name_length .. name .. metadata .. count на более короткий и эффективный вариант: bytes = bytes .. string.char( enbd, filter.input_side, filter.output_side, filter.input_slot, filter.output_slot, #filter.name ) .. filter.name .. string.char( filter.metadata, filter.count) Такой код не только требует меньше действий, но и выглядит понятнее, на мой вкус. Есть ещё пара строк в этой функции, за которые цепляется глаз: for i = 1, #filters, 1 do local filter = filters[i] Их хочется заменить одной строкой: for i, filter in ipairs(filters) do Такой код легче читается. Быстродействие этого участка кода, скорее всего ухудшится, что вряд ли сыграет какую-либо роль конкретно в этом месте. Вот примерно так можно улучшить функции чтения и записи конфигурации. Для дальнейшего улучшения можно было бы сменить формат файла, но это улучшение будет относительным. Сейчас выбран двоичный формат хранения данных. Его преимущество в компактности хранения. Зато некомпактен код работы с файлом конфигурации. В данной программе я бы предпочёл хранить конфигурацию в виде сериализованной таблицы. Это сильно упростит код загрузки и сохранения конфигурации, позволяя не думать о деталях. Заодно файл можно будет просматривать и редактировать простыми текстовыми редакторами, что может оказаться даже более удобным, нежели редактор, встроенный в программу. Сам файл конфигурации, правда, увеличится в размерах. Но вряд ли его размер будет играть существенную роль. Предлагаю рассмотреть этот вариант, хотя и совершенно не настаиваю на нём. А если вообще отказаться от встроенного редактора, то я бы предпочёл загрузку конфигурации в виде Lua-кода. Примеры кода сейчас не предлагаю, потому как не уверен в их востребованности в этой теме.
  24. 1 балл
    @eu_tomat Ознакомился с вашим сообщением, отредактировал тему. Вот пример практического применения подобной программы: Столик крафта из Tinkers Construct умеет взаимодействовать с ванильным сундуком. Почему-бы не реализовать механизм, который будет подавать частоиспользуемые ресурсы прямо в сундук? Это может упростить процесс крафта. Вообщем идея и схема для примера, но вот. Скрины: В компьютере прописано какой ресурс из какого сундука брать и сколько его поддерживать в целевом сундуке. Собственно файл фильтра: filter.txt Именно второй вариант. Мы выбираем инвентарь и предметы которые мы хотим в него положить / из него извлечь посредством ввода стороны света и слота, а также unlocalized name самого предмета. Я также должен сказать что не первый год пишу на lua в майне, но это моя первая "Публичная" программа. Я просто понял что мое умение писать код не будет развиваться, если я его(код) не опубликую.
  25. 1 балл
    Обнова! Теперь интернет-карту нужно установить только в компьютер сервера HoverHelm - еще меньше требований железа, еще дешевле юз! Для обновления скачайте дополнительную библиотеку HoverHelm: wget https://raw.githubusercontent.com/hohserg1/OpenComputersPrograms/master/player-monitoring/home/lib/hoverhelm/addition_radar.lua http://home/lib/hoverhelm//home/lib/hoverhelm/addition_radar.lua И обновите программу радара https://github.com/hohserg1/OpenComputersPrograms/blob/master/player-monitoring/radar.lua Теперь гитхаб токен хранится в addition_radar.lua, поэтому программу радара можно положить в папку ядра Большое спасибо @nikitaaaaa за тестирование программы на продакшене в боевых условиях
  26. 1 балл
    Есть ещё одно интересное свойство карты самоуничтожения. Будучи вставленной в робота, она просто его уничтожает. А вставленная в компьютер, она кроме того взрывает окружающие блоки и наносит урон мобам. Также проверялось на 1.7.10.
  27. 1 балл
    ПОЛНЫЙ ОБЗОР Computronics версии 1.5.5 Часть третья: Карточки Приветствую Вас, уважаемый читатель! Думаю, самое подходящее время, для того, чтобы написать новую часть обзора CX. В данной части я расскажу всё о карточках, которых тут как раз четыре вида: Beep Card (бипающая карта) Spoofing Card (карта-маскировщик) Particle Card (карта частиц) И, конечно же, Self-Destruct Card (карточка самоуничтожения). I. Beep Card. a.k.a. "Бипающая карта" Данная карта предоставляет продвинутый аналог системного динамика, который позволяет проигрывать сразу несколько звуков. Принимает таблицу следующего типа: Функции: beep.beep(freqLength:table):Boolean — запускает "аккорд" из таблицы freqLength, в которой записаны для каждой пары частота и длительность. beep.getBeepCount():Number — возвращает текущее количество проигрываемых нот. Предмет: II. Spoofing Card. a.k.a. "Карта-маскировщик" Данная карта позволяет отправлять сообщения с возможностью указывания адреса отправителя! Уже начали думать над взломом Банка? Тут-то и придёт конец вашей идее. Данная карта работает только для проводных сетей, но не для беспроводных, к сожалению. Функции. Те же, что и у обычной сетевой карты, но к функциям отправки сообщений в качестве необязательного первого аргумента можно передать желаемый адрес отправителя. Предмет: III. Particle Card. a.k.a "Карточка частиц" Данная карточка позволяет спаунить частицы в зоне 16х16х16 блоков с центром в блоке, где она находится. Полная таблица частиц доступна здесь: http://minecraft.gamepedia.com/Particles Функции: particle.spawn(particle_name:String, x:Number, y:Number, z:Number[, speed:Number]):Boolean || particle.spawn(particle_name:String, x:Number, y:Number, z:Number[, x_speed:Number, y_speed:Number, z_speed:Number]):Boolean — заспаунить частицу particle_name по определённым относительным координатам с определённой скоростью. Блок: IV. Self-Destruct Card. a.k.a "Карточка самоуничтожения" "Каждый нормальный цивилизованный житель нуждается в данной штуке", — гласит тултип к этой карточке. И он, знаете, прав! Я думаю, объяснять, что эта карточка делает, мне не нужно. Просто приведу список функций. Функции: self_destruct.start([timer:Number]):Number — устанавливает таймер на самоуничтожение. Если не указано, равно пяти секундам. Таймер самоуничтожения остановить или отредактировать невозможно! Трижды подумайте, прежде чем запускать отсчёт! self_destruct.time():Number — возвращает оставшееся время до самоуничтожения. Предмет: Вот я и закончил рассказывать о карточках в данном моде. Остались только апгрейды и предметы интеграции с другими модами, но о них я уже вряд ли буду рассказывать. А пока Вы можете оставить комментарий, подписаться на запись и поставить оценку)
  28. 1 балл
    У Self-Destruct Card можно сбросить отсчет взрыва, если включить компьютер. Сделать это можно по разному, например, переставить карту в выключенного робота и включить его или просто перезапустить текущего робота. Тестировал на майне 1.7.10.
  29. 1 балл
    А ещё wget ругается на невозможность октрыть файл в режиме записи. Скорее всего, система загружена с дискеты в режиме только для чтения. Чтобы исправить эту ошибку, требуется установить систему на жёсткий диск командой install и загрузить её с жёсткого диска.
  30. 1 балл
    Расскажи, как воспроизвести проблему: Какая версия Minecraft и OpenComputers? По какой ссылке скачана программа 3dprint? По какой ссылке скачана модель для печати?
  31. 1 балл
    этот пробывал ? http://pastebin.com/NuEi4gL8
  32. 1 балл
    Видимо, файл не так составлен, как программа ожидает, или принтер почему-то не подключился. Стандартные модели печатаются или нет?
  33. 1 балл
    @hohserg Закинул: https://pastebin.com/raw/qdYmD7Bw Только что перепроверил, вроде все тесты работают. Единственная странность - что Юникод рендерится дольше, чем простой ASCII, хотя вызовов к gpu.set остаётся столько же. Возможно, быстрее исчерпывается бюджет вызовов, но это сомнительно.
  34. 1 балл
    Объяснять тут нечего. Качаем jarник (нужна как минимум Java 8), запускаем, пользуемся продвинутым эмулятором. Ссылка на jarник: https://cc-ru.gitlab.io/ocelot/ocelot-desktop/ocelot.jar О багах сообщать на https://gitlab.com/cc-ru/ocelot/ocelot-desktop/-/issues Фичи: поддержка всех карточек OC, полное сохранение луа стейта и конфигурации воркспейса, ограничения по памяти, вызовам компонентов, и т.д. и т.п. Все основано на реальном моде. В разработке участвовали: LeshaInc (фронтенд на LWJGL), Totoro (бекенд), rason (дал живительный толчок проекту), разработчики OpenComputers (стырили у них текстуры и код мода).
  35. 1 балл
    ПОЛНЫЙ ОБЗОР Computronics версии 1.5.5 Часть вторая: стандартные блоки. И снова я приветствую Вас, уважаемый читатель этого блога! Вашему вниманию представляю вторую часть полного обзора CX версии 1.5.5, в которой я поведаю Вам о: Cipher Block (шифратор) Advanced Cipher Block (продвинутый шифратор) Colorful Lamp (разноцветная лампочка) Tape Drive + Cassette tapes (кассетный привод и, собственно, кассеты)) I. Cipher Block a.k.a. "Шифратор" Алгоритмов шифрования уже и так огороды, но суровые разрабы CX не сдаются) На самом деле, может быть очень полезным блоком для каких-нибудь там ивентов. Почему? Этот блок позволяет шифровать и дешифровать определённую строку, используя в качестве ключа... не что иное, как... предметы! Да, знаю, поворот весьма и весьма неожиданный, но так оно и есть на самом деле. Функции. cipher.decrypt(encrypted_string:String):String — пытается расшифровать строку, используя как "ключ" предметы в инвентаре шифратора. В случае неудачи (неправильный код) выдаёт ошибку. cipher.encrypt(string_to_encrypt:String):String — шифрует данную строку, используя как "ключ" предметы в инвентаре шифратора. Возвращает зашифрованную строку. cipher.setLocked(lock:Boolean) — блокирует/открывает доступ к инвентарю шифратора. При попытке открыть заблокированный шифратор, игроку выдаётся предупреждение в чат. cipher.isLocked():Boolean — возвращает состояние блокировки шифратора на данный момент. Скриншоты: II. Advanced Cipher Block. a.k.a. "Продвинутый шифратор" Этот вариант шифратора отличается от предыдущего тем, что здесь нет необходимости задать предметы как ключ — для шифровки/расшифровки используется алгоритм RSA. Функции. adv_cipher.createKeySet(prime1:Number, prime2:Number):Keygen — создаёт и запускает процесс генерации пары ключей на основе двух простых чисел. Возвращается структура, содержашая данные методы: key_set.finished():Boolean — так как процесс генерации ключей не мгновенный, использовать ключи сразу же после создания генератора нельзя. Данная функция возвращает готовность ключей: true при завершённом процессе генерации и false, если процесс генерации не завершён. key_set.getKeys():Table, Table — данная функция возвращает пару сгенерированных ключей или nil при незавершённом процессе генерации. [*]adv_cipher.createRandomKeySet():Keygen — идентичен полностью прошлой функции за исключением того, что числа простые указывать не нужно — они выберутся случайно. [*]adv_cipher.decrypt(encrypted_string:String, private_key:Table):String — дешифрует данную строку, используя приватный ключ RSA. [*]adv_cipher.encrypt(string_to_encrypt:String, public_key:Table):String — шифрует данную строку, используя публичный ключ RSA. III. Colorful Lamp. a.k.a "Разноцветная лампочка" Блок, испускающий свет, цвет которого можно менять программно (всего цветов 32768, включая чёрный). Функции. lamp.getLampColor():Number — возвращает текущий цвет лампочки. lamp.setLampColor(color:Number):Boolean — устанавливает текущий цвет лампочки. Если цвет равен нулю, то лампочка выключается. Скриншоты: IV. Кассеты и всё, что с ними связано. IV.1. Cassette Tapes. a.k.a. "Кассеты" Кассеты — мощное переносное хранилище информации, как текстовой, так и музыкальной. Всего видов кассет в CX — ни много, ни мало — 8 штук! И каждый тип различается требованиями по ресурсам и вместительностью музыки в минутах. Деревянная — 2 минуты Железная — 4 минуты Стальная — 6 минут Золотая дешёвая — 8 минут Золотая дорогая — 16 минут Алмазная — 32 минуты "Дешёвая" незерстарровская — 64 минуты И, наконец, самая ненажная и дорогущая одновременно вещь, которая когда-либо существовала в Майначе... Дорогущая незерстарровская — 128 минут. Это больше, чем на CD-диске!)) Но и качество хромает. Тем не менее, на такую кассету можно записать больше данных, чем на РЭЙД с 3 алмазными дисками) IV.2. Tape Drive. a.k.a. "Стример" Но одной кассетой Вы удолевтвориться не сможете... Для считывания и записывания информации необходим аналог CD-ROM'а, но для кассет — кассетный проигрыватель, называемый стримером. Функции. tape.stop():Boolean — останавливает проигрывание кассеты. tape.setSpeed(speed:Number):Boolean — устанавливает скорость воспроизведения (от 0.25 до 2). tape.getLabel():String — возвращает метку касссеты. Если не задано, равно "". tape.setLabel(label:String):String — устанавливает метку кассеты. Она видна в тултипе кассеты и в кассетном приводе, если там вставлена дискета. Возвращается новая установленная метка кассеты. tape.getState():String — возвращает текущий статус кассетного привода: "RUNNING", если проигрывается кассета, или "STOPPED", если нет дискеты, или воспроизведение было остановлено. tape.seek(amount:Number) — перематывает кассету на данное количество байтов вперёд/назад (при отрицательном значении). tape.setVolume(volume:Number) — устанавливает громкость кассеты от 0 до 1 (принимаются дробные значения). tape.getSize():Number — возвращает размер музыкального содержимого кассеты в байтах. Полезно вместе с функцией tape.seek() для перемотки в начало. tape.play() — начинает проигрывание музыкального содержимого кассеты. tape.isEnd():Boolean — возвращает true, если проигрывание содержимого завершилось. Полезно для зацикливания музыки на дискете в совокупности с tape.getSize() и tape.seek(). tape.isReady():Boolean — возвращает true, если в приводе присутствует дискета. tape.read([length:Number]):Number|String — читает всё (или определённое кол-во байтов), что записано на кассету. tape.write(data:Number|String) — записывает на кассету данные. Программа. При вставке кассеты в привод, появляется программа tape, которая позволяет совершать базовые операции над кассетой без необходимости использовать интерпретатор Lua. Доступно следующее: tape play — начать проигрывание. tape pause — приостановить проигрывание. tape stop — остановить проигрывание и перемотать в начало. tape rewind — перемотать кассету в начало. tape label — получить метку кассеты. tape label label — установить метку label кассете в приводе. tape volume volume — установить громкость volume музыке на кассете. tape speed speed — установить скорость speed проигрывания. tape write path/to/audio/file — записать на кассету файл с жёсткого диска компьютера. tape write URL — записать на кассету музыку с удалённого сервера, доступную по адресу URL. Скриншоты: IV.3. Формат звуковых файлов DFPWM. Прочитав рассказ о воспроизведении звука, наверняка, Вы уже начали искать тот самый файл с вашей любимой музыкой. Но не всё так просто! Дело в том, что используется странный и непонятный формат файлов — DFPWM... Но где его искать? Шаг первый. Конвертер. Первым делом, придётся открыть http://www.google.com/ в отдельной вкладке, ведь мороки с музыкой будет много. Сначала найдите в интернете конвертеры из формата Вашего файла в формат WAV (можно пропустить, если изначально в WAV). Для Линукса это ffmpeg, например. Шаг второй. Получение файла DFPWM. У вас должен быть на руках WAV-файл с музыкой. На всякий случай попробуйте открыть его в аудиопроигрывателе, дабы убедиться в "правильности" файла. Если всё ОК — закрываем Гугл и идём дальше. Загрузите данный файл на свой компьютер: https://dl.dropboxusercontent.com/u/93572794/LionRay.jar Это конвертер из WAV в DFMPW. Выставьте права на исполнение, если они не соблюдены, и запустите файл через Java. Укажите расположение исходного WAV-файла и запустите процесс конвертации. Шаг третий. Сохранение файла. Если у Вас есть доступ к папке сохранения, то всё просто — откройте папку ~/saves/<имя_мира>/opencomputers/<адрес_диска>/ и переместите туда Ваш получившийся аудиофайл. Затем вставьте кассету в стример, подключите последний к компьютеру и пропишите tape write <имя_аудиофайла>. Иначе Вам придётся искать хостинг, выдающий прямые ссылки на файл. Загрузите файл и скопируйте ссылку. Затем вставьте интернет-карту в компьютер и пропишите следующее: tape write <ссылка_на_аудиофайл>. Дождитесь окончания загрузки. Фух, вот я, наконец, и закончил. В сумме на написание этого гайда ушло около шести часов ночного времени, так что думаю, что он Вам понравится) Оставляйте оценки, лайки, жду комментариев! А в следующей части я расскажу о четырёх новых картах, которые добавляет CX. Ссылка на страницу мода: http://wiki.vex.tty.sh/wiki:computronics
  36. 1 балл
  37. 1 балл
    Ооооочень медленное, совершенно неторопливое, никуда не спешащее видео. Я смотрел его целых две минуты и успел увидеть только неторопливый обзор топика на форуме (вот этого, в который я сейчас пишу, и который я уже читал) и узнать что картинки вам рисовал художник, но "это мы разберем позже". А так нормально, голос приятный, контента бы интересного и взлетит.
  38. 1 балл
    Представляю вам свою первую более-менее серьезную программу для openComputers. Ее единственная функция - проиграть указанный музыкальный файл. Файл формата nbs, это трекерная музыка с майнкрафтовской спецификой (т.е. написана для музыкальных блоков) Для создания и редактирования файлов в этом формате используется программа Minecraft Note Block Studio, ссылка на тему на майнкрафтфоруме: http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-tools/1260747-minecraft-note-block-studio Там же можно найти уже готовые музыкальные треки. Большую часть их программа сможет воспроизвести, проблемы будут с треками, содержащими нестандартные музыкальные инструменты и использующие диапазон шире двух октав. Но даже их можно проиграть, предварительно поправив в редакторе, он делает это автоматически. Ссылка на программу: http://pastebin.com/yrtLYBhz Использование очень простое, параметр всего один - относительный путь до файла (с расширением).Да, и помимо собственно компьютера вам понадобятся железные музыкальные блоки из Computronics, подключенные к нему. Штук восемь, можно больше или меньше, если их будет меньше, чем того требует трек, программа помрет, если больше - запас, как говорится, карман не тянет. В планах упаковать программу в библиотечку, сделав возможность включать плеер из других программ. Например, для саундтрека в играх. Еще хотелось бы решить проблему со сбивающимся ритмом, если подскажете способ создавать задержки точнее, чем через pullSignal и os.sleep, буду очень благодарен. Видео работы:
  39. 1 балл
    Пожалуйста, скажи, что сисечки у аниму-тянки задумывались интерактивными в такт движений ракеткой
  40. 1 балл
    И когда продолжение то будет?
  41. 1 балл
    https://pastebin.com/xPzTDkrB пример использования: gcam south 16
  42. 1 балл
    Дроны - как керосин. Они есть везде. Еще года два назад это было просто еще одно интересное видео на Ютубе. Год назад они вдруг оказались в интернет магазинах. Затем просочились в рекламу на ТВ, и вот теперь - они есть и в OpenComputers! Пришла пора с ними разобраться. 1. Матчасть Дрон, в данном случае - квадрокоптер, это беспилотный летающий аппарат, приводимый в движение двумя парами горизонтальных винтов. Приостановливая вращение винтов с одного боку, дрон двигается в сторону (стрейф). Эти винты вращаются в разном направлении (два - по часовой срелке и два - против), за счет чего дрон не нуждается в стабилизирующем хвостовом пропеллере (как вертолет). За счет этого же он и разворачивается в воздухе, замедлив вращение однонаправленной пары винтов. Дрон обладает небольшой массой, для экономии энергии, которой у него не много (на 10-30 минут полета в среднем). (с) Википедия 2. Дроны и OpenComputers Приблизительное изображение дрона в OpenComputers =): В мире Майнкрафта дрон представляет из себя "сущность" (Entity). Это значит, что он обладает возможностями мобов Майнкрафта. (В то время как робот - это блок.) Его можно сдвинуть с места толкая. Он умеет пролетать сквозь двери и калитки (в отличии от робота). Он движется не последовательно, из блока в блок, а из точки в точку. Причем маршрут может лежать по диагонали. Конечно, движется он по кратчайшей линии, и если на пути окажется стена - дрон столкнется с нею. Программирование дрона как две капли воды похоже на программирование микроконтроллера. Вы точно так же записываете программу на EEPROM, и при необходимости меняете ее на верстаке. Только в отличии от контроллера, вам становится доступен новый компонент: drone. Подробнее об командах дрона можно узнать здесь: OpenComputers/Дрон. (Или здесь: ocdoc.wiki (англ.)) 3. План Нужна какая-нибудь несложная задача, для целей эксперимента. Используем программку send из предыдущего поста, для удаленного управления. Зальем ее на планшет. А дрон пусть... носит свиней. Будем оригинальными и непоследовательными. 1. Команда 'add X Y Z Name From'. Добавляем точку Name к маршруту, цепляя ее к точке From. Зададим дрону последовательность точек, которые образуют граф - безопасные маршруты. 2. Команда 'catch' - дрон ловит свинью. 3. Команда 'drop' - дрон выпускает свинью. 4. Команда 'to X' - дрон летит в точку Х. Для начала не будем особо заморачиваться с графом маршрутов. Это будет простое неориентированное дерево. Примерно такое: 4. Строим полигон Построим что-нибудь подходящее для тестов. Отметим ключевые точки будущего графа красными блоками. А синий блок - будет стартовой площадкой дрона. Поскольку я играю без модов на энергию, мой планшет и дрон будут работать вечно. И я не заморачиваюсь станцией подзарядки. Иначе, к схеме выше было бы необходимо добавить станцию, где дрон мог бы зарядить аккумулятор. 5. Пишем программу Скрипт для удаленного управления скопипастим из прошлого поста, подправим, чтобы умела отправлять несколько переменных и зальем на планшетик, для удобства. (Для этого, соберите планшет - не забудьте клавиатуру и видеокарту! - положите его в зарядник и запустите с подключенного компа команду install. Укажите адрес винчестера планшета - и все, что было у вас на компе автоматически загрузится в планшет, включая даже ваши собственные программы.) local com = require('component') local modem = com.modem local args = {...} modem.broadcast(27, table.unpack(args)) io.write("Message: ") print(table.unpack(args)) Далее - более сложная часть. Программа дрона. Программа предназначена для EEPROM. Значит соблюдаем те же правила: используем computer, component и API имеющихся у дрона компонентов. Включая его родной компонент drone. В нашем случае, дрон вооружен апргейдом-лассо (leash) и беспроводной сетевой картой (modem) для связи. Стоит отметить, что процесс отладки программы (по крайней мере в текущем билде мода) достаточно неудобен. В случае ошибки дрон отказывается включиться, издав тонкий писк, и не выводя никакой информации. Получить отчет об ошибке при помощи анализатора не выйдет - ведь Shift+ПКМ просто снимает дрона. Автор обещал в скором времени это исправить. Ну а пока - помучаемся. Отредактировать чип в стороннем редакторе, не вынимая его из дрона тоже не выйдет. В отличии от файловых систем, которые имеют удобную папку вида /saves/World/opencomputers/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/, чипы EEPROM хранят свой код в NBT тегах предмета. Этим же обусловлено и ограничение размера кода в 4 килобайта. 5.1. Основная часть Это цикл который ждет указаний, а затем запускает соответствующую функцию. drone = component.proxy(component.list("drone")()) modem = component.proxy(component.list("modem")()) leash = component.proxy(component.list("leash")()) modem.open(27) route = {} path = {} current = "" while true do name, _, sender, _, _, message, x, y, z, point, from = computer.pullSignal(1) if name == "modem_message" then if message == 'add' then add(tonumber(x), tonumber(y), tonumber(z), point, from) if current == "" then current = point end elseif message == 'to' then to(x) elseif message == 'catch' then catch() elseif message == 'drop' then drop() end end if #path > 0 and drone.getOffset() < 1 then drone.move(route[path[#path]].x-route[current].x, route[path[#path]].y-route[current].y, route[path[#path]].z-route[current].z) current = path[#path] path[#path] = nil end end modem.close() Чтобы облегчить себе жизнь (и тестирование bios), вы можете сделать так: напишите заглушку для компонента drone (и других, если надо), вроде этой: http://pastebin.com/EVYzN5Bj Просто скопируйте в папку на компьютере, где вы пишете программу для дрона. Затем измените первые строки программы следующим образом: component = require('component') computer = require('computer') drone = require('drone') modem = component.modem -- leash = component.proxy(component.list("leash")()) Затем добавьте в цикл условие выхода по нажатию кнопки: if name == 'key_down' then break end И вы можете просто запустить вашу программу для дрона на компьютере. Разумеется полноценной эмуляцией дрона тут и не пахнет, зато очень удобно отслеживать глупые синтаксические и логические ошибки. Как устроен код основного цикла? Переменная route - хранит таблицу "вейпоинтов" (waypoints). Это вершины графа и информация о связях между ними. Переменная path - хранит путь от текущей вершины до цели. Переменная current - отмечает текущее местоположение дрона в графе. В цикле мы читаем получаемые сообщения и вызываем соответствующие функции. Первая переданная вершина считается дроном текущей. Во второй части цикла происходит проверка. Если путь до цели - не пуст (это значит, что дрону надо куда-то лететь) и дрон уже долетел до текущей вершины (getOffset()), то программа берет следующую вершину из path, отправляет дрона к ней и объявляет ее текущей. 5.2. Функции-команды Теперь последовательно добавим функции для каждой команды. function add(x, y, z, name, from) route[name] = {x=x, y=y, z=z, link = {}} if from ~= nil then if route[name] == nil or route[from] == nil then drone.setStatusText("Error!") else table.insert(route[name].link, from) table.insert(route[from].link, name) end end end Тут все просто. Пишем вершину в список. Если он связана с другой вершиной (from ~= nil), то в специальную табличку link заносим две связи: из name в from, и из from в name. function search(target, point, prev) for key, name in pairs(route[point].link) do if name == target then table.insert(path, point) return true end end for key, name in pairs(route[point].link) do if name ~= prev then if search(target, name, point) then table.insert(path, point) return true end end end return false end function to(name) path = {} table.insert(path, name) search(name, current) end Функция to обнуляет старый путь (на всякий случай), затем вставляет в него цель пути (name) и запускает функцию search, которая рекурсивно ищет и записывает остальные промежуточные вершины на маршруте от name до current (текущей локации). Функция search сделана достаточно примитивно (возможно вы предложите более эффективный способ?). Поскольку мы договорились, в целях упрощения использовать граф-дерево (не содержаший петель), от любой точки к другой существует один и только один маршрут, который функция и находит перебором связанных вершин. function catch() for c = 2, 5 do if leash.leash(c) then return true end end return false end function drop() leash.unleash() end Тут все элементарно. 6. Подготовка Пишем программу на дрона, заряжаем планшет и выдвигаемся в зону действий. Дрона ставим на синий куб (стартовая площадка) и включаем. После уточнения на местности, составляем карту вейпоинтов и строим на бумажке будущий граф: Для каждого загона добавлены две точки - name и name_up. Основные "трассы" дрона лежат на высоте в 6 блоков. А в каждом загоне спускаются к земле. (Чтобы заарканить животное, выстреливая лассо вбок, дрону желательно находиться на одном уровне с жертвой). С планшета вносим координаты в память дрона. Примерно так: Главное - не ошибиться. Т.к. в код не была добавлена защита "от дурака" =) Алгоритм позволяет добавлять вершину "на лету". В любой момент вы можете добавить еще одну ветку к схеме. Теперь все готово к тесту. 7. Запуск Все готово. Проверим, как он двигается. Введем send to sheeps в консоль планшета. Дрон уверенно поднимается в воздух и опускается в загоне в овцами. Теперь введем send to pigs. Функция search снова вычислит путь и робот переместится в указанную вершину: Функции catch и drop тоже работают штатно =) Хотя и не лишены некоторых глюков (ведь физика веревки не просчитывается): 8. Итоги а) Дрон - любопытная штуковина. б) Полный код прошивки. использованный в этом посте - здесь: http://pastebin.com/Cy1UR6vy в) Навигация по вейпоинтам - интересный и очень распространенный способ организации сложного движения. Схему можно усложнить - опционально добавлять только одну связь в таблицу link - тогда получатся ребра с односторонним движением. Добавить петли, оптимизировать поиск кратчайшего пути. Еще можно облегчить правление дроном - хранить все команды для конкретной задачи в виде файла-скрипта, который запускать одной командой и т.д. Enjoy!
  43. 1 балл
    ПОЛНЫЙ ОБЗОР Computronics версии 1.5.5. Часть первая: стандартные блоки. Приветствую Вас, уважаемый читатель! В данном обзоре я попытаюсь рассказать о всём, что только есть в Computronics. И начать предлагаю со "стандартных" блоков. Итак, гостями сегодняшней части будут: Iron Note Block (железный нотный блок) Camera (камера) Radar (радар) Chatbox (чат-бокс) I: Iron Note Block. Железный нотный блок — аналог обычного нотного блока, управляемый исключительно компьютером и позволяющий указывать номер ноты (от нуля до 24) и инструмент. Последний указывается числом от нуля до шести: 0 — пианино; 1 — большой барабан; 2 — клики/палочки; 3 — малый барабан; 4 — бас-гитара; 5 — пианино; 6 — бас-гитара. Функции: iron_noteblock.playSound(instrument, note) — проигрывает ноту с номером note на инструменте instrument (кроме номера инструмента, можно написать название) Блок: II: Camera. Камера — блок, позволяющий Вам получать дистанцию до ближайшего блока. При этом, можно установить угол "поворота" камеры по обеим плоскостям (X и Y). Максимальное значение "поворота" равно единице, минимальное — -1. Функции : camera.distance([x, y]) — получить дистанцию до ближайшего блока с определённым углом "поворота". Если опущено, то равно 0, 0. Блок: III: Radar. Радар позволяет получать информацию об игроках, мобах, предметах на земле и энтитей в определённом радиусе, но не дальше указанного в файле конфигурации мода предела. Все координаты относительные! Функции: radar.getEntities([range]) — возвращает информацию обо всех сущностях. Структура возвращаемой таблицы у этой и последующих двух функций такова: radar.getPlayers([range]) — возвращает информацию об окружающих игроках radar.getMobs([range]) — возвращает информацию о мобах поблизости radar.getItems([range]) — возвращает таблицу с предметами на земле около радара. Структура данной таблицы: Блок: IV: Chatbox. Последний в данной части блок — чат-бокс. Этот компонент позволяет отправлять и принимать сообщения в/из игрового чат (-а) в определённом радиусе. Функции: chatbox.getDistance() — возвращает текущий установленный радиус действия чат-бокса. chatbox.getName() — возвращает текущее установленное "имя" чат-бокса. chatbox.say(msg[, range]) — отправляет сообщение msg в чат в радиусе range. Если не указано, равно установленному. Возвращает true при успехе. chatbox.setDistance(range) — устанавливает радиус действия чат-бокса. Возвращает новый радиус. chatbox.setName(name) — устанавливает "имя" чат-бокса. Возвращает новое "имя". События: chat_message(UUID, sender, msg) — генерируется при отправке сообщения msg в чат игроком sender с UUID UUID. Скриншоты: Вот мы и подошли к концу данной части. В следующей части я расскажу о Chiper Block, Advanced Chiper Block, Colorful Lamp и Tape Drive. А пока — жду комментариев, оценок Ссылка на страницу мода: http://wiki.vex.tty.sh/wiki:computronics << НАЗАД в башню Fingercomp
  44. 1 балл
    local debug = computer.addUserЭта строчка сильнее других провоцирует у меня расстройство сознания.Ну, почему «debug»? И далее: prov=debug(name) ... computer.removeUser(name)И почему тогда не сделано что-то типа: local bug = computer.removeUserТо есть, сначала обезжучили пользователя, а потом снова его нажучили.
  45. 1 балл
    MoonyLambda Вы наверное знаете что сейчас проходит конкурс MoonJam. В общем суть в том что бы написать что-нибудь на MoonScript за неделю. Как говорил уже тотора, что-то серьезное за неделю не написать, но попробовать, поразвлечься, и написать что-нибудь интересное можно. В общем в этой теме буду показывать мой проект. И это... Библиотека для функционального программирования (далее просто - ФП)! Луа не предоставляет стандартных средств для ФП, потому я написал свою библиотеку. Чем то она похожа на реализацию такового функционала в языке Python, а чем то нет. Суть в том что функциональное программирование очень сильно упрощает работу с данными, например вместо пяти строк мы напишем одну строку. Все примеры в MoonScript, но в спойлерах будет эквивалент на луа. f - библиотека подключенная через require Начнем с самого простого, сложить массив чисел. f.reduce f.add, {1, 2, 3} --> 6 Точно таким же образом, мы можем перемножить все числа f.mul вместо f.add, или соответственно разделить, вычесть или возвести в степень - f.div, f.sub и f.pow А если, к примеру, нам нужно взять массив и выкинуть из него все нечетные числа. Это тоже возможно. f.filter f.even, {1, 2, 3, 4} --> {2, 4} А теперь, достанем все числа которые одновременно и меньше пяти, и четные. Это не составит труда f.filter f.even, f.filter ((a) -> a < 5), {1, 2, 4, 5, 6, 7} --> {2, 4} Хочу отметить, что нам не обязательно постоянно писать огромные цепочки из вызовов. Все функции в библиотеке каррированы. selectEven = f.filter f.even selectEven {1, 2, 3, 4, 5} --> {2, 4} ...Но мы можем каррировать свою функцию! pow = f.curry (a, b) -> a ^ b pow2 = pow 2 pow 3, 3 --> 27 pow2 3 --> 8 К слову, именно так определена в библиотеке функция f.pow. Каррированная функция сама определяет, что ей вернуть, функцию которая примет недостающие аргументы, или результат. Мы можем каррировать функцию с любым количеством аргументов, так просто! Реверс таблицы - легко, расплющить таблицу - легко, изменить таблицу - легко. Но самое главное... Все функции чистые. Ни одна функция этой библиотеки не изменяет данные, и не зависит от внешних данных. Вполне продакшен-ready пример: first_letter = (a) -> a\sub 1, 1 f.group first_letter, {"Miku Chan", "Mayonnaise", "LeshaInc", "Loli", "Totoro"} --> {M: {"Miku Chan", "Mayonnaise"}, L: {"LeshaInc", "Loli"}, T: {"Totoro"}} И все это становится таким простым используя библиотеку MoonyLambda. Скачать Исходный код: pastebin.com/fFEFvX5D Скомпилированная и минифицированная версия: pastebin.com/K4b9sREs Документация ...будет постепенно писаться в этой теме. Ссылки будут тут. Функциональный стиль выглядит гораздо читаймей, чем императивней стиль. Когда в императивном стиле мы пишем непонятный цикл, который непонятно что делает, тут мы пишем несколько строк кода, который понятен с первого взгляда.
  46. 1 балл
    Подметите за Лёхой, пожалуйста, мы тащим ГИСТ. Сделанная по принципу pastebin и не сделанная про принципу pastebin, эта программа позволяет скачивать файлы с Гиста, получать информацию о них и закачивать на Гист файлы с OpenComputers. Почу Gist лучше Pastebin? У него нормальное API. Если бы OC поддерживал кастомные запросы, спокойно можно было бы изменять содержимое гиста, работать в аккаунте, ставить плюсы, форкать и писать комментарии. Возможность писать комментарии к гисту с поддержкой Markdown. Все изменения гиста логируются. Возможность форкнуть гист (склонировать), удобная реализация. Возможность работы через git (клонирование, пуш). Показывается 1 МБ на странице. Отедльным запросом можно получить до 10 МБ данных КАЖДОГО ФАЙЛА, то есть лимит этот не на весь гист, а на отдельные файлы. Для тех, кто толще 10 мегабайт, требуется git. Можно добавить описание к гисту. Несколько файлов в одном гисте. Интеграция с GitHub — удобнейшим хранилищем репозиториев. Минусов же только 2: Длинные хеши гистов. (20 символов против 8) Не было клиента гиста под OC. Второй минус я и решил своей программой, скачать которую можно через OPPM: oppm install gist Итак, о проге. Режим скачивания Пишем gist <ID гиста>. Если файл на гисте один, то его содержимое выведется на экран. Если же их несколько, перед айдишником пишите --f= и имя файла. Обращаю внимание на двойной минус! Ограничение OpenOS. Чтобы сохранить всё это, после айдишника пишем путь к нужному файлу. Вуаля! Если файл существует, и мы хотим перезаписать его, пишем -r. Режим загрузки на Гист Ключевой флаг -p. Если хотим секретный гист (не числится в публичных списках Гиста), пишем --P=s. Можно задать описание Гиста с помощью --d="описание". Затем указываем файлы для загрузки. Пишем путь к файлу, без пробела = и название файла на Гисте с расширением (для подстветки синтаксиса). Например: /usr/bin/gist.lua=gist.lua. Жмём [Enter], ожидаем хагрузки и переписываем ссылку на новоиспечённый гист Ещё есть несколько режимов, предлагаю изучить самому. Просто запустите прогу без аргументов, чтобы получить справку. P. S. Пишу на форум, а не в блог, т. к. считаю, что программа слишком нужная, что бы в блог писать заметку. P. P. S. Ключ -s пока не работает. P. P. P. S. Рекомендую почитать про лимиты ресурсов тут: https://developer.github.com/v3/#rate-limiting
  47. 1 балл
    Первая публичная реализация автокрафта на OpenComputers. Исполнительным элементом является робот, командующим же — компьютер. Хранилищем предметов здесь выступает МЭ-сеть, с интерфейсом в роли передатчика предметов в обе стороны. Для начала использования автокрафта Вам потребуется: Компьютер. Это главная часть системы, хранящая базу данных рецептов и экспортирующая предметы из дерева крафта в нужном порядке. Требования: Графическая карта второго уровня. Беспроводная сетевая карта. Процессор второго уровня и выше. Планки памяти уровня 2 и выше (зависит от размеров базы данных). Жёсткий диск уровня 1 и выше (зависит от размера базы данных). Интернет-карта (для скачивания программы). EEPROM. OpenOS Робот. Это исполняющая часть системы. По сигналу с модема "craft" она крафтит предметы и складирует полученное в МЭ. Требования: Апгрейд крафта. Контроллер инвентаря. Инвентарь. Клавиатура. Экран Т1. Дисковод. Интернет-карта (для скачивания программы). Беспроводная сетевая карта. Процессор уровня Т2 и выше. Планки памяти уровня Т2 и выше (возможная комбинация: Т2 и Т1.5). EEPROM. OpenOS. Жёсткий диск первого уровня. МЭ-сеть. Это хранящая часть системы, из которой достаются айтемы и в которую кладутся результаты крафтов. Требования: ME Drive и ячейки. Терминал для доступа к сети (может быть исключён). Интерфейс. После крафта всех необходимых вещей можно приступать к установке. Поставьте робота лицом в интерфейс. Убедитесь, что интерфейс готов к работе. Теперь соберите компьютер. Установите на робота и компьютер OpenOS. Скачайте программы, используя команды ниже, для робота и компьютера соответственно: Компьютер: pastebin get pXunJUE2 /usr/bin/craft.lua pastebin get ixwtEUr6 /usr/bin/recipes.lua pastebin get V2Zrnp6F /usr/share/db Робот: pastebin get tiwidCYt /autorun.lua pastebin get S1J5Y7mb /scan.lua Теперь запишите адреса сетевых карт на компьютере и роботе (components modem). Откройте файл /usr/bin/craft.lua на компьютере. В строке ROBOT замените значение на адрес сетевой карты робота. В строке DIR замените значение на сторону экспорта (сторона света, где находится робот относительно интерфейса). "north", "south", "east", "west", "up", "down". В строке TECH_SLOTS замените значение на количество слотов внизу робота (инструмент, дискета, контейнеры). После этого откройте файл /scan.lua на роботе и замените значение переменной COMP на адрес сетевой карты компьютера. Если всё сделано правильно, можно запустить файл /autorun на роботе и recipes на компьютере. Интерфейс у данных программ понятен без моих комментариев. Программа recipes предназначена для управления базой данных: удаление, изменение, добавление, просмотр рецептов. Программа craft на компьютере предназначена для самого процесса крафта. Напоследок, для сканирования рецептов нажмите 7 в recipes, выложите рецепт в роботе и в выделенный слот положите результат крафта. Затем запустите программу scan на роботе и выполните инструкции на компьютере. Скриншоты. Все вопросы, замеченные баги оставляйте в комментариях.
  48. 1 балл
    Символы и спецсимволы в шаблоне можно объединять в наборы (сеты). Для этого несколько символов заключаются в квадратные скобки. Набор замещает собой один из символов, входящих в его состав. Например, вот такой набор [%d%p] замещает цифру или знак препинания. А такой [_%w] - букву, цифру или символ подчеркивания. Порядок символов, в котором они идут в наборе значения не имеет. Теперь мы можем найти в строке значение переменной y даже если оно будет отрицательным или сопровождаться знаком + s:match('y=[%+%-]?%d+') Знаки % перед + и - говорят о том, что это не спецсимволы, а обычные + и - Знак ? после набора указывает на то, что + или - могут отсутствовать. А вот такой шаблон s:match('y=[%+%-%d]%d*%.?%d*') позволит найти число в формате с десятичной точкой. Если набор начинается с символа ^ , то такой набор интерпретируется как "всё кроме указанных символов".
  49. 0 баллов
    Сделай пожалуйсто уже готовое прошу, я как новенький (черпак) ничего не понял что мне нужно делать
Эта таблица лидеров рассчитана в Москва/GMT+03:00
×
×
  • Создать...