8.6 KiB
8.6 KiB
Kommand Lib
Paper/Bukkit サーバー向けのコマンド定義を DSL で記述するためのライブラリです。ルート定義から引数の型、補完、パーミッション伝播までを宣言的に表現でき、手続き的な CommandExecutor 実装を大きく簡略化します。
特徴
- Kotlin DSL で
command { literal { argument { ... } } }のようにネストを表現 - 型付き引数 (
string,integer,float,player,selector,coordinatesなど) と検証ロジックを組み込み - 1 つの定義から実行とタブ補完の両方を生成
- パーミッションや条件をノード単位で宣言し、子ノードへ自動伝播
suggests {}で引数ごとの補完候補を柔軟に制御permits-libとの連携により、コマンドツリーから Bukkit パーミッションを自動生成し、compileOnly依存として参照可能
依存関係
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")
}
使い方
- プラグインの
onEnableなどでkommand(plugin) { ... }DSL を呼び出します。 command("root", "alias") { ... }でコマンドを宣言し、literalやstring/integer引数を追加します。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を実行した場合に呼び出されます。
自動パーミッション生成 (permits-lib)
permits-lib を compileOnly で参照している場合、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-libのMutationSessionを使って Bukkit に適用されます。literalやcommandノードはデフォルトで Bukkit パーミッションとして登録されます。stringやintegerなどの引数ノードは、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> |
Bukkit の Bukkit.selectEntities をそのまま利用 |
coordinates("pos") |
Coordinates3 |
~ 相対座標を含む 3 軸をまとめて扱う |
Coordinates3 は coordinates("pos") { ... } 直後のコンテキストで coordinates("pos") もしくは location("pos", baseLocation) として取得できます。
ビルドとテスト
./gradlew build
ShadowJar タスクが実行され、build/libs に出力されます。Paper サーバーに配置して動作確認してください。