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

Lua-скриптинг: Устранение неполадок

webapp_banner.jpg

Эта страница охватывает распространённые проблемы, с которыми вы можете столкнуться при написании или отладке Lua-способностей, а также советы по миграции для авторов, переходящих с EliteScript. Если вы отлаживаете NPC-скрипты, см. NPC-скрипты. Для рабочих примеров см. Примеры и паттерны. Если вы только начинаете, см. Начало работы.

Общий движок Lua

EliteMobs использует движок Lua-скриптинга MagmaCore, общий для плагинов Nightbreak. Документацию по общим концепциям, таким как песочница, планировщик, зоны, API мира, таблицы сущностей и методы UI игрока, см. на странице Движок Lua-скриптинга MagmaCore.


Распространённые проблемы

1. Способность вообще не загружается

Проверьте серверную консоль на наличие ошибок при запуске сервера. Самая частая причина — синтаксическая ошибка Lua (отсутствие end, несовпадающие скобки и т.д.). Также убедитесь, что файл заканчивается на .lua и находится в правильном каталоге powers.

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_boss_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 мс после возврата вызова нет. Используйте перезарядки, меньше точек выборки или 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 действительно разрешаются в допустимую локацию.

7. Частицы не появляются

Используйте допустимое имя Bukkit Particle: и "FLAME", и "flame" приводятся к верхнему регистру. Проверьте позицию, количество и необходимые данные. Для BLOCK нужны данные блока, которые этот помощник мира не передаёт; используйте поддерживаемую частицу или API частиц скрипта с параметром материала.

8. Перезарядка, похоже, не работает

Убедитесь, что вы используете check_local(key, duration) (который проверяет И устанавливает за один вызов), а не local_ready(key) с последующим отдельным set_local(duration, key). Если вы используете только local_ready, вы лишь проверяете, но никогда не устанавливаете перезарядку.

9. Босс продолжает выполнять способность после смерти

Добавьте логику очистки в on_exit_combat и/или on_death для отмены задач планировщика. Если босс умирает, on_exit_combat должен сработать, но добавление явной очистки в оба хука безопаснее.


Чтение сообщений об ошибках

Когда что-то идёт не так в Lua-способности, консоль выводит дружественный блок ошибки с префиксом [Lua]. Эти сообщения сообщают вам точно, какой файл, какая строка, какой хук и что именно пошло не так — простым языком. Всегда читайте полное сообщение перед отладкой.

Типичная ошибка выглядит так:

[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. фактический тип).
Превышен бюджетВызов прерывается при превышении лимита в 250 000 инструкций Lua или 50 мс процессорного времени текущего потока. Если измерение процессорного времени недоступно, используется резервный лимит в 250 мс реального времени. Ошибка отключает экземпляр скрипта.
подсказка

Когда вы видите ошибку [Lua] в консоли, сообщение об ошибке точно сообщает вам, какой файл, какая строка, какой хук и что пошло не так простым языком. Прочитайте полное сообщение, прежде чем погружаться в код — обычно оно указывает прямо на решение.


Не предполагайте существование недокументированных псевдонимов

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).
  • entity:set_velocity(...) — используйте entity:set_velocity_vector(vector).
  • entity:set_facing(...) — используйте entity:face_direction_or_location(direction_or_location).

Если сомневаетесь, проверьте страницы Справочника API (Босс и сущности, Мир и окружение, Зоны и нацеливание). Если что-то не задокументировано там, оно не существует.


Советы по миграции для авторов EliteScript

Если вы уже пишете хорошие EliteScripts, самый простой способ изучить Lua-способности:

  1. Продолжайте думать в терминах событий, целей, зон, относительных векторов и частиц. Концепции те же — меняется только синтаксис. События EliteScript становятся именами хуков вроде on_spawn или on_boss_damaged_by_player. Цели и зоны передаются как таблицы в context.script с использованием тех же имён полей, что задокументированы на страницах Зоны EliteScript и Цели EliteScript.

  2. Перенесите поток управления в Lua. Случайные броски, общие вспомогательные функции, циклы, постоянное состояние (context.state) и планирование задач (context.scheduler) — это то, что Lua добавляет и что чистый EliteScript не может легко сделать. Начните с преобразования одной ветвящейся или условной способности в Lua, сохраняя всё остальное без изменений.

  3. Используйте context.script для нацеливания и геометрии зон. Это не имитация — передаваемая вами таблица-спецификация попадает в настоящий движок EliteScript, поэтому каждое поле, описанное на страницах EliteScript (targetType, shape, Target, Target2, FinalTarget, range, offset, relativeOffset, coverage, filter, ...), работает дословно, с теми же значениями по умолчанию и той же расстановкой заглавных букв. Держите документацию EliteScript открытой как справочник по спецификациям и используйте Lua исключительно для логического слоя.

  4. Следите за двумя системами зон. context.script:zone({shape = "SPHERE", ...}) — это движок EliteScript (ЗАГЛАВНЫЕ перечисления, таблицы-спецификации Target). context.zones — отдельная облегчённая реализация (строчный kind, простые локации origin/destination). Смешивание их стилей ключей молча даёт пустую зону.


Путь обучения для начинающих

Если вы хотите изучить эту систему с нуля, хорошо работает такая последовательность:

  1. Напишите файл только с api_version = 1 и on_spawn.
  2. Заставьте босса отправить сообщение или воспроизвести звук.
  3. Добавьте перезарядку с context.cooldowns.
  4. Добавьте один хук, вызываемый игроком, например on_boss_damaged_by_player.
  5. Добавьте одно отложенное действие с context.scheduler:run_after(...).
  6. Добавьте один простой запрос нативной Lua-зоны или один простой context.script:target(...).
  7. Только потом переходите к вращающимся атакам, конечным автоматам и многоэтапным механикам.

Каждый шаг строится на предыдущем, и вы можете тестировать на каждом этапе. Не пытайтесь написать многофазного босса в качестве своей первой Lua-способности.


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

  • Начало работы — структура файлов, хуки, пошаговый разбор первой способности, шаблоны для копирования
  • NPC-скрипты — скрипты близости, взаимодействия и жизненного цикла NPC
  • Примеры и паттерны — полностью рабочие способности для изучения и адаптации