Skip to content
MJP
/ 第三方軟依賴接點

Paper API · Paper 整合

第三方軟依賴接點

安全使用 Vault、PlaceholderAPI、packetevents 等第三方軟依賴:Hook + Bridge 分離、isPluginEnabled 後才建構、監聽器簽名規則、接 LinkageError,並以邊界測試驗證沒裝時也能載入。

Paper API已發布v1.0.0paper-softdepend-hook更新於 2026年10月9日

觸發關鍵字

軟依賴softdependVaultPlaceholderAPIpacketeventsNoClassDefFoundError

Paper Softdepend Hook

目的

讓插件在有裝第三方插件時多一項功能、沒裝時照常啟動。這是 paper-service-api(自家插件之間)的第三方版本:第三方型別不在你的 jar 裡,JVM 一載入引用它們的類別就會丟 NoClassDefFoundError,所以要用 Hook + Bridge + 自家 Port 三層隔開。


平台需求

  • Paper 1.21.11 / 26.2(兩版皆經編譯驗證,無版本差異)
  • 純 Paper API;VaultAPI 1.7.1、PlaceholderAPI 2.11.6、packetevents-spigot 2.13.0 皆為 compileOnly
  • Java 21(1.21.11)/25(26.2)

產生的代碼

VaultHook.java(沒有任何 Vault 型別)

public boolean install() {
    if (!plugin.getServer().getPluginManager().isPluginEnabled(PLUGIN_NAME)) {
        return false;                         // 沒裝:直接降級
    }
    try {
        money = new Guarded(VaultBridge.create(plugin.getServer().getServicesManager()));
        return true;
    } catch (LinkageError e) {                // 載入失敗或版本不合
        warnOnce(e);
        return false;
    }
}

MarketExpansion.java(PlaceholderAPI,任意執行緒讀快照)

@Override public boolean persist() { return true; }

@Override
public String onRequest(OfflinePlayer player, String params) {
    if (player == null) return null;
    double balance = publisher.current().balance(player.getUniqueId()); // volatile 不可變快照
    return "balance".equals(params) ? String.format(Locale.ROOT, "%,.2f", balance) : null;
}

邊界測試

// 軟依賴只用 compileOnly:測試 classpath 沒有它們,反射行為等同於「伺服器沒裝」
assertDoesNotThrow(() -> { type.getDeclaredFields(); type.getDeclaredMethods(); });

規則

  • isPluginEnabled 先於任何會載入第三方型別的程式碼;Bridge 是唯一 import 第三方 package 的類別
  • Listener 的任何成員簽名(含 lambda 捕獲)不得出現軟依賴型別,否則 registerEvents 失敗、監聽器靜默失效
  • Economy.format() 常含舊式色碼:轉成 Component 再當 MiniMessage placeholder,不要拼字串
  • 每次操作重新向 ServicesManager 取 Economy,不快取提供端
  • 接 LinkageError,降級並只警告一次;onDisable 取消註冊 expansion 與服務

執行緒安全

  • Vault 提供端只在主執行緒呼叫
  • PlaceholderAPI onRequest 可能在任何執行緒:只讀由主執行緒發佈的不可變 volatile 快照
  • packetevents listener 在 Netty 執行緒:只讀快照、不呼叫 Bukkit API