KotlinでMinecraft Modding

この記事は、[Kotlin Advent Calendar 2016](http://qiita.com/advent-calendar/2016/kotlin) 16日目の記事です。

---

今年の2月、Kotlinのバージョン1.0が公開され、公式のBlogに投稿されました。

>Kotlin 1.0 Released: Pragmatic Language for JVM and Android
https://blog.jetbrains.com/kotlin/2016/02/kotlin-1-0-released-pragmatic-language-for-jvm-and-android/

JVMへの完全互換とJavaへの高い親和性、IntelliJベースのAndroidStudioによる協力なサポートで、Androidでの勢力を伸ばしているKotlinですが、このページには他にも「こんなのでKotlinが使えるよ！」というのが書かれています。

>
- IntelliJ IDEA, Android Studio and Eclipse
- Maven, Gradle and Ant
- Spring Boot (Kotlin support released today!)
- GitHub, Slack and even Minecraft :)

> https://blog.jetbrains.com/kotlin/2016/02/kotlin-1-0-released-pragmatic-language-for-jvm-and-android/

一番最後ですが

> \>\>\> Minecraft <<<

ここで言うMinecraftとは、Minecraft Modのことです。

>「Modification」や「Modding file」の略で、主にパソコンゲーム用の改造データのこと。
ユーザーが拡張ファイルを作り、素の状態のファイル（バニラ）に追加・上書きを行うことでゲームの機能を拡張する。
https://www26.atwiki.jp/minecraft/pages/940.html

MinecraftがもともとJava製なため、Modは基本Javaで書かれてきました。
KotlinはJVM互換なので、もちろんMinecraft Modでも使うことができます。
MinecraftでModを作る事実上のスタンダードとなっているMinecraft ForgeにKotlinを導入する過程については、以下で詳しくまとまっているのでそちらをご覧ください。

[KotlinでMinecraftのModdingをするための環境構築](http://qiita.com/C6H2Cl2/items/b864bd504f2fa285656d)

### Kotlinによって便利に

Minecraft内部のソースコードは、最近はNullableアノテーションとかがしっかり付くようになってきており、Kotlin側から見た時に型のNullable/NonNullがしっかりしている箇所が多くなってきました。

Minecraft Modにおいて、nullでreturnするような箇所といえば、「プレイヤーが持っているアイテムが○○のとき」のような分岐をしたいときです。

```BlockBreakEventHandler.java
public class BlockBreakEventHandler {

    @SubscribeEvent
    public void onBlockBreak(BlockEvent.BreakEvent event) {
        EntityPlayer player = event.getPlayer();
        if (player == null) {
            return;
        }
        if (player.getHeldItemMainhand() == null) {
            return;
        }
        Item item = player.getHeldItemMainhand().getItem();
        if (item != null) {
            return;
        }
        // itemがnon-nullのときだけここに来る
    }
}
```

nullになる箇所は3箇所

- event.getPlayer()
- player.getHeldItemMainhand()
- player.getHeldItemMainhand().getItem()

(ちなみに、この内@Nullableアノテーションがしっかりと付いているのは真ん中の物のみです。)
ifをまとめて書けば多少はマシになりますが、とはいえ横に長くなりすぎて辛いです。

```BlockBreakEventHandler.java
public class BlockBreakEventHandler {
    @SubscribeEvent
    public void onBlockBreak(BlockEvent.BreakEvent event) {
        EntityPlayer player = event.getPlayer();
        if (event.getPlayer() == null || event.getPlayer().getHeldItemMainhand() == null || event.getPlayer().getHeldItemMainhand().getItem() != null) {
            return;
        }
        Item item = event.getPlayer().getHeldItemMainhand().getItem();
        // itemがnon-nullのときだけここに来る
    }
}
```

ここをKotlinで書くとこうなります。


```BlockBreakEventHandler.kt
class BlockBreakEventHandler {
    @SubscribeEvent
    fun onBlockBreak(event: BlockEvent.BreakEvent) {
        val item = event?.player?.heldItemMainhand?.item ?: return
        // itemがnon-nullのときだけここに来る
    }
}
```

nullでの早期returnが1行で済みました。

他の部分も見てみましょう

例えば、追加した動物のAIを書き換えるとします。
普通であれば以下のようにEntityのコンストラクタで指定します。
（第1引数が優先度、第2引数が追加するAIです）

```EntityNewAnimal.java
public EntityNewAnimal(World worldIn) {
    super(worldIn);
    setSize(0.9f, 0.9f);
    tasks.addTask(1, new EntityAISwimming(this));
    tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4f));
    tasks.addTask(6, new EntityAIMate(this, 1.0d));
    tasks.addTask(7, new EntityAIWander(this, 1.0d));
    tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0f));
    tasks.addTask(9, new EntityAILookIdle(this));
}
```

ここをKotlinでスッキリさせてみます。

まず、新しく`KotlinExtension.kt`といった適当な名前のファイルを作成します。
ここに、以下の関数を書きます。

```KotlinExtension.kt
fun EntityLiving.addTasks(vararg taskPairs: Pair<Int, EntityAIBase>) {
    taskPairs.forEach { tasks.addTask(it.first, it.second) }
}
```

すると、呼び出し元は以下のように書くことができます。

```EntityNewAnimal.kt
init {
    setSize(0.9f, 0.9f)
    addTasks(1 to EntityAISwimming(this),
             3 to EntityAILeapAtTarget(this, 0.4f),
             6 to EntityAIMate(this, 1.0),
             7 to EntityAIWander(this, 1.0),
             9 to EntityAIWatchClosest(this, EntityPlayer::class.java, 8.0f),
             9 to EntityAILookIdle(this))
}
```

メソッド呼び出しは減り、見た目もスッキリしました。

はじめに`KotlinExtension.kt`に書いたものが、Kotlinの拡張関数です。

今回は、`EntityLiving`クラスに`addTasks`という名前で、`IntとEntityAIBaseのPair`の可変長引数を取る関数を追加しているように見せています。
（EntityNewAnimalはEntityLivingのサブクラスです）

---

といった感じで、Kotlinを使ってMinecraft Moddingする際の便利なTipsを2点ほど載せてみました。

Minecraftの世界にもKotlinの波が来てほしいです。