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

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

Эта страница описывает частые проблемы при написании и отладке скриптов реквизита FreeMinecraftModels, а также генерацию конфигурации по мере необходимости. Примеры см. в Примерах и паттернах, вводное руководство — в Начале работы.

Среда выполнения Lua MagmaCore

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


Типичные проблемы​

1. Конфиг не загружается / Скрипт не привязан​

Симптом: Пропс появляется, но не реагирует на клики и никакие хуки не срабатывают.

Причины и решения:

  • Файл .yml ещё не существует. При первом создании пропса FMM пытается асинхронно создать его с настройками по умолчанию (включён, без скриптов), не перезаписывая существующий файл. Добавьте имена скриптов, успешно выполните /fmm reload и разместите новый пропс. Изменения YAML и Lua требуют перезагрузки.

  • В конфиге isEnabled: false. Откройте файл .yml рядом с файлом модели и установите isEnabled: true.

  • Список scripts: пуст. Добавьте имена файлов ваших скриптов:

    isEnabled: true
    scripts:
    - my_script.lua
  • Имя файла .yml не совпадает с именем файла модели. Конфиг должен иметь то же базовое имя, что и файл модели. Например, для torch_01.fmmodel нужен torch_01.yml в той же директории.


2. Файл скрипта не найден​

Симптом: Консоль показывает: [FMM Scripts] Script 'my_script.lua' not found in scripts/ folder

Причины и решения:

  • Неправильная директория. Файлы скриптов должны находиться в plugins/FreeMinecraftModels/scripts/, а не рядом с файлом модели.

  • Несовпадение имени файла. Имя в .yml должно совпадать с именем в scripts/, включая регистр. Расширение .lua рекомендуется, но необязательно: FMM добавляет его при отсутствии.

  • Файл отсутствует. Убедитесь, что файл .lua действительно существует в папке scripts/.

  • Кэш определения или одинаковые имена. FMM рекурсивно ищет скрипты при инициализации провайдера, но привязывает по имени файла, а не относительному пути. Имена должны быть уникальны во всех подпапках. После добавления или изменения Lua-файла требуется успешный /fmm reload; повторный спавн реквизита не перечитывает кэшированный код Lua.


3. Скрипт вообще не загружается​

Симптом: Нет ошибок в консоли, но хуки не срабатывают.

Проверьте консоль сервера на наличие синтаксических ошибок Lua при запуске. Наиболее частые причины:

  • Отсутствие end для закрытия функции или блока if
  • Непарные скобки
  • Отсутствие запятой между определениями хуков в возвращаемой таблице
  • Файл не заканчивается расширением .lua

Ошибки обнаружения файлов отображаются как предупреждения о недопустимых или выведенных из использования скриптах либо как ошибки загрузки. Сбои хуков и коллбэков используют блок [Lua] с именем файла и контекстом выполнения; номер строки появляется только тогда, когда его можно извлечь из исключения.


4. Хук никогда не срабатывает​

Симптом: Добавленное вами сообщение запуска появляется, но конкретный хук не срабатывает.

Убедитесь, что имя совпадает со справочником хуков. Типичные ошибки:

Неправильное имяПравильное имя
on_clickon_right_click или on_left_click
on_interacton_right_click
on_hiton_left_click
on_tickon_game_tick
on_removeon_destroy (вызывается только при окончательном удалении реквизита, а не при выгрузке чанка или перезагрузке)
on_arrow_hiton_projectile_hit

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


5. Таймаут / превышен бюджет выполнения​

Симптом: Консоль показывает ошибку Lua, поднятую изнутри VM. Вызов, вышедший за бюджет, прерывается посреди выполнения одним из сообщений:

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)

Каждый хук, коллбэк и вычисление файла могут выполнить не более 250 000 инструкций Lua либо израсходовать 50 мс процессорного времени текущего потока — что наступит раньше, — а вложенные вызовы разделяют одну квоту. (Третье сообщение появляется только на JVM, где замер процессорного времени по потокам недоступен; MagmaCore тогда использует более щедрое ограничение в 250 мс затраченного времени.) Скрипт выполнял слишком много работы за один вызов хука. Типичные причины:

  • Перебор слишком многих сущностей в on_game_tick
  • Создание слишком многих частиц за тик
  • Выполнение ресурсоёмких циклов без распределения работы по тикам

Решение: Перенесите тяжёлую работу за context.cooldowns или используйте scheduler:run_repeating() с разумным интервалом вместо on_game_tick.

Лимит инструкций ограничивает циклы Lua. Время проверяется каждые 1024 инструкции Lua и при возврате; эти проверки не прерывают блокирующий метод Java/Bukkit посреди вызова. Ограничивайте также объём работы внутри каждого коллбэка.


6. context.event равен nil в хуке клика​

В нормальных условиях это не должно происходить для on_left_click и on_right_click, но всегда проверяйте:

if context.event then
context.event.cancel()
end

Внутри обратных вызовов планировщика context.event всегда nil — это ожидаемое поведение. Модификация событий возможна только внутри самого хука события.


7. Анимация не воспроизводится​

Симптом: play_animation() возвращает false, или ничего видимого не происходит.

Причины и решения:

  • Неправильное имя анимации. Имя должно точно совпадать с определённым в файле модели. Проверьте ваш .bbmodel или .fmmodel на правильное имя анимации.

  • Модель не имеет анимаций. Не все модели имеют анимации. Убедитесь, что файл модели действительно содержит данные анимации.

  • У игрока нет пака или он вне дальности отображения модели. Нужны текущий ресурс-пак и видимая модель.

  • Запрос ожидает или подавлен. Значение true для blend использует единственное место в очереди, а не плавный переход. Более поздний запрос может его заменить, а запрос атаки во время пользовательской анимации может вернуть true, не прерывая её. Используйте позиционные аргументы из раздела Анимации и различайте принятие запроса и показ кадра.


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

  • Имена частиц принимают FLAME и flame; API нормализует регистр перед поиском частицы Bukkit.
  • Убедитесь, что местоположение находится в загруженном чанке. Если поблизости нет игроков, чанк может быть выгружен.
  • Убедитесь, что count не менее 1.
  • Некоторые частицы (например, DUST) требуют специальных дополнительных данных, которые базовый spawn_particle() может не поддерживать. Используйте стандартные частицы, такие как FLAME, HEART, HAPPY_VILLAGER, NOTE, ENCHANT и др.

9. Звук не воспроизводится​

  • Звуки принимают имена enum Bukkit без учёта регистра либо допустимые ключи, например minecraft:block.note_block.harp, и ключи собственных ресурспаков. Звук должен присутствовать в ресурспаке клиента.
  • Убедитесь, что координаты местоположения корректны (не все нули и не NaN).
  • Убедитесь, что громкость больше 0.

10. Состояние неожиданно сбрасывается​

context.state принадлежит одному экземпляру скрипта и сохраняется, пока тот активен. Разные записи списка scripts: одного реквизита не разделяют состояние. Если оно кажется сброшенным:

  • Пропс мог быть удалён и переспавнен, либо его чанк выгрузился и загрузился снова. Каждый спавн или загрузка чанка создаёт новый экземпляр. Значения, которые должны это пережить, сохраняйте через prop:set_persistent_data.
  • Успешная перезагрузка или ошибка скрипта могла завершить старый экземпляр. Проверьте консоль; правка файла не возобновляет завершённый экземпляр без перезагрузки и повторной привязки.
  • Вы можете читать состояние в обратном вызове планировщика, используя неправильную переменную context. Всегда используйте собственный параметр context обратного вызова.

11. Библиотека os недоступна​

Симптом: Скрипт падает с ошибкой вроде attempt to index nil (os).

Библиотека os полностью удалена из песочницы Lua. Вы не можете использовать os.time(), os.clock() или любую другую функцию os.

Решение: Используйте context.world:get_time() для времени мира либо отслеживайте прошедшее время вручную через флаги состояния и счётчики тиков в on_game_tick. Для перезарядок предпочтительнее context.cooldowns:check_local(key, ticks).


12. Скрипт предмета не активируется​

Формат скриптов экипированных предметов выведен из использования. Предмет с непустым списком scripts: или старыми зачарованиями без пространства имён пропускается с записью в журнал. Замените его текущим определением пользовательского предмета и зачарованиями с пространством имён. Скрипты реквизита по-прежнему используют scripts:.

13. Определение предмета недоступно​

Найдите точное имя файла в предупреждении при запуске или перезагрузке. Недоступный материал, например новый тип предмета Minecraft на старом сервере, также приводит к пропуску определения. Исправьте или замените его для вашей версии сервера; FMM не переписывает определения и не выбирает выдуманный заменяющий материал.

14. Хук срабатывает, но скрипт падает на методах сущности​

Симптом: Хук вроде on_right_click работает, но запланированный коллбэк падает с ошибкой attempt to call nil при вызове entity:damage() или entity:push().

Причина: context.world:get_nearby_entities() возвращает все сущности в радиусе, включая неживые: выброшенные предметы, сферы опыта и облака эффектов. У неживых сущностей нет методов damage(), push() или add_potion_effect().

Решение: Всегда проверяйте if entity.damage then перед вызовом методов живых сущностей:

local entities = context.world:get_nearby_entities(x, y, z, radius)
for _, entity in ipairs(entities) do
if entity.damage then
-- Safe to call living entity methods
entity:damage(5.0)
entity:push(0, 0.5, 0)
end
end

15. Данные, доступные только в событии, отсутствуют в запланированных коллбэках​

Симптом: Данные из события клика/боя отсутствуют внутри коллбэка scheduler:run_later() или scheduler:run_repeating().

Причина: Запланированные коллбэки получают свежий контекст без исходного события Bukkit или игрока общего события зоны. context.event всегда равен nil. Скрипты реквизита получают context.player верхнего уровня только в хуках, связанных с игроком, например при кликах и входе или выходе из зоны. Если игрок понадобится позже, сохраните его из context.player или context.event.player в исходном хуке.

Решение: Используйте свежий контекст для доступа к миру и состоянию. Нужного игрока или сущность исходного события сохраните до планирования, а при выполнении проверьте, что они ещё действительны.

on_right_click = function(context)
local clicked_player = context.player or (context.event and context.event.player)

context.scheduler:run_later(20, function(later_context)
-- Prop scripts should use a captured player when they need the clicker.
local player = later_context.player or clicked_player
if player and player.is_valid then
player:send_message("&aDelayed message!")
end
end)
end

Как работает ленивая генерация конфигурации​

Понимание системы ленивой генерации конфигурации помогает избежать путаницы при настройке новых пропсов:

  1. Первый спавн: Когда пропс появляется и не существует соседнего файла .yml, FMM создаёт конфиг асинхронно. Это означает, что файл записывается в фоновом потоке и не доступен немедленно.

  2. Значения по умолчанию: Сгенерированный конфиг имеет isEnabled: true и пустой список scripts: [].

  3. Нет скриптов при первом спавне: Поскольку конфиг создаётся после того, как пропс уже заспавнился (и не имеет перечисленных скриптов), при первом спавне у пропса не будет привязанных скриптов.

  4. Редактирование, перезагрузка и повторное создание: Добавьте имена скриптов и успешно выполните /fmm reload. Настройки, проверки размещения и списки скриптов в меню используют общий подготовленный каталог. Новый пропс использует этот каталог, не перечитывая изменённый YAML. Изменения Lua также требуют перезагрузки.

  5. Расположение конфига: Файл .yml создаётся в той же директории, что и файл модели, с тем же базовым именем. Например:

    • Модель: plugins/FreeMinecraftModels/models/fountain.fmmodel
    • Конфиг: plugins/FreeMinecraftModels/models/fountain.yml
Быстрый рабочий процесс настройки
  1. Поместите модель в models/
  2. Поместите скрипт в scripts/
  3. Заспавните пропс один раз (генерирует .yml)
  4. Отредактируйте .yml, добавив scripts: [my_script.lua]
  5. Перезагрузите плагин для обнаружения Lua-файла, разместите новый реквизит и проверьте добавленное вами сообщение запуска

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

Ошибки хуков и коллбэков создают блок [Lua] с файлом и контекстом выполнения. Номер строки указывается, если исключение содержит распознаваемую позицию исходного кода. Ошибки обнаружения и конфигурации могут использовать другие предупреждения.

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

[Lua] Error in 'my_door.lua' at line 12 during 'on_right_click':
[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Вы попытались вызвать метод или функцию, которая не существует. Проверьте опечатки и использование : vs ..
index expected, got nilВы попытались обратиться к полю чего-то, что является nil. Проверьте, что предыдущий код его инициализировал.
attempt to indexВы попытались обратиться к свойству nil или невалидного значения.
bad argumentФункция получила аргумент неправильного типа. Сообщение показывает ожидаемый и фактический тип.
Превышен бюджетLua instruction budget exceeded (250000 instruction limit) или Lua CPU-time budget exceeded (50ms current-thread CPU limit) — вызов был прерван посреди выполнения, а экземпляр скрипта отключён.
подсказка

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


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

API Lua FMM предоставляет определённый набор методов. Не предполагайте, что существуют сокращённые или альтернативные имена. Типичные ошибки:

  • context.prop:get_location() — не существует. Используйте context.prop.current_location (поле, а не метод).
  • context.prop:set_animation("open") — не существует. Используйте context.prop:play_animation("open", true, true).
  • context.event:setCancelled(true) — не существует. Используйте context.event.cancel().
  • context.cooldowns:check_global(...) — не существует. Используйте context.cooldowns:global_ready() и context.cooldowns:set_global(ticks) для глобальных перезарядок.
  • context.player — задан только в хуках реквизита FMM, связанных с игроком, например при кликах, входе или выходе из наблюдаемой зоны и в on_projectile_hit, если снаряд выпустил игрок. Это не общее поле владельца; в хуках жизненного цикла и коллбэках планировщика оно равно nil.
  • context.boss — не существует. Это из EliteMobs. Используйте context.prop в FMM.

Проверяйте поддерживаемые методы в API реквизита и связанных общих справочниках. Правдоподобное имя не доказывает существование метода.


Советы по отладке​

1. Используйте context.log:info() активно​

Добавляйте сообщения лога на каждом шаге при отладке:

on_right_click = function(context)
context.log:info("Right click received!")
context.log:info("Event present: " .. tostring(context.event ~= nil))
context.log:info("State is_open: " .. tostring(context.state.is_open))
end

2. Проверяйте консоль при запуске​

Добавьте context.log:info("Prop script started") в on_spawn, затем разместите новый реквизит после перезагрузки. Проверьте своё сообщение и ошибки скриптов или конфигурации в консоли. FMM не выводит сообщение об успехе для каждой привязки.

3. Проверяйте путь к файлу модели​

Конфиг .yml должен быть соседом файла модели (та же директория, то же базовое имя). Проверьте это:

  • Путь к файлу модели: plugins/FreeMinecraftModels/models/my_model.fmmodel
  • Путь к файлу конфига: plugins/FreeMinecraftModels/models/my_model.yml

4. Тестируйте хуки по отдельности​

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

5. Проверяйте опечатки в именах хуков​

Опечатка в имени — частая причина неработающего хука. Неизвестный хук приводит к отклонению всего скрипта при проверке. Изучите ошибку консоли, не ожидая выполнения остальных хуков этого файла.


Путь прогрессии для начинающих​

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

  1. Напишите файл только с api_version = 1 и on_spawn, который логирует сообщение.
  2. Добавьте скрипт в конфиг пропса и убедитесь, что сообщение лога появляется.
  3. Добавьте on_right_click и логируйте, когда по пропсу кликают.
  4. Добавьте on_left_click с context.event.cancel() для отмены стандартного урона от клика.
  5. Воспроизведите анимацию по правому клику.
  6. Добавьте звук по правому клику.
  7. Добавьте состояние и поведение переключения.
  8. Добавьте наблюдение за зоной для обнаружения приближения.
  9. Только после этого переходите к сложным скриптам с множеством хуков и планировщиками.

Каждый шаг строится на предыдущем, и вы можете тестировать на каждом этапе.


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