Go to file
2025-12-07 06:54:05 +09:00
gradle init: 1.0 2025-11-28 06:00:55 +09:00
permits-lib@90bad7f37c chore: ignore追加 2025-12-07 04:04:44 +09:00
src/main/kotlin/net/hareworks/kommand-lib feat: greedyString 2025-12-07 06:54:05 +09:00
.envrc init: 1.0 2025-11-28 06:00:55 +09:00
.gitattributes init: 1.0 2025-11-28 06:00:55 +09:00
.gitignore chore: ignore追加 2025-12-07 04:04:44 +09:00
.gitmodules chore: submoduleのブランチが指定されていなかった問題を修正 2025-12-04 07:18:31 +09:00
build.gradle.kts chore: 未使用の変数を削除 2025-12-04 12:38:05 +09:00
flake.lock init: 1.0 2025-11-28 06:00:55 +09:00
flake.nix 1.2 2025-11-29 04:20:26 +09:00
gradle.properties 1.2 2025-11-29 04:20:26 +09:00
gradlew init: 1.0 2025-11-28 06:00:55 +09:00
gradlew.bat init: 1.0 2025-11-28 06:00:55 +09:00
MIGRATION_GUIDE.md docs: マイグレーションガイド 2025-12-07 06:29:11 +09:00
README.md docs: マイグレーションガイド 2025-12-07 06:29:11 +09:00
settings.gradle.kts 1.2 2025-11-29 04:20:26 +09:00

Kommand Lib

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

特徴

  • Kotlin DSL で command { literal { argument { ... } } } のようにネストを表現
  • 型付き引数 (string, integer, float, player, selector, coordinates など) と検証ロジックを組み込み
  • 1 つの定義から実行とタブ補完の両方を生成
  • パーミッションや条件をノード単位で宣言し、子ノードへ自動伝播
  • suggests {} で引数ごとの補完候補を柔軟に制御
  • Brigadier (Paper 1.21 Lifecycle API) 対応により、クライアント側で <speed> <count> のような構文ヒントや、数値範囲の検証エラー(赤文字)が表示されます
  • permits-lib との連携により、コマンドツリーから Bukkit パーミッションを自動生成し、compileOnly 依存として参照可能

バージョン情報

現在のバージョン: 1.1 (Brigadier ネイティブ対応)

🔄 旧バージョンからの移行

旧バージョン (Brigadier 対応前) から移行する場合は、マイグレーションガイド を参照してください。

主な変更点:

  • coordinates() の返り値が Coordinates3 から io.papermc.paper.math.Position に変更
  • position.toLocation(world)Location に変換する方式に変更
  • Player/Entity セレクターの内部処理が改善され、より安定した動作を実現

依存関係

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")
    // ../permits-lib を includeBuild しているので module 参照でOK
    compileOnly("net.hareworks.hcu:permits-lib:1.0")
}

使い方

  1. プラグインの onEnable などで kommand(plugin) { ... } DSL を呼び出します。
  2. command("root", "alias") { ... } でコマンドを宣言し、literalstring/integer 引数を追加します。
  3. executes { ... } は必ず対象のノード (literal, player, integer など) の中にネストします。これにより、そのノードまでに宣言した引数を argument<String>("player")argument<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 = argument("target")
                                val amount = argument<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: List<Player> = argument("targets")
                                val speed = argument<Double>("value")
                                targets.forEach { it.walkSpeed = speed.toFloat() / 5.0f }
                                sender.sendMessage("Updated ${targets.size} players")
                            }
                        }
                    }
                }

                literal("setspawn") {
                    coordinates("point") { // "~ ~1 ~-2" のような入力を受け付ける
                        executes {
                            val player = sender as? Player ?: return@executes
                            val position = argument<io.papermc.paper.math.Position>("point")
                            val location = position.toLocation(player.world)
                            player.world.setSpawnLocation(location)
                            sender.sendMessage("Spawn set to ${location.x}, ${location.y}, ${location.z}")
                        }
                    }
                }

                literal("inspect") {
                    selector("entities") {
                        executes {
                            val entities: List<Entity> = argument("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 に記憶されます。取得時は argument<String>("name")argument<Int>("value") を呼び出してください。
  • float("speed")player("target")/players("targets")/selector("entities") は Minecraft の標準セレクター (@p, @a, @s など) やプレイヤー名を型付きで扱えます。実行時は argument<Double>("speed")argument<Player>("target")argument<List<Player>>("targets") のように取得できます。
  • suggests { prefix -> ... } を指定すると、タブ補完時に任意の候補リストを返せます。
  • coordinates("pos")x y z をまとめて 1 つの引数として受け取り、argument<io.papermc.paper.math.Position>("pos") で取得できます。position.toLocation(world)Location に変換できます (~ を使用した相対座標に対応)。
  • command や各ノードの condition { sender -> ... } で実行条件 (例: コンソール禁止) を追加できます。
  • ルートレベルで executes { ... } を指定すると、引数なしで /eco を実行した場合に呼び出されます。

自動パーミッション生成 (permits-lib)

permits-libcompileOnly で参照している場合、DSL から Bukkit パーミッションツリーを自動生成できます。

commands = kommand(this) {
    permissions {
        namespace = "hareworks"
        rootSegment = "command"
        defaultDescription { ctx ->
            "Allows /${ctx.commandName} (${ctx.path.joinToString(" ")})"
        }
    }

    command("eco") {
        permission {
            description = "Allows /eco"
        }

        literal("give") {
            permission {
                description = "Allows /eco give"
            }
        }
    }
}
  • permissions { ... } で名前空間やルートセグメント (rootSegment = "command") を定義すると、hareworks.command.eco, hareworks.command.eco.give のような ID が自動生成され、permits-libMutationSession を使って Bukkit に適用されます。
  • literalcommand ノードはデフォルトで Bukkit パーミッションとして登録されます。stringinteger などの引数ノードは、requires("permission.id") もしくは permission { id = ... } を記述した場合のみ登録され、それ以外は構造上のノードに留まります。これにより /money give <player> <amount> では ...money.give だけ付与すれば実行できます。
  • 各ブロック内の permission { ... } で説明文・デフォルト値 (PermissionDefault.TRUE/FALSE/OP/NOT_OP)・ワイルドカード・パスを細かく制御できます。requires(...) を使うと DSL での検証と permits 側の登録が同じ ID になります。
  • 引数ノードを明示的に登録したい場合は permission { id = "example.money.give.amount" }requires("example.money.give.amount") を設定してください。逆にリテラルでも不要なら skipPermission() で除外できます。
  • requires("custom.id") を指定した場合も、同じ ID が DSL の実行と permits-lib への登録の両方で利用されます (名前空間外の ID は登録対象外になります)。
  • KommandLib のライフサイクルに合わせて MutationSession が適用/解除されるため、プラグインの有効化・無効化に伴い Bukkit のパーミッションリストも最新状態に保たれます。

組み込み引数の一覧

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> エンティティセレクター (@e など)
coordinates("pos") io.papermc.paper.math.Position ~ 相対座標を含む 3 軸をまとめて扱う

Positioncoordinates("pos") { ... } 直後のコンテキストで argument<io.papermc.paper.math.Position>("pos") として取得でき、position.toLocation(world)Location に変換できます。

クライアント側構文ヒント (Brigadier)

Paper 1.21 以降の環境では、LifecycleEventManager を通じてコマンドが登録されるため、クライアントにコマンドの構造が送信されます。これにより以下のメリットがあります:

  • 構文の可視化: 入力中に <speed> <amount> のような引数名が表示されます。
  • クライアント側検証: integer("val", min=1, max=10) などの範囲指定がクライアント側でも判定され、範囲外の値を入力すると赤字になります。
  • 互換性: 内部的には Brigadier のノードに変換されますが、実際のコマンド実行は kommand-lib の既存ロジック(KommandContext)を使用するため、古いコードの修正は不要です。

ビルドとテスト

./gradlew build

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

ドキュメント

ライセンス

このプロジェクトは MIT ライセンスの下で公開されています。