Lua-скриптинг: Устранение неполадок
Эта страница охватывает распространённые проблемы, с которыми вы можете столкнуться при написании или отладке Lua-способностей, а также советы по миграции для авторов, переходящих с EliteScript. Если вы отлаживаете NPC-скрипты, см. NPC-скрипты. Для рабочих примеров см. Примеры и паттерны. Если вы только начинаете, см. Начало работы.
EliteMobs использует движок Lua-скриптинга MagmaCore, общий для плагинов Nightbreak. Документацию по общим концепциям, таким как песочница, планировщик, зоны, API мира, таблицы сущностей и методы UI игрока, см. на странице Движок Lua-скриптинга MagmaCore.
Распространённые проблемы
1. Способность вообще не загружается
Проверьте серверную консоль на наличие ошибок при запуске сервера. Самая частая причина — синтаксическая ошибка Lua (отсутствие end, несовпадающие скобки и т.д.). Также убедитесь, что файл заканчивается на .lua и находится в правильном каталоге powers.
Для собственных имён файлов проверьте, не задано ли isEnabled: false в файле метаданных .yml или .yaml с тем же базовым именем в том же каталоге: это исключает файл из обнаружения. Файл метаданных с тем же базовым именем рядом со встроенной способностью тоже применяет к ней свои поля isEnabled, effect, powerType и поля перезарядки. Если два файла способностей с одинаковым именем лежат в разных папках, загружается только один; консоль указывает пропущенную копию.
Файл должен возвращать таблицу с api_version = 1. В ней допустимы только api_version, необязательный priority и поддерживаемые имена хуков; значения хуков должны быть функциями. Оставляйте вспомогательные функции локальными, не добавляйте произвольные ключи в возвращаемую таблицу. Код верхнего уровня выполняется и при проверке определения, и при инициализации среды выполнения, без контекста хука.
2. Хук никогда не срабатывает
Убедитесь, что имя хука написано точно так, как указано в списке хуков. Частые ошибки: on_boss_hit (неправильно) vs. on_boss_damaged_by_player (правильно), или on_tick (неправильно) vs. on_game_tick (правильно).
Неподдерживаемый ключ в возвращаемой таблице мешает загрузке всего определения. Если оно загружается, но не выполняется, проверьте также, ссылается ли босс на имя файла способности, происходит ли нужное событие и не завершилась ли среда выполнения с ошибкой, не приостановлена ли она и не подавлены ли способности.
3. context.player равен nil
context.player заполняется только у тех хуков, чьё базовое событие несёт игрока. Он всегда равен nil в on_spawn, on_game_tick, on_boss_damaged, on_boss_damaged_by_elite, on_exit_combat, on_heal, on_death, on_phase_switch и on_mind_action.
Он заполняется в on_boss_damaged_by_player, on_reinforcement_damaged_by_player, on_player_damaged_by_boss, on_enter_combat и on_boss_target_changed, а также в on_zone_enter / on_zone_leave, когда участвующая сущность является игроком. Всегда проверяйте nil перед использованием. Полную таблицу см. в Хуках и жизненном цикле.
4. Таймаут / превышен бюджет выполнения
Каждый хук, запланированный коллбэк и вычисление файла делят с вложенными вызовами бюджет в 250 000 инструкций Lua и 50 мс процессорного времени потока. Если JVM не может измерить это время, используется резервный предел в 250 мс прошедшего времени. Ошибка указывает превышенный предел:
Lua instruction budget exceeded (250000 instruction limit)
Lua CPU-time budget exceeded (50ms current-thread CPU limit)
Lua elapsed-time fallback budget exceeded (250ms fallback; current-thread CPU time unavailable)
VM прерывает неуправляемые циклы Lua во время выполнения, но не может прервать выполняющийся вызов Java API. Отдельного предела в 50 мс реального времени после возврата вызова нет. Частые причины: перебор слишком большого числа сущностей, частицы в большой зоне без coverage и тяжёлая работа каждый тик. Используйте перезарядки, меньше точек выборки или context.scheduler:run_every(...), чтобы распределить работу между тиками.
5. Колбэк планировщика использует устаревшие данные
Вы, вероятно, используете внешний context вместо параметра колбэка. Измените function() ... context.boss ... end на function(tick_context) ... tick_context.boss ... end.
Запланированные колбэки получают новый контекст без исходного события, прямой цели и игрока. Явно сохраните нужную цель до планирования и проверьте её действительность при использовании. Заново считывайте текущие позиции и другие изменяющиеся свойства, не храните старый снимок таблицы.
6. Запрос зоны не возвращает сущности
Дважды проверьте определение зоны. Для нативных зон убедитесь, что kind указан в нижнем регистре ("sphere", а не "SPHERE"). Для скриптовых утилит убедитесь, что shape указан в верхнем регистре ("CONE", а не "cone"). Также проверьте, что origin или Target действительно разрешаются в допустимую локацию.
Проверьте также фильтр сущностей и мир. Нативные запросы зон босса по умолчанию используют living, исключают босса и ищут в его мире. Зоны скриптовых утилит по умолчанию используют PLAYER. Получайте цели Lua-зоны через full_target() или border_target() объекта зоны; у отдельного context.script:target({targetType = "ZONE_FULL"}) нет связанной зоны.
7. Частицы не появляются
Используйте допустимое имя Bukkit Particle: и "FLAME", и "flame" приводятся к верхнему регистру. Проверьте позицию, количество и необходимые данные. Для BLOCK нужны данные блока, которые этот помощник мира не передаёт; используйте поддерживаемую частицу или API частиц скрипта с параметром материала.
8. Перезарядка, похоже, не работает
check_local(key, duration) проверяет и устанавливает перезарядку за один вызов. Можно также вызвать local_ready(key), а затем set_local(duration, key), когда атака действительно начнётся. Одна проверка готовности не запускает перезарядку. Явные ключи общие для способностей одного босса, поэтому используйте префикс конкретной способности. Метаданные YAML не ограничивают выполнение Lua-хуков автоматически.
9. Босс продолжает выполнять способность после смерти
Задачи, принадлежащие context.scheduler, отменяются при закрытии среды выполнения способности. Отменяйте циклы атаки в on_exit_combat, если они должны остановиться до удаления босса. Проверьте и вызванный вспомогательный метод: некоторые Java-методы запускают собственные задачи. Например, world:run_fake_gold_nugget_damage(...) уже запускает цикл каждый тик со своим таймаутом; вызывайте его только один раз на группу снарядов.
Чтение сообщений об ошибках
При ошибке выполнения Lua-способности консоль выводит блок с префиксом [Lua], указывая файл и хук или колбэк. Номер строки появляется, если его можно извлечь из исходной ошибки. Перед отладкой читайте сообщение целиком. В файлах с -- @include номера строк относятся к развёрнутому исходному коду.
Типичная ошибка выглядит так:
[Lua] Error in 'push_zone.lua' at line 35 during 'on_boss_damaged_by_player':
[Lua] -> You tried to call a method or function that doesn't exist.
[Lua] -> Check the method name for typos, or make sure you're using ':' (colon) for method calls, not '.' (dot).
[Lua] -> Script has been disabled for this entity to prevent further errors.
Система переводит распространённые ошибки Lua на простой язык. Вот самые частые из них:
| Сырая ошибка Lua | Что говорит консоль |
|---|---|
attempt to call nil | Вы попытались вызвать несуществующий метод или функцию. Проверьте имя метода на опечатки или убедитесь, что используете : (двоеточие) для вызовов методов, а не . (точку). |
index expected, got nil | Вы попытались обратиться к полю чего-то, что равно nil. Проверьте, что более ранний код инициализировал это. |
attempt to index | Вы попытались обратиться к свойству nil или недопустимого значения. |
bad argument | Показывает конкретные детали несоответствия аргумента (ожидаемый тип vs. фактический тип). |
| Превышен бюджет выполнения | Сообщение указывает предел инструкций, процессорного времени или резервного прошедшего времени; см. раздел о бюджете выше. |
Неисправный экземпляр скрипта отключается для этой сущности. После исправления файла нужно перезагрузить определение и создать новую среду выполнения; неизменённый отключённый экземпляр не повторяет сбойный хук каждый тик.
Не предполагайте существование недокументированных псевдонимов
Lua API предоставляет определённый набор имён методов. Если вы пишете способности вручную или с помощью ИИ, не предполагайте, что существуют сокращённые или альтернативные имена. Следующие имена приведены как примеры тех, что не существуют и вызовут ошибки:
show_temporary_boss_bar()— используйтеplayer:show_boss_bar(title, color, style, duration).run_command_as_player()— используйтеplayer:run_command(command).em.location(...)— неправильное имя метода. Используйтеem.create_location(x, y, z), либоcontext.boss:get_location()/context.player.current_location.em.vector(...)— неправильное имя метода. Используйтеem.create_vector(x, y, z)или простые таблицы{x=0, y=1, z=0}.em.zone.sphere(...)— неправильное имя метода. Используйтеem.zone.create_sphere_zone(radius)или таблицу определения зоны вроде{kind = "sphere", radius = 5, origin = location}.entity:teleport_to(...)— используйтеentity:teleport_to_location(location).- В обёртках сущностей, предназначенных для боссов EliteMobs, используйте
entity:set_velocity_vector(vector). Общие обёртки MagmaCore также предоставляютset_velocity(...); проверьте, какую обёртку возвращает ваш контекст. entity:set_facing(...)— используйтеentity:face_direction_or_location(direction_or_location).
Проверьте справочник API используемой обёртки: Босс и сущности, Мир и окружение, Зоны и нацеливание или общий API. Похожие контексты могут предоставлять разные методы; правдоподобное имя псевдонима не делает его доступным.
Советы по миграции для авторов EliteScript
Если вы уже пишете хорошие EliteScripts, самый простой способ изучить Lua-способности:
-
Продолжайте думать в терминах событий, целей, зон, относительных векторов и частиц. Концепции те же — меняется только синтаксис. События EliteScript становятся именами хуков вроде
on_spawnилиon_boss_damaged_by_player. Цели и зоны передаются как таблицы вcontext.scriptс использованием тех же имён полей, что задокументированы на страницах Зоны EliteScript и Цели EliteScript. -
Перенесите поток управления в Lua. Случайные броски, общие вспомогательные функции, циклы, постоянное состояние (
context.state) и планирование задач (context.scheduler) — это то, что Lua добавляет и что чистый EliteScript не может легко сделать. Начните с преобразования одной ветвящейся или условной способности в Lua, сохраняя всё остальное без изменений. -
Используйте
context.scriptдля выбора целей и геометрии зон. Его таблицы спецификаций используют настоящие парсеры EliteScript дляtargetType,shape,Target,Target2,FinalTarget,range,offset,relativeOffset,coverageиfilter. Сверяйте имена полей и значения по умолчанию с документацией EliteScript, но прочитайте и ограничения объектов Lua. Статические объекты Lua не выполняют кэшированиеtrack: falseдействия YAML, а унаследованные данные действия отсутствуют.offsetиrelativeOffsetприменяются к определённым позициям, включаяLOCATION,LOCATIONSи выбранные точки зон. Явно задавайте фиксированное начало, если область должна оставаться на месте. -
Учитывайте две системы зон.
context.script:zone({shape = "SPHERE", ...})использует движок EliteScript с перечислениями в верхнем регистре и таблицамиTarget.context.zonesиспользует строчныйkindи обычные позицииorigin/destination. Смешивание этих форматов ключей может привести к пустой или неожиданной зоне, предупреждениям или ошибке выполнения.
Путь обучения для начинающих
Если вы хотите изучить эту систему с нуля, хорошо работает такая последовательность:
- Напишите файл только с
api_version = 1иon_spawn. - Заставьте босса отправить сообщение или воспроизвести звук.
- Добавьте перезарядку с
context.cooldowns. - Добавьте один хук, вызываемый игроком, например
on_boss_damaged_by_player. - Добавьте одно отложенное действие с
context.scheduler:run_after(...). - Добавьте один простой запрос нативной Lua-зоны или один простой
context.script:target(...). - Только потом переходите к вращающимся атакам, конечным автоматам и многоэтапным механикам.
Каждый шаг строится на предыдущем, и вы можете тестировать на каждом этапе. Не пытайтесь написать многофазного босса в качестве своей первой Lua-способности.
Следующие шаги
- Начало работы — структура файлов, хуки, пошаговый разбор первой способности, шаблоны для копирования
- NPC-скрипты — скрипты близости, взаимодействия и жизненного цикла NPC
- Примеры и паттерны — полностью рабочие способности для изучения и адаптации
