Minecraft 1.20.x forgeでMOD開発 カスタムコマンドの実装

こちらの続きになります。

https://qiita.com/simoyama2323/items/1f0e77fc4482e2a9b2d2

## 今回やりたいこと
MOD内で使用している言語切り替えるためのカスタムコマンドを実装していきます。
ローカライズのためのリソースの読み込みとかは完全にjavaの領分なので、今回はカスタムコマンドの実装のみ書いていきます。

## コード
イベントハンドラは```RegisterCommandsEvent```を使います。
```java
    @SubscribeEvent
    public void onRegisterLocalizeCommand(RegisterCommandsEvent event)
```

### コマンドの組み立て
まず、```lang```というコマンドを実装します。

```java
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("lang").executes( context -> {
        // コマンド実行者にメッセージ送信
        context.getSource().getPlayerOrException().sendSystemMessage(Component.nullToEmpty("test message"));
        return Command.SINGLE_SUCCESS;
    });
```

以上が基本形です。
```Commands.literal()```に作成したいコマンドの文字列。
```executes()```にデリゲートでコマンドの処理を差し込む感じです。
返り値には```Command.SINGLE_SUCCESS```というパッと見成功パターンの整数型しか用意されていません。
しかもこの整数をどこかで使っている感じもないので、Commandインターフェースを拡張してあげて以下のように失敗パターンの整数も用意してあげると可読性が増すかもしれません。

```java
// Command<S>を拡張するインターフェース
public interface ICommandEx extends Command {
  int SINGLE_FAILURE = 0;
}
```

```java
builder = Commands.literal("lang").executes( context -> {
        // コマンド実行者にメッセージ送信
        context.getSource().getPlayerOrException().sendSystemMessage(Component.nullToEmpty("test message"));

        // 例外が発生する可能性のある処理
        if(){
            return ICommandEx.SINGLE_FAILURE;
        }

        return ICommandEx.SINGLE_SUCCESS;
    });
```

実際こんな感じで返す値を0にしても動作上は何も変わりませんでした。
この値で正否判定を行っているわけではなさそうです。
可読性のためだけに書いているだけなので、今回は普通に```Command.SINGLE_SUCCESS;```で統一します。

### コマンドの分岐
```then()```というメソッドが定義されているのでここでコマンドの分岐を書いていきます。

```java
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("lang")
        .then(Commands.literal("en")
                .executes(context -> {
                        // 言語切替処理省略
                        context.getSource().getPlayerOrException().sendSystemMessage(Component.nullToEmpty("[Dev]:MOD language is now English."));
                        return Command.SINGLE_SUCCESS;
                }))
        .then(Commands.literal("ja")
                .executes(context -> {
                        // 言語切替処理省略
                        context.getSource().getPlayerOrException().sendSystemMessage(Component.nullToEmpty("[Dev]:MODの言語が日本語になりました。"));
                        return Command.SINGLE_SUCCESS;
                }));
```

そのままですね。
基本形からthenでコマンドを派生していけるといった感じです。

### コマンドの登録
最後にイベントハンドラからコマンドを登録して完了です。
```java
@SubscribeEvent
public void onRegisterLocalizeCommand(RegisterCommandsEvent event) {

        LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("lang")
                .then(Commands.literal("en")
                        .executes(context -> {
                        // 処理省略
                        context.getSource().getPlayerOrException().sendSystemMessage(Component.nullToEmpty("[Dev]:MOD language is now English."));
                        return Command.SINGLE_SUCCESS;
                        }))
                .then(Commands.literal("ja")
                        .executes(context -> {
                        // 処理省略
                        context.getSource().getPlayerOrException().sendSystemMessage(Component.nullToEmpty("[Dev]:MODの言語が日本語になりました。"));
                        return Command.SINGLE_SUCCESS;
                        }));
        // コマンドの登録
        event.getDispatcher().register(builder);
}
```

## 動作確認
![スクリーンショット 2023-06-29 12.54.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855584/d256df4a-d5a4-db11-a8a4-872689d8c387.png)

しっかりTAB補完も効いてコマンドも実行できました。

## 感想
サーバーサイドのみの実装だとクライアントのUIにあまり干渉できないので、アクションを起こさせたいときなどはこういったカスタムコマンドで操作させることになると思います。


## 参考
https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe45_commands