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

Лидеры


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

Показан контент с высокой репутацией за 29.03.2023 в Записи блога

  1. 4 балла
    Интересные изменения: Добавили поддержку Lua 5.4.4. Он не сильно отличается от Lua 5.3, но у него есть атрибуты локальных переменных (<const>, <close>). Последний выглядит очень полезным. Архитектура пока экспериментальна, нужно явно включать в конфиге мода. С остальными либами Lua тоже капитальная переделка. Наконец-то пофиксили инты в методах компонентов (если вам вызов когда-либо говорил invalid key to 'next', это оно). Сами либы обновили до последних версий (например, 5.3.6) и пересобрали с оптимизациями. В том числе под 64-битные армы (линукс, мак). Чтобы не путаться, на текстуре блока redstone i/o точками, как у игральной кости, показывается, какая это сторона: . Теперь рисуются юникод-символы выше U+FFFF. Там, например, всякие емоджи запиханы. Лишь бы в шрифте глифы под них имелись. Патчить шрифт можно ресурс-паками. Причём необязательно собирать глифы для всех символов: если в ресурс-паке глиф не указан, то он берётся из шрифта мода. Интернет-карту переписали на другую HTTP-библиотеку, чтобы она умела слать PATCH-запросы. Ссылка: 1.7.10 / 1.12.2.
  2. 1 балл
    Здесь опишу такие штучки, которые могут потребоваться продвинутым 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.
  3. 0 баллов
    В прошлый раз я патчил OpenComputers, чтобы пробрасывать нативную либу debug. Пойдём дальше. Добавим нативных либ package и os. Прокинем дефолтное окружение внутрь песочницы. Пропатчим мод, чтобы можно было загружать си-модули. Загрузим профилятор и посмотрим, что из этого вышло. На винде ничего не заработает. Гарантирую. Если надо профилировать, ставьте нормальные оси или мучайтесь. 0. Сырцы мода Так как мы будем патчить мод, надо сначала подготовить исходники. $ git clone https://github.com/MightyPirates/OpenComputers.git $ cd OpenComputers $ git checkout master-MC1.12 $ ./gradlew setupDecompWorkspace На третьей строке версию выбираем по вкусу и выпекаем всё необходимое для компиляции. 1. Нативные либы Здесь всё просто. Открываем файл src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala. Творим следующее: Вуаля. Теперь в machine.lua будут глобальные переменные package и _os. Отмечу отдельно, что меняем мы только архитектуру Lua 5.3. Уже на этом этапе у нас может сломаться персистентность. Это не страшно: она и должна сломаться. 2. Прокидываем окружение Поступаем аналогично тому, что делали в прошлой записи: меняем src/main/resources/assets/opencomputers/lua/machine.lua: Внутри песочницы в глобальной переменной env запечатлено будет всё окружение machine.lua. 3. C-модули Уже сейчас можно загрузить OpenOS и прописать env.require("libname"). Проблема в том, что C-модули так подключить не получится. Связано это с особенностью Lua. Абстрактно задача заключается в том, чтобы загрузить библиотку Lua с dlopen(..., RTLD_GLOBAL). System.loadLibrary в жаве флаг этот упускает по очевидным причинам, а нам он нужен. Значит, пришло время костылей. 3.1. Подключаем JNA: build.gradle Первый ханк нужен, чтобы можно было потом компилировать мод. Почему-то у курсов мавен не работает, а разбираться мне лень. 3.2. Патчим ещё раз src/main/scala/li/cil/oc/server/machine/luac/LuaStateFactory.scala Во-первых, подключаем хэшмапу. Потребуется. Во-вторых, импортируем JNA. Вернее, его часть. В-третьих, патчим код, чтобы он загружал Lua 5.3 через JNA. Магическая константа 0x101 — это значение RTLD_LAZY | RTLD_GLOBAL на моей системе. На фряхе, маке оно может отличаться. На этом этапе Lua 5.2 не будет работать. Включаться будет только Lua 5.3 из-за конфликта имён. Кроме того, JNA — это, вообще, огромная либа. Ради одной функции её подключать — это оверкилл. Но я в тонкостях JVM и JNI не силён. Как уже сказал, разбираться мне лень. 3.3. Компилируем $ ./gradlew assemble Выхлоп в build/libs. Берём жарник без суффиксов вроде -javadoc, -api, -sources. 4. Настраиваем профилятор Профилятор я написал сам на Rust. Вот ссылка: https://github.com/Fingercomp/lprofile-rs Очевидно, нам надо его скомпилировать. 4.1. Компилируем профилятор Ставим cargo (мультитул раста такой) любым удобным способом. Собираем: $ cd .. $ git clone --recurse-submodules https://github.com/Fingercomp/lprofile-rs.git $ cd lprofile-rs $ cargo build --release В target/release будет лежать liblprofile.so. Тырим его. 4.2. Определяем pwd Кидаем пропатченный OC в моды и запускаем игру. Пишем в опенкомпе env._os.getenv("PWD"), чтобы определить текущую директорию. Кидаем либу-профилятор в неё. 4.3. Профилируем Наконец, можно заняться мясом. local profiler = env.require("lprofile").Profiler() local result = profiler(function() local v = 0 for i = 1, 10e6, 1 do v = v + i end end) table.sort(result, function(lhs, rhs) return lhs.totalTime < rhs.totalTime end) print("Name", "# of calls", "Total time", "Total time, excluding inner calls") for _, v in ipairs(result) do print(("%s\t%d\t%.6f s\t%.6f s"):format(v.name, v.calls, v.totalTime, v.totalSelfTime)) end print("total time:", result.totalTime) 5. Зачем Мы получили наполовину сломанную версию OpenComputers: без Lua 5.3, без персистентности. Зато можем профилировать программы. Этот пост я написал, чтобы не забыть самому. Сомневаюсь, что кому-то интересно заниматься такой норкомагией.
  4. 0 баллов
    ; — это код, который делает приблизительно ничего. Не абсолютно: об этом статья. Когда я писал crateriform (видяшки в гайде про корутины этим набором прог зарендерены), я отталкивался от Lua-парсера на Lua от @LeshaInc (спасибо ему ещё раз): Lua-часть принимала исходный файл с кодом, парсила его на AST и генерировала по нему обратно код. С костылями. Для рендера нужен был "сценарий" — файл, в котором указаны, какие символы изменились и на какое значение, помимо прочего. Генерируемые костыли как раз этим занимались: они оборачивались вокруг выражений и писали в файл высчитанное значение. Чтобы показать кадр с "n = 10", как на гифке, и следующий за ним, сценарий такой: 5,16 5,16 expr 10 Здесь через пробел: позиция начала в исходном коде, позиция конца, опкод (expr) и значение выражения. Так как генерируемый код делает то же, что и исходный, но ещё рисует сценарий, а выражения могут быть засунуты где угодно и раскрываться сразу в несколько значений, я создаю лямбду (анонимную функцию) и тут же её вызываю. Вот как выглядит часть сгенерированного кода, который занимается строкой выше: highlight({5, 16}, {5, 16}, (function(...) local values = table.pack(...) return function() return table.unpack(values, 1, values.n) end end)((n)), 'expr') Выглядит очень сложно (для сложности есть причины), но сейчас интересует только это: (function(...) <...> end)(...) Эта конструкция присутствует как третий аргумент к highlight. Программисты на JS с этим шаблоном должны быть знакомы: создаётся лямбда и тут же вызывается с некими аргументами. В месте, где синтаксис позволяет указывать только выражение — в списке аргументов вызова в нашем случае, — таким образом размещаем стейтменты. На всякий случай скажу, что стейтмент — любая цельная конструкция, кроме выражений. Если вы берёте часть кода, которая отдельно, вырванная из кода, не выдаёт синтаксическую ошибку (например, цикл for или local x = 3), и её нельзя поместить после x = , это стейтмент. А теперь суть. (function(x) print(x^2) end)(2) (function(y) print(y + 2) end)(2) Этот код задумывался так, чтобы он два раза принтнул четвёрку. Запускаем: $ lua5.3 semicolon.lua 4.0 lua5.3: semicolon.lua:1: attempt to call a nil value stack traceback: semicolon.lua:1: in main chunk [C]: in ? ..? Lua игнорирует пробельные символы. Код вроде такого: func(3) (4)(5) ...эквивалентен такому: func(3)(4)(5) В примере с ошибкой вызывается первая лямбда, возвращающая nil, который мы затем пытаемся вызвать с аргументом — второй лямбдой. Поэтому получаем "attempt to call a nil value". Чиним с помощью ;: (function(x) print(x^2) --> 4.0 end)(2) ; (function(y) print(y + 2) --> 4 end)(2) Кстати, чтобы ещё с толку сбить: комментарий вместо ; ошибку не исправит. Ещё один ошибочный пример: local pi = math.pi (function(r) print(2 * pi * r) end)(3) Как чинить, вы уже знаете. Отдельно упомяну точку с запятой после return. return обязан быть последним стейтментом в блоке. А ; — это стейтмент. Почему тогда можно делать так? local function f(x) return x^2; end print(f(2)) --> 4.0 Ответ: потому что ; — это опциональная часть return, а не отдельный стейтмент. Если же залипает клавиша и получается вот так: local function f(x) return x^2;; end print(f(2)) ...то будет синтаксическая ошибка. Вторая точка с запятой — теперь отдельный стейтмент, которых после return быть не должно. Поэтому с уважением относитесь к этому стейтменту. Точка с запятой делает приблизительно ничего, но с умом.
Эта таблица лидеров рассчитана в Москва/GMT+03:00
×
×
  • Создать...