Lua Scripting:入门指南
本页将教你编写第一个 FreeMinecraftModels prop 的 Lua script,从空文件一直到一个可工作的交互式 prop。读完之后,你将理解 hook、context、Prop API 以及每个 prop script 文件的通用结构。
熟悉基础知识后,请继续阅读配套页面:
- Prop API --
context.prop、context.event、context.world和其他 context API - 示例与模式 -- 可供学习和改编的完整可运行 script
- 故障排除 -- 常见错误、调试技巧和 QC 检查清单
Lua prop script 目前为实验性功能。随着 FreeMinecraftModels 的演进,hook 名称、辅助方法和行为仍可能发生变化,因此在生产服务器上使用前请仔细测试。
FreeMinecraftModels 与 EliteMobs 共享同一个 Lua 引擎(Magmacore)。如果你已经在为 EliteMobs 编写 Lua 能力,核心概念 -- hook、context、api_version、scheduler、区域和 sandbox -- 是相同的。区别在于:
- EliteMobs 的 script 运行在 Boss 上,拥有
on_boss_damaged_by_player、on_enter_combat等 hook。 - FMM 的 script 运行在 prop 上,拥有
on_right_click、on_left_click、on_projectile_hit等 hook。
context.world、context.zones、context.scheduler、context.state 和 context.log API 在两个 plugin 中完全相同。本页仅涵盖 FMM prop 特有的内容。
什么是 Prop Script
Prop script 是位于 plugins/FreeMinecraftModels/scripts/ 文件夹中的独立 .lua 文件。它们通过模型文件旁边的 YAML config 文件引用,并在 prop 被生成到世界中时运行。
Prop Script 擅长什么
Prop script 在以下场景中表现出色:
- 响应玩家点击的交互式 prop(门、拉杆、按钮)
- 玩家无法破坏的无敌装饰 prop
- 检测玩家进入或离开区域的接近触发器
- 在交互或定时器触发时播放动画的 prop
- 被点击或靠近时播放声音的 prop
- 任何需要超越静态装饰的逻辑的 prop 行为
如果你的 prop 纯粹是装饰性的且不需要交互,则不需要 script。
本页的目标读者
本页面面向三类读者:
- 已经了解 EliteMobs Lua scripting,想学习 FMM 特有 hook 和 API 的人
- Lua scripting 新手,需要完整且名称精确的 prop 参考的人
- 使用 AI 起草 prop script,需要足够细节来辨别 AI 是否编造了虚假内容的人
你不需要在编写有用的 prop script 之前成为一名完整的 Lua 开发者。对于大多数实用的 prop script,你真正需要的只是:
- 如何在返回的表中放置有效的 hook
- 如何从
context读取值 - 如何用
if ... then return end提前退出 - 如何精确调用几个辅助方法
Lua 快速入门
如果你完全没接触过 Lua,以下是你需要的最少语法。
变量
使用 local 存储一个值:
local animation_name = "open"
local sound_volume = 1.0
函数
函数是可复用的逻辑块:
local function log_click(context)
context.log:info("Prop was clicked!")
end
if 检查
当某件事只应在某些情况下发生时使用 if:
if context.event == nil then
return
end
nil
nil 表示"无值"。你会经常检查它:
if context.event ~= nil then
context.event.cancel()
end
表
Lua 使用表来表示具有命名键的对象以及返回的 script 定义:
return {
api_version = 1,
on_spawn = function(context)
end
}
注释
使用 -- 添加注释:
-- Cancel the damage event so the prop cannot be broken
context.event.cancel()
如需更详细的 Lua 入门,请参阅 EliteMobs 入门指南 页面。语法基础完全相同。
文件存放位置
Script 文件
将 .lua 文件放在中央 scripts 文件夹中:
plugins/
FreeMinecraftModels/
scripts/
invulnerable.lua
interactive_door.lua
proximity_sound.lua
FMM 在启动时会发现 plugins/FreeMinecraftModels/scripts/ 中的所有 .lua 文件。
模型文件和 Config 文件
每个模型文件可以在同一目录中有一个对应的 .yml config 文件:
plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- torch_01 的 script config
scripts/
invulnerable.lua <-- 被 torch_01.yml 引用
.yml config 是连接模型与其 script 的桥梁。
Config 文件格式
模型文件旁边的 YAML config 文件有两个字段:
isEnabled: true
scripts:
- invulnerable.lua
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
isEnabled | boolean | true | 此 prop 的 script 是否激活 |
scripts | 字符串列表 | [] | scripts/ 文件夹中 .lua 文件的文件名 |
你可以将多个 script 附加到同一个 prop。每个 script 都是独立的实例。
Config 延迟生成
当 prop 生成且没有对应的 .yml 文件时,FMM 会自动创建一个默认 config 文件,包含 isEnabled: true 和空的 scripts: 列表。这是异步进行的,因此 prop 在首次生成时不会有 script -- 只有在 config 创建后并编辑添加 script 文件名之后才会生效。
这意味着:
- 将模型文件放入
models/ - 生成 prop 一次(FMM 自动创建
.yml) - 编辑生成的
.yml添加你的 script 文件名 - 重新生成 prop 或重载服务器(script 现在已激活)
Hook 参考
每个 Lua prop script 文件返回一个表。该表中的每个键(除了 api_version 和 priority)必须是下面列出的 hook 之一。运行时在对应的游戏事件触发时调用匹配的函数。
| Hook | 触发时机 | 说明 |
|---|---|---|
on_spawn | Prop 被生成到世界中 | Script 绑定时运行一次 |
on_game_tick | 每个服务器 tick 一次(50 毫秒) | 仅在 script 定义了此 hook 时激活 |
on_destroy | Prop 从世界中移除 | 清理 hook |
on_left_click | 玩家左键点击(攻击)prop | context.event 是伤害事件 |
on_right_click | 玩家右键点击 prop | context.event 是交互事件 |
on_projectile_hit | 抛射物击中 prop | context.event 是抛射物命中事件 |
on_zone_enter | 玩家进入监视区域 | 需要先设置区域监视 |
on_zone_leave | 玩家离开监视区域 | 需要先设置区域监视 |
最小文件契约
每个 Lua prop script 必须 return 一个表。
必填和可选的顶层字段
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
api_version | 是 | Number | 目前必须为 1 |
priority | 否 | Number | 执行优先级。较小的值先执行。默认为 0 |
| 支持的 hook 键 | 否 | Function | 必须使用 Hook 参考中列出的精确 hook 名称之一 |
验证规则
- 文件必须返回一个表。
api_version为必填,目前必须为1。priority如果存在必须为数字。- 每个额外的顶层键必须是受支持的 hook 名称。
- 每个 hook 键必须指向一个函数。
- 未知的顶层键会被拒绝。
辅助函数和局部常量应放在最后的 return 上方,而不是在返回的表内部。
一步步构建你的第一个可工作的 Prop Script
步骤 1 之前:设置 Config
- 将模型文件(例如
my_prop.fmmodel)放入plugins/FreeMinecraftModels/models/ - 生成 prop 一次以生成
.ymlconfig - 在
plugins/FreeMinecraftModels/scripts/first_test.lua创建你的 script 文件 - 编辑
plugins/FreeMinecraftModels/models/my_prop.yml:
isEnabled: true
scripts:
- first_test.lua
- 重新生成 prop 或重载服务器
步骤 1:让文件加载
return {
api_version = 1,
on_spawn = function(context)
end
}
如果这在控制台中无错误地加载,你证明了:
- 文件是有效的 Lua
- FMM 在
scripts/文件夹中找到了它 - Config 正确引用了它
- 返回表的结构正确
步骤 2:让 Prop 做一件可见的事
return {
api_version = 1,
on_spawn = function(context)
context.log:info("Prop script loaded for: " .. (context.prop.model_id or "unknown"))
end
}
检查服务器控制台。如果你看到日志消息,说明你的 hook 正在触发。
步骤 3:响应玩家点击
return {
api_version = 1,
on_right_click = function(context)
context.log:info("Prop was right-clicked!")
end
}
在游戏中右键点击 prop。如果控制台显示消息,说明点击 hook 正在工作。
步骤 4:取消伤害使 Prop 无敌
return {
api_version = 1,
on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}
这是预制 invulnerable.lua script 使用的模式。它取消伤害事件,使 prop 底层的 armor stand 无法被摧毁。
步骤 5:点击时播放动画
return {
api_version = 1,
on_right_click = function(context)
context.prop:play_animation("open", true, false)
end
}
这会在 prop 模型上播放 "open" 动画,带有混合且不循环。
什么是 context?
每个 hook 函数接收一个名为 context 的参数。把它想象成一个工具箱,FMM 在每次发生事件时交给你 -- 它包含了你与 prop、世界、区域等交互所需的一切。
on_right_click = function(context)
-- context.prop = 被点击的 prop
-- context.event = 点击事件(可以取消)
-- context.world = 粒子、声音、方块查询工具
-- context.state = 你自己的持久存储
-- context.log = 控制台日志
-- context.scheduler = 延迟和重复任务
-- context.zones = 空间区域的创建和监视
end
你不需要自己创建 context -- FMM 会创建它并传递给你的 hook。
context 在每次 hook 调用时都会重新创建,除了 context.state,它在 prop 的整个生命周期中持续存在。这意味着你可以将数据存储在 context.state 中,稍后在不同的 hook 中读取。
关键 context API
以下是可用功能的摘要。完整详情请参见 Prop API。
-
context.prop-- Prop 实体。提供model_id、current_location、play_animation()和stop_animation()。 -
context.event-- 触发此 hook 的 Bukkit 事件。在on_left_click、on_right_click和on_projectile_hit中可用。提供cancel()、uncancel()和is_cancelled。在没有事件的 hook 中(如on_spawn和on_game_tick)为nil。 -
context.state-- 在 prop 生命周期内持续存在的普通 Lua 表。用于存储标志、切换状态、任务 ID 以及需要在 hook 之间记住的任何内容。 -
context.log-- 通过log:info(msg)、log:warn(msg)和log:error(msg)进行控制台日志记录。 -
context.scheduler-- 通过scheduler:run_later(ticks, callback)和scheduler:run_repeating(delay, interval, callback)实现延迟和重复任务。回调接收全新的 context。通过scheduler:cancel(taskId)取消任务。 -
context.world-- 世界交互:粒子、声音、方块查询、闪电、附近实体。完整方法列表请参见 Prop API。 -
context.zones-- 创建和监视空间区域(球体、圆柱体、长方体)。完整方法列表请参见 Prop API。
方法语法:: vs .
在 Lua 中,object:method(arg) 是 object.method(object, arg) 的简写。FMM API 接受两种形式:
context.log:info("hello")
context.log.info("hello") -- 相同效果
所有文档统一使用 :。
即拷即用的模板
最小的有效 Prop Script
return {
api_version = 1,
on_spawn = function(context)
end
}
无敌 Prop 模板
return {
api_version = 1,
on_left_click = function(context)
if context.event then
context.event.cancel()
end
end
}
交互式 Prop 模板
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
}
第一个实际工作流程
构建全新的 prop script 时,请按以下顺序进行:
- 创建
.lua文件并让on_spawn正常工作。 - 将 script 文件名添加到 prop 的
.ymlconfig 中。 - 切换到你实际需要的 hook(例如
on_right_click)。 - 先添加一条日志消息,然后再添加动画或特效。
- 添加一个真实的特效(动画、声音、粒子)。
- 只有在此之后,才添加辅助函数、state、scheduler 逻辑或区域。
这个顺序能极大简化调试,因为每次只改变一件事。
预制 Script
FMM 附带一个预制的 Lua script:
invulnerable.lua-- 取消左键点击伤害事件,使 prop 不可摧毁。这是最简单实用的 prop script。
更多示例请参见 示例与模式 页面。
Lua Sandbox
Prop script 在与 EliteMobs 相同的沙盒化 LuaJ 环境中运行。Sandbox 限制完全相同。已移除的全局变量和可用标准库函数的完整列表请参见 EliteMobs Hooks & Lifecycle 页面。
概要:
- 已移除:
debug、dofile、io、load、loadfile、luajava、module、os、package、require - 可用: 所有
math.*、string.*、table.*函数、pairs、ipairs、type、tostring、tonumber、pcall、print等
print 会输出到服务器控制台,但建议使用 context.log:info(msg) 进行输出。日志消息会以 script 文件名为前缀,便于追踪是哪个 script 产生的消息。
下一步
- Prop API --
context.prop、context.event、context.world、context.zones和context.scheduler的完整参考 - 示例与模式 -- 附带详解的完整可运行 script
- 故障排除 -- 常见问题、调试技巧和 QC 检查清单
如果你同时在编写 EliteMobs 的 Lua 能力,共享 API(context.world、context.zones、context.scheduler、context.state、context.log)的工作方式完全相同。Boss 特有的 API 请参见 EliteMobs Lua 文档。