kommand-lib/README.md
2025-11-28 06:00:55 +09:00

6.0 KiB

Kommand Lib

Paper/Bukkit サーバー向けのコマンド定義を DSL で記述するためのライブラリです。ルート定義から引数の型、補完、パーミッション伝播までを宣言的に表現でき、手続き的な CommandExecutor 実装を大きく簡略化します。

特徴

  • Kotlin DSL で command { literal { argument { ... } } } のようにネストを表現
  • 型付き引数 (string, integer, float, player, selector, coordinates など) と検証ロジックを組み込み
  • 1 つの定義から実行とタブ補完の両方を生成
  • パーミッションや条件をノード単位で宣言し、子ノードへ自動伝播
  • suggests {} で引数ごとの補完候補を柔軟に制御

依存関係

build.gradle.kts では Paper API と Kotlin 標準ライブラリのみを compileOnly に追加しています。Paper 1.21.10 対応の API を利用しています。

plugins {
    kotlin("jvm") version "2.2.21"
    id("de.eldoria.plugin-yml.paper") version "0.8.0"
    id("com.gradleup.shadow") version "9.2.2"
}

dependencies {
    compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
    compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
}

使い方

  1. プラグインの onEnable などで kommand(plugin) { ... } DSL を呼び出します。
  2. command("root", "alias") { ... } でコマンドを宣言し、literalstring/integer 引数を追加します。
  3. executes { ... } 内で string("player")int("amount") を使ってパース済みの値を取得します。

サンプル: 経済コマンド

class EconomyPlugin : JavaPlugin() {
    private lateinit var commands: KommandLib

    override fun onEnable() {
        commands = kommand(this) {
            command("eco", "economy") {
                description = "Economy management"
                permission = "example.eco"

                literal("give") {
                    player("target") // プレイヤー名 or セレクター (@p 等)
                    integer("amount", min = 1)

                    executes {
                        val target = player("target")
                        val amount = int("amount")
                        sender.sendMessage("Giving $amount to ${target.name}")
                    }
                }

                literal("speed") {
                    players("targets") // @a, プレイヤー名, などをまとめて取得
                    float("value", min = 0.1, max = 5.0)

                    executes {
                        val targets = players("targets")
                        val speed = float("value")
                        targets.forEach { it.walkSpeed = speed.toFloat() / 5.0f }
                        sender.sendMessage("Updated ${targets.size} players")
                    }
                }

                literal("setspawn") {
                    coordinates("point") // "~ ~1 ~-2" のような入力を受け付ける

                    executes {
                        val base = (sender as? Player)?.location ?: return@executes
                        val target = location("point", base)
                        plugin.server.worlds.first().setSpawnLocation(target)
                        sender.sendMessage("Spawn set to ${target.x}, ${target.y}, ${target.z}")
                    }
                }

                literal("inspect") {
                    selector("entities")

                    executes {
                        val entities = selector("entities")
                        sender.sendMessage("Selector resolved ${entities.size} entities")
                    }
                }
            }
        }
    }

    override fun onDisable() {
        commands.unregister()
    }
}

DSL 構文のポイント

  • literal("sub") { ... } は固定語句を表すノードです。requires("permission.node") でその枝のみにパーミッションを設定できます。
  • string("name")integer("value", min = 0) は値をパースし、成功すると KommandContext に記憶されます。
  • float("speed") は倍精度を、player("target")/players("targets")/selector("entities") は Minecraft の標準セレクター (@p, @a, @s など) やプレイヤー名を型付きで扱えます。
  • suggests { prefix -> ... } を指定すると、タブ補完時に任意の候補リストを返せます。
  • coordinates("pos")x y z をまとめて 1 つの引数として受け取り、location("pos", player.location) で現在位置を基準に解決できます (~ を使用した相対座標に対応)。
  • command や各ノードの condition { sender -> ... } で実行条件 (例: コンソール禁止) を追加できます。
  • ルートレベルで executes { ... } を指定すると、引数なしで /eco を実行した場合に呼び出されます。

組み込み引数の一覧

DSL 返り値 補足
string("name") String 任意のトークン。suggests {} で補完可
integer("value", min, max) Int 範囲チェック付き
float("speed", min, max) Double 小数/指数表記に対応
player("target", allowSelectors = true) Player @p などのセレクターまたはプレイヤー名を 1 人に解決
players("targets") List<Player> @a/@r など複数指定、プレイヤー名入力も可
selector("entities") List<Entity> Bukkit の Bukkit.selectEntities をそのまま利用
coordinates("pos") Coordinates3 ~ 相対座標を含む 3 軸をまとめて扱う

Coordinates3coordinates("pos") { ... } 直後のコンテキストで coordinates("pos") もしくは location("pos", baseLocation) として取得できます。

ビルドとテスト

./gradlew build

ShadowJar タスクが実行され、build/libs に出力されます。Paper サーバーに配置して動作確認してください。