--- name: integrate-vault description: 為 Bukkit/Paper 插件整合 Vault 經濟 API,產生 EconomyManager 類別、pom.xml 依賴設定、plugin.yml 宣告,含存款、扣款、查詢餘額、軟依賴降級處理。當使用者說「Vault 整合」、「經濟插件」、「EconomyManager」、「Vault API」、「存款扣款」、「幫我整合 Vault」時自動應用。 --- # Integrate Vault Skill ## 目標 為 Bukkit/Paper 插件正確接入 Vault 經濟 API,產生 `EconomyManager` 類別,封裝常用的餘額查詢、存款、扣款操作,並處理 Vault 未安裝時的降級或停用邏輯。 --- ## 使用流程 1. **確認基本資訊**:插件名稱、套件名、Vault 是 `depend`(必裝)還是 `softdepend`(選裝) 2. **更新 pom.xml**:加入 Vault API 依賴(`provided` scope) 3. **更新 plugin.yml**:宣告 `depend` 或 `softdepend` 4. **產生 EconomyManager.java**:含初始化、存取餘額、存款、扣款、關閉 --- ## pom.xml 依賴 ```xml com.github.MilkBowl VaultAPI 1.7.1 provided ``` 需在 `pom.xml` 的 `` 加入 JitPack: ```xml jitpack.io https://jitpack.io ``` --- ## plugin.yml 宣告 **必裝模式(depend):** ```yaml depend: [Vault] ``` **選裝模式(softdepend):** ```yaml softdepend: [Vault] ``` --- ## 代碼範本 ### EconomyManager.java ```java package com.example.myplugin.managers; import com.example.myplugin.MyPlugin; import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.EconomyResponse; import org.bukkit.OfflinePlayer; import org.bukkit.plugin.RegisteredServiceProvider; public class EconomyManager { private final MyPlugin plugin; private Economy economy; public EconomyManager(MyPlugin plugin) { this.plugin = plugin; } // ---- 初始化 ---- /** * 嘗試取得 Vault Economy 服務。 * @return true 若成功取得,false 若 Vault 未安裝或無經濟插件 */ public boolean setupEconomy() { if (plugin.getServer().getPluginManager().getPlugin("Vault") == null) { plugin.getLogger().warning("找不到 Vault 插件,經濟功能停用。"); return false; } RegisteredServiceProvider rsp = plugin.getServer().getServicesManager().getRegistration(Economy.class); if (rsp == null) { plugin.getLogger().warning("Vault 找不到任何經濟插件(如 EssentialsX、CMI),經濟功能停用。"); return false; } economy = rsp.getProvider(); plugin.getLogger().info("Vault 經濟整合成功:" + economy.getName()); return true; } // ---- 狀態查詢 ---- /** 是否已成功連接到 Vault 經濟服務 */ public boolean isAvailable() { return economy != null; } // ---- 餘額查詢 ---- public double getBalance(OfflinePlayer player) { ensureAvailable(); return economy.getBalance(player); } public boolean has(OfflinePlayer player, double amount) { ensureAvailable(); return economy.has(player, amount); } // ---- 存款 ---- /** * 存入金額至玩家帳戶。 * @return EconomyResponse(含 type、errorMessage) */ public EconomyResponse deposit(OfflinePlayer player, double amount) { ensureAvailable(); return economy.depositPlayer(player, amount); } // ---- 扣款 ---- /** * 從玩家帳戶扣除金額。 * @return EconomyResponse(含 type、errorMessage) */ public EconomyResponse withdraw(OfflinePlayer player, double amount) { ensureAvailable(); return economy.withdrawPlayer(player, amount); } // ---- 輔助 ---- private void ensureAvailable() { if (economy == null) { throw new IllegalStateException("Vault Economy 服務未初始化,請先呼叫 setupEconomy()"); } } /** 在 onDisable 中呼叫,清除參照 */ public void shutdown() { economy = null; } } ``` --- ### 在主類中初始化與關閉 ```java private EconomyManager economyManager; @Override public void onEnable() { economyManager = new EconomyManager(this); if (!economyManager.setupEconomy()) { // 必裝模式:初始化失敗則停用插件 getLogger().severe("Vault 經濟初始化失敗,插件停用。"); getServer().getPluginManager().disablePlugin(this); return; } } @Override public void onDisable() { if (economyManager != null) { economyManager.shutdown(); } } public EconomyManager getEconomyManager() { return economyManager; } ``` --- ### EconomyResponse 處理範例 ```java EconomyResponse resp = plugin.getEconomyManager().withdraw(player, 100.0); if (resp.transactionSuccess()) { player.sendMessage("已扣除 100 金幣,剩餘:" + resp.balance); } else { player.sendMessage("扣款失敗:" + resp.errorMessage); } ``` --- ## 常見錯誤與修正 | 錯誤 | 原因 | 修正 | |------|------|------| | `NullPointerException` 於 `economy.xxx()` | `setupEconomy()` 未被呼叫或回傳 false | 在 `onEnable` 確認初始化成功才繼續 | | `ClassNotFoundException: net.milkbowl.vault.economy.Economy` | pom.xml 中 VaultAPI 依賴設定錯誤或未加入 JitPack repo | 確認 repository 與 dependency 均已設定 | | Vault 已安裝但 `rsp == null` | 伺服器上沒有任何實作 Economy 的插件(如 EssentialsX) | 安裝一個經濟插件作為 Vault 後端 | | 在非同步執行緒呼叫 Vault API | Vault Economy API 為同步 API,不保證執行緒安全 | 透過 `Bukkit.getScheduler().runTask()` 切回主執行緒再呼叫 | | `plugin.yml` 缺少 `depend: [Vault]` | Vault 可能比插件晚載入,導致 `getPlugin("Vault")` 回傳 null | 在 `plugin.yml` 加入 `depend` 或 `softdepend` | --- ## 更多範例 詳見 [examples.md](examples.md)