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

131 lines
6.0 KiB
Markdown

# 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 を利用しています。
```kotlin
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") { ... }` でコマンドを宣言し、`literal` や `string`/`integer` 引数を追加します。
3. `executes { ... }` 内で `string("player")``int("amount")` を使ってパース済みの値を取得します。
### サンプル: 経済コマンド
```kotlin
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 軸をまとめて扱う |
`Coordinates3``coordinates("pos") { ... }` 直後のコンテキストで `coordinates("pos")` もしくは `location("pos", baseLocation)` として取得できます。
## ビルドとテスト
```bash
./gradlew build
```
ShadowJar タスクが実行され、`build/libs` に出力されます。Paper サーバーに配置して動作確認してください。