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

Lua-скриптинг: Начало работы

Эта страница научит вас написать первый Lua-скрипт для пропса FreeMinecraftModels — от пустого файла до работающего интерактивного пропса. К концу вы поймёте хуки, context, API пропсов и общую структуру каждого файла скрипта пропса.

Когда освоите основы, переходите к сопутствующим страницам:

Экспериментальная функция

Lua-скрипты пропсов в настоящее время являются экспериментальными. Имена хуков, вспомогательные методы и поведение ещё могут измениться по мере развития FreeMinecraftModels, поэтому тщательно тестируйте перед использованием на продакшн-сервере.

Связь с Lua в EliteMobs

FreeMinecraftModels использует тот же Lua-движок (Magmacore), что и EliteMobs. Если вы уже пишете Lua-способности для EliteMobs, основные концепции — хуки, context, api_version, планировщик, зоны и песочница — идентичны. Разница в следующем:

  • Скрипты EliteMobs работают на боссах и имеют хуки вроде on_boss_damaged_by_player, on_enter_combat и т.д.
  • Скрипты FMM работают на пропсах и имеют хуки вроде on_right_click, on_left_click, on_projectile_hit и т.д.

API context.world, context.zones, context.scheduler, context.state и context.log, описанные здесь, — это версии FreeMinecraftModels/MagmaCore. Скрипты NPC EliteMobs используют те же общие таблицы MagmaCore плюс context.npc; силы боссов EliteMobs используют специфичные для боссов варианты нескольких таблиц. Эта страница охватывает то, что специфично для пропсов и предметов FMM.


Что такое скрипты пропсов

Скрипты пропсов — это отдельные файлы .lua, которые находятся в папке plugins/FreeMinecraftModels/scripts/. Они указываются в YAML-конфиге, расположенном рядом с файлом модели, и запускаются при каждом спавне пропса в мире.

Для чего хороши скрипты пропсов

Скрипты пропсов отлично подходят, когда вам нужны:

  • Интерактивные пропсы, реагирующие на клики игроков (двери, рычаги, кнопки)
  • Неуязвимые декоративные пропсы, которые не могут быть сломаны игроками
  • Триггеры приближения, определяющие, когда игроки входят или покидают область
  • Анимированные пропсы, воспроизводящие анимации при взаимодействии или по таймеру
  • Звуковые пропсы, воспроизводящие звуки при клике или приближении
  • Любое поведение пропса, требующее логики помимо статичного украшения

Если ваш пропс чисто декоративный и не требует взаимодействия, скрипт вам не нужен.


Что такое скрипты предметов

Скрипты предметов используют тот же формат файлов .lua и ту же папку scripts/, что и скрипты пропсов. Разница в том, что они привязываются к пользовательским предметам — моделям, у которых в YML-конфиге задано поле material:. Если скрипты пропсов запускаются при спавне сущности-пропса в мире, то скрипты предметов запускаются, когда игрок экипирует пользовательский предмет (основная рука, вторая рука или слот брони), и останавливаются, когда предмет снимается.

Как работают скрипты предметов

  • Активация: экземпляр скрипта создаётся, когда игрок экипирует пользовательский предмет FMM. Скрипты работают на пару (игрок, тип предмета) — один ScriptInstance на каждую пару (player, itemId).
  • Деактивация: экземпляр скрипта уничтожается, когда предмет снимается (перемещён из активного слота, выброшен, либо игрок отключился).
  • Идентификация предмета: пользовательские предметы определяются по PDC-ключу (PersistentDataContainer) fmm_item_id, который отличается от model_id пропса. Чтобы получить корректно помеченный предмет, используйте /fmm giveitem <id> или админ-меню.
  • Контекст: хуки предметов получают context с context.player, context.item, context.world, context.state, context.scheduler, context.log и (где применимо) context.event.

Для чего хороши скрипты предметов

Скрипты предметов отлично подходят, когда вам нужны:

  • Пользовательское оружие с особыми способностями (морозные мечи, волшебные палочки)
  • Инструменты с уникальными действиями по правому клику или shift-клику
  • Расходуемые предметы с пользовательскими эффектами
  • Броня с пассивными эффектами при ношении
  • Предметы, отслеживающие использование или имеющие ограниченную прочность
  • Любое поведение удерживаемого предмета за пределами ванильной механики

Для кого эта страница

Эта страница написана для трёх категорий читателей:

  • Тех, кто уже знает Lua-скриптинг EliteMobs и хочет изучить хуки и API, специфичные для FMM
  • Тех, кто новичок в Lua-скриптинге и нуждается в полном, точном справочнике имён для пропсов
  • Тех, кто использует ИИ для создания скриптов пропсов и нуждается в достаточных деталях, чтобы понять, когда ИИ придумал что-то несуществующее

Вам не нужно становиться полноценным Lua-разработчиком, чтобы писать полезные скрипты пропсов. Для большинства практических скриптов вам действительно нужно только:

  • Как разместить валидный хук в возвращаемой таблице
  • Как читать значения из context
  • Как прервать выполнение с помощью if ... then return end
  • Как точно вызвать несколько вспомогательных методов

Краткий курс Lua

Чтобы писать скрипты FMM, необязательно быть экспертом по Lua. В большинстве скриптов используется лишь горстка понятий: переменные (local x = 5), функции (function foo() end), проверки if (if x then ... end), таблицы ({key = value}) и nil (значение «ничего» в Lua). Синтаксис лёгкий — ни точек с запятой, ни фигурных скобок, только end для закрытия блоков.

Полное пошаговое введение с примерами см. в Движок Lua-скриптов MagmaCore — краткий курс Lua. Этот курс общий для всех плагинов Nightbreak, поэтому изучив его один раз, вы примените его везде.


Расположение файлов

Файлы скриптов

Размещайте файлы .lua в центральной папке скриптов:

plugins/
FreeMinecraftModels/
scripts/
invulnerable.lua
interactive_door.lua
proximity_sound.lua

FMM обнаруживает все файлы .lua в plugins/FreeMinecraftModels/scripts/ при запуске.

В списке scripts: модели указывайте расширение .lua для наглядности. FMM также принимает записи без расширения и добавляет .lua внутренне. Файл на диске всё равно обязан оканчиваться на .lua, а имена остаются чувствительными к регистру.

Файлы моделей и конфигурации

Каждый файл модели может иметь соседний файл конфигурации .yml в той же директории:

plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- конфиг скрипта для torch_01
scripts/
invulnerable.lua <-- указан в torch_01.yml

Файл .yml — это то, что связывает модель с её скриптами.


Формат файла конфигурации

YAML-файл конфигурации, расположенный рядом с файлом модели, имеет следующие поля:

isEnabled: true
voxelize: false
solidify: false
scripts:
- invulnerable.lua

Для пользовательских предметов (моделей, которые игроки могут держать или экипировать) вы также задаёте поле material и при желании name, lore и enchantments:

isEnabled: true
material: DIAMOND_SWORD
name: "&bFrost Blade"
lore:
- "&7A sword forged in eternal ice"
- "&7Slows enemies on hit"
enchantments:
- "SHARPNESS,5"
- "UNBREAKING,3"
scripts:
- frost_sword.lua
ПолеТипПо умолчаниюПримечания
isEnabledbooleantrueАктивны ли скрипты для этого пропса/предмета
scriptsсписок строк[]Имена файлов .lua скриптов в папке scripts/
voxelizebooleanfalseПривязывает размещение к поворотам на 90 градусов и к сетке блоков
solidifybooleanfalseРазмещает пакетные блоки-барьеры в габаритах пропса (требует voxelize)
materialstring""Валидное имя Bukkit Material (например, DIAMOND_SWORD). Установка этого поля превращает модель в пользовательский предмет, который игроки могут держать или экипировать, что активирует систему скриптов предметов
namestring""Отображаемое имя пользовательского предмета. Поддерживает цветовые &-коды
loreсписок строк[]Строки lore, показываемые в подсказке предмета. Поддерживает цветовые &-коды
enchantmentsсписок строк[]Зачарования, применяемые к предмету. Формат: "ENCHANTMENT_NAME,LEVEL" (например, "SHARPNESS,5")

Вы можете привязать несколько скриптов к одному пропсу. Каждый скрипт является независимым экземпляром.

ID предмета и ограничение на скрипты
  • ID предмета выводится из имени YML-файла без расширения. Например, frost_sword.yml даёт ID предмета frost_sword. Именно этот ID используется командой /fmm giveitem и PDC-ключом fmm_item_id.
  • Предметы привязывают только один скрипт из списка scripts:. FMM проверяет записи по порядку и использует первый скрипт, который смог разрешить, а последующие записи игнорирует. В отличие от предметов, пропсы запускают каждый разрешённый скрипт из списка как независимый экземпляр.
  • Расширение .lua автоматически дописывается к именам файлов скриптов, если вы его опустили, поэтому frost_sword и frost_sword.lua в списке scripts: эквивалентны.

Ленивая генерация конфигурации

Когда пропс спавнится и не существует соседнего файла .yml, FMM автоматически создаёт файл конфигурации по умолчанию с isEnabled: true и пустым списком scripts:. Это происходит асинхронно, поэтому при первом спавне у пропса не будет скриптов — только после создания конфига и добавления имён файлов скриптов.

Это означает:

  1. Поместите файл модели в models/
  2. Заспавните пропс один раз (FMM создаст .yml автоматически)
  3. Отредактируйте сгенерированный .yml, чтобы добавить имена файлов скриптов
  4. Переспавните пропс или перезагрузите (скрипты теперь активны)

Справочник хуков

Каждый файл Lua-скрипта пропса возвращает таблицу. Каждый ключ в этой таблице (кроме api_version и priority) должен быть одним из хуков, перечисленных ниже. Среда выполнения вызывает соответствующую функцию всякий раз, когда происходит соответствующее игровое событие.

ХукСрабатывает когдаПримечания
on_spawnПропс появляется в миреВыполняется один раз при привязке скрипта
on_game_tickКаждый серверный тик (50 мс)Активен только если скрипт определяет этот хук
on_destroyПропс удаляется из мираХук очистки
on_left_clickИгрок кликает левой кнопкой (бьёт) по пропсуcontext.event — событие урона
on_right_clickИгрок кликает правой кнопкой по пропсуcontext.event — событие взаимодействия
on_zone_enterИгрок входит в наблюдаемую зонуТребует настроенного наблюдения за зоной
on_zone_leaveИгрок покидает наблюдаемую зонуТребует настроенного наблюдения за зоной
Зарезервированный хук пропса

Текущий валидатор скриптов принимает on_projectile_hit для скриптов пропсов, но текущая среда выполнения пока не рассылает попадания снарядов скриптам пропсов. Используйте хук предмета on_projectile_hit для поведения снарядов, привязанного к скриптуемому предмету, либо Bukkit-API ModeledEntityHitByProjectileEvent для обработки снарядов по моделированным сущностям на стороне плагина.


Справочник хуков предметов

Скрипты предметов возвращают таблицу так же, как и скрипты пропсов, с api_version = 1 и функциями-хуками. Для скриптов предметов доступны следующие хуки. Все хуки предметов получают context с context.player, context.item и (где применимо) context.event.

В столбце примечаний указано имя соответствующего семейства событий Bukkit. Lua-обёртка не предоставляет необработанные поля, специфичные для Bukkit, такие как target, block, projectile или item; используйте context.player, context.event.player и вспомогательные запросы к сущностям/миру, когда вам нужен дополнительный контекст.

Боевые хуки

ХукСрабатывает когдаПримечания
on_attack_entityИгрок атакует сущность, держа предметcontext.event — событие урона
on_kill_entityИгрок убивает сущность, держа предметcontext.event — событие смерти
on_take_damageИгрок получает урон, когда предмет экипированcontext.event — событие урона
on_shield_blockИгрок блокирует урон щитомcontext.event — событие урона
on_shoot_bowИгрок стреляет из лукаcontext.event — событие выстрела из лука
on_projectile_hitСнаряд, выпущенный игроком, во что-то попадаетcontext.event — событие попадания снаряда
on_projectile_launchИгрок запускает снарядcontext.event — событие запуска снаряда

Хуки взаимодействия

ХукСрабатывает когдаПримечания
on_right_clickИгрок кликает правой кнопкой, держа предметcontext.event — событие взаимодействия
on_left_clickИгрок кликает левой кнопкой, держа предметcontext.event — событие взаимодействия
on_shift_right_clickИгрок кликает Shift+правая кнопка, держа предметcontext.event — событие взаимодействия
on_shift_left_clickИгрок кликает Shift+левая кнопка, держа предметcontext.event — событие взаимодействия
on_interact_entityИгрок кликает правой кнопкой по сущности, держа предметcontext.event — событие взаимодействия с сущностью

Хуки экипировки

ХукСрабатывает когдаПримечания
on_equipПредмет экипируется (перемещается в активный слот)Хорошее место для инициализации состояния
on_unequipПредмет снимается (перемещается из активного слота)Хорошее место для очистки
on_swap_handsИгрок перекладывает предмет между основной и второй рукойcontext.event — событие перекладывания
on_dropИгрок выбрасывает предметcontext.event — событие выбрасывания

Вспомогательные хуки

ХукСрабатывает когдаПримечания
on_break_blockИгрок ломает блок, держа предметcontext.event — событие разрушения блока
on_consumeИгрок потребляет предмет (еда/зелье)context.event — событие потребления
on_item_damageПредмет теряет прочностьcontext.event — событие повреждения предмета
on_fishИгрок использует удочкуcontext.event — событие рыбалки
on_deathИгрок умирает, когда предмет экипированcontext.event — событие смерти

Хук жизненного цикла

ХукСрабатывает когдаПримечания
on_game_tickКаждый серверный тик, пока предмет экипированИспользуйте осторожно — выполняется 20 раз в секунду

Минимальный контракт файла

Каждый Lua-скрипт пропса должен возвращать (return) таблицу.

Обязательные и необязательные поля верхнего уровня

ПолеОбязательноТипПримечания
api_versionДаNumberВ настоящее время должно быть 1
priorityНетNumberВалидируется, если присутствует, но FMM в настоящее время не сортирует по нему скрипты. Пропсы выполняются в порядке списка scripts:; предметы привязывают только первый валидный скрипт
поддерживаемые ключи хуковНетFunctionДолжны использовать одно из точных имён хуков из справочника

Правила валидации

  • Файл должен возвращать таблицу.
  • api_version обязателен и в настоящее время должен быть 1.
  • priority должен быть числом, если указан.
  • Каждый дополнительный ключ верхнего уровня должен быть поддерживаемым именем хука.
  • Каждый ключ хука должен указывать на функцию.
  • Неизвестные ключи верхнего уровня отклоняются.
примечание

priority полезен, чтобы скрипты оставались переносимыми между средами выполнения на базе MagmaCore, но текущий порядок выполнения в FreeMinecraftModels задаётся конфигом. Располагайте скрипты пропсов в списке scripts: модели в том порядке, в котором они должны выполняться.

Вспомогательные функции и локальные константы должны располагаться над финальным return, а не внутри возвращаемой таблицы.


Ваш первый рабочий скрипт пропса, пошагово

Перед шагом 1: Настройка конфигурации

  1. Поместите файл модели (например, my_prop.fmmodel) в plugins/FreeMinecraftModels/models/
  2. Заспавните пропс один раз для генерации конфига .yml
  3. Создайте файл скрипта в plugins/FreeMinecraftModels/scripts/first_test.lua
  4. Отредактируйте plugins/FreeMinecraftModels/models/my_prop.yml:
isEnabled: true
scripts:
- first_test.lua
  1. Переспавните пропс или перезагрузите сервер

Шаг 1: Загрузка файла

return {
api_version = 1,

on_spawn = function(context)
end
}

Если это загружается без ошибок в консоли, вы подтвердили:

  • Файл является валидным Lua
  • FMM нашёл его в папке scripts/
  • Конфиг правильно на него ссылается
  • Структура возвращаемой таблицы корректна

Шаг 2: Пропс делает что-то видимое

return {
api_version = 1,

on_spawn = function(context)
context.log:info("Prop script loaded for: " .. (context.prop.model_id or "unknown"))
end
}

Проверьте консоль сервера. Если вы видите сообщение в логе, ваш хук работает.

Шаг 3: Реакция на клик игрока

return {
api_version = 1,

on_right_click = function(context)
context.log:info("Prop was right-clicked!")
end
}

Кликните правой кнопкой по пропсу в игре. Если консоль показывает сообщение, хук клика работает.

Шаг 4: Отмена урона для неуязвимости пропса

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

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

Шаг 5: Воспроизведение анимации по клику

return {
api_version = 1,

on_right_click = function(context)
context.prop:play_animation("open", true, false)
end
}

Это воспроизводит анимацию «open» на модели пропса, с плавным переходом и без зацикливания.


Что такое context?

Каждая функция хука получает один аргумент, называемый context. Думайте о нём как о наборе инструментов, который FMM передаёт вам каждый раз, когда что-то происходит — он содержит всё необходимое для взаимодействия с пропсом, миром, зонами и многим другим.

Вы не создаёте context сами — FMM создаёт его и передаёт в ваш хук. Полные подробности об общих API контекста (context.state, context.log, context.cooldowns, context.scheduler, context.world, context.zones) см. на странице Движок Lua-скриптов MagmaCore.


Ключевые API context

Вот краткое описание доступного. Подробности см. в Prop API.

  • context.prop — (только скрипты пропсов) Сущность пропса. Предоставляет model_id, current_location, play_animation() и stop_animation().

  • context.item — (только скрипты предметов) Пользовательский предмет. Предоставляет id, material(), get_amount(), set_amount(), consume(), get_uses(), set_uses(), get_name(), set_name(), get_lore(), set_lore(), get_durability(), get_durability_percentage(), use_durability() и use_durability_percentage(). Полные подробности см. в API пропсов и предметов.

  • context.player — Игрок для хуков, вызванных игроком. Скрипты предметов определяют его по владельцу предмета; хуки кликов по пропсам и общие хуки зон — по игроку, вызвавшему событие. Равен nil в хуках жизненного цикла пропса, в отложенных обратных вызовах пропса и в хуках, не связанных с игроком.

  • context.event — Небольшая обёртка для события Bukkit или игрока-актора, вызвавшего этот хук. Доступен в хуках кликов, боя, взаимодействия и в общих хуках зон. Предоставляет event.player, is_cancelled, а когда базовое событие Bukkit отменяемо — cancel() / uncancel(); он не предоставляет специфичные для Bukkit поля вроде target, block, projectile или item. Равен nil в хуках без события или игрока-актора (таких как on_spawn, on_game_tick и on_equip).

  • context.state — Обычная Lua-таблица, которая сохраняется на протяжении жизни экземпляра скрипта. См. context.state.

  • context.cooldowns — Помощники локальных и глобальных перезарядок. Используйте context.cooldowns:check_local("key", ticks) для обычных перезарядок в рамках скрипта. См. context.cooldowns.

  • context.log — Логирование в консоль. См. context.log.

  • context.scheduler — Отложенные и повторяющиеся задачи. См. context.scheduler.

  • context.world — Взаимодействие с миром: частицы, звуки, запросы блоков, молнии, ближайшие сущности. См. context.world.

  • context.zones — Создание и наблюдение пространственных зон (сферы, цилиндры, кубоиды). См. context.zones.


Синтаксис методов: : и .

Объяснение синтаксиса методов : и . в Lua см. на странице Движок Lua-скриптов MagmaCore. API FMM принимает обе формы.


Готовые шаблоны для копирования

Минимальный валидный скрипт пропса

return {
api_version = 1,

on_spawn = function(context)
end
}

Шаблон неуязвимого пропса

return {
api_version = 1,

on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}

Шаблон интерактивного пропса

return {
api_version = 1,

on_spawn = function(context)
context.state.is_active = false
end,

on_right_click = function(context)
context.state.is_active = not context.state.is_active

if context.state.is_active then
context.prop:play_animation("activate", true, true)
else
context.prop:stop_animation()
end
end
}

Минимальный валидный скрипт предмета

return {
api_version = 1,

on_equip = function(context)
end
}

Шаблон предмета с действием по правому клику

return {
api_version = 1,

on_right_click = function(context)
if not context.cooldowns:check_local("activate", 40) then return end

-- Ваше действие здесь
context.player:send_message("&aItem activated!")
end
}

Расширенная структура файла

local ANIMATION_NAME = "idle"

local function do_something(context)
context.log:info("Doing something!")
end

return {
api_version = 1,
priority = 0,

on_spawn = function(context)
context.state.task_id = nil
end,

on_right_click = function(context)
do_something(context)
end,

on_destroy = function(context)
if context.state.task_id ~= nil then
context.scheduler:cancel(context.state.task_id)
end
end
}

Первый реальный рабочий процесс

При создании совершенно нового скрипта пропса используйте этот порядок:

  1. Создайте файл .lua и заставьте on_spawn работать.
  2. Добавьте имя файла скрипта в конфиг .yml пропса.
  3. Переключитесь на нужный хук (например, on_right_click).
  4. Сначала добавьте сообщение в лог, до анимаций и эффектов.
  5. Добавьте один реальный эффект (анимацию, звук, частицы).
  6. Только после этого добавляйте вспомогательные функции, состояние, логику планировщика или зоны.

Такой порядок значительно упрощает отладку, потому что меняется только одна вещь за раз.


Готовые скрипты

FMM поставляется с четырьмя готовыми Lua-скриптами:

  • invulnerable.lua — Отменяет события урона при левом клике, делая пропс неразрушимым. Это простейший полезный скрипт пропса.
  • pickupable.lua — Позволяет игрокам подбирать пропс, ударив его три раза. Каждый удар воспроизводит анимацию повреждения на пропсе, а при третьем ударе пропс удаляется и выбрасывает свой предмет размещения для сбора игроком.
  • storage_double.lua — Превращает пропс в двойной сундук (54 слота). Правый клик открывает постоянный GUI инвентаря. Воспроизводит анимации и звуки открытия/закрытия. Содержимое сохраняется в пропсе и переживает перезапуски сервера. При уничтожении всё содержимое выбрасывается.
  • storage_single.lua — То же, что и storage_double, но с 3 рядами (27 слотов) вместо 6.

Больше примеров можно найти на странице Примеры и паттерны.


Песочница Lua

Скрипты пропсов и предметов работают в той же изолированной среде LuaJ, что и EliteMobs. Ограничения песочницы идентичны. Полный список удалённых глобальных переменных и доступных функций стандартной библиотеки см. на странице Движок Lua-скриптов MagmaCore.


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

Если вы также пишете Lua-силы боссов EliteMobs, песочница, api_version, таблица состояния, понятия перезарядок и структура на основе хуков будут вам знакомы, но контекст босса использует специфичные для EliteMobs имена методов. Точные API боссов см. в документации Lua для EliteMobs.