【Kotlin+Spigot】2.2 コマンドを新しく作る

## 0. 初めに
`build.gradle` や `plugin.yml` の設定は終わらせた前提で進めます。
[**プロジェクトを作成する**](https://qiita.com/sya-ri/items/1ecccdba38fd3260f5ff) を参考にメインクラスを作るまで終わらせておいてください。

## 1. plugin.yml にコマンドを登録する
プロジェクトを作った段階で以下の5つは設定し終わっていると思います。

```plugin.yml
name: "NewCommand"
version: "1.0"
api-version: "1.15"
main: me.syari.p_2_2_new_command.Main
author: sya_ri
```

`author` 以降に コマンド登録を書いていきます。
今回は、`/hello` というコマンドを登録してみましょう。

```plugin.yml
name: "NewCommand"
version: "1.0"
api-version: "1.15"
main: me.syari.p_2_2_new_command.Main
author: sya_ri
commands:
  hello: # コマンドの名前
    usage: "/<command>" # コマンドの短い説明
    description: "挨拶します" # コマンドの説明
    aliases: # コマンドの別名
      - hi
```

**`commands`** というセクションの中にコマンドを書いていきます。
残りは全てコメントに書いてある通りです。補足的に説明をすると、
**`aliases`** というのを登録しておくとそのコマンドでも同じ処理をしてくれます。
今回であれば、`/hello` と打っても、`/hi` と打っても処理は同じです。
また、設定項目の省略も可能です。`permission` や `permission-message` という項目もありますが省略しています。
`aliases` は無ければ省略しましょう。`usage` や `description` は書いておいた方がいいでしょう。

## 2. コマンドの処理を書く
新規ファイルで `HelloCommand` というクラスを作ってください。
`CommandExecutor` というのを継承してください。
これを継承することで、コマンドの処理を書けるようになります。

```HelloCommand.kt
class HelloCommand: CommandExecutor {

}
```

そうするとこのように赤くなると思います。`Implement members` をクリックしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/d2ce85e3-c013-7425-3715-52c0b411cf84.png)
`onCommand` が選択されていると思うので、`OK` をクリックしましょう。
以下のように自動でコード生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/d74b3f8f-fd2a-2806-4f97-28918261a48c.png)
`onCommand` というのがコマンドの処理を書く関数になります。
この関数の引数について説明します。
**`sender: CommandSender`** コマンドを実行したプレイヤーやコンソール が入っています。
**`command: Command`** ほとんど使いません。`plugin.yml` で設定したものを取得出来ます。
**`label: String`** コマンドラベルです(`/ラベル`)。 今回であれば `hello` か `hi` になります。
**`args: Array<out String>`** コマンドの引数 が全て入っています(`/ラベル 引数0 引数1 引数2 ...`)。
また、戻り値は `Boolean` になっていますが、基本的に `true` を返しておけば大丈夫です。(異常終了だったら `false`)

では実際の処理を書きましょう。
とりあえず自分に挨拶を返してもらいましょう。

```kotlin
    override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        sender.sendMessage("Hello ${sender.name}")
        return true
    }
```

コマンドの処理は終わりです。

## 3. コマンドを登録する。
イベントの時同様に処理をしてもらう為には登録しなければなりません。
「`plugin.yml` でやったじゃないか！！」と思う人もいるかもしれませんが、あれで登録したのは
「**どのプラグインで処理をするか**」だけです。**どのクラスで処理をするか** は登録してません。
イベント同様、`onEnable` に書きます。

```kotlin
    override fun onEnable() {
        getCommand("hello")?.setExecutor(HelloCommand())
    }
```
**`getCommand("hello")`** でhelloコマンドを取得します。しかし、**コマンドが存在しない可能性**もありますね。
存在しないコマンドであれば **`null`** という値が入っています。
**`?.`** というのは存在するのであればそれ以降の処理をする。といった感覚です。
**`setExecutor(HelloCommand())`** で実行クラスを変更しています。

さあ、プラグインをサーバーで試してましょう。

コンソールから実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/63057bed-57f9-4d3c-e05e-34b5265a2f5e.png)

プレイヤーから実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/2ff149bf-c5a0-47b7-22f4-6d5544b5c23d.png)

こうなっていたら成功です。

## 4. コマンド登録できたか通知する
このままだと、もしもタイプミスしてしまった時にコマンドが登録出来なかったことに気付くのが難しくなります。
登録できたかどうかをコンソール出力してみましょう。
さて、コマンドが登録できない場合はどういった時でしょうか？
答えは、`getCommand` の戻り値が `null` であった時です。
であれば、`null` かどうかを比較して処理を分岐してあげればいいのです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/61472e21-0c6e-33d6-075c-4da37eb4a4b7.png)
こんな感じになります。実際にプログラムにしましょう。

```kotlin
    override fun onEnable() {
        val helloCommand = getCommand("hello")
        if(helloCommand != null){
            helloCommand.setExecutor(HelloCommand())
            logger.info("/hello を登録しました")
        } else {
            logger.severe("/hello を登録できませんでした")
        }
    }
```

`getCommand` の戻り値を `helloCommand` という変数で保存しています。
`helloCommand` が `null` でなければ 登録 & 成功メッセージ
それ以外(`helloCommand` が `null`)であれば 失敗メッセージ
**`if`** は条件分岐です。括弧の中は `Boolean` です。`if(true)` であれば実行されます。
**`else`** も条件分岐ですが、他・それ以外 みたいな意味です。直前の `if` が `false` であれば実行されます。

```kotlin
if(false){
    // 実行されない
} else {
    // 実行される
}
```
`if` `else` は基本構文なので覚えておきましょう。(`else if` もありますが、Kotlin では不要。次の記事で解説します。)

さて、サーバーでテストしてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/d5da775c-346d-6fa1-6b45-7a4567c94d14.png)
このようにメッセージがあれば成功です。

いつも通り関数化しましょう。

```Main.kt
class Main: JavaPlugin() {
    override fun onEnable() {
        registerCommand("hello", HelloCommand())
    }

    private fun registerCommand(label: String, executor: CommandExecutor){
        val command = getCommand(label)
        if(command != null){
            command.setExecutor(executor)
            logger.info("/$label を登録しました")
        } else {
            logger.severe("/$label を登録できませんでした")
        }
    }
}
```

今回は、コマンドラベルと処理クラスの2つあるので、引数は2つです。
それぞれ `label` と `executor` としています。
この関数は `Main` でしか使わないので `fun` の前に `private` と付けておきます。
サーバーでテストしてみて、同じようにメッセージが出たら成功です。
また、`registerCommand("hello", HelloCommand())` の `"hello"` を `"hell"` みたいに違うコマンドにして
「登録できませんでした」 というメッセージが出たら成功です。

これで、終了です。お疲れ様でした。

## 5. 【+α】 Kotlin っぽくかく
内容が難しいかもしれません。ここまでの内容で頭がパンパンの人は見ないでください。

<details><summary>**見てみる**</summary><div>
理解できなくても不安がらないでください。あくまで紹介程度です。
難しく考えず、「こう書けるんだ」程度に思ってください。

こう書けます。

```kotlin
    private fun registerCommand(label: String, executor: CommandExecutor){
        getCommand(label)?.run {
            setExecutor(executor)
            logger.info("/$label を登録しました")
        } ?: logger.severe("/$label を登録できませんでした")
    }
```

新しく出てきたものが2つあります。
**`?.run {}`** は `null` でなければ中括弧の中を実行する。
**`?:`** は `null` であれば別の処理をする。といった感じです。

場合分けして考えてみましょう。
**1. `getCommand` が `null` ではなかった場合**
単純に `run {}` の中が実行されます。

```kotlin
/* null ではない getCommand(label) */.run {
    setExecutor(executor)
    logger.info("/$label を登録しました")
}
```

**2. `getCommand` が `null` だった場合**
`getCommand` が `null` だと `?.run {}` も null になります。
`?:` の挙動は `null ?: /* これが実行される */` こういう感じなので
失敗メッセージのみが残ります。

```kotlin
/* 全部 null になって ?: 以降だけ残る
getCommand(label)?.run {
    setExecutor(executor)
    logger.info("/$label を登録しました")
} ?: */ logger.severe("/$label を登録できませんでした")
```

Kotlin ならではの書き方というのがあります。
それを使うと変数宣言の量が減ったり、分かりやすく書けたり することもあります。
あくまで、「そういうこともある」 というだけなので、覚える必要はないです。
自分ひとりで構想を立てて、実際にプラグインを作れるようになってからで十分です。
それでも、「やったことがある」 という記憶は役に立ちます。お疲れ様でした。

</div></details>

## リンク
### [次の記事に進む](https://qiita.com/sya-ri/items/bebee642f72b46b59d0d)
### [目次に戻る](https://qiita.com/sya-ri/items/bc7eb01ed687b9ff9566#%E7%9B%AE%E6%AC%A1)