Lua 腳本:入門指南
本頁將教你為 FreeMinecraftModels 道具或自訂物品撰寫第一個 Lua 腳本,從一個空白檔案一路到一個可運作的互動式腳本。讀完之後,你將理解鉤子、context、道具與物品 API,以及每個腳本檔案的通用架構。
熟悉基礎知識後,請繼續閱讀配套頁面:
- 道具與物品 API --
context.prop、context.item、context.event、context.world和其他 context API - 範例與模式 -- 可供學習和改寫的完整可運行道具與物品腳本
- 疑難排解 -- 常見錯誤、除錯技巧和 QC 檢查清單
Lua 道具腳本與物品腳本目前為實驗性功能。隨著 FreeMinecraftModels 的演進,鉤子名稱、輔助方法和行為仍可能發生變化,因此在正式伺服器上使用前請仔細測試。
FreeMinecraftModels 使用 MagmaCore Lua 執行環境。如果你已經在為 EliteMobs 撰寫 Lua 能力,核心概念——回傳一個表的腳本檔案、api_version、鉤子、context、狀態、冷卻、排程和沙盒——都會很熟悉。確切的鉤子與 context 方法名稱仍取決於各個外掛:
- EliteMobs 的腳本運行在 Boss 上,擁有
on_boss_damaged_by_player、on_enter_combat等鉤子。 - FMM 道具腳本運行在道具上,擁有
on_right_click、on_left_click、on_zone_enter等鉤子。 - FMM 物品腳本運行在自訂物品上,擁有
on_equip、on_attack_entity、on_consume、on_game_tick等鉤子。
本頁記載的 context.world、context.zones、context.scheduler、context.state 和 context.log API 是 FreeMinecraftModels/MagmaCore 的版本。EliteMobs NPC 腳本使用相同的通用 MagmaCore 表加上 context.npc;EliteMobs Boss 能力則對其中數個表使用 Boss 專屬的變體。本頁涵蓋的是 FMM 道具與物品特有的內容。
什麼是道具腳本
道具腳本是位於 plugins/FreeMinecraftModels/scripts/ 資料夾中的獨立 .lua 檔案。它們透過模型檔案旁邊的 YAML 配置檔案引用,並在道具被生成到世界中時執行。
道具腳本擅長什麼
道具腳本在以下場景中表現出色:
- 回應玩家點擊的互動式道具(門、拉桿、按鈕)
- 玩家無法破壞的無敵裝飾道具
- 偵測玩家進入或離開區域的接近觸發器
- 在互動或計時器觸發時播放動畫的道具
- 被點擊或靠近時播放聲音的道具
- 任何需要超越靜態裝飾的邏輯的道具行為
如果你的道具純粹是裝飾性的且不需要互動,則不需要腳本。
什麼是物品腳本
物品腳本使用與道具腳本相同的 .lua 檔案格式和相同的 scripts/ 資料夾。差別在於它們附加在自訂物品上——也就是在 YML 設定檔中設定了 material: 欄位的模型。道具腳本在道具實體被生成到世界中時執行,而物品腳本則在玩家裝備該自訂物品(主手、副手或護甲欄位)時執行,並在物品被卸下時停止。
物品腳本如何運作
- 啟動: 當玩家裝備自訂 FMM 物品時會建立一個腳本實例。腳本是「每位玩家每種物品」的——每組(玩家、itemId)配對對應一個
ScriptInstance。 - 停止: 當物品被卸下(移出有效欄位、丟棄,或玩家離線)時,該腳本實例會被銷毀。
- 物品辨識: 自訂物品是透過
fmm_item_idPDC(PersistentDataContainer)鍵來辨識的,這與道具的model_id不同。若要取得正確標記的物品,請使用/fmm giveitem <id>或管理員選單。 - Context: 物品鉤子接收的
context包含context.player、context.item、context.world、context.state、context.scheduler、context.log,以及(在適用時)context.event。
物品腳本擅長什麼
物品腳本在以下場景中表現出色:
- 具有特殊能力的自訂武器(霜之劍、魔杖)
- 具有獨特右鍵或 shift-點擊動作的工具
- 帶有自訂效果的消耗品
- 穿戴時具有被動效果的護甲
- 追蹤使用次數或具有有限耐久度的物品
- 任何超越原版機制的手持物品行為
本頁的目標讀者
本頁面面向三類讀者:
- 已經了解 EliteMobs Lua 腳本,想學習 FMM 特有鉤子和 API 的人
- Lua 腳本新手,需要完整且名稱精確的道具參考的人
- 使用 AI 起草道具腳本,需要足夠細節來辨別 AI 是否編造了虛假內容的人
你不需要在撰寫有用的道具腳本之前成為一名完整的 Lua 開發者。對於大多數實用的道具腳本,你真正需要的只是:
- 如何在回傳的表中放置有效的鉤子
- 如何從
context讀取值 - 如何用
if ... then return end提前退出 - 如何精確呼叫幾個輔助方法
Lua 快速入門
你不需要成為 Lua 專家也能撰寫 FMM 腳本。大多數腳本只會用到少數幾個概念:變數(local x = 5)、函式(function foo() end)、if 檢查(if x then ... end)、表({key = value})和 nil(Lua 的「什麼都沒有」值)。語法很輕量——沒有分號、沒有大括號,只用 end 來結束區塊。
如需附有範例的完整逐步說明,請參閱 MagmaCore Lua 腳本引擎 — Lua 快速入門。該入門是所有 Nightbreak 外掛共用的,因此學一次就能處處適用。
檔案存放位置
腳本檔案
將 .lua 檔案放在中央 scripts 資料夾中:
plugins/
FreeMinecraftModels/
scripts/
invulnerable.lua
interactive_door.lua
proximity_sound.lua
FMM 在啟動時會發現 plugins/FreeMinecraftModels/scripts/ 中的所有 .lua 檔案。
在模型的 scripts: 清單中,為求清楚請寫上 .lua 副檔名。FMM 也接受不帶副檔名的項目,並在內部自動補上 .lua。但磁碟上的檔案仍必須以 .lua 結尾,且名稱仍區分大小寫。
模型檔案和配置檔案
每個模型檔案可以在同一目錄中有一個對應的 .yml 配置檔案:
plugins/
FreeMinecraftModels/
models/
torch_01.fmmodel
torch_01.yml <-- torch_01 的腳本配置
scripts/
invulnerable.lua <-- 被 torch_01.yml 引用
.yml 配置是連接模型與其腳本的橋樑。
配置檔案格式
模型檔案旁邊的 YAML 配置檔案有以下欄位:
isEnabled: true
voxelize: false
solidify: false
scripts:
- invulnerable.lua
對於自訂物品(玩家可以手持或裝備的模型),你還要設定 material 欄位,並可選擇性地設定 name、lore 和 enchantments:
isEnabled: true
material: DIAMOND_SWORD
name: "&bFrost Blade"
lore:
- "&7A sword forged in eternal ice"
- "&7Slows enemies on hit"
enchantments:
- "SHARPNESS,5"
- "UNBREAKING,3"
scripts:
- frost_sword.lua
| 欄位 | 類型 | 預設值 | 說明 |
|---|---|---|---|
isEnabled | boolean | true | 此道具/物品的腳本是否啟用 |
scripts | 字串列表 | [] | scripts/ 資料夾中 .lua 檔案的檔名 |
voxelize | boolean | false | 將放置對齊到 90 度旋轉與方塊格線 |
solidify | boolean | false | 在道具佔用範圍內放置只以封包送出的屏障方塊(需要 voxelize) |
material | string | "" | 有效的 Bukkit Material 名稱(例如 DIAMOND_SWORD)。設定此欄位會把該模型變成玩家可以手持或裝備的自訂物品,並啟用物品腳本系統 |
name | string | "" | 自訂物品的顯示名稱。支援 & 顏色代碼 |
lore | 字串列表 | [] | 顯示在物品提示中的說明行。支援 & 顏色代碼 |
enchantments | 字串列表 | [] | 套用到物品上的附魔。格式:"ENCHANTMENT_NAME,LEVEL"(例如 "SHARPNESS,5") |
你可以將多個腳本附加到同一個道具。每個腳本都是獨立的實例。
- 物品 ID 取自 YML 檔名去掉副檔名的部分。例如
frost_sword.yml產生的物品 ID 是frost_sword。這就是/fmm giveitem和fmm_item_idPDC 鍵所使用的 ID。 - 物品只會從
scripts:清單中繫結一個腳本。FMM 會依序檢查各項目,使用第一個能解析的腳本,然後忽略後續項目。與物品不同,道具會把每個能解析的清單項目都當成獨立實例執行。 - 如果你省略
.lua副檔名,系統會自動補上,因此在scripts:清單中frost_sword和frost_sword.lua是等價的。
配置延遲生成
當道具生成且沒有對應的 .yml 檔案時,FMM 會自動建立一個預設配置檔案,包含 isEnabled: true 和空的 scripts: 列表。這是非同步進行的,因此道具在首次生成時不會有腳本——只有在配置建立後並編輯新增腳本檔名之後才會生效。
這意味著:
- 將模型檔案放入
models/ - 生成道具一次(FMM 自動建立
.yml) - 編輯產生的
.yml新增你的腳本檔名 - 重新生成道具或重新載入(腳本現在已啟用)
鉤子參考
每個 Lua 道具腳本檔案回傳一個表。該表中的每個鍵(除了 api_version 和 priority)必須是下面列出的鉤子之一。運行時在對應的遊戲事件觸發時呼叫相符的函式。
| 鉤子 | 觸發時機 | 說明 |
|---|---|---|
on_spawn | 道具被生成到世界中 | 腳本繫結時執行一次 |
on_game_tick | 每個伺服器 tick 一次(50 毫秒) | 僅在腳本定義了此鉤子時啟用 |
on_destroy | 道具從世界中移除 | 清理鉤子 |
on_left_click | 玩家左鍵點擊(攻擊)道具 | context.event 是傷害事件 |
on_right_click | 玩家右鍵點擊道具 | context.event 是互動事件 |
on_zone_enter | 玩家進入監視區域 | 需要先設定區域監視 |
on_zone_leave | 玩家離開監視區域 | 需要先設定區域監視 |
目前的腳本驗證器會接受道具腳本中的 on_projectile_hit,但目前的執行環境還不會把拋射物命中派送給道具腳本。若要處理繫結在腳本化物品上的拋射物行為,請使用物品的 on_projectile_hit;若要在外掛端處理模型化實體的拋射物,請使用 Bukkit 的 ModeledEntityHitByProjectileEvent API。
物品鉤子參考
物品腳本就像道具腳本一樣回傳一個表,包含 api_version = 1 和鉤子函式。以下鉤子可用於物品腳本。所有物品鉤子都會接收帶有 context.player、context.item 以及(在適用情況下)context.event 的 context。
說明欄位列出底層的 Bukkit 事件族。Lua 包裝器不會暴露原始的 Bukkit 特定欄位,例如 target、block、projectile 或 item;當你需要額外的上下文時,請使用 context.player、context.event.player 以及實體/世界輔助查詢。
戰鬥鉤子
| 鉤子 | 觸發時機 | 說明 |
|---|---|---|
on_attack_entity | 玩家手持該物品攻擊實體 | context.event 是傷害事件 |
on_kill_entity | 玩家手持該物品殺死實體 | context.event 是死亡事件 |
on_take_damage | 玩家在裝備該物品時受到傷害 | context.event 是傷害事件 |
on_shield_block | 玩家用盾牌擋下傷害 | context.event 是傷害事件 |
on_shoot_bow | 玩家射箭 | context.event 是射箭事件 |
on_projectile_hit | 玩家射出的拋射物擊中某物 | context.event 是拋射物命中事件 |
on_projectile_launch | 玩家發射拋射物 | context.event 是拋射物發射事件 |
互動鉤子
| 鉤子 | 觸發時機 | 說明 |
|---|---|---|
on_right_click | 玩家手持該物品右鍵點擊 | context.event 是互動事件 |
on_left_click | 玩家手持該物品左鍵點擊 | context.event 是互動事件 |
on_shift_right_click | 玩家手持該物品按 Shift+右鍵點擊 | context.event 是互動事件 |
on_shift_left_click | 玩家手持該物品按 Shift+左鍵點擊 | context.event 是互動事件 |
on_interact_entity | 玩家手持該物品右鍵點擊實體 | context.event 是實體互動事件 |
裝備鉤子
| 鉤子 | 觸發時機 | 說明 |
|---|---|---|
on_equip | 物品被裝備(移入啟用的欄位) | 適合用來初始化狀態 |
on_unequip | 物品被卸下(移出啟用的欄位) | 適合用來清理 |
on_swap_hands | 玩家在主手與副手之間交換該物品 | context.event 是交換事件 |
on_drop | 玩家丟棄該物品 | context.event 是丟棄事件 |
公用鉤子
| 鉤子 | 觸發時機 | 說明 |
|---|---|---|
on_break_block | 玩家手持該物品破壞方塊 | context.event 是方塊破壞事件 |
on_consume | 玩家食用該物品(食物/藥水) | context.event 是食用事件 |
on_item_damage | 物品受到耐久度損耗 | context.event 是物品損耗事件 |
on_fish | 玩家使用釣魚竿 | context.event 是釣魚事件 |
on_death | 玩家在裝備該物品時死亡 | context.event 是死亡事件 |
生命週期鉤子
| 鉤子 | 觸發時機 | 說明 |
|---|---|---|
on_game_tick | 物品被裝備期間的每個伺服器 tick | 請謹慎使用——每秒執行 20 次 |
最小檔案契約
每個 Lua 道具腳本必須 return 一個表。
必填和選填的頂層欄位
| 欄位 | 必填 | 類型 | 說明 |
|---|---|---|---|
api_version | 是 | Number | 目前必須為 1 |
priority | 否 | Number | 若有提供會被驗證,但 FMM 目前不會依它排序腳本。道具依 scripts: 清單順序執行;物品只會繫結第一個有效的腳本 |
| 支援的鉤子鍵 | 否 | Function | 必須使用鉤子參考中列出的精確鉤子名稱之一 |
驗證規則
- 檔案必須回傳一個表。
api_version為必填,目前必須為1。priority如果存在必須為數字。- 每個額外的頂層鍵必須是受支援的鉤子名稱。
- 每個鉤子鍵必須指向一個函式。
- 未知的頂層鍵會被拒絕。
priority 有助於讓腳本在各個以 MagmaCore 為基礎的執行環境間保持可移植性,但 FreeMinecraftModels 目前的執行順序是由設定決定的。請在模型的 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:讓檔案載入
return {
api_version = 1,
on_spawn = function(context)
end
}
如果這在主控台中無錯誤地載入,你證明了:
- 檔案是有效的 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 腳本使用的模式。它取消傷害事件,使道具底層的盔甲架無法被摧毀。
步驟 5:點擊時播放動畫
return {
api_version = 1,
on_right_click = function(context)
context.prop:play_animation("open", true, false)
end
}
這會在道具模型上播放 "open" 動畫,帶有混合且不循環。
什麼是 context?
每個鉤子函式接收一個名為 context 的參數。把它想像成一個工具箱,FMM 在每次發生事件時交給你——它包含了你與道具、世界、區域等互動所需的一切。
你不需要自己建立 context——FMM 會建立它並傳遞給你的鉤子。關於共用 context 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.item--(僅限物品腳本)自訂物品。提供id、material()、get_amount()、set_amount()、consume()、get_uses()、set_uses()、get_name()、set_name()、get_lore()、set_lore()、get_durability()、get_durability_percentage()、use_durability()和use_durability_percentage()。完整詳情請參見道具與物品 API。 -
context.player-- 由玩家驅動的鉤子中的玩家。物品腳本會從物品持有者解析出它;道具點擊鉤子與通用區域鉤子則從觸發的玩家解析。在道具生命週期鉤子、道具排程回呼,以及不涉及玩家的鉤子中為nil。 -
context.event-- 觸發此鉤子的 Bukkit 事件或玩家行為者的輕量包裝。在點擊、戰鬥、互動與通用區域鉤子中可用。提供event.player、is_cancelled,以及在底層 Bukkit 事件可取消時的cancel()/uncancel();它不會暴露target、block、projectile或item等 Bukkit 專屬欄位。在沒有事件或玩家行為者的鉤子中(如on_spawn、on_game_tick和on_equip)為nil。 -
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。
方法語法:: vs .
關於 Lua 中 : 與 . 方法語法的說明,請參閱 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
}
最小的有效物品腳本
return {
api_version = 1,
on_equip = function(context)
end
}
帶有右鍵動作的物品範本
return {
api_version = 1,
on_right_click = function(context)
if not context.cooldowns:check_local("activate", 40) then return end
-- Your action here
context.player:send_message("&aItem activated!")
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)。 - 先新增一條日誌訊息,然後再新增動畫或特效。
- 新增一個真實的特效(動畫、聲音、粒子)。
- 只有在此之後,才新增輔助函式、state、排程器邏輯或區域。
這個順序能大幅簡化除錯,因為每次只改變一件事。
預製腳本
FMM 附帶四個預製的 Lua 腳本:
invulnerable.lua-- 取消左鍵點擊傷害事件,使道具不可摧毀。這是最簡單實用的道具腳本。pickupable.lua-- 讓玩家透過攻擊道具三次來撿起它。每次攻擊都會在道具上播放受傷動畫,第三次攻擊時道具會被移除並掉落其放置物品供玩家撿取。storage_double.lua-- 把道具變成一個大型儲物箱(54 格)。右鍵點擊會開啟持久化的物品欄 GUI。會播放開啟/關閉動畫與音效。內容會儲存在道具上,並在伺服器重啟後保留。道具被摧毀時,所有內容都會掉落。storage_single.lua-- 與storage_double相同,但只有 3 列(27 格)而非 6 列。
更多範例請參見範例與模式頁面。
Lua 沙盒
道具腳本與物品腳本在與 EliteMobs 相同的沙盒化 LuaJ 環境中執行。沙盒限制完全相同。已移除的全域變數和可用標準函式庫函式的完整列表,請參閱 MagmaCore Lua 腳本引擎頁面。