【Kotlin+Spigot】2.3 プレイヤーの頭を取得する

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

また、コマンドを作成して進めていきます。
詳しい説明は **[2.2 コマンドを新しく作る](https://qiita.com/sya-ri/items/f69f1489b60650a7dd41)** でしていますので省略します。

## 1. plugin.yml にコマンドを登録する

```yml
commands:
  head:
    usage: "/<command> [Player]"
    description: "プレイヤーの頭を取得します"
    permission: minecraft.command.give
```

このようなコマンドを登録しておきます。一番初めの引数はプレイヤーのIDです。
これは `/give` の拡張コマンドです。実行に `minecraft.command.give` という権限を必要としています。

## 2. 準備をする
コマンド処理のところで、メッセージに色をつけたいので `getColored` 関数を実装しておきましょう。詳しくは **[こちら](https://qiita.com/sya-ri/items/2b3e9e063048699ead50#4-%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%AB%E8%89%B2%E3%82%92%E4%BB%98%E3%81%91%E3%81%A6%E3%81%BF%E3%82%8B)**

```Util.kt
object Util {
    fun getColored(text: String): String {
        return ChatColor.translateAlternateColorCodes('&', text)
    }
}
```

さあ、プレイヤーの頭を取得する関数を作りましょう。
それも `Util` に実装しましょう。プレイヤーの頭というアイテムは `ItemStack` というクラスになります。
任意のプレイヤーの頭を取得するので、プレイヤーを引数としておきます。

```kotlin
    fun getPlayerHead(player: OfflinePlayer): ItemStack {
        
    }
```

実際の処理を書いていきましょう。
まず、アイテム(`ItemStack`)を作成します。

```kotlin
    fun getPlayerHead(player: OfflinePlayer): ItemStack {
        val head = ItemStack(Material.PLAYER_HEAD)

    }
```

`ItemStack` の中には `ItemMeta` というアイテムのデータが入っています。
それを一度、変数として保持しておきましょう。

```kotlin
    fun getPlayerHead(player: OfflinePlayer): ItemStack {
        val head = ItemStack(Material.PLAYER_HEAD)
        val meta = head.itemMeta

    }
```

アイテムのデータと言っても、アイテムの種類(`Material`)によって固有の設定というものが存在しています。
今回は `PLAYER_HEAD` 、答えから言うと `SkullMeta` という固有の設定があります。
また、`SkullMeta` というものは `ItemMeta` の拡張になっています。
要するに **`meta` が `SkullMeta` であれば 固有の設定 も行える** のです。

```kotlin
    fun getPlayerHead(player: OfflinePlayer): ItemStack {
        val head = ItemStack(Material.PLAYER_HEAD)
        val meta = head.itemMeta
        if(meta is SkullMeta){

        }
    }
```

では、頭の見た目を変えてみましょう。

```kotlin
    fun getPlayerHead(player: OfflinePlayer): ItemStack {
        val head = ItemStack(Material.PLAYER_HEAD)
        val meta = head.itemMeta
        if(meta is SkullMeta){
            meta.owningPlayer = player
            head.itemMeta = meta
        }
    }
```

**`owningPlayer`** という値を書き換えると見た目がそのプレイヤーになります。
しかし、**設定を変えたらそれをアイテムに反映する必要があります**。
そのため、アイテムメタに対して代入しています。
残りは、`head` を戻り値とするだけです。

```kotlin
    fun getPlayerHead(player: OfflinePlayer): ItemStack {
        val head = ItemStack(Material.PLAYER_HEAD)
        val meta = head.itemMeta
        if(meta is SkullMeta){
            meta.owningPlayer = player
            head.itemMeta = meta
        }
        return head
    }
```

これで完成になります。

## 3. コマンドの処理を書く

`CommandExecutor` を継承した `GetHeadCommand` というクラスを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/f91be449-6280-4485-5e8f-75beb6d5ce9d.png)

実行したプレイヤーが頭を取得する仕様にしましょう。
その場合、コンソールから実行することは出来ません。言い換えると **プレイヤーのみ実行可能** です。
その条件分岐を書きましょう。

```kotlin
    override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        if(sender is Player){
            
        } else {
            sender.sendMessage(getColored("&cプレイヤーからのみ実行できるコマンドです"))
        }
        return true
    }
```

`Player` に対してのみ処理をしたいので `is` を使います。
`if` の中括弧の中であれば `sender` は `Player` として扱われます。

次に、コマンド引数の処理をしましょう。(一番最初の引数がプレイヤーのIDです。)
`args` には引数が全て入っていますが、これが空っぽだったら、プレイヤーのIDは入力されていません。

```kotlin
        if(sender is Player){
            if(args.isNotEmpty()){
                
            } else {
                sender.sendMessage(getColored("&c頭を取得するプレイヤーのIDを入力してください"))
            }
        }
```

`args.isNotEmpty()` というのは `0 < args.size` と書いても正しいですが、`isNotEmpty` を使うことを IDE が推奨してくると思います。
一番最初の引数と表現していましたが、それは `args` の何番目でしょう？
正解は、0 です。これを間違えるとおかしくなってしまうので覚えておきましょう。
では、0番目の引数を一旦変数に保存しておきましょう。

```kotlin
        if(sender is Player){
            if(args.isNotEmpty()){
                val id = args[0]

            } else {
                sender.sendMessage(getColored("&c頭を取得するプレイヤーのIDを入力してください"))
            }
        }
```

そのまま、プレイヤーの取得をしましょう。
オンラインプレイヤー以外の頭も取得できるようにしたいので、`getOfflinePlayer` を使います。

```kotlin
        if(sender is Player){
            if(args.isNotEmpty()){
                val id = args[0]
                val player = Bukkit.getOfflinePlayer(id)
            } else {
                sender.sendMessage(getColored("&c頭を取得するプレイヤーのIDを入力してください"))
            }
        }
```

そうするとこのように、取り消し線がかかれると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/4c487442-5bb5-747c-14aa-e891c0e3b832.png)
非推奨関数と言って、「あまり使わないほうがいいよ」といった意味です。
`getOfflinePlayer(String)` というものが非推奨になっているのは、プレイヤーのIDが変更可能だからです。
例えば、データを保存するという時にIDを使っていると、データが消えてしまいます。
そういった場合には `UUID` を使いましょう。しかし、今回は問題ありません。
このまま無視してしまうと毎回警告が出るので、出ないようにしておきましょう。

取り消し線の上で右クリック → `Inspection 'DEPRECATION' options` → `... for val` を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/1a923813-e39a-972c-9968-ccd0303e75fc.png)
そうすると、このようになると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/5cc4b686-1419-fa1c-7eee-0fc3e63ff03f.png)
**`＠抑制する("非推奨")`** こんなような意味です。
注意しなければいけないのは、非推奨であったということ。その使い方が間違えではないかを考えて使いましょう。

では、先ほど作成した `getPlayerHead` を使ってプレイヤーの頭を取得しましょう。

```kotlin
            if(args.isNotEmpty()){
                val id = args[0]
                @Suppress("DEPRECATION") val player = Bukkit.getOfflinePlayer(id)
                val head = getPlayerHead(player)
                
            }
```

これを `sender` に渡します。言い換えると、インベントリにアイテムを追加します。
ついでに、メッセージも送信しておきましょう。

```kotlin
            if(args.isNotEmpty()){
                val id = args[0]
                @Suppress("DEPRECATION") val player = Bukkit.getOfflinePlayer(id)
                val head = getPlayerHead(player)
                sender.inventory.addItem(head)
                sender.sendMessage(getColored("&b${player.name} &fの頭を取得しました"))
            }
```

これでコマンドの処理は書き終わりました。

完成するとこんな風になっていると思います。

```GetHead.kt
class GetHeadCommand: CommandExecutor {
    override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        if(sender is Player){
            if(args.isNotEmpty()){
                val id = args[0]
                @Suppress("DEPRECATION") val player = Bukkit.getOfflinePlayer(id)
                val head = getPlayerHead(player)
                sender.inventory.addItem(head)
                sender.sendMessage(getColored("&b${player.name} &fの頭を取得しました"))
            } else {
                sender.sendMessage(getColored("&c頭を取得するプレイヤーのIDを入力してください"))
            }
        } else {
            sender.sendMessage(getColored("&cプレイヤーからのみ実行できるコマンドです"))
        }
        return true
    }
}
```

## 4. コマンドを登録する
```Main.kt
class Main: JavaPlugin() {
    override fun onEnable() {
        registerCommand("head", GetHeadCommand())
    }

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

さあ、サーバーでテストしてみましょう。

まず、コンソールから入力したときにエラーが出るか確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/32e520b8-4135-17f8-459e-25fee7bc43be.png)
このように出ていたら成功です。

ではプレイヤーからコマンドを入力しましょう。正常に頭が取得できれば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/7a79d592-0295-12aa-9d25-acb2f07f0341.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628547/3332682d-a0b8-77c3-1a04-26607e73beb8.png)

お疲れ様でした。

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