Lua-скриптинг: API пропов
На этой странице описаны все API, доступные для скриптов пропов FreeMinecraftModels: context.prop, context.event, context.world, context.zones, context.scheduler, context.state и context.log. Если вы новичок в скриптинге пропов, начните с раздела Начало работы.
context.prop
Таблица prop предоставляет информацию о сущности пропа и методы для управления его анимациями. Она пересоздаётся заново при каждом вызове hook.
Поля
| Поле | Тип | Примечания |
|---|---|---|
prop.model_id | string | Имя модели blueprint (например, "torch_01") |
prop.current_location | location table | Позиция пропа на момент создания контекста |
Таблица location имеет стандартные поля: x, y, z, world, yaw, pitch.
Пример: чтение информации о пропе
return {
api_version = 1,
on_spawn = function(context)
context.log:info("Prop spawned: " .. (context.prop.model_id or "unknown"))
local loc = context.prop.current_location
if loc then
context.log:info("Location: " .. loc.x .. ", " .. loc.y .. ", " .. loc.z)
end
end
}
prop:play_animation(name, blend, loop)
Воспроизводит именованную анимацию на модели пропа.
| Параметр | Тип | По умолчанию | Примечания |
|---|---|---|---|
name | string | обязательный | Имя анимации, как определено в файле модели |
blend | boolean | true | Смешивать ли с текущей анимацией |
loop | boolean | true | Зацикливать ли анимацию |
Возвращает true, если анимация найдена и запущена, иначе false.
Пример
return {
api_version = 1,
on_right_click = function(context)
local success = context.prop:play_animation("open", true, false)
if not success then
context.log:warn("Animation 'open' not found on this model!")
end
end
}
prop:stop_animation()
Останавливает все воспроизводимые анимации на пропе.
Параметры не принимает.
Пример
return {
api_version = 1,
on_right_click = function(context)
context.prop:stop_animation()
end
}
context.event
Данные события для текущего hook. Доступны в on_left_click, on_right_click и on_projectile_hit. Возвращает nil в hook-ах без связанного события (on_spawn, on_game_tick, on_destroy, on_zone_enter, on_zone_leave).
Поля и методы
| Поле или метод | Тип | Примечания |
|---|---|---|
event.is_cancelled | boolean | Отменено ли событие в данный момент |
event.cancel() | function | Отменяет событие (например, предотвращает урон или взаимодействие) |
event.uncancel() | function | Снимает отмену с ранее отменённого события |
Не все события можно отменить. Если базовое событие Bukkit не реализует Cancellable, event.cancel() и event.uncancel() будут отсутствовать, а event.is_cancelled всегда будет false.
Пример: Сделать проп неуязвимым
Пример
return {
api_version = 1,
on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}
Пример: Проверка состояния отмены
Пример
return {
api_version = 1,
on_left_click = function(context)
if context.event and not context.event.is_cancelled then
context.event.cancel()
context.log:info("Damage cancelled!")
end
end
}
Внутри запланированных callback-ов (scheduler:run_later, scheduler:run_repeating) context.event всегда равен nil. Изменение события возможно только во время самого hook-а события.
context.world
Таблица world предоставляет методы для запроса и взаимодействия с миром Minecraft. Она создаётся на основе текущего мира пропа.
API context.world общий с EliteMobs. Пропы FMM используют ту же таблицу world Magmacore, что и боссы EliteMobs. Методы, задокументированные здесь, доступны в базовой реализации Magmacore. Полную справку смотрите на странице EliteMobs World & Environment -- методы, перечисленные там как часть базового API context.world, доступны и в FMM.
world.name
Строковое поле, содержащее имя мира.
world:get_block_at(x, y, z)
Возвращает название материала блока по заданным координатам в виде строки в нижнем регистре (например, "stone", "air").
| Параметр | Тип | Примечания |
|---|---|---|
x | int | X-координата блока |
y | int | Y-координата блока |
z | int | Z-координата блока |
Пример
return {
api_version = 1,
on_spawn = function(context)
local loc = context.prop.current_location
if loc then
local block = context.world:get_block_at(
math.floor(loc.x),
math.floor(loc.y) - 1,
math.floor(loc.z)
)
context.log:info("Prop is standing on: " .. block)
end
end
}
world:spawn_particle(particle, x, y, z, count, dx, dy, dz, speed)
Создаёт частицы в указанном месте.
| Параметр | Тип | По умолчанию | Примечания |
|---|---|---|---|
particle | string | обязательный | Имя enum Particle из Bukkit, ЗАГЛАВНЫМИ (например, "FLAME", "DUST") |
x | number | обязательный | X-координата |
y | number | обязательный | Y-координата |
z | number | обязательный | Z-координата |
count | int | 1 | Количество частиц |
dx | number | 0 | Разброс/смещение по X |
dy | number | 0 | Разброс/смещение по Y |
dz | number | 0 | Разброс/смещение по Z |
speed | number | 0 | Скорость частиц |
Пример
return {
api_version = 1,
on_right_click = function(context)
local loc = context.prop.current_location
if loc then
context.world:spawn_particle("HEART", loc.x, loc.y + 1, loc.z, 5, 0.3, 0.3, 0.3, 0)
end
end
}
world:play_sound(sound, x, y, z, volume, pitch)
Воспроизводит звук в указанном месте.
| Параметр | Тип | По умолчанию | Примечания |
|---|---|---|---|
sound | string | обязательный | Имя enum Sound из Bukkit, ЗАГЛАВНЫМИ (например, "ENTITY_EXPERIENCE_ORB_PICKUP") |
x | number | обязательный | X-координата |
y | number | обязательный | Y-координата |
z | number | обязательный | Z-координата |
volume | number | 1.0 | Громкость |
pitch | number | 1.0 | Высота звука |
Пример
return {
api_version = 1,
on_right_click = function(context)
local loc = context.prop.current_location
if loc then
context.world:play_sound("BLOCK_NOTE_BLOCK_PLING", loc.x, loc.y, loc.z, 1.0, 1.2)
end
end
}
world:strike_lightning(x, y, z)
Ударяет молнией в указанном месте (визуально и с уроном).
| Параметр | Тип | Примечания |
|---|---|---|
x | number | X-координата |
y | number | Y-координата |
z | number | Z-координата |
Пример
return {
api_version = 1,
on_projectile_hit = function(context)
local loc = context.prop.current_location
if loc then
context.world:strike_lightning(loc.x, loc.y, loc.z)
end
end
}
world:get_time()
Возвращает текущее время мира в тиках.
world:set_time(ticks)
Устанавливает время мира.
| Параметр | Тип | Примечания |
|---|---|---|
ticks | int | Время мира (0 = рассвет, 6000 = полдень, 13000 = ночь, 18000 = полночь) |
world:get_nearby_entities(x, y, z, radius)
Возвращает массив таблиц-обёрток сущностей для всех живых сущностей в ограничивающем параллелепипеде, центрированном по заданным координатам.
| Параметр | Тип | Примечания |
|---|---|---|
x | number | Центр X |
y | number | Центр Y |
z | number | Центр Z |
radius | number | Радиус поиска (используется как половина протяжённости по всем трём осям) |
Пример
return {
api_version = 1,
on_right_click = function(context)
local loc = context.prop.current_location
if loc then
local entities = context.world:get_nearby_entities(loc.x, loc.y, loc.z, 10)
context.log:info("Found " .. #entities .. " entities nearby")
end
end
}
world:get_nearby_players(x, y, z, radius)
Возвращает массив таблиц-обёрток игроков для всех игроков в ограничивающем параллелепипеде, центрированном по заданным координатам.
| Параметр | Тип | Примечания |
|---|---|---|
x | number | Центр X |
y | number | Центр Y |
z | number | Центр Z |
radius | number | Радиус поиска |
Пример
return {
api_version = 1,
on_right_click = function(context)
local loc = context.prop.current_location
if loc then
local players = context.world:get_nearby_players(loc.x, loc.y, loc.z, 20)
for i = 1, #players do
context.log:info("Nearby player: " .. (players[i].name or "unknown"))
end
end
end
}
context.zones
Таблица zones позволяет создавать пространственные зоны и отслеживать события входа/выхода игроков. Зоны привязаны к экземпляру скрипта и автоматически очищаются при удалении пропа.
zones:create_sphere(x, y, z, radius)
Создаёт сферическую зону с центром в заданных координатах. Возвращает числовой дескриптор зоны.
| Параметр | Тип | Примечания |
|---|---|---|
x | number | Центр X |
y | number | Центр Y |
z | number | Центр Z |
radius | number | Радиус сферы |
zones:create_cylinder(x, y, z, radius, height)
Создаёт цилиндрическую зону. Возвращает числовой дескриптор зоны.
| Параметр | Тип | Примечания |
|---|---|---|
x | number | Центр X |
y | number | Центр Y (основание) |
z | number | Центр Z |
radius | number | Радиус цилиндра |
height | number | Высота цилиндра |
zones:create_cuboid(x, y, z, xSize, ySize, zSize)
Создаёт зону в форме параллелепипеда. Возвращает числовой дескриптор зоны.
| Параметр | Тип | Примечания |
|---|---|---|
x | number | Центр X |
y | number | Центр Y |
z | number | Центр Z |
xSize | number | Половина протяжённости по X |
ySize | number | Половина протяжённости по Y |
zSize | number | Половина протяжённости по Z |
zones:watch(handle, onEnterCallback, onLeaveCallback)
Начинает отслеживание зоны на события входа/выхода игроков. Когда игрок входит или покидает зону, срабатывает соответствующий hook (on_zone_enter или on_zone_leave).
| Параметр | Тип | Примечания |
|---|---|---|
handle | int | Дескриптор зоны из вызова create_* |
onEnterCallback | function или nil | Вызывается, когда игрок входит в зону |
onLeaveCallback | function или nil | Вызывается, когда игрок покидает зону |
Возвращает true, если отслеживание успешно настроено, nil, если дескриптор зоны недействителен.
Проверки зон выполняются каждый серверный тик для всех игроков в том же мире. Поддерживайте разумное количество зон, чтобы избежать снижения производительности.
zones:unwatch(handle)
Прекращает отслеживание зоны и освобождает её ресурсы.
| Параметр | Тип | Примечания |
|---|---|---|
handle | int | Дескриптор зоны для прекращения отслеживания |
Пример: Зона срабатывания по приближению
Пример
return {
api_version = 1,
on_spawn = function(context)
local loc = context.prop.current_location
if loc == nil then return end
-- Create a sphere zone around the prop
local handle = context.zones:create_sphere(loc.x, loc.y, loc.z, 5)
-- Watch for player enter/leave
context.zones:watch(
handle,
function(player)
context.log:info("Player entered zone!")
end,
function(player)
context.log:info("Player left zone!")
end
)
-- Store handle so we can clean up later if needed
context.state.zone_handle = handle
end,
on_destroy = function(context)
if context.state.zone_handle then
context.zones:unwatch(context.state.zone_handle)
end
end
}
context.scheduler
Планировщик позволяет запускать отложенные и повторяющиеся задачи. Все задачи принадлежат экземпляру скрипта и автоматически отменяются при удалении пропа.
scheduler:run_later(ticks, callback)
Запускает callback однократно после задержки. Возвращает числовой идентификатор задачи.
| Параметр | Тип | Примечания |
|---|---|---|
ticks | int | Задержка в серверных тиках (20 тиков = 1 секунда) |
callback | function | Вызывается с новым context, когда истекает задержка |
Пример
return {
api_version = 1,
on_right_click = function(context)
context.log:info("Something will happen in 2 seconds...")
context.scheduler:run_later(40, function(delayed_context)
local loc = delayed_context.prop.current_location
if loc then
delayed_context.world:spawn_particle("EXPLOSION_EMITTER", loc.x, loc.y, loc.z, 1)
end
end)
end
}
scheduler:run_repeating(delay, interval, callback)
Запускает callback с фиксированным интервалом. Возвращает числовой идентификатор задачи.
| Параметр | Тип | Примечания |
|---|---|---|
delay | int | Начальная задержка в тиках перед первым запуском |
interval | int | Тики между каждым последующим запуском |
callback | function | Вызывается с новым context на каждом интервале |
Пример
return {
api_version = 1,
on_spawn = function(context)
-- Emit particles every second
context.state.particle_task = context.scheduler:run_repeating(0, 20, function(tick_context)
local loc = tick_context.prop.current_location
if loc then
tick_context.world:spawn_particle("FLAME", loc.x, loc.y + 1, loc.z, 3, 0.1, 0.1, 0.1, 0.02)
end
end)
end,
on_destroy = function(context)
if context.state.particle_task then
context.scheduler:cancel(context.state.particle_task)
end
end
}
scheduler:cancel(taskId)
Отменяет запланированную задачу по её идентификатору.
| Параметр | Тип | Примечания |
|---|---|---|
taskId | int | Идентификатор задачи, возвращённый run_later или run_repeating |
Если вы запустили повторяющуюся задачу, всегда отменяйте её в on_destroy (или когда она больше не нужна). Забытая отмена оставляет фоновую задачу работающей до удаления пропа с сервера, что расходует производительность и может вызывать ошибки.
context.state
Обычная Lua-таблица, которая сохраняется на протяжении всего времени жизни пропа. Используйте её для хранения флагов, счётчиков, идентификаторов задач, состояний переключателей и любых данных, которые нужно разделять между hook-ами.
on_spawn = function(context)
context.state.is_open = false
context.state.click_count = 0
context.state.task_id = nil
end,
on_right_click = function(context)
context.state.click_count = (context.state.click_count or 0) + 1
end
Между вызовами hook-ов сохраняется только context.state. Все остальные таблицы контекста (context.prop, context.world, context.event и т. д.) пересоздаются каждый раз заново.
context.log
Методы логирования в консоль. Сообщения выводятся в серверную консоль с префиксом имени файла скрипта.
| Метод | Примечания |
|---|---|
log:info(message) | Информационное сообщение |
log:warn(message) | Предупреждение |
log:error(message) | Сообщение об ошибке (функционально аналогично warn) |
Пример
return {
api_version = 1,
on_spawn = function(context)
context.log:info("Script loaded!")
end,
on_right_click = function(context)
context.log:warn("Prop was clicked -- this is a debug warning")
end
}
Модель выполнения
Один runtime на проп
Каждая сущность пропа со скриптами получает собственный независимый экземпляр Lua-runtime. При спавне пропа FMM загружает исходный код Lua, выполняет его в новой изолированной среде и сохраняет возвращённую таблицу. При удалении пропа runtime останавливается.
Это означает:
- Локальные переменные, объявленные на уровне файла, являются приватными для данного экземпляра пропа.
context.stateполностью изолирован между экземплярами пропов, даже если они используют один и тот же файл скрипта.
Владение задачами
Все задачи, созданные через context.scheduler, принадлежат runtime, который их создал. При удалении пропа:
- Runtime останавливается.
- Все принадлежащие задачи -- как одноразовые, так и повторяющиеся -- автоматически отменяются.
- Все отслеживания зон очищаются.
Бюджет выполнения
Каждый вызов hook и каждый вызов callback замеряются по времени. Если один вызов занимает более 50 миллисекунд, скрипт отключается с предупреждением в консоли:
[Lua] my_script.lua took 73ms in 'on_game_tick' (limit: 50ms) -- script disabled to prevent lag.
Чтобы уложиться в бюджет:
- Избегайте неограниченных циклов внутри hook-ов.
- Держите обработчики
on_game_tickлёгкими -- они выполняются каждый тик. - Используйте
context.scheduler:run_repeating(...)для распределения работы по тикам.
Дальнейшие шаги
- Примеры и шаблоны -- полные работающие скрипты пропов с пояснениями
- Устранение неполадок -- частые проблемы, советы по отладке и чек-лист QC
- Начало работы -- структура файлов, hook-и, пошаговое руководство по первому скрипту