Lua-скриптинг: Программы поведения
Программа поведения заменяет нативный ИИ моба поведениями на 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); выполняется при остановке поведения. |
Жизненный цикл
- Пока поведение остановлено, оно проверяет
can_start. Когда функция возвращаетtrueи поведение может арендовать все объявленные элементы управления, выполняетсяstart. - Каждый тик, пока поведение работает, включая тик запуска, сначала выполняется
can_continue. Если она возвращаетfalse, поведение останавливается с причинойcompleted; иначе выполняетсяtick. 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.move | c.actuator:move_to(...), c.actuator:stop_moving() |
ai.controls.look | c.actuator:look_at(...) |
ai.controls.jump | c.actuator:jump() |
ai.controls.target | c.actuator:set_target(...), c.actuator:clear_target() |
ai.controls.attack | c.actuator:attack(...) |
ai.controls.action | c.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_micros | 2000 | Колбэк, выполняющийся дольше, завершает колбэки моба на этот тик. |
entity_micros | 4000 | Общее время колбэков на одного моба за тик. |
server_micros | 20000 | Колбэки моба прекращаются на этот тик, как только все программы Mind вместе израсходовали столько времени. |
max_callbacks | 128 | Колбэков на одного моба за тик. |
max_action_requests | 8 | Запросов действий на одного моба за тик; не более 64. |
Все значения должны быть положительными, callback_micros не может превышать entity_micros, а тот не может превышать server_micros.
runaway задаёт жёсткие ограничения, прерывающие отдельный колбэк:
| Ключ | По умолчанию | Примечания |
|---|---|---|
cpu_micros | 50000 | Процессорное время потока на один колбэк. |
max_instructions | 50000 | Инструкций Lua на один колбэк. |
budget также принимает max_instructions, как во встроенном basic_melee.lua. Объявляйте его в budget или в runaway, но не в обоих. Колбэк, превысивший жёсткое ограничение, завершается сбоем так же, как при любой другой ошибке колбэка.
Следующие шаги
- Создание боссов: behavior -- выбор программы для босса
- Справочник Lua API: контекст программы Mind -- все методы
c.memory,c.perception,c.actuatorиc.actions - Хуки и жизненный цикл --
on_mind_actionи другие хуки Lua-способностей
