Lua API
Здесь основные вызовы, и каждый работает в текущей версии evolve.
start
1Скрипт — это файл .lua в папке scripts: в меню игры её открывает Настройки › Скрипты › «Открыть папку». Он возвращает таблицу колбэков и перечитывается при сохранении. Lua 5.4, одна общая VM на все скрипты, колбэки вызываются по очереди.
- 1Рисуй в on_draw, играй в on_update. renderer.* вне кадра ничего не нарисует, orders.* внутри on_draw уйдут не тем путём.
- 2Сущность из entities.* годится на один кадр: сначала e:valid(), а сам список бери заново через entities.*.
- 3Первые ~10 секунд после запуска идёт обход схемы. Позиция, команда и здоровье читаются сразу, броня, способности и gamerules до конца обхода отдают 0.
- 4Пока client.local_hero() пустой, фильтры по команде считают врагом всех подряд — сначала герой, потом таргетинг.
- 5Координаты — мировые юниты Source: карта примерно от -8288 до 8288 по X и Y, точка это vec(x, y, z).
client.local_hero()→ Entity|nilГерой локального игрока. nil, пока ты не в матче.
client.screen_size()→ number, numberШирина и высота окна в пикселях.
client.cursor_world()→ Vector|nilТочка под курсором на z-плоскости героя.
globals.curtime()→ numberИгровое время в секундах. Часы для любых таймеров.
globals.tickcount()→ numberНастоящий тик клиента, 30 Гц, монотонный.
ОсторожноПродолжает идти, когда игровое время на паузе.
vec(x, y, z)→ VectorТочка или направление. Рядом vec2, angle и color(r, g, b, a) — все компоненты 0..255.
log.info(msg)Строка в evolve.log. Первый инструмент, когда скрипт молчит.
-- scripts/hello.lua
local s = {}
function s.on_update() -- game logic
local me = client.local_hero()
if not (me and me:valid()) then return end
end
function s.on_draw() -- renderer.* only
local me = client.local_hero()
if not (me and me:valid()) then return end
local x, y, on = renderer.world_to_screen(me:position())
if on then
renderer.text(font_default, x, y - 40, color(255, 255, 255, 255), "hello")
end
end
return spacks
2Пак — это папка: pack.json, точка входа, модули, картинки и данные. Одна и та же папка работает брошенной в scripts/ и её же вы загружаете в магазин zip-архивом (Кабинет автора › Версии).
- 1pack.json: version обязательна (вида 1.2.0), entry — файл входа (по умолчанию main.lua), name — имя в списке скриптов, evolve — сборка, под которую написан пак.
- 2Имя папки — имя пака. Модули грузятся от него: require("my_pack.lib.util") читает lib/util.lua. В магазине папка называется по слагу листинга.
- 3Путь с ./ — это папка вашего пака: renderer.load_image("./art/icon.png"), иконки меню "./art/…", fs.read("./data/x.json"). Имя папки знать не нужно.
- 4В паке можно .lua, картинки .png .jpg .bmp .tga .gif (до 4096 px по стороне) и данные .json .txt .md .csv. Zip до 16 МБ, до 1500 файлов.
- 5При загрузке каждый .lua компилируется, каждая картинка и json открываются. Ответ перечисляет все ошибки сразу, с файлом и строкой, а версию потом читает админ.
renderer.load_image(path)→ numberКартинка по пути: "./art/x.png" из своего пака, "panorama/images/…" из игры. Возвращает хэндл или -1; кэшируется по пути.
ОсторожноГрузите один раз при загрузке скрипта, не каждый кадр. После перезагрузки скриптов изменённый файл подхватывается сам.
renderer.load_image_bytes(bytes [, key])→ numberКартинка из байтов, которые скрипт уже держит (fs.read, http). Кэш по key, без него по содержимому.
-- scripts/my_pack/pack.json : { "version": "1.0.0", "name": "My Pack" }
-- scripts/my_pack/main.lua
local util = require("my_pack.lib.util") -- scripts/my_pack/lib/util.lua
local icon = renderer.load_image("./art/icon.png")
local cfg = json.decode(fs.read("./data/defaults.json") or "{}")
local s = {}
function s.on_draw()
local me = client.local_hero()
if not (me and me:valid()) or icon < 0 then return end
local x, y, on = renderer.world_to_screen(me:position())
if on then renderer.image(icon, x - 16, y - 80, 32, 32) end
end
return sevents
3Колбэки. Всё, что приходит из потоков движка, буферизуется и разбирается с пампа кадра — обработчик выполняется рядом с on_update, поэтому в нём можно звать любой вызов API.
- 1Два способа подписаться: вернуть из файла таблицу { on_update = fn } или позвать events.register. Оба живут до перезагрузки скрипта.
- 2Данные события валидны только внутри колбэка — разбирай сразу, не складывай таблицу «на потом».
- 3Обработчик, который бросил, отцепляется и пишет трейс; diag.errors() показывает его в клиенте.
events.register(name, fn)Подписка на колбэк.
- namestring
- Имя колбэка, например "on_update".
- fnfunction
- Обработчик. Аргументы зависят от события.
"on_update" fn()Логический тик, каждый кадр. Место для orders.* и всей игровой логики.
"on_draw" fn()Кадр. renderer.* работает здесь и только здесь.
"on_game_event" fn(name, data)Любое нативное событие: подписки на конкретные имена нет, приходит всё.
- namestring
- Имя события движка, например "dota_player_gained_level".
- datatable
- Все поля события по именам, снятые в момент срабатывания.
ОсторожноПриезжает с пампа кадра, не позже чем через кадр после срабатывания, в порядке возникновения.
"on_key" fn(vk, down)Сырая клавиша из wndproc.
- vknumber
- Код клавиши Windows.
- downboolean
- Нажатие или отпускание.
ОсторожноВозврат игнорируется: скрипт не может съесть клавишу у игры.
"on_projectile" fn(p)Создан tracking-снаряд.
- ptable
- handle, source, target, ability, move_speed, is_attack, dodgeable, expire_time, max_impact_time.
ОсторожноСущности внутри резолвятся в момент доставки: тот, кто умер в этом же кадре, придёт как nil.
"on_modifier_gained" fn(npc, mod)Появился модификатор. Пара к нему — on_modifier_lost(npc, name).
- npcEntity
- Кому повесили.
- modtable
- name, stacks, duration и остальные поля модификатора.
"on_prepare_unit_orders" fn(o)Приказ уходит на сервер. Верни false, чтобы отменить его; o.synthetic отделяет приказ скрипта от клика человека.
- otable
- order, target, position, ability, unit, queue, issuer, synthetic.
ОсторожноЕдинственный колбэк, который выполняется прямо на игровом потоке. Движок ждёт VM 2 мс и, не дождавшись, пропускает приказ без вето — считай это в events.veto_timeouts().
timers.after(sec, fn)→ numberОдноразовый таймер, возвращает id. Рядом timers.every(sec, fn) и timers.clear(id).
- secnumber
- Через сколько секунд игрового времени.
- fnfunction
- Что выполнить.
events.dropped()→ numberСколько событий потеряно из-за переполненной очереди. Должно быть 0.
local s = {}
function s.on_projectile(p)
local me = client.local_hero()
if not (me and me:valid()) then return end
if p.target == me and p.dodgeable then
log.info("incoming, impact in " ..
string.format("%.2f", p.max_impact_time - globals.curtime()))
end
end
return sentities
4Списки сущностей. Снимок обновляется примерно 20 раз в секунду, userdata интернируется — сущность можно класть ключом в таблицу.
- 1Фильтр — обычная таблица: { team = "enemy", alive = true, around = pos, radius = 600, class = "creep" }. Поле class сравнивается подстрокой имени класса.
- 2team — строка "enemy" или "ally", не число.
- 3Половина живых нейтралов на карте — это заранее заспавненная следующая волна лагеря: она неуязвима и отсеивается через e:is_waiting_to_spawn().

entities.all()→ Entity[]Все сущности из снимка клиента.
entities.heroes(team)→ Entity[]Герои, при желании только одной стороны.
- teamstring?
- "enemy", "ally" или ничего — тогда все.
entities.npcs(filter)→ Entity[]Юниты по фильтру: крипы, иллюзии, саммоны, вышки.
- filtertable?
- team / alive / around / radius / class.
entities.in_radius(pos, radius, filter)→ Entity[]Всё, что стоит в круге.
- posVector
- Центр круга.
- radiusnumber
- Радиус в мировых юнитах.
- filtertable?
- Тот же фильтр, что у npcs.
entities.closest(pos, filter)→ Entity|nilБлижайший юнит, себя не считает.
- posVector
- Откуда мерить.
- filtertable?
- Тот же фильтр, что у npcs.
entities.by_class(name)→ Entity[]Точное совпадение класса и всех наследников — не угадывание по имени.
- namestring
- Класс C++, например "C_DOTA_BaseNPC_Tower".
entities.trees()→ Entity[]Деревья. Рядом entities.runes() и entities.physical_items() — предметы, лежащие на земле.
entities.get(index)→ Entity|nilСущность по индексу; entities.from_handle(h) — по хэндлу.
- indexnumber
- Индекс сущности.
local s = {}
function s.on_draw()
local me = client.local_hero()
if not (me and me:valid()) then return end
for _, h in ipairs(entities.heroes("enemy")) do
if h:valid() and h:is_alive() and not h:is_dormant() then
renderer.circle_3d(h:position(), h:attack_range(), color(255, 80, 80, 120))
end
end
end
return sentity
5Объект сущности. Один и тот же тип для героя, крипа, способности и предмета — набор осмысленных методов зависит от того, что это.
- 1Первым делом e:valid(). Исчезнувшая сущность отвечает дефолтами, а не ошибкой.
- 2Сущность в тумане (e:is_dormant()) заморожена на последнем виденном состоянии: позиция, здоровье и инвентарь у неё старые.
- 3Ключом в таблице держи e:handle() — индекс переиспользуется, хэндл несёт ещё и серийный номер.
e:valid()→ booleanСущность ещё есть в игре.
e:position()→ VectorМировая позиция. Синоним e:origin().
e:health()→ numberТекущее здоровье. Рядом max_health(), health_pct() (0..100), mana(), mana_pct().
e:is_alive()→ booleanЖив. Рядом is_enemy(), is_ally(), is_illusion(), is_hero(), is_dormant().
e:distance(target)→ numberРасстояние в мировых юнитах.
- targetVector|Entity
- Точка или другая сущность.
e:ideal_speed()→ numberТочная текущая скорость со всеми модификаторами: ботинки, хаст, слоу, ауры.
Осторожноe:move_speed() — это базовая характеристика из сети, без модификаторов. Для движения нужен ideal_speed.
e:effective_bat()→ numberБазовое время атаки с учётом модификаторов — то, из чего считается таймлайн атаки. Рядом e:aps() и e:seconds_per_attack().
e:is_stunned()→ booleanБит состояния юнита. Тем же способом читаются is_silenced, is_rooted, is_hexed, is_disarmed, is_magic_immune, is_invulnerable.
ОсторожноБиты отвечают «да или нет» и не несут часов. Сколько осталось — считается по модификаторам, не по биту.
e:modifiers()→ table[]Все модификаторы: name, class, stacks, duration, creation_time, die_time, ability, caster, parent, is_aura.
Осторожноparent — тот, на ком висит бафф, и для дебаффа это не кастер.
e:has_modifier(name)→ booleanБыстрая проверка одного модификатора; e:get_modifier(name) вернёт его таблицу.
- namestring
- Внутреннее имя, например "modifier_bloodseeker_thirst".
e:handle()→ numberПолный 32-битный хэндл: индекс и серийный номер. Безопасный ключ между кадрами.
e:visible_to_enemy()→ booleanВидит ли тебя противник. Надёжно для своих же юнитов.
abilities
6Способности и предметы — тоже сущности, со своими методами: кулдаун, мана, дальность, каст-пойнт и данные из KV.
- 1Кулдаун, мана и фаза каста реплицируются только владельцу. У чужого героя способность всегда выглядит готовой — это «нам не сказали», а не «не на кулдауне».
- 2e:get_ability(name) при промахе возвращает невалидную сущность, а не nil, и каждый вызов сканирует снимок. Резолвь один раз и держи в локальной переменной.
- 3Пробивает ли спелл BKB, решает ab:immunity_type() == 1, а не догадка по названию.

hero:get_ability(name)→ EntityСпособность по имени.
- namestring
- Внутреннее имя, например "lion_impale".
hero:abilities()→ Entity[]Все способности героя в порядке слотов.
ОсторожноСканирует снимок на каждый вызов — кэшируй, а не зови в цикле отрисовки.
hero:get_item(slot)→ EntityПредмет в слоте; hero:items() отдаёт весь инвентарь.
- slotnumber
- Слот инвентаря, считая с нуля: 0..5 — то, что на герое, дальше рюкзак и стэш.
ab:cooldown()→ numberОстаток кулдауна в секундах, 0 — готово.
ab:is_ready()→ booleanУровень выше нуля и кулдаун истёк.
ab:mana_cost()→ numberСтоимость на текущем уровне; ab:ability_level() — сам уровень.
ab:cast_range()→ numberДальность каста со всеми бонусами.
ab:cast_point()→ numberКаст-пойнт в секундах — задержка между приказом и срабатыванием.
ab:special(key, level)→ numberЛюбое число из данных способности, с талантами и аганимом.
- keystring
- Ключ из AbilityValues, например "duration" у lion_impale. Список ключей — в данных способности, не на глаз.
- levelnumber?
- Уровень; -1 или ничего — текущий.
ОсторожноУ неизученной способности текущий уровень даёт 0 — передавай уровень явно: ab:special("duration", 0).
ab:immunity_type()→ number|nil0 никак, 1 пробивает по врагам, 2 не пробивает, 3 и 4 — то же для союзников.
Осторожноnil означает «не смогли прочитать», и это не то же самое, что 0.
ab:dispellable_type()→ number|nil0 не диспеллится, 1 сильный диспелл, 2 обычный, 3 нет.
it:item_charges()→ numberЗаряды предмета: бутылка, вэнд, урна. У способностей заряды считает ab:charges().
local s = {}
local hex
function s.on_update()
local me = client.local_hero()
if not (me and me:valid()) then return end
hex = hex or me:get_ability("lion_voodoo")
if not (hex and hex:valid() and hex:is_ready()) then return end
if me:mana() < hex:mana_cost() then return end
for _, h in ipairs(entities.heroes("enemy")) do
if h:is_alive() and not h:is_magic_immune()
and me:distance(h) < hex:cast_range() then
orders.cast_target(hex, h)
return
end
end
end
return sorders
7Настоящие приказы движка — тот же путь, которым идёт клик мышью. Отправляй их с кадра, из on_update.
- 1У каждого приказа есть необязательный последний аргумент queue: orders.move_to(pos, true) встанет в очередь как шифт-клик.
- 2Приказ из чужого потока (колбэк GC, сетевой хук, свой поток) обязан лечь в буфер и уйти уже из on_update. Вызов движка из чужого потока вешает клиент на мьютексе.
- 3Приказ каждый кадр — это не «быстрее», а спам: сервер обрабатывает последний, а поток приказов виден в реплее.

orders.move_to(pos, queue)Приказ на движение. Рядом attack_move(pos) и move_to_target(entity).
- posVector
- Куда идти.
- queueboolean?
- Поставить в очередь вместо замены.
orders.attack(target, queue)Атака по цели.
- targetEntity
- Кого атаковать.
- queueboolean?
- Поставить в очередь.
orders.cast_target(ability, target, queue)Каст по юниту.
- abilityEntity
- Способность или предмет.
- targetEntity
- Цель каста.
- queueboolean?
- Поставить в очередь.
orders.cast_position(ability, pos, queue)Каст по точке.
- abilityEntity
- Способность или предмет.
- posVector
- Точка каста.
- queueboolean?
- Поставить в очередь.
orders.cast_no_target(ability, queue)Каст без цели; cast_toggle(ability) переключает тогл.
- abilityEntity
- Способность или предмет.
- queueboolean?
- Поставить в очередь.
orders.use_item(item, target)Применить предмет.
- itemEntity
- Предмет из инвентаря.
- targetEntity|Vector?
- Цель или точка, если предмет их требует.
orders.stop()Остановка; orders.hold() — держать позицию.
orders.unit_move_to(unit, pos, queue)Приказ не своему герою, а подконтрольному юниту; так же устроен unit_attack.
- unitEntity
- Кем приказываем: саммон, курьер, второй мипо.
- posVector
- Куда идти.
- queueboolean?
- Поставить в очередь.
orders.raw(type, opts)Полный набор dotaUnitOrder_t, если нужного именованного вызова нет.
- typenumber
- Код приказа из orders.types.
- optstable
- target, position, ability, unit, queue, issuer, show_effects.
renderer
8Immediate-рендер на d3d11. Всё, что нарисовано в on_draw, живёт один кадр; мировые примитивы можно вместо этого зарегистрировать один раз, и их перепроецирует C++.
- 1Цвет — color(r, g, b, a), 0..255. Текст и измерение принимают шрифт первым аргументом, глобальный font_default уже готов.
- 2world_to_screen возвращает три значения, и третье — «точка вообще на экране». Без этой проверки объекты за спиной рисуются зеркально.
- 3Шрифт грей заранее: метрики до перестройки атласа отдают фолбэк на 47 пикселей, и вёрстка по ним разъедется.
renderer.world_to_screen(pos)→ number, number, booleanПроекция точки: x, y и признак того, что она на экране.
- posVector
- Мировая точка.
renderer.text(font, x, y, col, str, align, shadow)Текст на экране; renderer.measure_text(font, str) вернёт его ширину и высоту.
- fontFont
- Шрифт; годится глобальный font_default.
- x, ynumber
- Экранные координаты.
- colColor
- Цвет.
- strstring
- Текст.
- alignstring?
- Комбинация из "c" — по центру и "r" — вправо, "v" — по вертикали.
- shadowboolean?
- Тень под текстом.
renderer.load_font(name, size, weight, flags)→ FontШрифт, закэшированный по параметрам — звать повторно безопасно.
- namestring
- Имя системного шрифта.
- sizenumber
- Размер в пикселях.
- weightnumber?
- Насыщенность.
- flagsnumber?
- Флаги начертания.
renderer.line(x1, y1, x2, y2, col, thick)Линия. Рядом rect, rect_outline, rounded_rect, triangle, poly и gradient.
renderer.circle_filled(x, y, r, col, segs)Залитый круг на экране; renderer.circle рисует контур.
renderer.circle_3d(center, radius, col, segs, thick)Кольцо на земле — индикатор дальности как в самой игре.
- centerVector
- Центр в мире.
- radiusnumber
- Радиус в мировых юнитах.
renderer.load_image(path)→ ImageКартинка; renderer.image(handle, x, y, w, h, tint) её рисует.
- pathstring
- Файл на диске или ресурс panorama из самой игры.
renderer.world_circle(id, center, radius, col, opts)Мировая фигура, которую дальше ведёт C++: регистрируешь на тике, а перепроецируется она каждый кадр. Рядом world_line, world_poly, world_text, world_image и world_clear.
- idstring
- Идентификатор фигуры: та же строка заменяет прошлую.
- centerVector
- Центр.
- radiusnumber
- Радиус в мировых юнитах.
- colColor
- Цвет.
- optstable?
- ttl (по умолчанию 0.25 с), thickness, segments, filled, layer.
ОсторожноРегистрация — не отрисовка: звать её можно из on_update, и рисуется она под тем, что рисует on_draw.
local s = {}
local font = renderer.load_font("Verdana", 13)
function s.on_draw()
local me = client.local_hero()
if not (me and me:valid()) then return end
for _, h in ipairs(entities.heroes("enemy")) do
if h:is_alive() then
local x, y, on = renderer.world_to_screen(h:position())
if on then
local w = renderer.measure_text(font, h:health() .. "")
renderer.text(font, x - w / 2, y - 60, color(255, 90, 90, 255), h:health() .. "")
end
end
end
end
return sui
9Меню. Контролы объявляются один раз на уровне файла и переживают хот-релоад; значение читается через :get().
- 1Первый аргумент — "вкладка.группа", разбивается по первой точке; без точки контрол уедет в группу general.
- 2Ключ настройки — это "вкладка.группа.подпись". Переименовал подпись — потерял всё, что под ней сохранено; для косметики есть c:caption().
- 3У combo и listbox :get() отдаёт индекс с нуля: options[c:get() + 1].
ui.checkbox(group, label, default)→ ControlГалочка.
- groupstring
- "вкладка.группа".
- labelstring
- Подпись, она же часть ключа настройки.
- defaultboolean
- Значение при первом запуске.
ui.slider_int(group, label, min, max, default, opts)→ ControlЦелочисленный слайдер; ui.slider_float добавляет число знаков после запятой.
- min, maxnumber
- Границы.
- optstable?
- { unit = " ms", min_text = "Off", max_text = "Max" }.
ui.combo(group, label, items, default_index)→ ControlВыпадающий список; ui.multiselect и ui.listbox устроены так же.
- itemsstring[]
- Варианты.
- default_indexnumber
- Индекс с нуля.
ui.hotkey(group, label, key, mode)→ ControlКлавиша. :get() отвечает «зажата прямо сейчас», :key() — какой код на неё назначен.
- keynumber
- Код клавиши по умолчанию.
- modestring?
- "hold", "toggle", "always" или "off".
c:get()Текущее значение; c:set(v) его пишет.
c:on_change(fn)→ ControlРеакция на изменение. Возвращает сам контрол, вызовы можно цеплять.
- fnfunction
- Вызывается без аргументов — значение читай через :get().
c:depend(other, expected, mode)→ ControlПодчинить контрол другому.
- otherControl
- Контрол-родитель.
- expectednumber?
- Значение, при котором этот активен, по умолчанию 1.
- modestring?
- "grey" — показать серым, "hide" — убрать со страницы.
local enabled = ui.checkbox("Draw.ESP", "Enemy circles", true)
local radius = ui.slider_int("Draw.ESP", "Radius", 100, 900, 400, { unit = " u" })
radius:depend(enabled)
local s = {}
function s.on_draw()
if not enabled:get() then return end
for _, h in ipairs(entities.heroes("enemy")) do
if h:is_alive() then
renderer.circle_3d(h:position(), radius:get(), color(255, 80, 80, 120))
end
end
end
return sml
10ONNX в процессе игры, синхронно или на рабочем потоке. Модель — обычный файл рядом со скриптом, ядро о ней ничего не знает.
- 1Нужен onnxruntime.dll рядом с evolve.dll. ml.available() отвечает, есть ли он, ml.gpu_available() — доступен ли DirectML.
- 2net:run синхронный и стоит 0.2–0.5 мс. Если модель тяжелее, отдавай её в net:submit и забирай через net:poll в следующем кадре.
- 3history отдаёт сырые мировые значения. Нормализация — задача скрипта, а не буфера.
ml.load(path, provider)→ Model|nilМодель, закэшированная по пути и провайдеру. nil при ошибке, причина в ml.last_error().
- pathstring
- Файл .onnx.
- providerstring?
- "cpu" по умолчанию, "gpu" или "auto".
net:run(input)→ Tensor|nilСинхронный прогон.
- inputTensor|Tensor[]
- Один тензор или список по числу входов.
net:submit(input)Асинхронный прогон на рабочем потоке; net:poll() забирает результат, net:busy() говорит, что он ещё считается.
ml.tensor(rows, cols)→ TensorОбнулённый тензор. Индексы с единицы: t:set(r, c, v), t:get(r, c), t:table().
- rowsnumber
- Число строк.
- colsnumber?
- Число столбцов; без него получится вектор.
history.tensor(entity, frames)→ Tensor|nilБуфер движения [frames, 11], спереди дополненный нулями. Каналы перечислены в history.CH.
- entityEntity
- За кем следим.
- framesnumber
- Сколько кадров истории, от старых к новым.
history.set_rate(hz)Частота общего буфера. history.track_all(true) расширит его со всех героев на всех юнитов — это заметно дороже.
- hznumber
- Частота записи, по умолчанию 20.
data
11Файлы, настройки, лог и JSON. Всё, что скрипту нужно, чтобы пережить перезапуск клиента.
- 1Свои файлы держи в fs.datadir(), а соседние файлы пакета ищи от fs.scriptdir(). Путь из имени чанка в ошибке доставать нельзя: Lua обрезает его на 60 символах.
- 2Для того, что пишется всю игру, есть fs.append. Цикл read-modify-write через fs.write квадратичен по размеру файла и теряет всё при падении на середине.
log.info(msg)Строка в evolve.log; рядом log.warn и log.error.
fs.read(name)→ string|nilПрочитать файл целиком; fs.exists и fs.listdir рядом.
- namestring
- Путь к файлу.
fs.append(name, str)Дописать в конец, создав родительскую папку. Способ вести журнал по ходу матча.
fs.datadir()→ stringПапка data/ клиента, уже созданная. fs.scriptdir() отдаёт абсолютный путь к scripts/.
config.write_int(key, value)Типизированное хранилище скрипта: read_int, read_float, read_bool, read_string и их write-пары, плюс config.has и config.delete.
- keystring
- Свой ключ, не связанный с меню.
- valuenumber
- Значение.
config.get(path)Значение контрола меню по его пути; config.set(path, v) его пишет.
- pathstring
- "вкладка.группа.подпись" — тот же ключ, что в config.export().
ОсторожноБросает, если такого пути нет, — и это нарочно: иначе опечатка в пути молча выглядит как выключенная настройка.
json.encode(value)→ stringТаблица в строку; json.decode(str) — обратно.
diag.errors()→ table[]Ошибки скриптов, свёрнутые по одной строке на уникальный сбой: event, script, msg, traceback, count. Рядом diag.profile() со временем каждого обработчика.