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

Lua-скриптинг: Программы поведения

webapp_banner.jpg

Программа поведения заменяет нативный ИИ моба поведениями на Lua, которые решают, куда он движется, куда смотрит, какую сущность выбирает целью и когда атакует. EliteMobs запускает эти программы в нативной системе Brain Minecraft через среду выполнения Mind из MagmaCore.

Программы поведения — это не Lua-способности. Lua-способность реагирует на события босса, например on_boss_damaged_by_player; программа поведения выполняется каждый тик и управляет движением моба. Босс может использовать и то и другое: поведение может попросить способности босса выполнить действие через on_mind_action.

Выбор программы

Задайте behavior в файле пользовательского босса, как описано в разделе Создание боссов, или в файле свойств моба для типа сущности. behavior: native сохраняет ванильный ИИ. Если версия сервера не поддерживает нативный Mind, EliteMobs при запуске выводит в журнал Native Mind interface is unavailable on this Minecraft version.. Тогда пользовательский босс, выбравший программу, не спавнится, а в журнал записывается Cannot spawn <boss file>: .... Босс, чей behavior указывает на отсутствующую или некорректную программу, точно так же не спавнится на любой версии.


Файлы​

Файлы поведения находятся в plugins/EliteMobs/behaviors/. EliteMobs записывает следующие встроенные файлы, если их нет:

plugins/
EliteMobs/
behaviors/
basic_melee.lua
modules/
target.lua
pursuit.lua
melee.lua
wander.lua
  • Файл .lua внутри любой папки с именем modules является модулем. Любой другой файл .lua является программой.
  • Босс ссылается на программу по её пути относительно behaviors/, например basic_melee.lua или guards/patrol_guard.lua.
  • EliteMobs загружает файлы поведения при запуске своей службы Mind. Файл, не прошедший проверку, пропускается, а в консоль выводится Could not load behavior <file> или Could not load behavior module <file> с указанием причины.
  • ID программ и модулей используют пространство имён elitemobs, например elitemobs:behavior/basic_melee. ID пишутся строчными буквами и могут содержать a-z, 0-9, ., _ и -, а после двоеточия ещё и /. Две программы не могут иметь одинаковый ID, и каждый ID модуля должен объявляться только в одном файле.
  • Одно пространство имён вмещает не более 256 модулей. Модуль может перечислить не более 32 зависимостей, программа может подключить не более 64 модулей, а каждый исходный файл ограничен 1 000 000 символов.

Действуют те же правила песочницы, что и для других Lua-скриптов. См. раздел Песочница Lua. Каждый моб, выполняющий программу, получает собственную среду Lua, поэтому локальные переменные файла не разделяются между мобами.


Файл программы​

Файл программы возвращает ai.program { ... }:

return ai.program {
id = 'elitemobs:behavior/basic_melee', revision = 1,
modules = {
'elitemobs:behavior/target', 'elitemobs:behavior/pursuit',
'elitemobs:behavior/melee', 'elitemobs:behavior/wander'
},
budget = {
callback_micros = 2000, entity_micros = 4000, server_micros = 5000,
max_callbacks = 20, max_instructions = 12000, max_action_requests = 2
}
}
ПолеТипПримечания
idстрокаОбязательно. ID с пространством имён, elitemobs:... для файлов в behaviors/.
revisionположительное целоеОбязательно.
modulesмассив строкНеобязательно. ID модулей, чьи памяти, сенсоры и поведения входят в программу. Зависимости загружаются раньше модулей, которым они нужны. Дубликаты и циклические зависимости отклоняются.
memories, sensors, behaviorsтаблицыНеобязательно. Программа может объявлять собственные элементы в том же формате, что и модуль.
budgetтаблицаНеобязательно. Мягкие ограничения планирования. См. Бюджеты.
runawayтаблицаНеобязательно. Жёсткие ограничения на каждый колбэк. См. Бюджеты.

Файл модуля​

Файл модуля возвращает ai.module { ... } и объединяет многократно используемые памяти, сенсоры и поведения:

ПолеТипПримечания
idстрокаОбязательно. ID модуля с пространством имён.
revisionположительное целоеОбязательно.
dependenciesмассив строкНеобязательно. ID других модулей, которые нужны этому модулю.
memoriesтаблицаНеобязательно. См. Памяти.
sensorsмассивНеобязательно. Записи ai.sensor { ... }.
behaviorsмассивНеобязательно. Записи ai.behavior { ... }.

sensors, behaviors, modules и dependencies должны быть простыми массивами без пропусков и именованных ключей. Имена сенсоров, поведений и памятей должны быть уникальны во всех модулях программы.


Памяти​

Памяти — это типизированные значения, которые сенсоры и поведения используют совместно для одного моба. Объявите каждую по имени:

memories = {
candidate = { type = 'uuid', persistent = false },
next_attack = 'integer'
}
ТипЗначение Lua
stringстрока
booleanлогическое значение
integerцелое число
numberчисло
uuidстрока UUID
position{ world = 'world', x = 0, y = 64, z = 0 }

Имя без двоеточия помещается в пространство имён программы. persistent по умолчанию равно false; true помечает память для сериализации вместе с сохранённым состоянием Mind. Читайте и записывайте памяти через c.memory:get(name), c.memory:set(name, value, ttl_ticks), c.memory:forget(name) и c.memory:contains(name). Третий аргумент set необязателен; если он указан, значение истекает через это количество тиков. Использование необъявленного имени вызывает ошибку.


Сенсоры​

Сенсор собирает информацию, обычно в памяти. Сенсоры выполняются раньше поведений.

ai.sensor {
id = 'elitemobs:behavior/find_target', interval = 20,
sense = function(c)
local target = c.perception:nearest_player(35)
if target then c.memory:set('candidate', target.uuid, 21)
else c.memory:forget('candidate') end
end
}
ПолеТипПо умолчаниюПримечания
idстрокаОбязательно.
intervalположительное целое1Тики между запусками.
senseфункцияОбязательно. Получает контекст Mind.

Сенсоры не могут использовать c.actuator; вызов метода актуатора из сенсора вызывает ошибку.


Поведения​

Поведение воздействует на моба, пока удерживает объявленные им элементы управления.

ai.behavior {
id = 'elitemobs:behavior/attack', priority = 10,
controls = { ai.controls.attack },
can_start = function(c) return c.perception:current_target() ~= nil end,
can_continue = function(c) return c.perception:current_target() ~= nil end,
tick = function(c)
local target = c.perception:current_target()
if target then c.actuator:attack(target) end
end
}
ПолеТипПо умолчаниюПримечания
idстрокаОбязательно.
priorityцелое число0Меньшие числа имеют приоритет.
controlsмассивнетЭлементы управления, которые это поведение арендует, пока выполняется.
can_startфункциявсегда trueДолжна возвращать true или false.
can_continueфункциякак can_startДолжна возвращать true или false.
startфункциянетВыполняется один раз при запуске поведения.
tickфункцияОбязательно. Выполняется каждый тик, пока поведение работает.
stopфункциянетfunction(c, reason); выполняется при остановке поведения.

Жизненный цикл​

  1. Пока поведение остановлено, оно проверяет can_start. Когда функция возвращает true и поведение может арендовать все объявленные элементы управления, выполняется start.
  2. Каждый тик, пока поведение работает, включая тик запуска, сначала выполняется can_continue. Если она возвращает false, поведение останавливается с причиной completed; иначе выполняется tick.
  3. stop(c, reason) получает одну из причин: completed, preempted, program_replaced, entity_removed, callback_failed или handle_closed. Остановка освобождает элементы управления поведения и прекращает удерживаемые им движение, выбор цели или атаку.

Ошибка Lua в can_continue, start или tick останавливает поведение с причиной callback_failed. Возврат из can_start или can_continue чего-либо, кроме логического значения, считается ошибкой. Для каждого сбоя в консоль выводится Mind <program> callback <callback> failed with ....

Элементы управления​

Элемент управленияРазрешает
ai.controls.movec.actuator:move_to(...), c.actuator:stop_moving()
ai.controls.lookc.actuator:look_at(...)
ai.controls.jumpc.actuator:jump()
ai.controls.targetc.actuator:set_target(...), c.actuator:clear_target()
ai.controls.attackc.actuator:attack(...)
ai.controls.actionc.actions:request(...)
ai.controls.use_itemЗарезервировано; у Lua-актуатора нет метода для предметов.

Каждый элемент управления принадлежит одновременно только одному работающему поведению. Поведение с меньшим числом priority забирает элемент управления у поведения с большим числом, и то останавливается с причиной preempted. Забрать элемент управления у поведения с меньшим числом нельзя. При равных приоритетах элемент управления получает поведение, чей id идёт первым по алфавиту. Вызов метода актуатора без удержания соответствующего элемента управления вызывает ошибку. c.actuator:stop_all() останавливает только то, что удерживает поведение.

Во встроенных модулях выбор цели использует приоритет 5, атаки ближнего боя — 10, преследование — 20, а праздное блуждание — 50, поэтому преследование забирает движение у блуждания, как только появляется цель.

Запрос действий босса​

Поведение, удерживающее ai.controls.action, может вызвать c.actions:request(identifier, payload). EliteMobs передаёт запрос Lua-способностям босса через on_mind_action. Идентификатор должен быть ключом с пространством имён в нижнем регистре длиной не более 128 символов, например 'elitemobs:slam'. Полезная нагрузка содержит не более 16 записей с ключами в нижнем регистре длиной не более 64 символов, а строковые значения ограничены 256 символами; недопустимый идентификатор или полезная нагрузка вызывают ошибку. Вызов возвращает accepted, deferred или rejected; запросы сверх max_action_requests программы за один тик возвращают deferred. Формат полезной нагрузки описан в разделе Контекст действия Mind.


Бюджеты​

budget задаёт мягкие ограничения планирования. Когда моб достигает одного из них за тик, среда выполнения Mind пропускает оставшиеся колбэки этого моба до следующего тика. Уже выполняющийся колбэк всегда завершается.

КлючПо умолчаниюПримечания
callback_micros2000Колбэк, выполняющийся дольше, завершает колбэки моба на этот тик.
entity_micros4000Общее время колбэков на одного моба за тик.
server_micros20000Колбэки моба прекращаются на этот тик, как только все программы Mind вместе израсходовали столько времени.
max_callbacks128Колбэков на одного моба за тик.
max_action_requests8Запросов действий на одного моба за тик; не более 64.

Все значения должны быть положительными, callback_micros не может превышать entity_micros, а тот не может превышать server_micros.

runaway задаёт жёсткие ограничения, прерывающие отдельный колбэк:

КлючПо умолчаниюПримечания
cpu_micros50000Процессорное время потока на один колбэк.
max_instructions50000Инструкций Lua на один колбэк.

budget также принимает max_instructions, как во встроенном basic_melee.lua. Объявляйте его в budget или в runaway, но не в обоих. Колбэк, превысивший жёсткое ограничение, завершается сбоем так же, как при любой другой ошибке колбэка.


Следующие шаги​