kommand-lib/MIGRATION_GUIDE.md
2025-12-07 06:03:18 +09:00

11 KiB

kommand-lib マイグレーションガイド

このガイドでは、旧バージョンの kommand-lib から最新の Brigadier ネイティブ対応バージョンへの移行方法を説明します。


📋 目次

  1. 変更の概要
  2. 破壊的変更
  3. マイグレーション手順
  4. コード例の比較
  5. トラブルシューティング

変更の概要

🎯 主な変更点

  1. Brigadier ネイティブ対応

    • Paper 1.21 の Lifecycle API を使用したコマンド登録
    • クライアント側での構文ヒントと検証
    • より正確な型システム
  2. 引数の型変更

    • coordinates() の返り値が Coordinates3 から io.papermc.paper.math.Position に変更
    • Player/Entity セレクターの内部処理が改善
  3. 自動登録

    • kommand() 関数の呼び出しで自動的にコマンドが登録されるように変更

破壊的変更

🔴 1. coordinates() 引数の型変更

旧バージョン (動作しません)

coordinates("point") {
    executes {
        val coords = argument<Coordinates3>("point")
        val target = coords.resolve(player.location)
        // Coordinates3 型は存在しません
    }
}

新バージョン (正しい方法)

coordinates("point") {
    executes {
        val position = argument<io.papermc.paper.math.Position>("point")
        val location = position.toLocation(player.world)
        // Position 型を使用します
    }
}

理由: Paper の Brigadier API は io.papermc.paper.math.Position を返します。これは Paper の公式 API に準拠しています。


🟡 2. Player/Entity セレクターの内部処理

ユーザーコードに変更は不要ですが、内部的に以下の変更が行われました:

内部処理の改善

// 旧: 直接キャスト (実行時エラーの原因)
val player = context.getArgument("player", Player::class.java)

// 新: Resolver を使用して解決 (正しい方法)
val resolver = context.getArgument("player", PlayerSelectorArgumentResolver::class.java)
val player = resolver.resolve(source).firstOrNull()

影響: Player/Entity 引数がより安定して動作するようになりました。


マイグレーション手順

ステップ 1: 依存関係の確認

build.gradle.kts で Paper API のバージョンを確認してください:

dependencies {
    compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
    // 1.21 以降が必要です
}

ステップ 2: import 文の追加

coordinates() を使用している場合、import を追加してください:

import io.papermc.paper.math.Position

ステップ 3: コードの更新

以下のパターンを検索して置換してください:

パターン 1: coordinates の型指定

検索:

argument<Coordinates3>("

置換:

argument<io.papermc.paper.math.Position>("

または、import を追加して:

argument<Position>("

パターン 2: coordinates の解決方法

検索:

val coords = argument<Coordinates3>("pos")
val location = coords.resolve(baseLocation)

置換:

val position = argument<Position>("pos")
val location = position.toLocation(world)

ステップ 4: ビルドとテスト

./gradlew build

エラーがないことを確認してから、サーバーでテストしてください。


コード例の比較

例 1: スポーン地点の設定

旧バージョン

literal("setspawn") {
    coordinates("point") {
        executes {
            val base = (sender as? Player)?.location ?: return@executes
            val coords = argument<Coordinates3>("point")
            val target = coords.resolve(base)
            plugin.server.worlds.first().setSpawnLocation(target)
            sender.sendMessage("Spawn set to ${target.x}, ${target.y}, ${target.z}")
        }
    }
}

新バージョン

literal("setspawn") {
    coordinates("point") {
        executes {
            val player = sender as? Player ?: return@executes
            val position = argument<Position>("point")
            val location = position.toLocation(player.world)
            player.world.setSpawnLocation(location)
            sender.sendMessage("Spawn set to ${location.x}, ${location.y}, ${location.z}")
        }
    }
}

変更点:

  • Coordinates3Position
  • coords.resolve(base)position.toLocation(world)
  • より明確な変数名

例 2: テレポートコマンド

旧バージョン

literal("tp") {
    coordinates("destination") {
        executes {
            val player = sender as? Player ?: return@executes
            val coords = argument<Coordinates3>("destination")
            val target = coords.resolve(player.location)
            player.teleport(target)
        }
    }
}

新バージョン

literal("tp") {
    coordinates("destination") {
        executes {
            val player = sender as? Player ?: return@executes
            val position = argument<Position>("destination")
            val location = position.toLocation(player.world)
            player.teleport(location)
        }
    }
}

例 3: 範囲指定コマンド

旧バージョン

literal("fill") {
    coordinates("pos1") {
        coordinates("pos2") {
            executes {
                val player = sender as? Player ?: return@executes
                val base = player.location
                val pos1 = argument<Coordinates3>("pos1").resolve(base)
                val pos2 = argument<Coordinates3>("pos2").resolve(base)
                // 処理...
            }
        }
    }
}

新バージョン

literal("fill") {
    coordinates("pos1") {
        coordinates("pos2") {
            executes {
                val player = sender as? Player ?: return@executes
                val world = player.world
                val pos1 = argument<Position>("pos1").toLocation(world)
                val pos2 = argument<Position>("pos2").toLocation(world)
                // 処理...
            }
        }
    }
}

Position API リファレンス

Position のメソッド

interface Position {
    fun x(): Double
    fun y(): Double
    fun z(): Double
    
    fun blockX(): Int
    fun blockY(): Int
    fun blockZ(): Int
    
    fun toLocation(world: World): Location
}

使用例

val position = argument<Position>("pos")

// 座標の取得
val x = position.x()
val y = position.y()
val z = position.z()

// ブロック座標の取得
val blockX = position.blockX()
val blockY = position.blockY()
val blockZ = position.blockZ()

// Location への変換
val location = position.toLocation(player.world)

トラブルシューティング

問題 1: Coordinates3 が見つからない

エラー:

Unresolved reference: Coordinates3

解決方法: Coordinates3 は存在しません。io.papermc.paper.math.Position を使用してください。

// ❌ 間違い
argument<Coordinates3>("pos")

// ✅ 正しい
argument<Position>("pos")

問題 2: resolve() メソッドが見つからない

エラー:

Unresolved reference: resolve

解決方法: Position には resolve() メソッドはありません。toLocation(world) を使用してください。

// ❌ 間違い
val location = position.resolve(baseLocation)

// ✅ 正しい
val location = position.toLocation(world)

問題 3: Player セレクターが動作しない

症状: Player 引数を使用するとエラーが発生する

解決方法: 最新バージョンに更新してください。内部的に ArgumentResolver が自動的に処理します。

// これは自動的に動作します
val player = argument<Player>("target")
val players = argument<List<Player>>("targets")

問題 4: コマンドが登録されない

症状: /help にコマンドが表示されない

解決方法: 最新バージョンでは kommand() 関数の呼び出しで自動的に登録されます。

class MyPlugin : JavaPlugin() {
    private lateinit var commands: KommandLib
    
    override fun onEnable() {
        // これだけで自動的に登録されます
        commands = kommand(this) {
            command("mycommand") {
                // ...
            }
        }
    }
}

よくある質問 (FAQ)

Q1: 相対座標 (~) は引き続き使えますか?

A: はい、引き続き使えます。Position は相対座標を完全にサポートしています。

// "~ ~1 ~-2" のような入力が可能
val position = argument<Position>("pos")

Q2: 旧バージョンとの互換性はありますか?

A: coordinates() 引数の型が変更されているため、互換性はありません。マイグレーションが必要です。

ただし、player(), players(), selector() などの他の引数は互換性があります。


Q3: マイグレーションにどのくらい時間がかかりますか?

A: プロジェクトの規模によりますが、通常は以下の通りです:

  • 小規模 (1-5 コマンド): 5-10 分
  • 中規模 (5-20 コマンド): 15-30 分
  • 大規模 (20+ コマンド): 30-60 分

主な作業は検索と置換なので、比較的短時間で完了します。


Q4: 段階的な移行は可能ですか?

A: いいえ、coordinates() を使用している場合は一度にすべて移行する必要があります。

ただし、coordinates() を使用していないコマンドは変更不要です。


サポート

問題が発生した場合は、以下のドキュメントを参照してください:


まとめ

マイグレーションチェックリスト

  • Paper API 1.21 以降を使用していることを確認
  • Coordinates3Position に置換
  • coords.resolve()position.toLocation() に置換
  • 必要な import を追加
  • ビルドが成功することを確認
  • サーバーでテストして動作を確認

🎉 完了!

マイグレーションが完了すると、以下のメリットが得られます:

  • クライアント側での構文ヒント
  • より正確な型チェック
  • Paper の公式 API に準拠
  • より安定した動作

ご不明な点がございましたら、お気軽にお問い合わせください。