Lua 脚本:入门指南
本页带您从空文件开始,编写一个可运行的 FreeMinecraftModels 交互道具脚本,并学习钩子、上下文、道具 API 和脚本文件的基本结构。
熟悉基础后,请继续阅读:
- 道具 API —
context.prop、context.event、context.world和其他上下文 API - 示例与模式:可供学习和改编的道具脚本,附带设置说明
- 故障排除 — 常见错误、调试技巧和质量检查清单
Lua 道具脚本目前属于实验性功能。随着 FreeMinecraftModels 开发推进,钩子名称、辅助方法和行为可能变化,请在用于生产服务器前仔细测试。
FreeMinecraftModels 使用 MagmaCore Lua 运行时。如果您写过 EliteMobs Lua 技能,就会熟悉返回表的脚本文件、api_version、钩子、context、状态、冷却、调度和沙盒。具体钩子及上下文方法名仍取决于插件:
- EliteMobs 脚本运行在 Boss 上,使用
on_boss_damaged_by_player、on_enter_combat等钩子。 - FMM 道具脚本运行在道具上,使用
on_right_click、on_left_click、on_zone_enter等钩子。 - 物品附魔使用独立的自定义附魔上下文。
这里的 context.world、context.zones、context.scheduler、context.state 和 context.log 是 FreeMinecraftModels/MagmaCore 版本。EliteMobs NPC 脚本使用相同的通用 MagmaCore 表,另加 context.npc;Boss 技能的部分表使用 Boss 专用实现。本页介绍 FMM 道具的特有内容。
什么是道具脚本
道具脚本是放在 plugins/FreeMinecraftModels/scripts/ 的独立 .lua 文件,由模型旁的 YAML 配置引用,在道具生成到世界时运行。
道具脚本的用途
它们适合实现:
- 响应点击的门、拉杆、按钮等交互道具
- 忽略默认左键伤害的装饰道具
- 检测玩家进入或离开范围的接近触发器
- 通过交互或定时器播放动画的道具
- 被点击或靠近时发声的道具
- 超出静态装饰、需要额外逻辑的行为
纯装饰且无需交互的道具不需要脚本。
自定义物品与附魔
自定义物品仍在模型旁的 YAML 中指定 material,但效果现已改用带命名空间的附魔。旧的装备物品脚本运行时已经停用。带非空 scripts: 列表的自定义物品会被跳过,并在控制台输出提示信息;其定义保持不变。
scripts/ 中被识别为已停用装备物品脚本的 Lua 文件,会在发现阶段之前和导入之后保存在 plugins/MagmaCore/outdated files/FreeMinecraftModels/<archive batch>/scripts/。归档规则要求有效的旧脚本定义包含已停用的物品钩子,例如 on_equip 或 on_attack_entity。道具脚本、仅使用共享钩子的文件、格式错误的脚本以及 enchantments/ 中的当前效果不会被归入此类。恢复方法见已停用的配置文件。
当前物品定义见自定义物品与魔法武器,效果见自定义附魔。道具的 scripts: 列表仍然有效。
本页的目标读者
本页面向三类读者:
- 熟悉 EliteMobs Lua,想学习 FMM 特有钩子和 API 的人
- Lua 新手,需要名称准确且完整的道具参考的人
- 使用 AI 起草脚本,需要辨别虚构 API 的人
编写实用脚本不需要先掌握整门 Lua 语言。多数情况下,只需知道:
- 如何在返回的表中定义有效钩子
- 如何读取
context的值 - 如何用
if ... then return end提前退出 - 如何准确调用所需的辅助方法
Lua 快速入门
多数脚本只用到变量(local x = 5)、函数(function foo() end)、条件(if x then ... end)、表({key = value})和 nil(表示没有值)。语法不要求分号,代码块用 end 结束,而非花括号。
完整讲解和示例见 MagmaCore Lua 脚本引擎 — Lua 快速入门。这是所有 Nightbreak 插件共享的基础。
文件存放位置
脚本文件
将 .lua 文件放在中央脚本文件夹中:
plugins/
FreeMinecraftModels/
scripts/
invulnerable.lua
interactive_door.lua
proximity_sound.lua
FMM 初始化脚本提供器时会递归查找 plugins/FreeMinecraftModels/scripts/ 中的 .lua 文件,并缓存源码。绑定使用文件名而非相对路径,因此子文件夹之间也要保持名称唯一。添加或编辑 Lua 后,先成功完成 /fmm reload,再建立新绑定。仅重新生成道具不会重读 Lua 源码。
为清楚起见,请在模型的 scripts: 列表中包含 .lua 扩展名。FMM 也接受省略扩展名的条目,并在内部补上它。磁盘上的文件必须以 .lua 结尾,名称区分大小写。
模型文件和配置文件
每个模型可以在同一目录中拥有对应的 .yml 配置:
plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- script config for torch_01
scripts/
invulnerable.lua <-- referenced by torch_01.yml
该 .yml 将模型与脚本关联。
配置文件格式
模型旁的 YAML 有以下字段:
isEnabled: true
voxelize: false
solidify: false
scripts:
- invulnerable.lua
道具字段如下:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isEnabled | 布尔值 | true | 是否启用定义 |
scripts | 字符串列表 | [] | scripts/ 中的道具 Lua 文件名 |
voxelize | 布尔值 | false | 将放置位置对齐到方块网格,旋转对齐到 90 度 |
solidify | 布尔值 | false | 为体素化道具添加通过数据包呈现的屏障方块 |
列表中的每个道具脚本都有独立实例。手持物品则应定义 material、name、lore 和带命名空间的 enchantments,详见自定义物品。
配置延迟生成
道具生成时若没有对应 .yml,FMM 会异步创建包含 isEnabled: true 和空 scripts: 列表的默认配置。因此首次生成时没有脚本,需要等待配置创建,再添加脚本文件名。
- 将模型放入
models/。 - 生成道具一次,让 FMM 创建
.yml。 - 编辑
.yml,添加脚本文件名。 - 编辑 YAML 或 Lua 后,都应成功完成
/fmm reload,再放置新道具并检查其日志或交互。道具设置、放置检查和菜单脚本列表共用已准备好的目录;仅重新生成道具不会重读 YAML。
钩子参考
每个 Lua 道具脚本返回一个表。除了 api_version 和 priority,所有键都必须是下列钩子之一。对应游戏事件发生时,运行时调用相应函数。
| 钩子 | 触发时机 | 说明 |
|---|---|---|
on_spawn | 道具生成到世界,或随其区块重新加载 | 每次绑定脚本时运行一次 |
on_game_tick | 每个服务器刻,20 TPS 时为 50 ms | 仅在定义此钩子时启用 |
on_destroy | 道具被永久移除 | 清理钩子。道具所在区块卸载或 FMM 重载时不会调用 |
on_left_click | 玩家左键点击或攻击道具 | context.event 包含 FMM 默认伤害取消标记 |
on_right_click | 玩家右键点击道具 | context.event 包含 FMM 默认交互取消标记 |
on_zone_enter | 玩家进入监视区域 | 需要先设置区域监视 |
on_zone_leave | 玩家离开监视区域 | 需要先设置区域监视 |
on_projectile_hit | 投射物击中道具 | context.projectile 为该投射物;context.event.player 为射击者。参见道具 API |
物品效果
旧装备物品钩子已不再分发。效果应写在自定义附魔中,使用其专用钩子和上下文。不要将附魔 Lua 放进道具的 scripts: 列表,也不要原样复制旧物品脚本。
最小文件要求
每个 Lua 道具脚本必须 return 一个表。
必填和可选的顶层字段
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
api_version | 是 | 数字 | 当前必须为 1 |
priority | 否 | 数字 | 存在时会校验,但 FMM 当前不按它排序,而是按 scripts: 列表顺序运行 |
| 支持的钩子键 | 否 | 函数 | 必须使用钩子参考中的准确名称 |
验证规则
- 文件必须返回表。
api_version必填,当前必须为1。priority如果存在,必须是数字。- 其他顶层键必须是受支持的钩子名称。
- 每个钩子的值必须是函数。
- 未知顶层键会被拒绝。
priority 有助于脚本在各个基于 MagmaCore 的运行时之间移植,但 FMM 的执行顺序由配置决定。请按所需顺序排列模型的 scripts: 列表。
辅助函数和局部常量应放在最后的 return 上方,而非返回的表内。
一步步编写第一个道具脚本
步骤1之前:准备配置
- 将
my_prop.fmmodel等模型放在plugins/FreeMinecraftModels/models/。 - 生成道具一次,以创建
.yml。 - 创建
plugins/FreeMinecraftModels/scripts/first_test.lua。 - 编辑
plugins/FreeMinecraftModels/models/my_prop.yml:
isEnabled: true
scripts:
- first_test.lua
- 保存下方步骤 1 的 Lua 内容,运行
/fmm reload并确认成功。必要时重新生成道具。后续每次编辑 Lua 后也要成功重载。
步骤1:加载文件
return {
api_version = 1,
on_spawn = function(context)
end
}
控制台没有错误是有用的初步检查。请用步骤2的明确日志确认:
- 文件是有效的 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 使用的模式。它取消这次点击造成的 FMM 默认道具伤害,不会让底层实体免疫所有伤害来源。
步骤5:点击时播放动画
return {
api_version = 1,
on_right_click = function(context)
context.prop:play_animation("open", true, false)
end
}
这会请求在当前状态刻之后播放不循环的“open”动画,模型必须包含它。blend 不进行渐变;队列只有一个位置,后续请求可能替换先前请求。终止死亡状态可能拒绝请求。请在游戏中检查显示结果,接受请求本身不证明播放。
什么是 context?
每个钩子函数接收一个名为 context 的参数。它包含与道具、世界、区域等交互所需的操作和数据。
FMM 会创建并传入它,您无需自行创建。共享 API context.state、context.log、context.cooldowns、context.scheduler、context.world 和 context.zones 的详情见 MagmaCore Lua 脚本引擎。
主要 context API
以下是概要,完整参考见道具 API。
-
context.prop— 道具实体,仅用于道具脚本。提供model_id、current_location、play_animation()和stop_animation()。 -
context.player— 道具点击和通用区域钩子的触发玩家;在on_projectile_hit中,如果投射物由玩家射出,则为该射击者。在道具生命周期钩子、计划回调和不涉及玩家的钩子中为nil。 -
context.event— 点击钩子中的 FMM 取消标记和玩家,on_projectile_hit中的投射物命中事件及其射击者,或通用区域钩子中仅包含行为者的表。提供event.player、is_cancelled,点击和投射物命中时还提供cancel()/uncancel()。不暴露 Bukkit 特有的target、block、projectile或item。没有事件或玩家的钩子,如on_spawn、on_game_tick,此值为nil。 -
context.projectile—on_projectile_hit中的投射物实体,在其他所有钩子中为nil。参见on_projectile_hit。 -
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。
方法语法:: 与 .
区别见 MagmaCore Lua 脚本引擎。FMM API 接受两种形式。
可复制的起步模板
最小有效道具脚本
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
}
较大文件的结构
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
}
第一个实际工作流程
从头编写道具脚本时,按以下顺序进行:
- 创建
.lua并让on_spawn工作。 - 将脚本文件名加入道具的
.yml。 - 改用实际需要的钩子,如
on_right_click。 - 先添加日志,再添加动画或效果。
- 添加一个实际效果,如动画、声音或粒子。
- 最后再添加辅助函数、状态、调度逻辑或区域。
每次编辑 Lua 后保存文件并成功完成 /fmm reload,再检查新行为。每个步骤只改一件事,让日志或可见效果说明哪些部分正常运行。
附带脚本
FMM 会写入缺失的四个预制 Lua 脚本,也会替换识别出的旧默认源码,但保留其他现有编辑。若行为与当前默认值不同,请检查安装的文件:
invulnerable.lua— 取消 FMM 默认左键伤害,不阻止所有可能的伤害来源。pickupable.lua:取消默认左键伤害,累计三次允许的敲击后请求拾取。计数器属于绑定,玩家之间共享;较早一次敲击后的 100 服务器刻任务会重置它。允许的敲击请求受伤闪烁。脚本阻止受保护或地牢位置的操作,但具有 FMM 保护绕过权限或管理员状态的玩家例外。storage_double.lua:请求 54 格物品栏、开关动画与声音。采用相同的保护检查和管理员例外,每 5 服务器刻查询查看者追踪。道具的物品栏由所有查看者共享,并在最后一位查看者关闭它或区块卸载时保存。销毁时请求drop_inventory(),它会先保存打开中的物品栏,因此放入仍打开的物品栏中的物品也会一并掉落。详情参见prop:open_inventory。storage_single.lua— 与上者相同,但为 3 行、27 格,而非 6 行。
更多示例见示例与模式。
Lua 沙盒
道具脚本运行在与 EliteMobs 相同的 LuaJ 沙盒中,限制相同。已移除的全局变量和可用标准库函数列表见 MagmaCore Lua 脚本引擎。
下一步
- 道具 API —
context.prop、context.event、context.world、context.zones和context.scheduler的完整参考 - 示例与模式:附带讲解和设置说明的道具脚本
- 故障排除 — 常见问题、调试和质量检查
如果您也编写 EliteMobs Boss Lua 技能,沙盒、api_version、状态表、冷却和钩子结构会很熟悉,但 Boss 上下文使用 EliteMobs 专用方法名。准确的 Boss API 见 EliteMobs Lua 文档。