跳到主要内容

MagmaCore Lua 脚本引擎

MagmaCore 提供 EliteMobs Boss 能力、NPC 脚本、FreeMinecraftModels 道具和自定义附魔效果使用的 Lua 沙箱及 ScriptInstance 运行时。它们共享文件验证和实例管理,但可用钩子、上下文表、冷却作用域和生命周期取决于提供方。附魔查询还有受限上下文和提供方专属的额度,请参阅下方的附魔参考。

FreeMinecraftModels 道具和 EliteMobs NPC 直接继承通用上下文表(context.world、context.zones、context.scheduler、context.cooldowns、context.log、context.event、context.player、context.state),并添加 context.prop 或 context.npc。Boss 能力使用同一运行时,但提供若干表的 Boss 专属变体(context.world、context.zones、context.cooldowns、context.scheduler、context.log、context.event),以及 context.players、context.entities、context.vectors、context.settings 和 context.script 等专属辅助表。

目前,下列插件使用此引擎:

  • EliteMobs —— 自定义 Boss 的 Lua 能力(on_boss_damaged_by_player、on_enter_combat 等),以及处理交互和接近行为的 NPC Lua 脚本(on_npc_interact、on_npc_proximity_enter 等)。
  • FreeMinecraftModels —— 道具的 Lua 脚本(on_right_click、on_left_click 等),以及自定义物品的附魔效果。

本页介绍 FMM 使用的通用 MagmaCore 功能,并说明 EliteMobs Boss 和 NPC 变体的差异。Boss 的准确上下文请查阅 EliteMobs Lua 页面;NPC 请查阅 NPC Lua 脚本。


极简 Lua 入门​

如果你完全没接触过 Lua,下面是为任意 Nightbreak 插件编写脚本时所需的最小语法。

变量​

使用 local 来存储一个值:

local cooldown_key = "fire_burst"
local damage_multiplier = 1.5

local 表示该变量只属于当前文件或代码块。

函数​

函数是可复用的逻辑块:

local function warn_player(player)
player:send_message("&cMove!")
end

之后可以调用它:

warn_player(some_player)

if 判断​

仅在某些条件下需要执行时使用 if:

if context.player == nil then
return
end

意思是“如果当前钩子没有玩家,则在这里停止”。

nil​

nil 表示“没有值”。它是 Lua 中表示“什么都没有”的方式。

你常会用以下方式检查 nil:

if context.event ~= nil then
-- do something with the event
end

~= 表示“不等于”。

表​

Lua 使用表来完成多种工作:

  • 列表
  • 带命名键的对象
  • 最终返回的脚本定义

带命名键的表示例:

local particle = {
particle = "FLAME",
amount = 1,
speed = 0.05
}

返回脚本定义​

在每个脚本文件的末尾,你需要返回一个表:

return {
api_version = 1,

on_spawn = function(context)
end
}

返回的表就是脚本定义。加载器先执行文件进行验证,再为每个运行时实例重新执行保存的源码。因此,文件局部变量属于各自实例,不会由使用同一文件的所有实体共享。编辑文件不会改变已加载定义或运行中的实例;请使用提供方插件的流程重新加载。

注释​

使用 -- 给人类读者写注释:

-- This cooldown stops the attack from firing every hit
context.cooldowns:set_local(60, "fire_burst")

Lua 沙箱​

所有 Lua 脚本都在沙箱化的 LuaJ 环境中运行。可能访问文件系统或 Java 运行时的多个全局变量已被移除。所有使用 MagmaCore 的插件遵循相同的沙箱规则。

已移除的全局变量​

以下标准 Lua 全局变量被设为 nil 且无法使用:

已移除项原因
debug暴露 VM 内部状态
dofile文件系统访问
io文件系统访问
load任意代码加载
loadfile文件系统访问
luajava直接的 Java 类访问
module模块系统(不需要)
os操作系统访问
package模块系统(不需要)
require模块系统 / 文件系统访问

可用的标准库​

LuaJ 标准环境提供以下常用 Lua 功能:

类别函数
数学math.abs、math.ceil、math.floor、math.max、math.min、math.random、math.sin、math.cos、math.sqrt、math.pi,以及所有其他 math.* 函数
字符串string.byte、string.char、string.find、string.format、string.gsub、string.len、string.lower、string.match、string.rep、string.sub、string.upper,以及所有其他 string.* 函数
表table.insert、table.remove、table.sort、table.concat,以及所有其他 table.* 函数
迭代器pairs、ipairs、next
类型type、tostring、tonumber、select、unpack
错误处理pcall、xpcall、error、assert
其他print、rawget、rawset、rawequal、rawlen、setmetatable、getmetatable
os 库不可用

os 已完全移除。通用接口用 context.world:get_time() 读取世界时间,Boss 能力也通过世界表的共享基类获得它。要计算经过时间,可在 on_game_tick 中维护 context.state 的 tick 计数器。

提示

print 会写入服务器控制台,但建议使用 context.log:info(msg)。每个接口都会暴露 context.log:FreeMinecraftModels 脚本和 EliteMobs NPC 脚本暴露 info、warn 和 error;EliteMobs Boss 能力暴露 info、warn 和 debug。


文件契约​

每个 Lua 脚本都必须 return 一个表。该表的格式是严格的。

必填与可选的顶层字段​

字段必填类型说明
api_version是number当前必须为 1
priority否number默认 0,转换为整数。EliteMobs Boss 和 NPC 先运行较小值;FMM 验证该字段,但道具按 scripts: 顺序执行
支持的钩子键否function必须使用插件支持的准确钩子名

验证规则​

  • 文件必须返回一个表。
  • api_version 是必填项,且目前必须为 1。
  • 若存在 priority,必须是数字。
  • 其他每个顶层键都必须是受支持的钩子名。
  • 每个钩子键都必须指向一个函数。
  • 未知的顶层键会被拒绝。
priority 因提供方而异

验证器在所有 MagmaCore 脚本中接受 priority。EliteMobs 用它排序 Boss 和 NPC 脚本,FMM 道具则按 scripts: 列表顺序运行。自定义附魔使用提供方的目录以及动作和查询生命周期。

脚本文件放在哪里​

各提供方选择自己的目录。通用引擎递归查找 .lua,排除已归档配置路径,先按不区分大小写的规范化绝对路径排序,再按准确路径排序。对于每个转成小写的文件名,只选择第一个,并记录内容与选中文件不同的被跳过重复文件。选中的文件验证失败也不会改用重复文件。注册查找保留所选文件的实际拼写,引用时请使用该拼写。Boss 能力和附魔目录有自己的发现规则,共用验证器不代表目录可以互换。

文件名必须唯一

脚本是按文件名(而不是路径)作为键的。位于不同子文件夹中的两个 door.lua 是同一个键,最终只有其中一个会被注册。请给每个脚本取一个各不相同的文件名。

辅助函数与本地常量应放在最终 return 之上,而不应放在返回表内部,除非它们本身就是钩子。

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)
do_something(context)
end
}

常见钩子名称​

这些名称用于多个运行时,但每种脚本只启用适用于其所有者的钩子。FMM 道具使用以下全部钩子;Boss 使用专属列表;NPC 使用 on_spawn、on_remove、on_game_tick、on_npc_interact、on_npc_proximity_enter、on_npc_proximity_leave、on_zone_enter 和 on_zone_leave。

钩子触发时机
on_spawn由 FMM 道具、EliteMobs Boss 或 NPC 的插件生成流程触发;仅构造 ScriptInstance 不会触发
on_game_tick已初始化的实例处于活动且未暂停状态时,每个服务器 tick 调用
on_zone_enter当运行时支持的区域报告进入事件时调用。FMM 与 EliteMobs NPC 的区域监视是基于玩家的;EliteMobs 顶层 Boss 区域钩子来自 EliteScript 区域事件。
on_zone_leave当运行时支持的区域报告离开事件时调用。FMM 与 EliteMobs NPC 的区域监视是基于玩家的;EliteMobs 顶层 Boss 区域钩子来自 EliteScript 区域事件。

方法语法:: 与 .​

在 Lua 中,object:method(arg) 是 object.method(object, arg) 的简写。MagmaCore 的上下文表方法会剥离那个隐式的第一个参数,因此对于 context.log.info 这类方法,两种形式都可以工作:

context.log:info("hello")
context.log.info("hello") -- same thing

em 下的全局辅助函数是普通函数,请用 . 调用:em.create_location(...)、em.zone.create_sphere_zone(...) 和 em.location.is_protected(...)。EliteMobs 的事件修改函数 context.event.set_damage_amount(...) 和 context.event.multiply_damage_amount(...) 也必须用 .。本文档对上下文表方法使用 :,对普通函数使用 .。


执行预算​

每一次钩子调用、每一次调度回调,以及脚本文件本身的初次求值,都在一个硬性执行预算下运行。该预算在 Lua VM 内部强制执行,因此它在你的代码仍在运行时就已生效,而不是事后才检查时钟。

普通运行时使用以下限制:

限制取值
当前线程的 CPU 时间50 毫秒
已执行的 Lua 指令数250,000

超限会抛出 Lua 错误。指令数逐条检查,时钟每 1,024 条指令及调用返回后检查。它不能打断正在执行的 Java/Bukkit 方法,50 毫秒 CPU 时间也不是 50 毫秒实际经过时间的期限。钩子或回调中的未捕获错误会关闭实例;初次验证时则拒绝定义。无法测量当前线程 CPU 时间时,改用 250 毫秒经过时间及相同指令上限。附魔查询批次可能使用更小的额度。

无限循环会被中止,但仍会占用 tick

VM 在执行 Lua 指令时检查预算,因此 while true do end 会被中止。它仍可能耗尽服务器线程的 CPU 额度,并导致脚本被禁用;不要因为有这层保护就编写无界循环。

嵌套调用共享同一份预算:整条调用链共用50毫秒 CPU 时间和250,000条指令,每次调用没有独立额度。

为保持在预算内:

  • 避免在钩子内使用无界循环。
  • 让 on_game_tick 处理函数保持轻量 —— 它们每个 tick 都会运行。
  • 使用 context.scheduler:run_repeating(...) 将工作分散到多个 tick。
  • 用 context.cooldowns 或合理间隔限制高开销处理。
脚本运行时错误

钩子或调度回调中的未捕获错误会关闭实例并取消其所属工作。修复并重新加载定义,然后由提供方创建新实例。仅保存文件无法修复已关闭的实例。附魔提供方也有自己的失败抑制规则。


context.state​

一个在脚本实例的整个生命周期内持续存在的普通 Lua 表。用它来存储标志、计数器、任务 ID、开关状态以及任何需要在钩子之间共享的数据。

return {
api_version = 1,

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
}
信息

context.state 在实例的钩子间共享数据,只存于内存,不能跨重载或重启保存。其他表按需构建,可能包含快照。FMM 缓存 context.prop,但 current_location 等字段按需读取。要获得当前值,请重新读取动态字段;保存到变量中的表不会自行更新。给动态字段赋值会遮蔽其 getter。


context.log​

所有脚本都可使用控制台日志。通用 log.info 带 [Lua] 前缀;需要识别来源时,请自行加入脚本标签。错误诊断会标明文件,普通日志不会自动添加文件名。FMM 和 NPC 提供 info、warn、error;Boss 提供 info、warn、debug。

方法说明
log:info(message)信息性消息
log:warn(message)警告消息
log:error(message)警告级别的消息(以 WARN 级别记录,与 log:warn 相同)
EliteMobs 变体

EliteMobs 的 context.log 注册的是 debug 而不是 error。请使用 log:debug(message) 输出调试信息(会以 info 级别加上 debug 前缀显示)。

context.log:info("Script loaded!")
context.log:warn("Something unexpected happened")
-- Generic/FMM/NPC logger:
context.log:error("Critical failure in zone setup")
-- EliteMobs boss logger:
context.log:debug("Boss debug detail")

context.cooldowns​

本地冷却和通用全局冷却将每个配置 tick 换算为单调时钟的 50 毫秒,包括暂停和低 TPS 期间。Boss 全局冷却则按服务器 tick 计数。共有两类作用域:

  • 本地冷却使用所有者存储中的准确字符串键,作用域见下文。nil 选择默认键,即 __lua: 加脚本文件名。
  • 全局冷却在同一所有者实体上的所有脚本之间共享(例如同一道具上的所有脚本,或同一 Boss 上的所有 Lua 能力)。

cooldowns:check_local(key?, duration)​

检查本地键,若就绪则启动并返回 true,否则返回 false。这是一次同步 Lua 调用,不检查或启动全局冷却。该存储不是独立的跨线程锁 API。

参数类型说明
keystring(可选)冷却标识符。nil 选择默认键
durationint冷却时长,单位为 tick(20 = 1 秒)
on_right_click = function(context)
-- Only allow this action once every 3 seconds
if not context.cooldowns:check_local("interact", 60) then
return
end
-- ... do the action
end
统一的冷却参数顺序

Boss、NPC 和 FMM 都使用 check_local(key?, duration)。默认键需写为 check_local(nil, 60);check_local(60) 不表示默认键的 60 tick 冷却。早期实验性 NPC 版本使用 check_local(duration, key?),请更新旧脚本的顺序。

cooldowns:local_ready(key?)​

若本地冷却已结束(或从未设置过),返回 true;若仍在进行中,返回 false。

cooldowns:local_remaining(key?)​

返回本地冷却剩余的 tick 数,若已就绪则返回 0。

cooldowns:set_local(duration, key?)​

设置本地冷却,不检查是否已经处于冷却状态。用于无条件重置冷却。

参数类型说明
durationint时长(tick)。传入 0 或负数表示清除。
keystring(可选)冷却标识符。nil 选择默认键

cooldowns:global_ready()​

若全局冷却(在同一实体上的所有脚本之间共享)已结束,返回 true。

cooldowns:set_global(duration)​

设置全局冷却。通用表中零或负数会清除它。Boss 全局冷却保留较晚的到期时间:较短的时长永远不会缩短正在生效的全局冷却,零或负数时长不起作用。设置本地冷却不会自动设置全局冷却,反之亦然。

参数类型说明
durationint时长(tick)
全局冷却的作用范围

全局冷却的“所有者”取决于脚本类型:

  • EliteMobs Lua 能力 —— 按 EliteEntity 共享(每个 Boss 一个桶)。global_ready() / set_global() 使用 Boss 内置的能力冷却系统;本地冷却也会在同一 Boss 的所有能力之间共享。
  • EliteMobs NPC 脚本 —— 通过 NPC 的单个适配器共享全局冷却和显式本地键。两个脚本用同一本地键时会相互影响;省略键仍按文件区分。
  • FMM 道具脚本 —— 全局按已放置道具共享;各脚本有独立适配器和本地存储。
  • 附魔动作 —— 全局在提供方内按玩家共享;本地属于玩家和附魔 ID,可跨装备交换和动作结束保留。参阅自定义附魔。

context.scheduler​

调度器运行延迟和重复任务。通过它创建的任务属于实例,在实例关闭时取消。这不会撤销生成实体、世界编辑或其他 UI、效果辅助方法创建的计时器。暂停时,已到期的单次任务等待恢复,重复任务跳过暂停期间的调用。

本节使用 MagmaCore 的调度器方法名(run_later、run_repeating、cancel)。EliteMobs 的名称(run_after(ticks, callback)、run_every(interval, callback)、cancel_task(taskId))在每一个接口上都作为别名被接受,因此两种写法在任何脚本中都可用。

scheduler:run_later(ticks, callback)​

在延迟后运行一次回调。返回一个数字任务 ID。

参数类型说明
ticksint延迟时长,单位为服务器 tick(20 tick = 1 秒)
callbackfunction延迟结束时以全新的 context 调用
context.scheduler:run_later(40, function(delayed_context)
delayed_context.log:info("2 seconds have passed!")
end)

scheduler:run_repeating(delay, interval, callback)​

按固定间隔重复运行回调。返回一个数字任务 ID。

参数类型说明
delayint首次运行前的初始延迟 tick 数
intervalint之后每次运行之间的 tick 间隔
callbackfunction每次间隔以全新的 context 调用
context.state.particle_task = context.scheduler:run_repeating(0, 20, function(tick_context)
tick_context.log:info("Another second passed!")
end)

scheduler:cancel(taskId)​

按 ID 取消本实例所属的任务。未知 ID 或其他实例的 ID 不产生效果。

参数类型说明
taskIdintrun_later 或 run_repeating 返回的任务 ID
用途结束后停止重复工作

实例关闭会自动取消所属任务。如果交互或战斗阶段结束后实例仍存在,应更早取消,例如在 Boss 的 on_exit_combat 中。两组调度器名称均可用。请使用提供方的生命周期钩子;因错误关闭实例时,不保证执行自定义清理钩子。

回调接收的是全新的 context

回调接收不含原始事件或执行者的新上下文。请使用该参数,不要使用外层上下文。如后续需要原玩家,在钩子中保存 UUID,之后重新查找并确认可用。保留的外层上下文还可能在原事件结束后才解析延迟事件字段。


context.world​

世界表提供查询和操作方法,首次构建时捕获所有者当前的世界。保存该表不会使它跟随之后的世界变化。

各接口共用的 world 表

这里介绍 FMM 道具和 NPC 使用的共享表,包括造成伤害的 strike_lightning(...)。FMM 添加可选的 EliteMobs 运输和战利品辅助方法;参阅 FMM 的世界表扩展。EliteMobs 在共享表上添加检查区块加载状态的视觉闪电、NPC 和 Boss 生成功能。Boss 能力还会覆盖或添加专属方法;参阅 EliteMobs 世界与环境。

坐标式与位置表式重载并存

在通用接口上,context.world 同时接受坐标式调用(例如 strike_lightning(x, y, z)、spawn_particle(name, x, y, z, ...))和位置表式重载(例如 strike_lightning_at_location(loc)、spawn_particle_at_location(loc, ...))。EliteMobs Boss 能力也支持这两大类写法,但部分位置表方法带有 Boss 专属行为。

共享位置参数必须有 x、y、z。省略或无法解析的世界使用该表的世界。共享的位置式粒子仍使用捕获的世界,请传入同世界的位置。Boss 专用转换器和 em.location.* 查询遵循各自参考中的规则。

完整世界表参考还介绍实体查找、可通行性及保护查询、彩色粉尘、地形冲击和位置表重载。

world.name​

包含世界名称的字符串字段。


world:get_block_at(x, y, z)​

返回 "stone"、"air" 等小写材质名。目标区块未加载时返回 "air",不会强制加载。

参数类型说明
xint方块 X 坐标
yint方块 Y 坐标
zint方块 Z 坐标

world:set_block_at(x, y, z, material)​

在主线程设置方块,避免强制加载区块。

参数类型说明
xint方块 X 坐标
yint方块 Y 坐标
zint方块 Z 坐标
materialstringBukkit Material 名称,小写(例如 "stone"、"air"、"oak_planks")

材质有效且修改已入队时返回 true,材质无效返回 false。任务执行时区块未加载会跳过修改。

此方法不自动检查区域保护。请使用方块材质,并先执行相关保护查询。接受请求不证明方块已改变。


world:spawn_particle(particle, x, y, z, count, dx, dy, dz, speed)​

在某位置生成粒子。

参数类型默认值说明
particlestring必填Bukkit Particle 枚举名;查找时转为大写,例如 "FLAME"、"HAPPY_VILLAGER"
xnumber必填X 坐标
ynumber必填Y 坐标
znumber必填Z 坐标
countint1粒子数量
dxnumber0X 偏移/扩散范围
dynumber0Y 偏移/扩散范围
dznumber0Z 偏移/扩散范围
speednumber0粒子速度
需要数据的粒子

某些粒子需要此简易 API 不提供的额外数据,例如 DUST 和 FALLING_DUST。请使用 CLOUD、SMOKE、CAMPFIRE_COSY_SMOKE、SNOWFLAKE、FLAME、HAPPY_VILLAGER、HEART、NOTE、ENCHANT 等无数据粒子。彩色粉尘请使用世界表参考中的 world:spawn_dust(...)。

由于这个简单 API 不提供 Bukkit 粒子数据对象,需要数据的粒子可能会抛出底层的 Bukkit 异常并停用脚本实例。在 FMM/MagmaCore 脚本中请优先使用上面这些不需要数据的粒子名称。


world:play_sound(sound, x, y, z, volume, pitch)​

在某位置播放声音。

参数类型默认值说明
soundstring必填Bukkit Sound 枚举名(例如 "ENTITY_EXPERIENCE_ORB_PICKUP")或自定义资源包声音键(例如 "mypack:boss.roar")
xnumber必填X 坐标
ynumber必填Y 坐标
znumber必填Z 坐标
volumenumber1.0音量
pitchnumber1.0音调
自定义资源包音效

该名称会先与 Bukkit 已注册的音效进行匹配(不区分大小写,因此 "ENTITY_ARROW_SHOOT" 和 "entity_arrow_shoot" 是同一个)。如果没有匹配项,它会被规范化为小写的带命名空间键,并作为自定义资源包音效播放。既不是已知音效、也不是有效命名空间键的值不会播放任何东西,也不会抛出错误,因此拼写错误只是静默地什么都不做,而不会停用你的脚本。

同样的规则适用于 world:play_sound_at_location(loc, sound, volume, pitch)。


world:strike_lightning(x, y, z)​

在某位置召唤闪电(视觉效果与伤害)。

参数类型说明
xnumberX 坐标
ynumberY 坐标
znumberZ 坐标

world:get_time()​

返回当前世界时间(tick)。


world:set_time(ticks)​

设置世界时间。

参数类型说明
ticksint世界时间(0 = 黎明,6000 = 正午,13000 = 夜晚,18000 = 子夜)

world:get_nearby_entities(x, y, z, radius)​

返回以指定坐标为中心、轴对齐盒内的实体包装表,数组从 1 开始。不排除脚本所有者。

参数类型说明
xnumber中心 X
ynumber中心 Y
znumber中心 Z
radiusnumber搜索半径(在三个坐标轴上都作为半边长)
警告

返回范围内所有实体,包括掉落物、投射物等非生物实体。调用生物方法前请检查 if entity.damage then。


world:get_nearby_players(x, y, z, radius)​

返回轴对齐盒内的玩家表数组,从 1 开始,radius 是每个轴的半边长。不按游戏模式过滤。

参数类型说明
xnumber中心 X
ynumber中心 Y
znumber中心 Z
radiusnumber搜索半径

world:spawn_entity(entity_type, x, y, z)​

在给定位置生成一个原版 Minecraft 实体。

参数类型说明
entity_typestringBukkit 实体类型名称,小写(例如 "zombie"、"skeleton"、"pig")
xnumberX 坐标
ynumberY 坐标
znumberZ 坐标

返回实体表,适用时含生物方法。未知枚举名返回 nil;有效但不可生成的类型可能抛出错误。此方法不检查区块是否已加载。


world:get_highest_block_y(x, z)​

返回 Bukkit 查询到的 X/Z 最高方块 Y。与 get_block_at 不同,不检查区块加载状态。

参数类型说明
xint方块 X 坐标
zint方块 Z 坐标

world:raycast(from_x, from_y, from_z, dir_x, dir_y, dir_z, [max_distance], [fluid_mode], [blocks_only], [loaded_only])​

从某点沿某方向投射射线,并返回命中信息。

参数类型默认值说明
from_xnumber必填起点 X
from_ynumber必填起点 Y
from_znumber必填起点 Z
dir_xnumber必填方向 X 分量
dir_ynumber必填方向 Y 分量
dir_znumber必填方向 Z 分量
max_distancenumber50最大射线距离
fluid_modestring"NEVER"NEVER、SOURCE_ONLY 或 ALWAYS
blocks_onlybooleanfalse忽略实体,只检测方块
loaded_onlybooleanfalse射线包围矩形内有未加载区块时,不追踪射线而返回 nil

使用 loaded_only=true 时也要检查 nil 结果。否则,未命中会返回命中字段为 nil 的表:

字段类型说明
hit_entityentity table or nil射线命中的第一个实体,若无则为 nil
hit_locationlocation table or nil射线命中位置的精确坐标
hit_blocktable or nil命中方块的 {x, y, z, material},若未命中方块则为 nil

方块材质为小写。组合检测使用实体射线尺寸 0.5,没有实体过滤器。区块检查覆盖整个包围矩形,因此可能因偏离实际射线的未加载区块而拒绝。不合法的流体模式名会抛出错误。


world:spawn_firework(x, y, z, colors, [type], [power])​

在给定位置生成烟花火箭。

参数类型默认值说明
xnumber必填X 坐标
ynumber必填Y 坐标
znumber必填Z 坐标
colorstable必填颜色字符串数组,例如 {"RED", "BLUE", "WHITE"}
typestring"BALL"烟花形状:"BALL"、"BALL_LARGE"、"STAR"、"BURST"、"CREEPER"
powerint1飞行强度,0-127
-- Example: red and yellow firework burst
context.world:spawn_firework(loc.x, loc.y, loc.z, {"RED", "YELLOW"}, "BURST", 2)

world:place_temporary_block(x, y, z, material, [ticks], [require_air])​

放置临时方块,并在正数时长后恢复保存的 BlockData。不保存完整方块状态或物品栏。

参数类型默认值说明
xint必填方块 X 坐标
yint必填方块 Y 坐标
zint必填方块 Z 坐标
materialstring必填Bukkit Material 名称(例如 "stone"、"ice")
ticksint0恢复前的 tick 数。零或负数会永久放置方块且不追踪
require_airbooleanfalse若为 true,只有目标为空气时才放置方块

材质无效或目标区块未加载时返回 false,否则请求被接受后返回 true。require_air=true 且目标不是空气时,即使请求被接受,管理器也会跳过替换。

tick 为正数时,管理器会保存原方块数据,并在计时器结束、区块或世界卸载,或插件关闭时恢复。只有当该位置仍是这个临时方块时才会恢复,因此之后的编辑不会被覆盖。在仍为临时方块的位置再次放置,会重新开始计时,并保留第一次保存的原方块。带有方块实体的方块(例如箱子和告示牌),以及已被其他临时方块所有者(例如附魔租约)占用的方块会被跳过。破坏临时方块不会掉落任何东西,并会结束追踪且不恢复;在该处放置方块同样会结束追踪。

这些计时器不会随 Lua 实例取消,放置也不自动检查区域保护。


world:drop_item(x, y, z, material, [amount])​

在给定位置以自然散落的方式掉落一个物品实体。

参数类型默认值说明
xnumber必填X 坐标
ynumber必填Y 坐标
znumber必填Z 坐标
materialstring必填Bukkit Material 名称
amountint1堆叠数量

返回所掉落物品的实体表,若材质无效则返回 nil。


context.zones​

区域表创建空间区域并监视玩家进出。FMM 道具和 NPC 使用这里的 create_sphere(...)、create_cylinder(...)、create_cuboid(...)、watch(...)、unwatch(...),触发 on_zone_enter 和 on_zone_leave。区域属于实例,实例关闭时自动清理。Boss 使用独立专属表,含 get_entities_in_zone(...)、get_locations_in_zone(...)、watch_zone(...);参阅 EliteMobs 区域与目标。

zones:create_sphere(x, y, z, radius)​

以指定坐标为中心创建球体,返回数字句柄;所有者没有世界时返回 nil。

参数类型说明
xnumber中心 X
ynumber中心 Y
znumber中心 Z
radiusnumber球体半径

zones:create_cylinder(x, y, z, radius, height)​

创建圆柱,返回数字句柄;所有者没有世界时返回 nil。

参数类型说明
xnumber中心 X
ynumber中心 Y(底部)
znumber中心 Z
radiusnumber圆柱半径
heightnumber圆柱高度

zones:create_cuboid(x, y, z, xSize, ySize, zSize)​

创建长方体并返回数字句柄;所有者没有世界时返回 nil。通用监视检测玩家的位置点,因此尺寸为相对中心的半边长,与 Boss 区域的实体重叠和采样点操作不同。若 zSize 为零而 xSize 非零,共享形状会用 xSize 替代 Z。

参数类型说明
xnumber中心 X
ynumber中心 Y
znumber中心 Z
xSizenumberX 方向上的半边长
ySizenumberY 方向上的半边长
zSizenumberZ 方向上的半边长

zones:watch(handle, onEnterCallback, onLeaveCallback)​

启用已创建区域的进出通知。回调参数必须是 Lua 函数,但不会直接执行函数体,逻辑通过 on_zone_enter 和 on_zone_leave 运行。成员追踪从创建时开始,因此稍后添加 watch 不会为已经记录在内部的玩家补发进入事件。后续调用传入 nil 不会清除已启用的通知。

参数类型说明
handleint来自 create_* 调用的区域句柄
onEnterCallbackfunction 或 nil函数启用 on_zone_enter,函数体被忽略
onLeaveCallbackfunction 或 nil函数启用 on_zone_leave,函数体被忽略

若监视设置成功返回 true,若区域句柄无效则返回 nil。

信息

检查在 on_game_tick 前执行,使用所有者当前世界中玩家的位置点,而不是整个玩家包围盒。球体要求严格位于半径内;圆柱要求在半径内,并包含底面、不包含顶面。区域保留创建时的坐标和世界。玩家断线或移除监视不保证触发离开钩子。请保持合理的区域数量。


zones:unwatch(handle)​

停止监视某区域并清理其资源。

参数类型说明
handleint要停止监视的区域句柄

示例:邻近触发区域​

return {
api_version = 1,

on_spawn = function(context)
local loc = context.prop.current_location
if loc == nil then return end

local handle = context.zones:create_sphere(loc.x, loc.y, loc.z, 5)

-- Pass placeholder functions to enable on_zone_enter and on_zone_leave hooks.
-- The placeholder bodies are ignored; the actual logic goes in the hooks below.
context.zones:watch(handle, function() end, function() end)

context.state.zone_handle = handle
end,

on_zone_enter = function(context)
context.log:info("Player entered zone!")
end,

on_zone_leave = function(context)
context.log:info("Player left zone!")
end,

on_destroy = function(context)
if context.state.zone_handle then
context.zones:unwatch(context.state.zone_handle)
end
end
}

context.event​

钩子有事件或执行者时存在该表。FMM 道具点击使用插件取消标志;NPC 交互和通用区域钩子可以提供执行者而没有 Bukkit 事件。在 on_game_tick、调度回调和没有触发玩家的生命周期钩子中不存在。

FMM 道具的 on_projectile_hit 钩子会收到可取消的 FreeMinecraftModels 弹射物命中事件,因此提供 cancel() 和 uncancel()。该钩子在 FMM 处理完命中之后才运行,所以取消并不能阻止非穿透弹射物被消耗。如果其他插件已经取消了这次命中,该钩子不会触发。当射击者是生物实体时,context.event.player 就是射击者(可能是怪物),否则不存在;只有当玩家发射弹射物时才会设置 context.player。该钩子还以实体表形式提供弹射物 context.projectile。

通用 on_zone_enter、on_zone_leave 中的 context.player 和 context.event.player 指进入或离开的玩家。它们没有可取消的 Bukkit 事件,因此不提供 cancel()、uncancel()。

字段 / 方法类型说明
is_cancelledboolean首次解析事件子表时的取消状态。调用 cancel() 或 uncancel() 不会刷新它
cancel()method取消事件;只在可取消事件中存在
uncancel()method撤销取消;只在可取消事件中存在
playerentity table参与该事件的玩家(如有)
on_right_click = function(context)
if context.event and context.event.cancel then
context.event:cancel() -- prevent the default right-click interaction
end
end
EliteMobs 事件表

EliteMobs 能力脚本提供更详细的事件表,包含伤害、原因、来源及修改方法。完整字段请参阅 Lua API 参考。


em 辅助命名空间​

em 在文件加载时可用。构造器只创建数据,不加载世界、注册监视或验证完整形状。返回的 setter 修改其自身表,并保留传入位置表的引用。

函数用途
em.create_location(x, y, z [, world, yaw, pitch])创建带有可选世界名、yaw 和 pitch 的位置表。返回的表还有一个 .add(dx, dy, dz) 方法,可就地偏移位置并返回自身以便链式调用。
em.create_vector(x, y, z)创建向量表
em.zone.create_sphere_zone(radius)创建球形区域定义
em.zone.create_dome_zone(radius)创建圆顶区域定义
em.zone.create_cylinder_zone(radius, height)创建圆柱形区域定义
em.zone.create_cuboid_zone(x, y, z)创建长方体区域定义
em.zone.create_cone_zone(length, radius)创建锥形区域定义
em.zone.create_static_ray_zone(length, thickness)创建静态射线区域定义
em.zone.create_rotating_ray_zone(length, point_radius, animation_duration)创建旋转射线区域定义
em.zone.create_translating_ray_zone(length, point_radius, animation_duration)创建平移射线区域定义
em.location.is_in_dungeon(loc)检查位置是否位于任意已注册地下城区域内(任何报告了 "dungeon" 类型的拥有者)
em.location.is_protected(loc)检查位置是否位于任意受保护区域内(WorldGuard、GriefPrevention、EM 区域等)
em.location.owned_by(loc, namespace)检查指定的插件命名空间是否拥有该位置(例如 "EliteMobs")
em.location.owners(loc)拥有该位置的命名空间字符串数组
em.location.kinds_at(loc)该位置的类型标签数组("elitemobs"、"dungeon"、"world_package"、"open_world_dungeon"、地下城规模分类)
em.location.has_kind(loc, kind)检查位置是否被打上指定类型标签
区域插件会被自动检测

MagmaCore 自带 WorldGuard 和 GriefPrevention 适配器,使用方在启用时初始化,保护查询也可按需初始化。查询覆盖已注册提供方,不是所有保护插件。已注册区域适配器失败时报告为受保护。没有提供方或保护归属时返回 false;警告会指出缺少的提供方。

em.location.* 要求 world 为已加载世界名或 {name=...},不会回退到所有者世界。世界缺失、无法解析或输入为 nil 时返回 false 或空数组;缺失坐标默认为零,但格式错误的数字字段可能抛出错误。所有权命名空间区分大小写,类型标签不区分。拥有某位置不自动代表它属于地下城或受到保护。数组顺序没有保证。

区域构造器返回可链式调用的表,提供 :set_center(loc)(或根据区域类型提供 :set_origin(loc) / :set_destination(loc)):

-- At file scope: create a reusable zone shape
local blast_zone = em.zone.create_sphere_zone(5)

return {
api_version = 1,

on_spawn = function(context)
-- Anchor the zone at call time
blast_zone:set_center(context.boss:get_location())
local entities = context.zones:get_entities_in_zone(blast_zone)
-- ...
end
}
信息

em 命名空间在 EliteMobs 中尤其有用,区域定义会与 context.zones:get_entities_in_zone() 以及 context.script 一起使用。在 FreeMinecraftModels 中,更常用的是 context.zones:create_sphere(...) / create_cylinder(...) / create_cuboid(...) 方法来创建基于监视的简单区域。


实体表​

下表概述常用方法。完整 Lua API 参考还包含投射物字段、移动路径检查、所有权及保护辅助方法。它们适用于通用实体表;Boss 能力的专属包装表另有章节。

世界查询和事件数据返回实体表,根据实体类型提供不同层次的字段和方法。

实体基础字段​

字段类型说明
uuidstring实体的 UUID
entity_typestring实体类型(例如 "player"、"zombie"、"skeleton"、"villager")
is_validboolean实体引用是否仍然有效
is_deadboolean实体是否已死亡
is_playerboolean实体是否为玩家
is_hostileboolean是否实现 Bukkit Monster,并非当前是否具有敌意
is_passiveboolean是否实现 Bukkit Animals,不保证被动行为
current_locationlocation table实体当前位置(x、y、z、world、yaw、pitch)
worldstring实体所在的世界名称

实体基础方法​

方法返回值说明
teleport(location_table)void必须提供 x、y、z;世界省略或无法解析时使用实体世界。yaw、pitch 默认零。不自动检查移动路径或保护
remove()void将实体从世界中移除。仅在实体仍然有效时生效。
set_silent(flag)void抑制或重新启用实体的声音。
set_invulnerable(flag)void使实体免疫或恢复受到伤害。
set_gravity(flag)void启用或禁用实体的重力。
set_glowing(flag)void切换实体的发光描边效果。

生物实体字段​

生物实体(玩家、生物等)拥有所有实体基础字段,并额外包含:

字段类型说明
healthnumber当前生命值
maximum_healthnumber最大生命值
namestring显示名称
is_aliveboolean实体是否存活

生物实体方法​

方法返回值说明
damage(amount)void对实体造成指定数值的伤害
push(x, y, z)void立即将向量加到当前速度上
set_facing(x, y, z)void在原地以新朝向传送
add_potion_effect(effect, duration, amplifier)void添加药水效果。effect 为字符串(例如 "speed"、"slowness"、"regeneration")。duration 单位为 tick。amplifier 为效果等级减 1(0 = 等级 I)。
remove_potion_effect(effect)void按名称移除药水效果
get_scale()number返回包含修饰符的有效缩放值,不可用时返回 1.0
set_scale(value)void属性可用时修改其基础缩放值
警告

并非所有由 get_nearby_entities() 返回的实体都是生物实体。你可以使用 entity.is_player、entity.is_hostile 或 entity.is_passive 按类别过滤,或者在调用 damage()、push()、add_potion_effect() 等生物实体方法前先用 if entity.damage then 进行检查。


插件集成字段​

实体表可包含插件集成字段。没有对应增强器时字段不存在,读取为 nil。跟踪标志、模型身份、管理员状态和大部分嵌套数据是构建时快照,不随之后的跟踪变化更新。共享 EliteMobs 生命值字段按需读取;模型及权限方法在调用时查询底层对象。

EliteMobs 字段​

在已安装 EliteMobs 时可用。

字段类型说明
is_eliteboolean实体是否为 EliteMobs 精英
is_custom_bossboolean实体是否为 EliteMobs 自定义 Boss(在 elite 子表中也可用)
is_significant_bossboolean实体是否为生命值倍率大于 1 的自定义 Boss(即设计过的遭遇战,而非填充用的精英)
elitetable or nil精英信息子表(见下文)。若实体不是精英则为 nil。

elite 子表包含:

字段类型说明
elite.levelint精英的等级
elite.namestring 或 nil精英的显示名称
elite.healthnumber当前生命值
elite.max_healthnumber最大生命值
elite.is_custom_bossboolean是否为自定义 Boss(而非自然出现的精英)
elite.health_multipliernumber配置文件定义的生命值倍率
elite.damage_multipliernumber配置文件定义的伤害倍率
elite:remove()void通过 EliteMobs 移除流程删除精英,不等于击杀,也不保证掉落
示例:对精英造成不同伤害
local entities = context.world:get_nearby_entities(x, y, z, 5)
for _, entity in ipairs(entities) do
if entity.is_elite then
-- Deal double damage to elites
entity:damage(20)
context.log:info("Hit elite: " .. entity.elite.name .. " (level " .. entity.elite.level .. ")")
elseif entity.is_hostile then
entity:damage(10)
end
end

FreeMinecraftModels 字段​

在已安装 FreeMinecraftModels 时可用。

字段类型说明
is_modeledboolean实体是否附加了 FMM 模型(DynamicEntity 或 PropEntity)
is_propboolean实体是否为 FMM 道具(静态装饰实体)
modeltable or nil模型信息子表(见下文)。若实体未模型化则为 nil。

model 子表包含:

字段类型说明
model.model_idstring 或 nil模型蓝图 ID(例如 "torch_01")
model.is_dynamicboolean模型化实体是否为 DynamicEntity(生物/Boss 模型),否则为静态道具
model:play_animation(name, [blend], [loop])boolean接受动画请求。blend 和 loop 默认 false。排队或被抑制的攻击也可能返回 true,不代表立即播放。终结死亡状态会拒绝播放
model:stop_animations()void清除播放队列,并在可用时回到待机动画;不改变终结死亡状态
model:remove()void通过 FMM 的正确清理流程移除模型化实体。

在玩家实体表上,FreeMinecraftModels 增强器还会添加:

字段 / 方法类型说明
is_opboolean该玩家是否为服务器 OP。
has_permission(node)boolean该玩家是否拥有该权限节点。

这两个玩家值只存在于 FreeMinecraftModels 脚本中。在 EliteMobs 内部构建的共享表上,EliteMobs 自己的 FreeMinecraftModels 适配器会添加 is_modeled、is_prop 和 model,但不会添加 is_op 或 has_permission。

FMM 玩家表还提供 is_holding_elitemobs_item(filename),检查主手 EliteMobs 文件名标签(包括 .yml),而不是显示名。空手、其他物品或集成不可用返回 false。EliteMobs 自己的共享增强器在数据可用时添加包含战斗 tier 的 elite_player;FMM 的可选 EliteMobs 适配器不添加该子表。

桥接和道具的默认值

模型桥接的 blend、loop 默认为 false。道具表 的 play_animation 两者默认 true,启用排队和循环。blend 控制排队,不是动画交叉淡化。

示例:按类型过滤实体
local entities = context.world:get_nearby_entities(x, y, z, 10)
for _, entity in ipairs(entities) do
if entity.is_prop then
-- Skip props
elseif entity.is_player then
entity:damage(5)
elseif entity.entity_type == "villager" then
-- Don't hurt villagers
elseif entity.is_elite then
entity:damage(20)
elseif entity.is_hostile then
entity:damage(10)
end
end

玩家专属字段​

玩家实体拥有所有实体和生物实体字段,并额外包含:

字段类型说明
game_modestring玩家的游戏模式("creative"、"survival"、"adventure"、"spectator")

玩家专属方法​

文本格式化

只要 API 中写着「支持颜色代码」,Magmacore 就会把该字符串交给内置的 MiniMessage 解析器处理。传统的 & 代码、&#RRGGBB 十六进制、<#RRGGBB>、<gradient>、<rainbow> 以及 MiniMessage 其余的标签集都可用,并且可以自由混用。Kyori Adventure 不是依赖项,脚本也无法使用任何 Adventure API —— 你并不需要它。

Lua 文本方法转为传统颜色代码字符串,颜色、渐变和装饰可渲染;交互标签(<hover>、<click>、<insert>、<font>、<key>、<lang>)虽被解析但不生效。常见格式见颜色代码。

方法返回值说明
send_message(msg)void向玩家发送聊天消息。支持颜色代码。
chat(text)void将经过颜色处理的文本交给 Bukkit 玩家聊天;带斜杠的命令以该玩家身份执行
get_held_item()table or nil返回玩家主手物品的 {type, amount, display_name},若手为空则为 nil。type 是大写的 Bukkit Material 名称,例如 "GOLD_INGOT"。
consume_held_item(amount?)void消耗请求入队,针对执行时的主手物品。默认 1;不保留检查过的物品,也不确认付款。请传正数,负数不会被拒绝
has_item(material, amount?)boolean若玩家在背包中拥有至少 amount(默认 1)数量的指定材质,返回 true
get_target_entity([range])entity table 或 nil从眼部检测方块和实体,排除玩家自身,实体射线尺寸 0.5。忽略流体及可通行方块,默认范围 50
get_eye_location()location table返回玩家眼睛高度处的位置表
get_look_direction()table返回玩家注视方向的 {x, y, z} 方向向量
send_block_change(x, y, z, material, [ticks])boolean客户端方块修改入队,正数 tick 后复原。true 表示入队;无效或非方块材质返回 false
reset_block(x, y, z)boolean将真实方块的显示恢复入队,并返回 true
sleep(x, y, z)void创建真实白床及尝试睡眠的请求入队。醒来或断线时只恢复 BlockData,不恢复方块实体内容
wake_up()void唤醒正在睡眠的玩家。

排队的方块修改和恢复在执行时读取玩家世界,不要依赖它们在换世界后恢复原先的方块。睡眠清理在 20 tick 后检查,之后每 10 tick 检查。这些任务不归 context.scheduler 所有。


玩家 UI 方法​

这些是共享玩家表方法。Boss 能力的玩家表使用不同 UI 签名,见 API 参考。共享条按宿主插件的 MagmaCore 副本内的玩家标识,不按脚本区分。到期计时器只会移除启动它的那个 Boss 条或重复动作栏,因此旧的到期不会清除新的条目。其 Bukkit 计时器不属于 context.scheduler。

player:show_boss_bar(text, [color], progress, [ticks])​

替换共享的实心 Boss 条。进度限制在 0..1,无效颜色变为白色。省略颜色时传 nil,确保进度仍是第三个参数。

参数类型默认值说明
textstring必填要显示的文字。支持 & 颜色代码。
colorstring"WHITE"血条颜色。可选:"RED"、"BLUE"、"GREEN"、"YELLOW"、"PURPLE"、"PINK"、"WHITE"
progressnumber必填填充比例,0.0(空)到 1.0(满)
ticksint-1正数安排移除;否则保持到隐藏或替换

player:hide_boss_bar()​

从玩家屏幕上移除 Boss 血条。无参数。

player:show_action_bar(text, [ticks])​

在动作栏(快捷栏上方)显示文字。

参数类型默认值说明
textstring必填要显示的文字。支持 & 颜色代码。
ticksint-1正数每 40 tick 重发直到到期;否则只发一次。新调用取消旧重复任务

player:show_title(title, [subtitle], fade_in, stay, fade_out)​

向玩家显示标题界面。

参数类型默认值说明
titlestring必填主标题文字。支持 & 颜色代码。
subtitlestring""主标题下方的副标题文字。可选。
fade_inint必填淡入时长(tick)
stayint必填标题在屏幕上停留的时长(tick)
fade_outint必填淡出时长(tick)

下一步​

获取插件专属的钩子、API 和工作流: