Lua-скриптинг: Устранение неполадок
Эта страница описывает частые проблемы при написании и отладке скриптов реквизита FreeMinecraftModels, а также генерацию конфигурации по мере необходимости. Примеры см. в Примерах и паттернах, вводное руководство — в Начале работы.
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_click | on_right_click или on_left_click |
on_interact | on_right_click |
on_hit | on_left_click |
on_tick | on_game_tick |
on_remove | on_destroy (вызывается только при окончательном удалении реквизита, а не при выгрузке чанка или перезагрузке) |
on_arrow_hit | on_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
Как работает ленивая генерация конфигурации
Понимание системы ленивой генерации конфигурации помогает избежать путаницы при настройке новых пропсов:
-
Первый спавн: Когда пропс появляется и не существует соседнего файла
.yml, FMM создаёт конфиг асинхронно. Это означает, что файл записывается в фоновом потоке и не доступен немедленно. -
Значения по умолчанию: Сгенерированный конфиг имеет
isEnabled: trueи пустой списокscripts: []. -
Нет скриптов при первом спавне: Поскольку конфиг создаётся после того, как пропс уже заспавнился (и не имеет перечисленных скриптов), при первом спавне у пропса не будет привязанных скриптов.
-
Редактирование, перезагрузка и повторное создание: Добавьте имена скриптов и успешно выполните
/fmm reload. Настройки, проверки размещения и списки скриптов в меню используют общий подготовленный каталог. Новый пропс использует этот каталог, не перечитывая изменённый YAML. Изменения Lua также требуют перезагрузки. -
Расположение конфига: Файл
.ymlсоздаётся в той же директории, что и файл модели, с тем же базовым именем. Например:- Модель:
plugins/FreeMinecraftModels/models/fountain.fmmodel - Конфиг:
plugins/FreeMinecraftModels/models/fountain.yml
- Модель:
- Поместите модель в
models/ - Поместите скрипт в
scripts/ - Заспавните пропс один раз (генерирует
.yml) - Отредактируйте
.yml, добавивscripts: [my_script.lua] - Перезагрузите плагин для обнаружения 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. Проверяйте опечатки в именах хуков
Опечатка в имени — частая причина неработающего хука. Неизвестный хук приводит к отклонению всего скрипта при проверке. Изучите ошибку консоли, не ожидая выполнения остальных хуков этого файла.
Путь прогрессии для начинающих
Если вы хотите изучить эту систему с нуля, такая прогрессия работает хорошо:
- Напишите файл только с
api_version = 1иon_spawn, который логирует сообщение. - Добавьте скрипт в конфиг пропса и убедитесь, что сообщение лога появляется.
- Добавьте
on_right_clickи логируйте, когда по пропсу кликают. - Добавьте
on_left_clickсcontext.event.cancel()для отмены стандартного урона от клика. - Воспроизведите анимацию по правому клику.
- Добавьте звук по правому клику.
- Добавьте состояние и поведение переключения.
- Добавьте наблюдение за зоной для обнаружения приближения.
- Только после этого переходите к сложным скриптам с множеством хуков и планировщиками.
Каждый шаг строится на предыдущем, и вы можете тестировать на каждом этапе.
Следующие шаги
- Начало работы — структура файлов, хуки, первый скрипт, шаблоны для копирования
- API реквизита — полный справочник API
- Примеры и паттерны — полные рабочие скрипты для изучения и адаптации