agentsclimarketplace

Generate placeholder expansion

Skill MrPippi/MJP-Claude-Skills/.cursor/skills/generate-placeholder-expansion

Minecraft Java Plugin Claude Skills

Install
npx -y skills add MrPippi/MJP-Claude-Skills --skill generate-placeholder-expansion

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

為 Bukkit/Paper 插件產生 PlaceholderAPI Expansion 類別,依使用者提供的 placeholder 清單產生對應的處理邏輯,含 pom.xml 依賴設定、plugin.yml 軟依賴宣告、PlaceholderAPI 是否安裝的檢查,以及測試方法。當使用者說「PlaceholderAPI」、「PAPI 擴充」、「幫我寫 placeholder」、「%myplugin_xxx%」時自動應用。

SKILL.md

7.7 KB, as published. Nobody here has run it

Generate Placeholder Expansion Skill

目標

產生符合 PlaceholderAPI 規範的 PlaceholderExpansion 子類別,依使用者提供的 placeholder 清單,自動產生 onRequest() 方法中的分派邏輯,並附上完整的整合說明。


使用流程

  1. 詢問基本資訊:插件名稱(用於 identifier)、placeholder 清單(名稱 + 說明)
  2. 產生 Expansion 類別:依 placeholder 清單產生 switch 分派
  3. 說明 plugin.yml 設定softdepend 宣告
  4. 說明在主類中的註冊方式
  5. 提供測試指令說明

輸入參數說明

參數範例說明
plugin_nameMyPlugin用於 Expansion 類別命名
identifiermypluginPlaceholder 前綴,即 %myplugin_xxx% 中的 myplugin
packagecom.example.myplugin.hooks類別放置的套件
placeholdersbalance, rank, playtimePlaceholder 名稱清單

pom.xml 依賴

<!-- PlaceholderAPI(provided,由伺服器提供) -->
<dependency>
    <groupId>me.clip</groupId>
    <artifactId>placeholderapi</artifactId>
    <version>2.11.6</version>
    <scope>provided</scope>
</dependency>

需要加入 PlaceholderAPI 的 Maven repository:

<repository>
    <id>placeholderapi</id>
    <url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>

plugin.yml 設定

# 硬依賴(若 PAPI 不存在插件不啟動)
# depend: [PlaceholderAPI]

# 軟依賴(若 PAPI 不存在插件仍啟動,功能降級)
softdepend: [PlaceholderAPI]

代碼範本

MyPluginExpansion.java

package com.example.myplugin.hooks;

import com.example.myplugin.MyPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * 提供以下 placeholders:
 *
 * %myplugin_balance%      - 玩家目前餘額
 * %myplugin_rank%         - 玩家目前排名
 * %myplugin_playtime%     - 玩家累計遊玩時間(小時)
 * %myplugin_status%       - 玩家目前狀態(online/offline)
 */
public class MyPluginExpansion extends PlaceholderExpansion {

    private final MyPlugin plugin;

    public MyPluginExpansion(MyPlugin plugin) {
        this.plugin = plugin;
    }

    // ---- 必要元數據 ----

    @Override
    public @NotNull String getIdentifier() {
        return "myplugin";
    }

    @Override
    public @NotNull String getAuthor() {
        return String.join(", ", plugin.getDescription().getAuthors());
    }

    @Override
    public @NotNull String getVersion() {
        return plugin.getDescription().getVersion();
    }

    /**
     * 回傳 false 表示不需要 PlaceholderAPI 插件先載入(自訂 Expansion 通常回傳 false)。
     * 若依賴 PlaceholderAPI 的其他 API,回傳 true。
     */
    @Override
    public boolean canRegister() {
        return true;
    }

    /**
     * 回傳 false 表示 Expansion 不會隨 /papi reload 被卸載,保持持久性。
     */
    @Override
    public boolean persist() {
        return true;
    }

    // ---- Placeholder 處理 ----

    @Override
    public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) {
        if (player == null) return "";

        return switch (params.toLowerCase()) {
            case "balance"   -> getBalance(player);
            case "rank"      -> getRank(player);
            case "playtime"  -> getPlaytime(player);
            case "status"    -> player.isOnline() ? "線上" : "離線";
            default          -> null; // null 表示未知的 placeholder
        };
    }

    // ---- 各 Placeholder 取值邏輯 ----

    private String getBalance(OfflinePlayer player) {
        // 從你的資料來源取得餘額
        double balance = plugin.getDatabaseManager()
            .getBalance(player.getUniqueId().toString())
            .join(); // 注意:join() 會阻塞,PAPI 建議在此同步取值

        return String.format("%.2f", balance);
    }

    private String getRank(OfflinePlayer player) {
        // 範例:從記憶體快取取得排名
        int rank = plugin.getRankManager().getRank(player.getUniqueId());
        return rank > 0 ? "#" + rank : "未排名";
    }

    private String getPlaytime(OfflinePlayer player) {
        // 取得玩家遊玩時間(Minecraft 以 ticks 計算)
        // 1 小時 = 3600 秒 = 72000 ticks
        long ticks = player.getStatistic(org.bukkit.Statistic.PLAY_ONE_MINUTE);
        long hours = ticks / 72000;
        return hours + " 小時";
    }
}

帶動態參數的 Placeholder(進階)

支援 %myplugin_top_1%%myplugin_top_2% 這類帶數字參數的格式:

@Override
public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) {
    // 動態參數:%myplugin_top_1%, %myplugin_top_2%, ...
    if (params.startsWith("top_")) {
        String rankStr = params.substring(4);
        try {
            int rank = Integer.parseInt(rankStr);
            return plugin.getLeaderboard().getPlayerAtRank(rank);
        } catch (NumberFormatException e) {
            return "無效排名";
        }
    }

    return switch (params.toLowerCase()) {
        case "balance" -> getBalance(player);
        default        -> null;
    };
}

在主類中註冊

@Override
public void onEnable() {
    // ...初始化其他元件...

    // 檢查並註冊 PlaceholderAPI Expansion
    if (getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
        new MyPluginExpansion(this).register();
        getLogger().info("PlaceholderAPI 整合已啟用。");
    } else {
        getLogger().info("未偵測到 PlaceholderAPI,placeholder 功能停用。");
    }
}

所有可用 Placeholder 清單

使用 /papi list 可查看已安裝的 Expansion,使用 /papi parse <玩家名> %myplugin_balance% 可測試 placeholder 輸出。

Placeholder說明範例輸出
%myplugin_balance%玩家目前餘額1234.56
%myplugin_rank%玩家排名#42
%myplugin_playtime%累計遊玩時數12 小時
%myplugin_status%玩家線上狀態線上 / 離線

測試方式

# 在伺服器控制台或遊戲內(需要權限)
/papi parse <玩家名稱> %myplugin_balance%
/papi parse <玩家名稱> %myplugin_rank%

# 查看所有已載入的 Expansion
/papi list

# 重新載入 Expansion(若 persist() 回傳 false)
/papi reload myplugin

常見錯誤與修正

錯誤原因修正
Placeholder 顯示原始文字 %myplugin_xxx%Expansion 未成功註冊確認 register() 有被呼叫、PAPI 已安裝
onRequest() 回傳 nullparams 名稱不符使用 /papi parse 確認 params 實際值
伺服器卡頓join() 阻塞主執行緒改用記憶體快取(預先載入玩家資料),避免在 onRequest 中同步查詢 DB
ClassNotFoundException PlaceholderExpansion依賴未設定為 provided確認 pom.xml 中 <scope>provided</scope>
Expansion 被 /papi reload 卸載persist() 回傳 true設定 persist() 回傳 true 以保持持久性

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.