MagmaCore Lua 脚本引擎
MagmaCore 提供多个 Nightbreak 插件共用的 Lua 沙箱和通用 ScriptInstance 运行时。EliteMobs Boss 能力、EliteMobs NPC 脚本、FreeMinecraftModels 道具和 FreeMinecraftModels 物品都运行在这一运行时之上,并共享同一套文件验证、沙箱、上下文生命周期、调度器归属、冷却契约、日志契约、区域监视引擎以及 context.state 模型。
FreeMinecraftModels 道具/物品和 EliteMobs NPC 脚本直接继承通用的 MagmaCore 上下文表(context.world、context.zones、context.scheduler、context.cooldowns、context.log、context.event、context.player 和 context.state),并加上它们各自的归属表(context.prop、context.item 或 context.npc)。EliteMobs Boss 能力运行在同一运行时上,但会为若干表暴露更丰富的 Boss 专属变体(context.world、context.zones、context.cooldowns、context.scheduler、context.log、context.event),外加仅限 Boss 的辅助表,如 context.players、context.entities、context.vectors、context.settings 和 context.script。
目前,下列插件使用此引擎:
- EliteMobs —— 用于自定义 Boss 的 Lua 能力(如
on_boss_damaged_by_player、on_enter_combat等钩子) - FreeMinecraftModels —— 用于道具与自定义物品的 Lua 脚本(如
on_right_click、on_left_click、on_equip等钩子)
本页介绍共享引擎特性。关于插件专属的钩子、API 和工作流,请参阅上面链接的对应插件页面。
极简 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 且无法使用:
| Removed | Why |
|---|---|
debug | 暴露 VM 内部状态 |
dofile | 文件系统访问 |
io | 文件系统访问 |
load | 任意代码加载 |
loadfile | 文件系统访问 |
luajava | 直接的 Java 类访问 |
module | 模块系统(不需要) |
os | 操作系统访问 |
package | 模块系统(不需要) |
require | 模块系统 / 文件系统访问 |
可用的标准库
Lua 标准库的其余部分均可正常使用:
| Category | Functions |
|---|---|
| Math | math.abs、math.ceil、math.floor、math.max、math.min、math.random、math.sin、math.cos、math.sqrt、math.pi,以及所有其他 math.* 函数 |
| String | string.byte、string.char、string.find、string.format、string.gsub、string.len、string.lower、string.match、string.rep、string.sub、string.upper,以及所有其他 string.* 函数 |
| Table | table.insert、table.remove、table.sort、table.concat,以及所有其他 table.* 函数 |
| Iterators | pairs、ipairs、next |
| Type | type、tostring、tonumber、select、unpack |
| Error handling | pcall、xpcall、error、assert |
| Other | print、rawget、rawset、rawequal、rawlen、setmetatable、getmetatable |
os 库不可用os 库已完全从沙箱中移除。如需获取时间信息,可使用 context.world:get_time() 读取世界时间,或在 context.state 中通过 on_game_tick 钩子借助 tick 计数器来存储时间戳。
print 会写入服务器控制台,但建议使用 context.log:info(msg)。每个接口都会暴露 context.log:FreeMinecraftModels 脚本和 EliteMobs NPC 脚本暴露 info、warn 和 error;EliteMobs Boss 能力暴露 info、warn 和 debug。
文件契约
每个 Lua 脚本都必须 return 一个表。该表的格式是严格的。
必填与可选的顶层字段
| Field | Required | Type | Notes |
|---|---|---|---|
api_version | Yes | Number | 当前必须为 1 |
priority | No | Number | 可选的排序值。EliteMobs 的 Boss 脚本和 NPC 脚本会先运行值较小的。FMM 会验证该字段,但道具脚本按 scripts: 列表顺序运行,物品脚本只绑定第一个有效的脚本 |
| supported hook keys | No | Function | 必须使用该插件支持的某个精确钩子名 |
验证规则
- 文件必须返回一个表。
api_version是必填项,且目前必须为1。- 若存在
priority,必须是数字。 - 其他每个顶层键都必须是受支持的钩子名。
- 每个钩子键都必须指向一个函数。
- 未知的顶层键会被拒绝。
Lua 验证器对所有基于 MagmaCore 的脚本都接受 priority。EliteMobs 用它来决定 Boss 脚本和 NPC 脚本的顺序。FreeMinecraftModels 目前不会按 priority 排序:道具遵循配置中 scripts: 列表的顺序,而自定义物品只绑定该列表中第一个有效的脚本。
脚本文件放在哪里
每个插件都会给引擎指定一个脚本目录。该目录会被递归扫描,因此子文件夹中的 .lua 文件也会被采集,你可以按自己喜欢的方式组织脚本。在每个文件夹内,文件按字母顺序访问。
脚本是按文件名(而不是路径)作为键的。位于不同子文件夹中的两个 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
}
常见钩子名称
这些钩子名出现在多个 Lua 运行时中。每种脚本类型只启用对其拥有者有意义的那一部分:FMM 道具脚本使用全部这些钩子;FMM 物品脚本使用本组中的 on_game_tick 加上物品专属钩子;EliteMobs Boss 技能使用其 Boss 钩子列表;EliteMobs 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。
| Hook | When it fires |
|---|---|
on_spawn | 在受支持的拥有者(FMM 道具、EliteMobs Boss 以及 EliteMobs NPC 脚本)创建脚本实例时调用一次 |
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 的事件函数同样用 . 最为稳妥。本文档对上下文表方法使用 :,对普通函数使用 .。
执行预算
每一次钩子调用、每一次调度回调,以及脚本文件本身的初次求值,都在一个硬性执行预算下运行。该预算在 Lua VM 内部强制执行,因此它在你的代码仍在运行时就已生效,而不是事后才检查时钟。
单次调用一旦超出任一限制就会被中止:
| 限制 | 取值 |
|---|---|
| 挂钟时间 | 50 毫秒 |
| 已执行的 Lua 指令数 | 250,000 |
触及任一限制都会在脚本内部抛出一个 Lua 错误,运行时随后会像对待任何其他脚本错误一样处理它:记录该消息,并对该实体禁用此脚本实例。
由于检查是按指令进行的,while true do end 不再会冻结服务器 —— 它会在预算处被切断。但它仍然会烧掉它所在那一 tick 中最多 50 毫秒的时间,并且仍然会禁用该脚本,所以这是一张安全网,而不是编写无界循环的许可证。
正常返回但挂钟时间仍然超过 50 毫秒的调用同样会被禁用,并有它自己的控制台警告:
[Lua] my_script.lua took 73ms in 'on_game_tick' (limit: 50ms) -- script disabled to prevent lag.
嵌套调用共享同一份预算:如果一个钩子调用了一个回调,而该回调又调用了另一个,整条调用链会被作为单份 50 毫秒 / 250,000 指令的额度来计量,而不是每一层各有一份。
为保持在预算内:
- 避免在钩子内使用无界循环。
- 让
on_game_tick处理函数保持轻量 —— 它们每个 tick 都会运行。 - 使用
context.scheduler:run_repeating(...)将工作分散到多个 tick。 - 将开销大的工作移到基于状态的冷却或合理的间隔之后。
如果 Lua 脚本在任何钩子或调度回调中抛出运行时错误,该脚本实例会立即且永久地被禁用。请修复脚本文件中的错误 —— 下次实体生成时脚本会重新初始化。
context.state
一个在脚本实例的整个生命周期内持续存在的普通 Lua 表。用它来存储标志、计数器、任务 ID、开关状态以及任何需要在钩子之间共享的数据。
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 会在多次钩子调用之间持续存在。所有其他 context 表(context.prop、context.world、context.event 等)都会在每次调用时重建。context.state 不会重建 —— 它从脚本实例创建的那一刻起,一直存活到被销毁。
context.log
控制台日志方法。消息会带有脚本文件名前缀显示在服务器控制台中。
| Method | Notes |
|---|---|
log:info(message) | 信息性消息 |
log:warn(message) | 警告消息 |
log:error(message) | 警告级别的消息(以 WARN 级别记录,与 log:warn 相同) |
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
cooldowns 表用于管理脚本的基于时间的冷却。它有两个作用域:
- 本地冷却作用于单个脚本实例,由字符串 key 标识。如果未提供 key,则使用脚本文件名作为默认 key。
- 全局冷却在同一所有者实体上的所有脚本之间共享(例如同一道具上的所有脚本,或同一 Boss 上的所有 Lua 能力)。
cooldowns:check_local(key?, duration)
最常用的方法。检查冷却是否就绪,若就绪则开启冷却并返回 true;若未就绪则返回 false。这是一次原子的检查并设置 —— 不会出现竞态条件。
| Parameter | Type | Notes |
|---|---|---|
key | string (optional) | 冷却标识符。默认使用脚本文件名。 |
duration | int | 冷却时长,单位为 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 脚本和 FreeMinecraftModels 脚本 —— 都使用上面所示的统一顺序:check_local(key?, duration)。(早期的实验性 NPC 版本使用的是 check_local(duration, key?);请把旧的 NPC 脚本更新为统一顺序。)
cooldowns:local_ready(key?)
若本地冷却已结束(或从未设置过),返回 true;若仍在进行中,返回 false。
cooldowns:local_remaining(key?)
返回本地冷却剩余的 tick 数,若已就绪则返回 0。
cooldowns:set_local(duration, key?)
设置本地冷却,不检查是否已经处于冷却状态。用于无条件重置冷却。
| Parameter | Type | Notes |
|---|---|---|
duration | int | 时长(tick)。传入 0 或负数表示清除。 |
key | string (optional) | 冷却标识符。默认使用脚本文件名。 |
cooldowns:global_ready()
若全局冷却(在同一实体上的所有脚本之间共享)已结束,返回 true。
cooldowns:set_global(duration)
设置全局冷却。
| Parameter | Type | Notes |
|---|---|---|
duration | int | 时长(tick) |
全局冷却的“所有者”取决于脚本类型:
- EliteMobs Lua 能力 —— 按
EliteEntity共享(每个 Boss 一个桶)。global_ready()/set_global()使用 Boss 内置的能力冷却系统;本地冷却也会在同一 Boss 的所有能力之间共享。 - FMM 道具脚本 —— 按
PropEntity共享(每个已放置的道具一个桶)。 - FMM 物品脚本 —— 按玩家 UUID 共享(每个玩家一个桶)。物品上的本地冷却会跨穿戴/卸下周期持久化,键为
playerUUID → "itemId:scriptFile" → key,因此即使玩家把物品从快捷栏中换下再换上,冷却也会保留。
context.scheduler
调度器允许你运行延迟和重复任务。所有任务都属于脚本实例,并在脚本实例被销毁时(例如道具被移除或 Boss 死亡)自动取消。
本节使用 MagmaCore 的调度器方法名(run_later、run_repeating、cancel)。EliteMobs 的名称(run_after(ticks, callback)、run_every(interval, callback)、cancel_task(taskId))在每一个接口上都作为别名被接受,因此两种写法在任何脚本中都可用。
scheduler:run_later(ticks, callback)
在延迟后运行一次回调。返回一个数字任务 ID。
| Parameter | Type | Notes |
|---|---|---|
ticks | int | 延迟时长,单位为服务器 tick(20 tick = 1 秒) |
callback | function | 延迟结束时以全新的 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。
| Parameter | Type | Notes |
|---|---|---|
delay | int | 首次运行前的初始延迟 tick 数 |
interval | int | 之后每次运行之间的 tick 间隔 |
callback | function | 每次间隔以全新的 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 取消已调度的任务。
| Parameter | Type | Notes |
|---|---|---|
taskId | int | run_later 或 run_repeating 返回的任务 ID |
如果你启动了一个重复任务,请务必在清理钩子中将其取消(道具用 on_destroy,Boss 用 on_exit_combat / on_death,物品用 on_unequip)。忘记取消会导致后台任务一直运行直到脚本实例被销毁,浪费性能并可能导致错误。
调度回调在参数中接收的是全新的 context。请始终使用回调自身的 context 参数,而不是创建该回调的钩子中的外层 context。外层 context 中可能包含陈旧的引用。
context.world
world 表提供查询和与 Minecraft 世界交互的方法。它基于实体当前所在的世界构建。
此处记录的方法就是共享的 MagmaCore context.world 表。FreeMinecraftModels 的道具/物品以及 EliteMobs NPC 脚本会直接收到这张表,其中包括 strike_lightning(...)。FreeMinecraftModels 还额外叠加了三个可选的 EliteMobs 战利品辅助方法(drop_elitemobs_procedural_loot、drop_elitemobs_random_loot、drop_elitemobs_custom_loot),它们只有在安装了 EliteMobs 时才会起作用;参见 context.world:FreeMinecraftModels 追加内容。EliteMobs Boss 能力同样以这张基础表为起点,然后追加或覆盖 EliteMobs 专属方法,例如受保护的闪电、假抛射物、增援生成和玩家战利品辅助方法;参见 EliteMobs World & Environment。
在通用接口上,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 专属行为。
world.name
包含世界名称的字符串字段。
world:get_block_at(x, y, z)
返回指定坐标处方块的材质名称,以小写字符串表示(例如 "stone"、"air")。
| Parameter | Type | Notes |
|---|---|---|
x | int | 方块 X 坐标 |
y | int | 方块 Y 坐标 |
z | int | 方块 Z 坐标 |
world:set_block_at(x, y, z, material)
在指定坐标设置方块。在主线程上运行。
| Parameter | Type | Notes |
|---|---|---|
x | int | 方块 X 坐标 |
y | int | 方块 Y 坐标 |
z | int | 方块 Z 坐标 |
material | string | Bukkit Material 名称,小写(例如 "stone"、"air"、"oak_planks") |
若方块设置成功返回 true,否则返回 false。
world:spawn_particle(particle, x, y, z, count, dx, dy, dz, speed)
在某位置生成粒子。
| Parameter | Type | Default | Notes |
|---|---|---|---|
particle | string | required | Bukkit Particle 枚举名,全大写(例如 "FLAME"、"HAPPY_VILLAGER") |
x | number | required | X 坐标 |
y | number | required | Y 坐标 |
z | number | required | Z 坐标 |
count | int | 1 | 粒子数量 |
dx | number | 0 | X 偏移/扩散范围 |
dy | number | 0 | Y 偏移/扩散范围 |
dz | number | 0 | Z 偏移/扩散范围 |
speed | number | 0 | 粒子速度 |
某些粒子类型需要这个简单 API 不支持的额外数据。DUST、BLOCK_CRACK、FALLING_DUST、BLOCK_DUST 和 ITEM_CRACK 可能会失败或显示不出任何效果。请改用不需要数据的替代项:CLOUD、SMOKE、CAMPFIRE_COSY_SMOKE、SNOWFLAKE、FLAME、HAPPY_VILLAGER、HEART、NOTE、ENCHANT 等。
由于这个简单 API 不提供 Bukkit 粒子数据对象,需要数据的粒子可能会抛出底层的 Bukkit 异常并停用脚本实例。在 FMM/MagmaCore 脚本中请优先使用上面这些不需要数据的粒子名称。
world:play_sound(sound, x, y, z, volume, pitch)
在某位置播放声音。
| Parameter | Type | Default | Notes |
|---|---|---|---|
sound | string | required | Bukkit Sound 枚举名(例如 "ENTITY_EXPERIENCE_ORB_PICKUP")或自定义资源包声音键(例如 "mypack:boss.roar") |
x | number | required | X 坐标 |
y | number | required | Y 坐标 |
z | number | required | Z 坐标 |
volume | number | 1.0 | 音量 |
pitch | number | 1.0 | 音调 |
该名称会先与 Bukkit 已注册的音效进行匹配(不区分大小写,因此 "ENTITY_ARROW_SHOOT" 和 "entity_arrow_shoot" 是同一个)。如果没有匹配项,它会被规范化为小写的带命名空间键,并作为自定义资源包音效播放。既不是已知音效、也不是有效命名空间键的值不会播放任何东西,也不会抛出错误,因此拼写错误只是静默地什么都不做,而不会停用你的脚本。
同样的规则适用于 world:play_sound_at_location(loc, sound, volume, pitch)。
world:strike_lightning(x, y, z)
在某位置召唤闪电(视觉效果与伤害)。
| Parameter | Type | Notes |
|---|---|---|
x | number | X 坐标 |
y | number | Y 坐标 |
z | number | Z 坐标 |
world:get_time()
返回当前世界时间(tick)。
world:set_time(ticks)
设置世界时间。
| Parameter | Type | Notes |
|---|---|---|
ticks | int | 世界时间(0 = 黎明,6000 = 正午,13000 = 夜晚,18000 = 子夜) |
world:get_nearby_entities(x, y, z, radius)
返回在以给定坐标为中心的包围盒内所有实体的包装表数组。
| Parameter | Type | Notes |
|---|---|---|
x | number | 中心 X |
y | number | 中心 Y |
z | number | 中心 Z |
radius | number | 搜索半径(在三个坐标轴上都作为半边长) |
此方法返回范围内的所有实体,包括非生物实体(盔甲架、掉落物等)。在调用生物实体方法之前,请务必用 if entity.damage then 进行守卫。
world:get_nearby_players(x, y, z, radius)
返回在以给定坐标为中心的包围盒内所有玩家的包装表数组。
| Parameter | Type | Notes |
|---|---|---|
x | number | 中心 X |
y | number | 中心 Y |
z | number | 中心 Z |
radius | number | 搜索半径 |
world:spawn_entity(entity_type, x, y, z)
在给定位置生成一个原版 Minecraft 实体。
| Parameter | Type | Notes |
|---|---|---|
entity_type | string | Bukkit 实体类型名称,小写(例如 "zombie"、"skeleton"、"pig") |
x | number | X 坐标 |
y | number | Y 坐标 |
z | number | Z 坐标 |
返回所生成实体的实体表(若适用则带有生物实体方法),若实体类型无效则返回 nil。
world:get_highest_block_y(x, z)
返回给定 X/Z 位置上最高非空气方块的 Y 坐标。
| Parameter | Type | Notes |
|---|---|---|
x | int | 方块 X 坐标 |
z | int | 方块 Z 坐标 |
world:raycast(from_x, from_y, from_z, dir_x, dir_y, dir_z, [max_distance])
从某点沿某方向投射射线,并返回命中信息。
| Parameter | Type | Default | Notes |
|---|---|---|---|
from_x | number | required | 起点 X |
from_y | number | required | 起点 Y |
from_z | number | required | 起点 Z |
dir_x | number | required | 方向 X 分量 |
dir_y | number | required | 方向 Y 分量 |
dir_z | number | required | 方向 Z 分量 |
max_distance | number | 50 | 最大射线距离 |
返回包含以下字段的表:
| Field | Type | Notes |
|---|---|---|
hit_entity | entity table or nil | 射线命中的第一个实体,若无则为 nil |
hit_location | location table or nil | 射线命中位置的精确坐标 |
hit_block | table or nil | 命中方块的 {x, y, z, material},若未命中方块则为 nil |
world:spawn_firework(x, y, z, colors, [type], [power])
在给定位置生成烟花火箭。
| Parameter | Type | Default | Notes |
|---|---|---|---|
x | number | required | X 坐标 |
y | number | required | Y 坐标 |
z | number | required | Z 坐标 |
colors | table | required | 颜色字符串数组,例如 {"RED", "BLUE", "WHITE"} |
type | string | "BALL" | 烟花形状:"BALL"、"BALL_LARGE"、"STAR"、"BURST"、"CREEPER" |
power | int | 1 | 飞行强度,0-127 |
-- Example: red and gold firework burst
context.world:spawn_firework(loc.x, loc.y, loc.z, {"RED", "GOLD"}, "BURST", 2)
world:place_temporary_block(x, y, z, material, [ticks], [require_air])
放置一个会在延迟后自动恢复原状的方块。
| Parameter | Type | Default | Notes |
|---|---|---|---|
x | int | required | 方块 X 坐标 |
y | int | required | 方块 Y 坐标 |
z | int | required | 方块 Z 坐标 |
material | string | required | Bukkit Material 名称(例如 "stone"、"ice") |
ticks | int | 0 | 方块恢复前的时长(tick)。0 表示永久。 |
require_air | boolean | false | 若为 true,只有目标为空气时才放置方块 |
若方块被成功放置返回 true,若材质无效或未满足空气要求返回 false。
world:drop_item(x, y, z, material, [amount])
在给定位置以自然散落的方式掉落一个物品实体。
| Parameter | Type | Default | Notes |
|---|---|---|---|
x | number | required | X 坐标 |
y | number | required | Y 坐标 |
z | number | required | Z 坐标 |
material | string | required | Bukkit Material 名称 |
amount | int | 1 | 堆叠数量 |
返回所掉落物品的实体表,若材质无效则返回 nil。
context.zones
zones 表让脚本可以创建空间区域,并监视其中的玩家进入/离开事件。FMM 道具、FMM 物品和 EliteMobs NPC 脚本使用此处记录的通用 create_sphere(...) / create_cylinder(...) / create_cuboid(...) 以及 watch(...) / unwatch(...) 方法,并触发 on_zone_enter / on_zone_leave 钩子。区域绑定在脚本实例上,并在脚本实例被销毁时自动清理。EliteMobs Boss 能力使用一张独立的、Boss 专属的区域表,其方法包括 get_entities_in_zone(...)、get_locations_in_zone(...) 和 watch_zone(...);参见 EliteMobs Zones & Targeting。
zones:create_sphere(x, y, z, radius)
在给定坐标处创建以此为中心的球形区域。返回数字区域句柄。
| Parameter | Type | Notes |
|---|---|---|
x | number | 中心 X |
y | number | 中心 Y |
z | number | 中心 Z |
radius | number | 球体半径 |
zones:create_cylinder(x, y, z, radius, height)
创建圆柱形区域。返回数字区域句柄。
| Parameter | Type | Notes |
|---|---|---|
x | number | 中心 X |
y | number | 中心 Y(底部) |
z | number | 中心 Z |
radius | number | 圆柱半径 |
height | number | 圆柱高度 |
zones:create_cuboid(x, y, z, xSize, ySize, zSize)
创建长方体区域。返回数字区域句柄。
| Parameter | Type | Notes |
|---|---|---|
x | number | 中心 X |
y | number | 中心 Y |
z | number | 中心 Z |
xSize | number | X 方向上的半边长 |
ySize | number | Y 方向上的半边长 |
zSize | number | Z 方向上的半边长 |
zones:watch(handle, onEnterCallback, onLeaveCallback)
开始监视区域的玩家进入/离开事件。回调参数充当布尔信号 —— 传入非 nil 值(例如一个函数或 true)会启用脚本上对应的钩子。回调本身不会被直接调用。相反,进入/离开逻辑会通过脚本的 on_zone_enter / on_zone_leave 钩子触发。
| Parameter | Type | Notes |
|---|---|---|
handle | int | 来自 create_* 调用的区域句柄 |
onEnterCallback | any non-nil or nil | 非 nil 会为此区域启用 on_zone_enter 钩子 |
onLeaveCallback | any non-nil or nil | 非 nil 会为此区域启用 on_zone_leave 钩子 |
若监视设置成功返回 true,若区域句柄无效则返回 nil。
区域监视会在每个服务器 tick 针对同一世界中的所有玩家进行检查。请保持区域数量合理,以避免性能开销。
zones:unwatch(handle)
停止监视某区域并清理其资源。
| Parameter | Type | Notes |
|---|---|---|
handle | int | 要停止监视的区域句柄 |
示例:邻近触发区域
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
当当前钩子由游戏事件触发时(例如 on_right_click、on_zone_enter),event 表会存在。在 on_game_tick 或调度回调中不会存在。
| Field / Method | Type | Notes |
|---|---|---|
is_cancelled | boolean | 事件是否已被取消 |
cancel() | method | 取消事件(阻止默认行为) |
uncancel() | method | 取消先前的“取消”操作(恢复事件) |
player | entity table | 参与该事件的玩家(如有) |
on_right_click = function(context)
if context.event then
context.event:cancel() -- prevent the default right-click interaction
end
end
EliteMobs 能力脚本提供了更详细的事件表,包含伤害值、伤害原因、造伤者引用以及伤害修改方法。完整的 EliteMobs 事件字段请参阅 Lua API 参考。
em 辅助命名空间
em 表在文件加载时即可用(早于任何钩子运行)。它提供用于构建 API 中广泛使用的位置表、向量表和区域定义的辅助构造器。
| Function | Purpose |
|---|---|
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 适配器。消费插件会在启用期间初始化它们,而首次保护查询也会作为兜底执行惰性初始化 —— 无需任何配置,也没有硬依赖。如果两个插件都未安装、且没有任何内容插件注册归属关系,em.location.is_protected(loc) 就直接返回 false;启动/查询警告会指出没有可用的提供方。传入 nil 或无效位置时,各谓词返回 false,owners / kinds_at 返回空表;这些辅助方法从不抛出异常。
区域构造器返回可链式调用的表,提供 :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(...) 方法来创建基于监视的简单区域。
实体表
实体表由世界查询、事件数据和区域回调返回。它们根据实体类型提供分层的字段和方法集合。
实体基础字段
| Field | Type | Notes |
|---|---|---|
uuid | string | 实体的 UUID |
entity_type | string | 实体类型(例如 "player"、"zombie"、"skeleton"、"villager") |
is_valid | boolean | 实体引用是否仍然有效 |
is_dead | boolean | 实体是否已死亡 |
is_player | boolean | 实体是否为玩家 |
is_hostile | boolean | 实体是否为敌对生物(僵尸、骷髅等) |
is_passive | boolean | 实体是否为被动生物(牛、猪、鸡等) |
current_location | location table | 实体当前位置(x、y、z、world、yaw、pitch) |
world | string | 实体所在的世界名称 |
实体基础方法
| Method | Returns | Notes |
|---|---|---|
teleport(location_table) | void | 传送实体。位置表必须含有 x、y、z、world 字段;yaw 和 pitch 可选。 |
remove() | void | 将实体从世界中移除。仅在实体仍然有效时生效。 |
set_silent(flag) | void | 抑制或重新启用实体的声音。 |
set_invulnerable(flag) | void | 使实体免疫或恢复受到伤害。 |
set_gravity(flag) | void | 启用或禁用实体的重力。 |
set_glowing(flag) | void | 切换实体的发光描边效果。 |
生物实体字段
生物实体(玩家、生物等)拥有所有实体基础字段,并额外包含:
| Field | Type | Notes |
|---|---|---|
health | number | 当前生命值 |
maximum_health | number | 最大生命值 |
name | string | 显示名称 |
is_alive | boolean | 实体是否存活 |
生物实体方法
| Method | Returns | Notes |
|---|---|---|
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 | 返回当前实体缩放比例(在不支持 generic.scale 属性的服务器上默认为 1.0) |
set_scale(value) | void | 通过 generic.scale 属性设置实体缩放比例。在 1.20.5 之前的服务器上为空操作。 |
并非所有由 get_nearby_entities() 返回的实体都是生物实体。你可以使用 entity.is_player、entity.is_hostile 或 entity.is_passive 按类别过滤,或者在调用 damage()、push()、add_potion_effect() 等生物实体方法前先用 if entity.damage then 进行检查。
插件集成字段
实体表会自动包含用于检测和与其他 Nightbreak 插件管理的实体交互的字段。这些字段仅在相关插件已安装时才会填充 —— 否则默认为 false / nil,零开销。
EliteMobs 字段
在已安装 EliteMobs 时可用。
| Field | Type | Notes |
|---|---|---|
is_elite | boolean | 实体是否为 EliteMobs 精英 |
is_custom_boss | boolean | 实体是否为 EliteMobs 自定义 Boss(在 elite 子表中也可用) |
is_significant_boss | boolean | 实体是否为生命值倍率大于 1 的自定义 Boss(即设计过的遭遇战,而非填充用的精英) |
elite | table or nil | 精英信息子表(见下文)。若实体不是精英则为 nil。 |
elite 子表包含:
| Field | Type | Notes |
|---|---|---|
elite.level | int | 精英的等级 |
elite.name | string | 精英的显示名称 |
elite.health | number | 当前生命值 |
elite.max_health | number | 最大生命值 |
elite.is_custom_boss | boolean | 是否为自定义 Boss(而非自然出现的精英) |
elite.health_multiplier | number | 配置文件定义的生命值倍率 |
elite.damage_multiplier | number | 配置文件定义的伤害倍率 |
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 时可用。
| Field | Type | Notes |
|---|---|---|
is_modeled | boolean | 实体是否附加了 FMM 模型(DynamicEntity 或 PropEntity) |
is_prop | boolean | 实体是否为 FMM 道具(静态装饰实体) |
model | table or nil | 模型信息子表(见下文)。若实体未模型化则为 nil。 |
model 子表包含:
| Field | Type | Notes |
|---|---|---|
model.model_id | string | 模型蓝图 ID(例如 "torch_01") |
model.is_dynamic | boolean | 模型化实体是否为 DynamicEntity(生物/Boss 模型),否则为静态道具 |
model:play_animation(name, [blend], [loop]) | boolean | 播放指定动画。blend 默认 false,loop 默认 false。若找到该动画则返回 true。 |
model:stop_animations() | void | 停止模型上当前播放的所有动画。 |
model:remove() | void | 通过 FMM 的正确清理流程移除模型化实体。 |
在玩家实体表上,FreeMinecraftModels 增强器还会添加:
| Field / Method | Type | Notes |
|---|---|---|
is_op | boolean | 该玩家是否为服务器 OP。 |
has_permission(node) | boolean | 该玩家是否拥有该权限节点。 |
模型桥接(任何实体上都可用)的 blend 和 loop 默认为 false。道具表 的 play_animation 两者均默认为 true,因为道具通常希望使用混合的循环动画。
示例:按类型过滤实体
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
玩家专属字段
玩家实体拥有所有实体和生物实体字段,并额外包含:
| Field | Type | Notes |
|---|---|---|
game_mode | string | 玩家的游戏模式("creative"、"survival"、"adventure"、"spectator") |
玩家专属方法
只要 API 中写着「支持颜色代码」,Magmacore 就会把该字符串交给内置的 MiniMessage 解析器处理。传统的 & 代码、&#RRGGBB 十六进制、<#RRGGBB>、<gradient>、<rainbow> 以及 MiniMessage 其余的标签集都可用,并且可以自由混用。Kyori Adventure 不是依赖项,脚本也无法使用任何 Adventure API —— 你并不需要它。
Lua 文本方法会向下转换为传统的带颜色代码字符串,因此颜色、渐变和装饰都能正确渲染,而交互类标签(<hover>、<click>、<insert>、<font>、<key>、<lang>)虽会被解析但不产生任何效果。常见格式请参阅共享的 Color Codes 指南。
| Method | Returns | Notes |
|---|---|---|
send_message(msg) | void | 向玩家发送聊天消息。支持颜色代码。 |
get_held_item() | table or nil | 返回玩家主手物品的 {type, amount, display_name},若手为空则为 nil。type 是大写的 Bukkit Material 名称,例如 "GOLD_INGOT"。 |
consume_held_item(amount?) | void | 消耗玩家主手中的物品。amount 默认为 1 |
has_item(material, amount?) | boolean | 若玩家在背包中拥有至少 amount(默认 1)数量的指定材质,返回 true |
get_target_entity([range]) | entity table or nil | 通过射线检测返回玩家正在注视的实体,若无则为 nil。默认范围为 50。 |
get_eye_location() | location table | 返回玩家眼睛高度处的位置表 |
get_look_direction() | table | 返回玩家注视方向的 {x, y, z} 方向向量 |
send_block_change(x, y, z, material, [ticks]) | boolean | 向该玩家发送一个仅此玩家可见的假方块。若指定 ticks,则假方块在该时长后自动还原。成功返回 true,材质无效返回 false。 |
reset_block(x, y, z) | boolean | 对该玩家将假方块还原为真实方块。始终返回 true。 |
sleep(x, y, z) | void | 使玩家在指定坐标处进入床上睡眠动画。玩家停止睡眠时方块会自动恢复。 |
wake_up() | void | 唤醒正在睡眠的玩家。 |
玩家 UI 方法
这些方法适用于任何玩家实体表,并通过 Minecraft 内置的 UI 元素提供向玩家展示信息的方式。
player:show_boss_bar(text, [color], progress, [ticks])
向玩家显示一条 Boss 血条。
| Parameter | Type | Default | Notes |
|---|---|---|---|
text | string | required | 要显示的文字。支持 & 颜色代码。 |
color | string | "WHITE" | 血条颜色。可选:"RED"、"BLUE"、"GREEN"、"YELLOW"、"PURPLE"、"PINK"、"WHITE" |
progress | number | required | 填充比例,0.0(空)到 1.0(满) |
ticks | int | nil | 可选的自动消失延迟(tick)。若省略,血条会一直显示直到手动隐藏。 |
player:hide_boss_bar()
从玩家屏幕上移除 Boss 血条。无参数。
player:show_action_bar(text, [ticks])
在动作栏(快捷栏上方)显示文字。
| Parameter | Type | Default | Notes |
|---|---|---|---|
text | string | required | 要显示的文字。支持 & 颜色代码。 |
ticks | int | nil | 可选的持续时长(tick)。若提供,会每 40 tick 重新发送一次以保持显示完整时长。 |
player:show_title(title, [subtitle], fade_in, stay, fade_out)
向玩家显示标题界面。
| Parameter | Type | Default | Notes |
|---|---|---|---|
title | string | required | 主标题文字。支持 & 颜色代码。 |
subtitle | string | "" | 主标题下方的副标题文字。可选。 |
fade_in | int | required | 淡入时长(tick) |
stay | int | required | 标题在屏幕上停留的时长(tick) |
fade_out | int | required | 淡出时长(tick) |
下一步
获取插件专属的钩子、API 和工作流:
当前构造器公开 em.create_location(...)、em.zone.create_sphere_zone(...) 以及 em.location.is_protected(...) 等查询;玩家表还提供 has_permission(node)。准确上下文取决于钩子,请勿在计划回调中假定存在 context.event.player。