Лидеры
Популярный контент
Показан контент с высокой репутацией 30.07.2022 во всех областях
-
1 баллЧто такое RemoteOS RemoteOS - это мост для связи OpenComputers с внешним миром, написанный на языке C# (.NET 6.0). В чём отличие RemoteOS от уже существующих решений Начнём с самого очевидного и главного - на сервере имеется (почти) точная копия API OpenComputers. Это позволяет не только использовать подсказки в коде при работе с мостом в IDE но и проверять данные подаваемые в методы ещё до отправки их на компьютер OpenComputers(далее машина). Так-же такой подход позволяет реализовать кэширование, которое даёт возможность в некоторых местах не дёргать постоянно машину для опроса актуальных значений... Из-за того что на сервере наклёпано столько архитектуры он не такой легковесный как остальные мосты... Мой мост работает не из под OpenOS а прямо с EEPROM'а, что позволяет значительно снизить минимальные системные требования для работы этого моста но при этом это же отличие значительно его ограничивает - будут недоступны все те удобненькие библиотеки из опенос, то-есть нельзя работать ни с чем кроме того что предоставляет lua и клиентская ос моста... Не нужно запрашивать сигналы с машины, она сама их шлёт серверу... Какие у RemoteOS системные требования Для машины - интернет карта и EEPROM прошитый на клиентскую ос RemoteOS Для сервера - 1 открытый сетевой порт чтобы иметь связь с машиной и достаточная вычислительная мощность чтобы суметь запуститься а затем отсылать пакеты машинам, получать от них ответ и обрабатывать этот ответ... Как же всё таки пользоваться RemoteOS Начнём с того что C# - не самый удачный выбор для связки с LUA ибо C# это строго типизированный яп а луа не очень...и подгонять сервер под все причуды луа кода было тем ещё приключением...но у меня вроде как вышло, так что посмотрим как всем этим добром пользоваться... Ловля подключений: Компоненты: Сигналы: Выполнение произвольного кода: Директивы предпроцессора: Веб сервер: Какие у меня планы на RemoteOS [+] Планирую добавить документацию в код, чтобы можно было прямо в IDE зачитать что конкретный метод делает... [+] Хочу сделать веб-интерфейс для управления машинами Хочу довести кэширование до ума, чтобы надо было дёргать машины ещё меньше Может быть добавлю поддержку локальной(внутриигровой) сети на сетевых картах/ретрансляторах чтобы ещё больше снизить минимальные требования для клиентской ос Ссылки Репозиторий проекта: Тык (Github) Клиентская ОС: Тык (Github) Демо-видео: Тык (Яндекс диск)
-
1 баллВ моде OpenComputers есть интересное устройство, которое позволяет определить плотность блока на расстоянии. Но вот беда, данные он выдает довольно шумные и чем больше расстояние, тем больше шума. Чтобы определить подлинную плотность блока, можно просканировать его несколько раз, а результат усреднить. Шум, мешающий сканированию, имеет вероятностную природу. И после нескольких сканирований можно статистически найти, какая вероятней всего плотность у блока. За один тик мы можем просканировать 64 блока. Чтобы проанализировать всю доступную область (65 x 65 x 64) сотней итераций, нам понадобится 422500 тиков, что равно 21125 секунд или 352 минуты, то есть без малого 6 часов. Но сколько раз надо сканировать? Сто? Тысячу? Нам открыто тайное знание и есть точный ответ. Один. Всего за одно сканирование мы можем найти руду среди любых других блоков. Если хочется абсолютной уверенности, придется сделать пару магических пассов и просканировать повторно. Начнем с теории. Для начала откроем код мода и найдем функцию geolyzer.scan, она располагается [здесь] src/main/scala/li/cil/oc/integration/vanilla/EventHandlerVanilla.scala и называется onGeolyzerScan() Просмотрев код, мы можем понять, что функция принимает параметры, по этим параметрам сканирует блоки в мире. Делает разные проверки вроде world.blockExists(x, y, z) && !world.isAirBlock(x, y, z), чтобы убедится, что блок есть. Потом получает информацию о блоке по координатам, делает еще несколько проверок (опять проверить, что блок все-таки есть block != null, проверяет дополнительные параметры: includeReplaceable, isFluid(block), block.isReplaceable(world, blockPos.x, blockPos.y, blockPos.z)) Потом происходит измерение расстояния до блока. И в конце берется плотность, смешивается с шумом и расстоянием. Результат добавляется к таблице блоков и отправляется игроку. Вроде-бы ничего необычного. Шум, расстояние, плотность. Нам и так известна зависимость силы шума от расстояния. И вот тут начинается волшебство. Рассмотрим поподробнее код вычисления итоговой плотности блока. e.data(index) = e.data(index) * distance * Settings.get.geolyzerNoise + block.getBlockHardness(world, x, y, z) Коротко можно это записать в виде формулы: R = G * D * N + H G - это сгенерированный шум. D - расстояние до блока. N - множитель шума из конфига (стандартно - 2). H - настоящая плотность. R - результат работы геосканера. Если мы попробуем в качестве эксперимента отнять от результата предполагаемую плотность, то ничего нового не узнаем. Если обратим все операции с известными значениями, то получим только шум. А можем ли мы так же разобрать формулу шума? Давайте попробуем. Несколькими строками выше [ссылка]. Можно наблюдать получение массива случайных байт. val noise = new Array[Byte](e.data.length) world.rand.nextBytes(noise) Далее следует нормализация значений. noise.map(_ / 128f / 33f).copyToArray(e.data) Хм. Так-так-так. Если мы это все обьеденим с предыдущей формулой, то получится что-то вроде такого: R = G(RANDOM_BYTE / 128 / 33) * D * N + H И что это нам дает? А то, что исходное псевдослучайное число имеет жесткую дискретность. ГПСЧ дает случайные числа типа byte, а это только 256 значений (-128, +127). Нам известны все значения, кроме H и RANDOM_BYTE, что нам это дает? Мы можем предположить значение H и обратить всю формулу. (R - H) / D / N * 128 * 33 Для стандартного конфига можно сократить до: 2112 * (R - H) / D А теперь тайное знание для тех, кто не понял самостоятельно. Мы взяли желаемую плотность блока (например 3 для руды). Подставили вместо H. Получили случайное значение. Можем легко определить, угадали ли плотность или нет. Из-за дискретности случайных значений генератора, распределение вероятностей для блоков с разной плотностью не одинаковое. Перейдем к практике. Вот код простого скрипта, который в заданном радиусе ищет блоки с нужной плотностью. Результат выводится на голопроектор. local sqrt = math.sqrt local component = require('component') local geolyzer = component.geolyzer local hologram = component.hologram local function distance(x, y, z) return sqrt(x^2 + y^2 + z^2) end local function magic(R, H, D) return 2112 * (R - H) / D % 1 end local function visualize(hardness, elevation, size) hologram.clear() hologram.setScale(9) local blocks, result for x = -size, size do for z = -size, size do blocks = geolyzer.scan(x, z, elevation, 1, 1, 32) for i_y = 1, 32 do result = magic(blocks[i_y], hardness, distance(x, i_y+elevation-1, z)) if blocks[i_y] ~= 0 and (result > 0.9998 or result < 0.00005) then hologram.set(x+24, i_y, z+24, true) end end end end end local hrd, ele, siz = table.unpack({...}) hrd = hrd or 3 ele = ele or -32 siz = siz or 16 visualize(hrd, ele, siz) А вот результат: При сканировании заметны артефакты. Когда разные плотности близки на целочисленных расстояниях, позникают коллизии. Это можно частично компенсировать, если есть блок кандидат на ошибку. На любом расстоянии можно рассчитать абсолютный минимальный и максимальный уровень шума. С расстоянием, у близких плотностей пересечение значений увеличивается, но если плотность блока не в области пересечений, то можно точно определить к какой области он относится. Пересечение плотностей руды (3) и камня (1.5), точками обозначены три сканирования блока руды. Результаты обратного вычисления для разных плотностей хорошо это демонстрируют. Для компенсации артефактов надо ввести дополнительное условие: полученный RANDOM_BYTE должен быть в диапазоне -128:127. Вот финальный скрипт и результат. local sqrt = math.sqrt local component = require('component') local geolyzer = component.geolyzer local hologram = component.hologram local function distance(x, y, z) return sqrt(x^2 + y^2 + z^2) end local function magic(R, H, D) return 2112 * (R - H) / D end local function visualize(hardness, elevation, size) hologram.clear() hologram.setScale(9) local blocks, result for x = -size, size do for z = -size, size do blocks = geolyzer.scan(x, z, elevation, 1, 1, 32) for i_y = 1, 32 do result = magic(blocks[i_y], hardness, distance(x, i_y+elevation-1, z)) if blocks[i_y] ~= 0 and result > -128 and result < 127 and (result%1 > 0.9998 or result%1 < 0.0002) then hologram.set(x+24, i_y, z+24, true) end end end end end local hrd, ele, siz = table.unpack({...}) hrd = hrd or 3 ele = ele or -32 siz = siz or 16 visualize(hrd, ele, siz) Для более точного определения плотности можно сделать два сканирования. Одно сместить относительно другого так, чтобы расстояния с артефактами не совпадали. Чтобы не выполнять тяжелую операцию sqrt, можно создать словарь, где [x^2 + y^2 + z^2] = sqrt(x^2 + y^2 + z^2), всего понадобится 1742 уникальных значений. P.S. Пост является компиляцией знаний из [этой] темы. Собрал, чтобы перевести и опубликовать на официальном форуме. Автор идеи хакнуть геосканер - @eu_tomat
-
1 баллПофиксил геоанализатор, Добавил недостающие проверки в классе агента, Добавил метод для получения направления взгляда робота без улучшения "Навигация"... UPD спустя 4 часа: Нашел на форуме парочку очень интересных тем: фильтрация шума геоанализатора и вычисление константы шума геоанализатора...добавил соответствующие методы в код сервера Тема с фильтрацией шума Тема с получением шумовой константы UPD спустя 2 часа: Добил документацию во всех важных точках кода.можно сказать с доками покончено(естественно в будущем будут добавляться новые методы а с ними и новые доки, но на данный момент код полностью задокументирован)
-
1 баллПодмена computer.pullSignal или методика построения резидентных программ в OpenOS Допустим, захотелось нам иметь резидентную программу, которая будет работать на фоне OpenOS и, периодически, или по какому либо событию, что-то полезное делать. Воспользуемся нашим маленьким bibi, а в качестве boot.lua напишем такой код: local cl=component.list local gp=component.proxy(cl("gpu")()) --сохраняем исходные функции из _G.computer, для внутреннего пользования local cp={} for k,v in pairs(computer) do cp[k]=v end --подменяем функцию computer.pullSignal computer.pullSignal = function (...) local e={cp.pullSignal(...)} --для примера, по тильде (~) будем ребутать компьютер if e[1]=='key_down' and e[4]==41 then cp.shutdown(true) end --а по lAlt будем выводить список компонентов используя определенную в OpenOS функцию print и сохраненную cl if e[1]=='key_down' and e[4]==56 then for k,v in cl() do print(k,v) end end --OpenOS периодически сама дергает эвенты (чтобы курсор мигал и прочее) подробнее смотрите в lib/event.lua --поэтому мы можем выводить свои часики, куда-ж без них то ) и прочие полезности, например инфу о памяти local s=tostring(math.floor(cp.freeMemory()/1024))..'/'..tostring(cp.totalMemory()/1024)..' kb free '..os.date('!%R') local w,h = gp.maxResolution() gp.set(w-#s+1,1,s) --в конце мы должны вернуть событие ничего не подозревающей OpenOS return table.unpack(e) end --функцию мы подменили, теперь загружаем, компилируем и выполняем init.lua local fs=component.proxy(computer.getBootAddress()) local h=fs.open('init.lua') local s,r='','' while r do r=fs.read(h,math.huge) s=s..(r or "") end fs.close(h) load(s)() Запускаем компьютер, bibi пискнув и подождав секундочку, выполняет наш boot.lua, который хитро подменив pullSignal выполнит init.lua и после загрузки OpenOS в углу экрана радостно затикают часики. Теперь мы можем нажать lAlt и лицезреть список компонентов, или нажать тильду, и компьютер перезапустится. --==-- Конечно, вы можете сказать, что вывод списка компонентов портит экран, что перед выводом хорошо бы сохранять состояние экрана каким-нибудь gpu.get, вывод делать в свой интерфейс, а после, восстанавливать экран. И часики у меня никудышние. При скролле вниз (например в редакторе edit.lua) скроллируются вместе с текстом, вместо того, чтобы оставаться там где им положено. Но моя задача, показать метод на простейших примерах, а не переопределяя gpu строить интерфейсы. На основе этого метода можно построить API для TSR программ, а комбинируя его с перехватом component.proxy, component.invoke и последующей подменой gpu на виртуальный терминал, и вовсе, написать свою операционную систему. Для которой, кстати говоря, было бы неплохо уметь запускать OpenOS в окошке, но самое главное, в изолированной среде, чтобы труд программистов писавших ПО под OpenOS не пропадал зря. Я бы даже сказал, что изоляция OpenOS, на данном этапе, более важная задача, чем построение интерфейсов. Поэтому мы попробуем разобрать эту проблему в следующей статье. Посмотрим что выйдет. --==-- Небольшое дополнение: Если вам не охота играть с bibi, но нужно запустить резидентную программу прямо из-под OpenOS, то это сделать еще проще. local component = require("component") local cp = require("computer") local gpu = component.gpu local pullSignal = cp.pullSignal cp.pullSignal = function (...) local e={pullSignal(...)} if e[1]=='key_down' and e[4]==41 then cp.shutdown(true) end if e[1]=='key_down' and e[4]==56 then for k,v in component.list() do print(k,v) end end local s=' '..math.floor(cp.freeMemory()/1024)..'/'..(cp.totalMemory()/1024)..' kb free '..os.date('!%R') local w,h = gpu.maxResolution() gpu.set(w-#s+1,1,s) return table.unpack(e) end Сохраняем этот код в файл, допустим tsr.lua и запускаем его под OpenOSПрограмма выполнится, завершится, а резидентный участок останется в памяти. --==-- Если выбросить все что относится к демонстрации работы, у нас останется маленькая обертка local cp = require("computer") local pullSignal = cp.pullSignal cp.pullSignal = function (...) local e={pullSignal(...)} --код TSR программы return table.unpack(e) end Заключив в которую код своей TSR программы мы получим резидента, который будет выполнять TSR-код, где-то раз в пол секунды.
Эта таблица лидеров рассчитана в Москва/GMT+03:00
