跳至主要內容

FreeMinecraftModels API 與開發者指南

FreeMinecraftModels 既是獨立的外掛,也是其他外掛的 API 介面。

Maven 儲存庫

<repository>
<id>magmaguy-repo-releases</id>
<name>MagmaGuy's Repository</name>
<url>https://repo.magmaguy.com/releases</url>
</repository>
<repository>
<id>magmaguy-repo-snapshots</id>
<name>MagmaGuy's Snapshot Repository</name>
<url>https://repo.magmaguy.com/snapshots</url>
</repository>

依賴

<dependency>
<groupId>com.magmaguy</groupId>
<artifactId>FreeMinecraftModels</artifactId>
<version>LATEST.VERSION.HERE</version>
<scope>provided</scope>
</dependency>

使用 compileOnly/provided。不要將外掛 shade 進你自己的 jar 中。

核心入口點

  • ModeledEntityManager.modelExists(String)
  • ModeledEntityManager.reload()
  • ModeledEntityManager.getAllEntities()
  • ModeledEntityManager.getDynamicEntities()
  • ModeledEntityManager.propEntities()
  • DisguiseAPI — 將玩家偽裝/取消偽裝為已載入的模型
  • LocationAPI — 註冊地城偵測器與保護提供者(提供給 Lua em.location.* 判斷使用)
  • ScriptedItemAPI — 為第三方 ItemStack 標記 FMM 腳本化物品的中繼資料

ModeledEntityManager.getAllEntities()getDynamicEntities()propEntities() 回傳的是當下時間點的副本。變更回傳的集合或映射並不會修改 FMM 的即時註冊表。請使用 modelExists(String) 做存在性檢查,而不要保留並反覆複製整份註冊表。

核心執行階段類型

  • ModeledEntity
  • StaticEntity
  • DynamicEntity
  • PropEntity

建立實體

StaticEntity preview = StaticEntity.create("example_model", location);
DynamicEntity mobModel = DynamicEntity.create("example_model", livingEntity);
DynamicEntity mount = DynamicEntity.createWithInvisibility("example_model", livingEntity);
PropEntity prop = PropEntity.spawnPropEntity("example_model", location);

如果請求的模型 ID 未載入,所有建立路徑都會回傳 null

此外,當目標方塊上已經載入了同一個模型的道具時,PropEntity.spawnPropEntity 也會回傳 null —— 重複的道具會被拒絕而不是疊加上去,並且會記錄一則 [FMM Props] Prevented duplicate prop spawn ... 警告。請務必檢查它的回傳值是否為 null;只有非 null 的回傳值才能確認道具真的被建立了。

createWithInvisibility 是一種變體,會套用隱形藥水效果,而不是對客戶端隱藏實體。這會保持實體在客戶端被追蹤,這對於載具操控(由 /fmm mount 在內部使用)是必要的。

實用的執行階段方法

  • ModeledEntity#setDisplayName(String) -- 除非模型含有 tag_ 骨骼,否則會靜默地什麼都不做(見下方警告)
  • ModeledEntity#setDisplayNameVisible(boolean) -- 相同的要求
  • ModeledEntity#setLeftClickCallback(...)
  • ModeledEntity#setRightClickCallback(...)
  • ModeledEntity#setHitboxContactCallback(...)
  • ModeledEntity#setModeledEntityHitByProjectileCallback(...)
  • ModeledEntity#playAnimation(String, boolean blend, boolean loop) -- 當名稱既不符合任何內建狀態、也不符合模型中的動畫時回傳 falseblend 是排入佇列而非交叉淡入;loop 只適用於自訂動畫。請參閱動畫
  • ModeledEntity#stopCurrentAnimations()
  • ModeledEntity#hasAnimation(String)
  • ModeledEntity#damage(double) / damage(Entity damager, double) / damage(Entity damager) / damage(Projectile) -- 都經由該實體的 DamageableComponent 處理
  • ModeledEntity#attack(LivingEntity) / attack(LivingEntity, double damage)
  • ModeledEntity#teleport(Location, boolean teleportUnderlyingEntity)
  • ModeledEntity#setUnderlyingEntity(Entity) / ModeledEntity.getModeledEntity(Entity) -- 從 Bukkit 實體反查附加其上的模型的靜態方法,找不到則為 null
  • ModeledEntity#showUnderlyingEntity(Player) / hideUnderlyingEntity(Player) -- 針對每位玩家控制底層原版實體的可見性
  • ModeledEntity#getEntityID() -- 回傳模型 ID 字串
  • ModeledEntity#getModelInstanceId() -- 每個實例的 UUID,在該模型的生命週期內保持不變
  • ModeledEntity#isRemoved() / isDying() -- 生命週期旗標
  • ModeledEntity#getLocation() -- 回傳當前的 Location
  • ModeledEntity#getSpawnLocation() -- 回傳建立該模型時所在的 Location
  • ModeledEntity#getWorld() -- 回傳 World
  • ModeledEntity#getSkeleton() / getSkeletonBlueprint() / getMountPointManager() -- 執行階段結構存取
  • ModeledEntity#getInteractionComponent() / getHitboxComponent() / getDamageableComponent() / getAnimationComponent() -- 上述便利方法背後的元件物件
  • ModeledEntity.getLoadedModeledEntities() -- 所有已載入模型化實體的靜態即時集合
  • ModeledEntity#getViewers() -- 回傳可以看到該實體的玩家 HashSet<UUID>
  • ModeledEntity#getNametagBones() -- 回傳名牌骨骼的 List<Bone>(用於放置額外文字很有用)
  • ModeledEntity#getScaleModifier() / setScaleModifier(double)
  • ModeledEntity#removeWithDeathAnimation() -- 以死亡動畫移除(如果存在的話)
  • ModeledEntity#removeWithMinimizedAnimation() -- 以縮小動畫移除
  • ModeledEntity#remove() -- 立即移除實體與所有骨骼
  • ModeledEntity#setTintColor(Color) / getTintColor() -- 透過皮革護甲染料通道套用持續的顏色色調。受傷閃爍會短暫覆寫色調,然後淡回原色。傳入 null 來清除。
  • ModeledEntity#setViewDistanceOverride(int) / getEffectiveViewDistance() -- 針對單一實體覆寫 DefaultConfig.maxModelViewDistance。傳入 -1 來還原為外掛範圍的預設值。
  • DynamicEntity#setSyncMovement(boolean)
  • DynamicEntity#isDamagesOnContact() / setDamagesOnContact(boolean) -- 控制實體是否會透過碰撞箱接觸對玩家造成傷害
  • DynamicEntity.isDynamicEntity(Entity) / DynamicEntity.getDynamicEntity(Entity) -- 從 Bukkit 實體查詢的靜態方法
  • DynamicEntity#getBodyLocation() -- 以身體為基準的位置,與 getLocation() 不同
  • Bone#getBoneLocation()

PropEntity 專屬方法

  • PropEntity.isPropEntity(ArmorStand) / PropEntity.getPropEntityID(ArmorStand) -- 從支撐用的盔甲座辨識道具
  • PropEntity.hasLoadedPropOnSameBlock(String entityID, Location) -- spawnPropEntity 內部執行的重複檢查;如果你想先分支處理而不是檢查 null,可以先呼叫它
  • PropEntity.respawnPropEntityFromArmorStand(String entityID, ArmorStand) -- 圍繞在區塊重新載入後倖存的盔甲座重建道具
  • PropEntity.getPropEntities() -- 以盔甲座 UUID 為鍵的已載入道具即時映射
  • PropEntity#setPersistent(boolean) -- 切換支撐用盔甲座的持久化
  • PropEntity#setCustomDataString(NamespacedKey, String) / getCustomDataString(NamespacedKey) -- 在道具上讀寫你自己的 PDC 值。這與 Lua 的 set_persistent_data / get_persistent_data 輔助函式使用同一個儲存區,位於 fmm_lua_<key> 命名空間下
  • PropEntity#remove() / remove(boolean showRealBlocks) -- 移除模型但保留持久化項目
  • PropEntity#permanentlyRemove() -- 移除模型以及其持久化項目
  • PropEntity#setVoxelizeConfig(boolean voxelize, boolean solidify) / applySolidify() -- voxelize: / solidify: YML 欄位的執行階段等價方法
  • PropEntity#showFakePropBlocksToPlayer(Player) / showRealBlocksToPlayer(Player) 以及對應的 ...ToAllPlayers() 變體 -- 控制那些只以封包送出、用來讓實體化道具在客戶端具有碰撞的屏障方塊
名牌需要模型中有 tag_ 骨骼

setDisplayNamesetDisplayNameVisible 會走訪模型的名牌骨骼,而這些骨骼只存在於名稱以 tag_ 開頭的骨骼上。在沒有這種骨骼的模型上,該清單是空的,因此這兩個呼叫都會成功,卻什麼也不做 —— 沒有例外,也沒有日誌。

這裡沒有備援機制。DynamicEntity 會對客戶端隱藏其底層的生物實體,所以原版怪物名牌也不會顯示。最終結果就是一個完全沒有名字的怪物,即使你的外掛設定名稱時毫無錯誤。

如果你的外掛會為模型命名(Boss、NPC,或任何使用者可見的東西),請要求你所發佈的模型必須具備 tag_ 骨骼,或在附加模型時檢查 getNametagBones().isEmpty() 並警告內容作者。請參閱模型製作須知

事件介面

通用互動事件:

  • ModeledEntityLeftClickEvent
  • ModeledEntityRightClickEvent
  • ModeledEntityHitboxContactEvent
  • ModeledEntityHitByProjectileEvent

這四個都是可取消的 Bukkit 事件;FMM 的內建偵測路徑會在主伺服器執行緒上派發它們。取消其中一個會阻止 FMM 對應的回呼/預設行為。碰撞箱接觸掃描每兩個伺服器 tick 執行一次;點擊事件仍保有短暫的每位玩家去重複視窗,因此封包互動路徑與 OBB 射線路徑不會讓同一個動作觸發兩次。

生命週期事件:

  • FmmReloadedEvent -- 在 FMM 完成啟動時的初始化序列每次 /fmm reload 後觸發。永遠在主伺服器執行緒上觸發。

公開的互動介面刻意不區分模型類型。較舊的 StaticEntity*EventDynamicEntity*EventPropEntity*Event 變體,以及 ResourcePackGenerationEvent,都已不再屬於目前的 API。請改用上述四個通用的模型化實體事件與 FmmReloadedEvent

持有長期存在的 DynamicEntityPropEntity 參考的消費者外掛(EliteMobs、BetterStructures 等)必須處理此事件,在仍存在的底層實體上重新建立其模型附件。如果不這樣做,那些實體在重新載入後會變成不可見,因為 FMM 在 onDisable 期間拆除了 display entity,而消費者的參考現在已失效。

@EventHandler
public void onFmmReloaded(FmmReloadedEvent event) {
for (MyTrackedEntity tracked : myEntities) {
if (tracked.bukkitEntity != null && tracked.bukkitEntity.isValid()) {
tracked.fmmModel = DynamicEntity.create("my_model", tracked.bukkitEntity);
}
}
}

ModeledEntityHitByProjectileEvent 與 OBB 拋射物偵測

FMM 使用 OBB(定向碰撞箱)命中偵測來處理對模型化實體的拋射物。當拋射物與模型化實體的 OBB 碰撞箱相交時,FMM 會觸發 ModeledEntityHitByProjectileEvent。這是一個標準的可取消 Bukkit 事件。

關鍵細節:

  • 偵測會掃過拋射物從上一個 tick 起的整段移動軌跡,因此高速箭矢不會在取樣之間穿透薄型模型
  • 該線段上的候選目標會由近而遠依序解析,與模型註冊表的走訪順序無關
  • 非穿透型拋射物只會導向一個模型化目標。穿透(Piercing)箭矢可以依飛行順序命中 穿透等級 + 1 個不同的模型化目標,而且當 OBB 與原版底層碰撞箱同時觀察到它時也不會重複觸發
  • 預設的動態實體處理器會使用拋射物在撞擊當下的速度,把這次撞擊當成真正的拋射物傷害事件轉發給底層的生物實體。這保留了拋射物的傷害來源、戰鬥外掛的處理以及射擊者歸屬
  • 取消事件會阻止 FMM 的回呼/預設傷害行為,但該次碰撞仍會消耗掉這個拋射物的模型化目標額度。因此被取消的非穿透命中依然會消耗該拋射物
@EventHandler
public void onProjectileHitModel(ModeledEntityHitByProjectileEvent event) {
ModeledEntity target = event.getModeledEntity();
Projectile projectile = event.getProjectile();
// Cancel to prevent damage
event.setCancelled(true);
}

物品與模型公用程式

ModelItemFactory

用於以程式化方式建立模型相關 ItemStack 的工廠類別。

// Create a prop placement item (uses "model_id" PDC key)
ItemStack placementItem = ModelItemFactory.createModelItem("lamp_post", Material.STICK);

// Create a custom item from config (uses "fmm_item_id" PDC key)
PropScriptConfigFields config = ItemScriptManager.getItemDefinitions().get("magic_sword");
ItemStack customItem = ModelItemFactory.createCustomItem("magic_sword", config);
  • createModelItem(String modelId, Material material) -- 為道具建立放置物品。在 1.21.4+ 上,如果存在顯示 JSON 會自動套用 display model 渲染。
  • createCustomItem(String itemId, PropScriptConfigFields config) -- 從統一設定建立帶有名稱、物品說明、附魔與 display model 的自訂物品。
  • formatModelName(String modelId) -- 公用程式,將像 01_em_flame_sword 這樣的模型 ID 轉換為 Flame Sword

DisplayModelRegistry

追蹤哪些模型擁有可用的顯示 JSON 的簡單註冊表。

// Check if a model has a display model JSON registered
boolean has3D = DisplayModelRegistry.hasDisplayModel("magic_sword");
  • register(String modelId) -- 註冊模型 ID(在重新載入期間內部呼叫)
  • hasDisplayModel(String modelId) -- 如果此模型有 .json 顯示模型存在則回傳 true
  • getRegisteredModels() -- 回傳所有已註冊顯示模型的模型 ID 的不可變 Set<String>
  • shutdown() -- 清除所有註冊

ItemScriptManager

管理自訂物品(YML 設定中設定了 material: 的模型)的每位玩家 Lua 腳本的生命週期。

// Get all registered custom item definitions
Map<String, PropScriptConfigFields> items = ItemScriptManager.getItemDefinitions();

// Get the active Lua script instance for a player + item
ScriptInstance instance = ItemScriptManager.getActiveScript(playerUUID, "magic_sword");

// Get all active scripts for a player
Map<String, ScriptInstance> scripts = ItemScriptManager.getActiveScripts(playerUUID);
  • scanForCustomItems(File modelsFolder) -- 掃描模型 YML 設定以尋找自訂物品
  • updateEquippedScripts(Player player) -- 比對已裝備物品與執行中的腳本,觸發 equip/unequip 鉤子
  • removePlayer(Player player) -- 關閉玩家的所有腳本(在退出時呼叫)
  • getItemDefinitions() -- 回傳物品 ID 對應到 PropScriptConfigFields 的對映

ScriptedItemAPI

供外部外掛與 FMM 腳本化物品系統整合的公開 API。這讓其他外掛可以將自己的 ItemStack 標記上 FMM 腳本化物品資料(PDC 標籤 + 物品模型),使 FMM 的 Lua 腳本鉤子能對這些物品觸發,而 FMM 不會覆寫物品的名稱、物品說明或附魔。

// Check if a scripted item definition exists
boolean exists = ScriptedItemAPI.isValidItemId("flame_blade");

// Apply FMM scripted item data to an existing ItemStack
// This sets:
// - The fmm_item_id PDC tag (so FMM's script system recognizes the item)
// - The item model (1.21.4+) from FMM's display model registry
// Does NOT modify name, lore, enchantments, or any other item properties.
boolean success = ScriptedItemAPI.applyScriptedItemData(itemStack, "flame_blade");

// Get the config for a scripted item
PropScriptConfigFields config = ScriptedItemAPI.getItemConfig("flame_blade");
  • isValidItemId(String itemId) -- 如果物品 ID 已在 FMM 的物品定義中註冊則回傳 true
  • applyScriptedItemData(ItemStack itemStack, String itemId) -- 將 PDC 標籤和物品模型蓋印到現有的 ItemStack。成功則回傳 true,物品 ID 無效或 ItemStack 沒有 meta 時回傳 false弓/弩注意: 如果給定的 itemId 沒有顯示模型但 itemId + "_idle" 有(即該物品有弓/弩狀態模型),該方法會自動使用 _idle 模型作為顯示模型
  • getItemConfig(String itemId) -- 回傳指定物品 ID 的 PropScriptConfigFields,找不到時回傳 null
EliteMobs 整合

EliteMobs 透過 scriptedItem 設定欄位在內部使用此 API。當 EliteMobs 自訂物品設定 scriptedItem: flame_blade 時,EliteMobs 正常建構其物品(名稱、物品說明、附魔、等級),然後呼叫 ScriptedItemAPI.applyScriptedItemData() 在其上加入 FMM 的模型與腳本行為。

DisguiseAPI

玩家偽裝功能的公開入口點。第三方外掛應呼叫此類別,而非內部的 DisguiseManager,以使內部重構保持安全。

import com.magmaguy.freeminecraftmodels.api.DisguiseAPI;

// Disguise a player as a loaded model. Replaces any existing disguise cleanly.
boolean ok = DisguiseAPI.disguise(player, "dragon");

// Undisguise (returns true if a disguise was removed).
DisguiseAPI.undisguise(player);

// Query state.
boolean disguised = DisguiseAPI.isDisguised(player);
String modelID = DisguiseAPI.getDisguiseModelID(player); // null if not disguised

// Snapshot of all currently disguised players.
Collection<Player> all = DisguiseAPI.getDisguisedPlayers();
  • disguise(Player, String modelID) -- 如果模型 ID 未載入則回傳 false
  • undisguise(Player) -- 如果移除了偽裝則回傳 true
  • isDisguised(Player) -- 快速布林檢查
  • getDisguiseModelID(Player) -- 回傳活躍的模型 ID 或 null
  • getDisguisedPlayers() -- 偽裝中玩家的不可修改快照

被偽裝的玩家對其他人是隱形的,並保持這個狀態直到取消偽裝 — 牛奶桶、信標效果清除以及類似的互動都無法破壞隱形效果。

LocationAPI

供外掛貢獻地城偵測與區域保護檢查的公開 API。註冊的判斷會提供給 FMM 的 Lua em.location.is_in_dungeonem.location.is_protected 檢查使用(由 pickupable.luastorage_double.lua 等預製腳本使用)。

外掛傳入一般的 Predicate<Location>,所以沒有 shade 過的 FMM 類型會跨外掛 classloader。

import com.magmaguy.freeminecraftmodels.api.LocationAPI;

// On your plugin's enable, after WorldGuard/EliteMobs/etc. are available.
LocationAPI.registerDungeonLocator("EliteMobs",
location -> EliteMobs.isInsideDungeon(location));

LocationAPI.registerProtectionProvider("WorldGuard",
location -> WorldGuardBridge.isProtected(location));
  • registerDungeonLocator(String providerName, Predicate<Location> predicate) — 任何回傳 true 的已註冊判斷會將位置標記為「在地城中」
  • registerProtectionProvider(String providerName, Predicate<Location> predicate) — 任何回傳 true 的已註冊判斷會將位置標記為受保護

操作員可以使用 /fmm location 驗證註冊情況,該指令會回報即時的提供者數量並對當前位置測試這兩種判斷。

保護提供者與道具放置

同一批保護提供者也驅動了 preventPropPlacementInProtectedRegions 這項道具放置檢查,但該檢查呼叫的是 canBuild(player, location),而不是只看位置的 isProtected(location)。這個差別很重要:

提供者canBuild 行為
內建的 WorldGuard 轉接器會尊重 WorldGuard 自身的 bypass,然後交由 WorldGuard 的 testBuild 判斷 —— 因此區域成員與擁有者可以正常建造
內建的 GriefPrevention 轉接器該位置沒有領地即視為允許;在領地內則交由 GriefPrevention 針對該玩家的建造權限判斷
透過 LocationAPI.registerProtectionProvider 註冊的提供者退回使用 !isProtected(location) —— 只看位置,會阻擋所有人,只要你的判斷認為該位置受保護

這個退回行為是刻意且保守的:Predicate<Location> 無法表達針對特定玩家的權限,因此 FMM 不會替你憑空造一個出來。如果你希望自己的區域系統擁有真正能辨別玩家的行為,請直接實作 MagmaCore 的 RegionProtectionProvider 並覆寫 canBuild,然後以 LocationQueryRegistry.registerProtectionProvider 註冊它,而不要走判斷式的便利包裝。

還有另外兩個值得知道的行為:

  • 轉接器失敗時採取封閉式失敗。 如果某個提供者在建造查詢期間拋出例外,該次放置會被拒絕,並記錄一則指名該提供者的警告。它絕不會默默地退回「允許」。
  • 每一個已註冊的提供者都必須同意。 第一個說不的提供者說了算;透過 LocationOwnership 註冊的所有權提供者仍然只看位置,並會阻擋所有人。

PropScriptConfigFields

模型 YML 設定檔的統一設定類別。同時被道具腳本與自訂物品使用。

# Example: torch_01.yml
isEnabled: true
scripts:
- torch_glow.lua
material: STICK # If set, model becomes a custom item
name: "&eMagic Torch" # Custom display name (optional)
lore: # Custom lore lines (optional)
- "&7Glows in the dark"
enchantments: # Enchantments (optional, format: NAME,LEVEL)
- "FIRE_ASPECT,1"

主要方法:isCustomItem()getParsedMaterial()getParsedEnchantments()getScripts()

Lua 腳本

FreeMinecraftModels 透過 MagmaCore 2.0 腳本引擎支援針對道具自訂物品的 Lua 腳本。腳本檔案放置於 plugins/FreeMinecraftModels/scripts/,並透過放在模型檔案旁的同伴 YML 設定繫結到模型。磁碟上的腳本檔案必須以 .lua 結尾;設定項目中可以帶副檔名,也可以省略。

道具會把 scripts: 中列出的每個腳本都繫結為獨立的實例。自訂物品目前對每組玩家/物品配對只會繫結清單中第一個有效的腳本。

道具腳本鉤子

鉤子觸發時機
on_spawn道具被生成到世界中
on_game_tick道具存在的每個 tick
on_zone_enter玩家進入由腳本建立的受監看區域
on_zone_leave玩家離開由腳本建立的受監看區域
on_destroy道具被移除
on_left_click玩家左鍵點擊道具
on_right_click玩家右鍵點擊道具
on_projectile_hit保留:驗證時會被接受,但在目前的執行環境中不會派送給道具腳本

物品腳本鉤子

自訂物品(設定了 material: 的模型)支援 22 個 Lua 鉤子:

鉤子觸發時機
on_equip物品進入受追蹤的裝備槽位
on_unequip物品離開受追蹤的裝備槽位
on_game_tick物品裝備期間的每個 tick
on_attack_entity玩家持有該物品時攻擊實體
on_kill_entity玩家持有該物品時擊殺實體
on_take_damage玩家裝備該物品時受到傷害
on_shield_block玩家用盾牌格擋
on_shoot_bow玩家射出弓箭
on_projectile_hit玩家發射的拋射物擊中目標
on_projectile_launch玩家發射拋射物
on_right_click玩家持有該物品時右鍵點擊
on_left_click玩家持有該物品時左鍵點擊
on_shift_right_click玩家持有該物品時 shift-右鍵點擊
on_shift_left_click玩家持有該物品時 shift-左鍵點擊
on_interact_entity玩家持有該物品時右鍵點擊實體
on_swap_hands玩家在主副手間切換該物品
on_drop玩家丟棄該物品
on_break_block玩家持有該物品時破壞方塊
on_consume玩家消耗該物品
on_item_damage物品耐久度受損
on_fish玩家使用釣魚竿
on_death玩家裝備該物品時死亡

物品腳本會接收 context.item(包含物品 ID 與玩家資訊)而非 context.prop

道具腳本 Context 表格

道具腳本會接收一個 context 表格。以下是主要 API 的摘要 -- 完整細節請參閱 Lua 道具 API

context.prop

  • model_id -- 藍圖模型名稱
  • current_location -- 道具的當前位置
  • play_animation(name, blend, loop) -- 播放指定名稱的動畫(blend 與 loop 預設為 true
  • stop_animation() -- 停止所有當前的動畫
  • hurt_visual() -- 在道具上播放受傷(紅色閃爍)視覺效果
  • pickup() -- 將「移除道具並掉落其放置物品」排入佇列
  • mount(player) -- 將一次騎乘嘗試排入佇列;回傳 true 只表示該玩家與騎乘管理員是有效的,並不表示最終有分配到座位
  • dismount(player) -- 將一次下乘檢查排入佇列;回傳 true 只表示該玩家與騎乘管理員是有效的
  • get_passengers() -- 回傳當前騎在道具上的玩家清單
  • spawn_elitemobs_boss(filename, x, y, z) -- 在道具當前世界中的絕對座標處生成 EliteMobs Boss

context.event

  • 可用於道具的 on_left_clickon_right_clickon_zone_enteron_zone_leave,以及由玩家所引發的物品鉤子
  • 當底層鉤子可被取消時,可使用 cancel()uncancel()is_cancelled
  • player -- 觸發事件的玩家

context.world

  • spawn_entity(entity_type, x, y, z) -- 生成原版實體,若實體類型無效則回傳 nil
  • set_block_at(x, y, z, material) -- 若材料有效則將方塊變更排入佇列;未載入的區塊會被略過
  • 加上粒子、音效、方塊查詢、閃電以及附近實體查找

context.cooldowns

  • check_local(key, ticks) -- 檢查並啟動一個腳本層級的冷卻
  • global_ready() / set_global(ticks) -- 道具或玩家擁有者的共用冷卻

玩家物件(來自 context.playercontext.event.player):

  • get_held_item() -- 回傳玩家手持的物品;type 為大寫的 Bukkit 材料名稱
  • consume_held_item() -- 移除一個手持物品
  • has_item(material) -- 檢查玩家是否擁有物品
  • send_message(text) -- 向玩家發送聊天訊息
  • game_mode -- 玩家當前的遊戲模式

道具腳本範例

return {
api_version = 1,

on_spawn = function(context)
context.prop:play_animation("idle", true, true)
end,

on_right_click = function(context)
if context.cooldowns:check_local("activate", 40) then
context.prop:play_animation("activate", false, false)
end
end
}

物品腳本範例

return {
api_version = 1,

on_equip = function(context)
context.player:send_message("&6You equipped the Flame Blade!")
end,

on_attack_entity = function(context)
-- Fire effect on hit
context.player:send_message("&cBurn!")
end,

on_unequip = function(context)
context.player:send_message("&7Flame Blade sheathed.")
end
}

說明

  • FreeMinecraftModels 是一個已安裝外掛的依賴,並非可嵌入的函式庫。
  • 如果你的外掛需要新匯入的模型,請呼叫 ModeledEntityManager.reload(),而不要試著自行重建 FreeMinecraftModels 的狀態。
  • Nightbreak 生態系中的所有外掛現在都依賴 MagmaCore 2.2.0-SNAPSHOT,其中包含 FreeMinecraftModels 道具腳本與 EliteMobs Lua 能力共用的 Lua 腳本引擎,加上共用的 LocationQueryRegistryWorldFolderResolver
  • FreeMinecraftModels 將 WorldGuard、WorldEdit、GriefPrevention、Vault、floodgate 與 Geyser-Spigot 宣告為 softdepend。任一項都不是啟動外掛所必需的,但它們會解鎖特定功能:WorldGuard/WorldEdit/GriefPrevention 提供給 LocationAPI 以及能辨別玩家的道具放置檢查,Vault 啟用家具商店,而 floodgate/Geyser-Spigot 則啟用逐模型的 Bedrock 後端。
  • ModeledEntityManager.reload() 會執行一次完整的外掛重新載入週期(onDisable / onLoad / onEnable)。請在主伺服器執行緒上呼叫它。