跳到主要内容

Lua 脚本:入门指南

本页带您从空文件开始,编写一个可运行的 FreeMinecraftModels 交互道具脚本,并学习钩子、上下文、道具 API 和脚本文件的基本结构。

熟悉基础后,请继续阅读:

  • 道具 API — context.prop、context.event、context.world 和其他上下文 API
  • 示例与模式:可供学习和改编的道具脚本,附带设置说明
  • 故障排除 — 常见错误、调试技巧和质量检查清单
实验性功能

Lua 道具脚本目前属于实验性功能。随着 FreeMinecraftModels 开发推进,钩子名称、辅助方法和行为可能变化,请在用于生产服务器前仔细测试。

与 EliteMobs Lua 的关系

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: 列表的默认配置。因此首次生成时没有脚本,需要等待配置创建,再添加脚本文件名。

  1. 将模型放入 models/。
  2. 生成道具一次,让 FMM 创建 .yml。
  3. 编辑 .yml,添加脚本文件名。
  4. 编辑 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之前:准备配置​

  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 的 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
}

第一个实际工作流程​

从头编写道具脚本时,按以下顺序进行:

  1. 创建 .lua 并让 on_spawn 工作。
  2. 将脚本文件名加入道具的 .yml。
  3. 改用实际需要的钩子,如 on_right_click。
  4. 先添加日志,再添加动画或效果。
  5. 添加一个实际效果,如动画、声音或粒子。
  6. 最后再添加辅助函数、状态、调度逻辑或区域。

每次编辑 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 文档。