MCDevNight KotlinModding講座まとめ

# 始めに
この記事は、[Minecraft Advent Calender 2017](https://qiita.com/advent-calendar/2017/minecraft)の1日目の記事です。
[MCDevNight](https://mc-dev-night.tntmodders.com)という、マイクラ関連のデベロッパー向けイベントにおける、Kotlin Modding講座の内容をまとめ＆補足したものになります。
MinecraftForgeにおけるModdingとKotlinの文法をある程度理解している人向けの内容になります。
また、適宜当時使った[サンプルコード](https://github.com/C6H2Cl2/MCDevNightSample)を参照してください。

## 要求環境
- MinecraftForgeのModding環境(ここでは1.10.2を用いています)
- IntelliJ IDEA

# 何故Kotlin？
これは改めて説明する必要があるか、と言われるとそうでもないので簡単にまとめます。
**Better Java**
この一言で終わりです。

# 環境構築
MinecraftForge環境におけるModdingには、ビルドツールにGradleが使われているため、非常に簡単にKotlinを利用できます。
以下では、build.gradleの書き換えが必要な部分について説明します。

```groovy:build.gradle
buildscript {
    ext.kotlin_version = "1.1.51"
    repositories {
        jcenter()
        maven {
            name = "forge"
            url = "http://files.minecraftforge.net/maven"
        }
    }
    dependencies {
        classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'kotlin'
```
注目すべき点は3つです。
まず、１つ目の `ext.kotlin_version=1.1.51` ですが、ここでKotlinのバージョンを指定しています。
そして、`classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"`という部分でkotlinのgradle用プラグインを依存として追加しています。
そして、最後の`apply plugin: kotlin`でkotlinのgradle pluginを適用しています。

次に、dependencies句です。

```groovy:build.gradle
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
}
```
kotlinのstdlib(標準ライブラリ)を依存として追加します。

最後に、これはこのままコピペして追加する部分です。

```groovy:build.gradle
compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
```
コンパイル時にjava8をターゲットに指定しているだけですね。

# Kotlinにstaticが無い問題
Kotlinでは、javaのようなstatic修飾子がありません。
ですが、Moddingにおいては様々な場所でstaticな変数が要求されます。(SidedProxy,ModMetadata等)
ここでは、kotlinにおけるstaticの代用機能(コンパイル時にstaticに変換されるもの)を紹介し、Moddingにおけるそれぞれの用例を示します。
## Kotlinにおける、staticの代用機能
まず、Kotlinにおいてstaticの代用となるのは以下の3つです。

- Companion Object
- Top-level Declarations(Package-level Object)
- Object Declarations

以下で、それぞれの項目について紹介していきます。
### Companion Object
これが、Javaにおけるstaticに一番近い書き方です。
クラスに紐付けられた、Companion Objectというシングルトンオブジェクト内に変数等を定義します。
実際の挙動としては、

1. Companionという名前の、シングルトンオブジェクトがstaticでクラス内に定義される
2. Companion Object内に定義されたフィールドやメソッドは、このシングルトンオブジェクトに格納される。
3. Kotlinにおけるcompanion objectへのアクセスは クラス名.定義されたオブジェクト の形で行われる
4. これはコンパイル時に クラス名.Companion.定義されたオブジェクト の形に変換される
5. **@JvmStaticアノテーションをつけると、通常のstaticフィールドになる**

重要なのは、最後です。
Companion Objectは**そのままではstaticではありません**。
@JvmStaticをつけた時だけ、staticになります。

そして、Companion Objectの用途ですが、基本的に**そのクラスに関連性が高いオブジェクト**に使うべきである、と考えています。
Moddingにおいては、@Modアノテーションをつけたクラスに使うことが多いと思います。

用例：

```Kotlin:ExampleModCore.kt
@Mod(modid = MOD_ID, name = MOD_NAME, version = VERSION, dependencies = DEPENDENCIES, useMetadata = true, acceptedMinecraftVersions = "1.10.2")
class ExampleModCore {

    companion object {
        @JvmStatic
        @Metadata
        lateinit var META: ModMetadata

        @JvmStatic
        @SidedProxy(serverSide = "mcDevNight.example.common.CommonProxy", clientSide = "mcDevNight.example.client.ClientProxy")
        lateinit var PROXY: CommonProxy

        @JvmStatic
        @Instance
        lateinit var INSTANCE: ExampleModCore
}
//以下略
}
```

### Top-level Declarations (Package-level Object)

この機能、Kotlinの公式のドキュメントではTop-level Declarations表記なんですが、個人的にはPackage-level Objectって言い方のほうが好きなんですよね。
まぁここでは公式表記のTop-level Declarationsを使いますが。

この機能は、恐らくKotlinで最も多く使われているstatic表記です(詳しく調べたことはないから誰か統計取ってみてほしい)。
STDLibの拡張関数群なんかは大体コレで定義されていますね。
というかKotlinのmain関数もコレです。

```kotlin
fun main(args: Array<String>) {
    println("Hwello, World!")
}
```

このような、クラス外での定義がTop-level Declarationsです。
これは、全てがデフォルトでstaticです。
コンパイル時に専用クラスが生成され、その中にstaticな変数/メソッドとして定義されます。
個人的には、Top-level Declarationsだけを記述するファイルを作ると読みやすいです。

ちなみに、importしておけば、どのパッケージからでもそれぞれの変数/メソッド名だけを書いて呼び出すことができます。
同じパッケージ内であればimportする必要はありません。

以下、用例です。

```Kotlin:ExampleModConsts.kt
const val MOD_ID = "examplemod"
const val MOD_NAME = "ExampleModCore"
const val VERSION = "1.0.0"
const val DEPENDENCIES = "required:forgelin"

val tabExampleMod = object : CreativeTabs("exampleMod") {
    override fun getTabIconItem(): Item {
        return MCItem.APPLE
    }
}

//Item  ===========================================================================================
val exampleItem = createItem("exampleItem", "example_item")

//Block ===========================================================================================
val exampleBlock = createBlock("exampleBlock", "example_block", Material.ROCK, 20f, 20f).setCreativeTab(tabExampleMod)



//Register
fun register() {
    register(exampleItem)
    register(exampleBlock.initItemBlock())
    register(exampleBlock)
}
```

便利関数なんかは全部コレでいいと思います。

### Object-Declarations
これは、シングルトンオブジェクトを作成する機能です。
基本的な挙動はCompanion Objectと同様ですが、以下の点で異なります。

- 名前をつけて、通常のクラスと似たような振る舞いができる
- 名無しの状態で変数に代入可能
- クラスに紐付けされない

ちなみにstaticにするには@JvmStaticアノテーションが必要です。

用例：

```kotlin:ExampleModRegistry.kt
object ExampleModRegistry {
    // Items ======================================================================================
    val exampleAxe = ItemExampleAxe()
    val enchantedAxe = ItemExampleEnchantedAxe()

    // Blocks =====================================================================================


    // Registers ==================================================================================

    fun preinit(event: FMLPreInitializationEvent) {
        register(exampleAxe)
        register(enchantedAxe)

        if (event.side.isClient) {
            setTexture(exampleAxe)
            setTexture(enchantedAxe)
        }
    }

    fun init(event: FMLInitializationEvent) {
        addRecipe(ItemStack(exampleAxe), "EE", "ES", " S", 'E', exampleItem, 'S', MCItem.STICK)
        addRecipe(enchantedAxe.getEnchanted(), " B ", "BAB", " B ", 'B', exampleBlock, 'A', exampleAxe)
    }
//以下略
}
```

普通のクラスみたいに書けますね。

## Item/Blockのインスタンスをどのように保持するか
1.11.2までの時点で、ItemとBlockのインスタンスの保持はstaticな変数で行われているのが主流です。
それでは、Kotlinにおいてはどの方法を用いるべきでしょうか？

結論から言うと、最終的には「好み」になると思います。
Companion Objectは、クラスに関連性の高いオブジェクトに使用するべきと言う(個人的な)考えから、あまりオススメしません。
ただ、Top-level DeclarationsとObject Declarationsには両方長所と短所があるため、最終的な判断は個人の好みだと思います。

### Top-level Declarations
#### 長所
* 記述が簡潔になる
* 複数ファイルに定義を分割しても呼び出し側への影響が少ない 
* デフォルトでstatic
#### 短所
* フィールド名被りが発生すると対処が面倒
* 1.12.2でのForgeの仕様に対応するにはちょっと汚い書き方が必要になる

### Object Declarations
#### 長所
* 呼び出し側の記法がJavaにおけるstaticなフィールドと同じ
* なのでフィールド名被っても無問題
* 1.12.2でのForgeの仕様変更への対応も楽である
#### 短所
* static化するには@JvmStaticが必要
* 大量の変数定義が１つのobjectに並びがち

好きな方を選んでください。
ちなみに俺は前までObject Declarationsを使っていましたが、今はTop-level Declarationsを使っています。
1.12.2だとForgeの仕様に対応するための登録メソッドとCreativeTabsのインスタンス書くだけのobjectとか作らないといけないんですけどね。

# KotlinでModを書くときの便利技
今までmoddingしてて、Kotlin関連で便利だな、と思ったものをいくつか紹介します。
## @Modアノテーションの引数をconst valな変数で定義する
ModIDなんかは、割と参照する機会が多いと思います。
const valは、Javaのpublic static finalなプリミティブと同様に、コンパイル時にリテラルに変換される変数です。
Top-level Declarations使って定義しておきましょう。

## ItemBlockの取得関数を拡張関数化
Item.getItemFromBlock(block)って長いし、直感的じゃないですよね。
ついでに言うと、どこかのバージョンでか、ItemBlockをGameRegistry.register()してないと取得できないなんてことが発生していました。
ついでに言うと、ItemBlockのテクスチャのjsonファイルはサブフォルダに隔離したいし、Modelの登録はRegistryName使う事で簡易化したいしで、RegistryNameをModId:block/blockNameってしたいんですよね。
それで作ったのがコレ

```kotlin:ExampleModConsts.kt
private fun Block.getItemBlock(): Item {
        return Item.getItemFromBlock(this)!!
    }

    private fun Block.initItemBlock(registryName: ResourceLocation = this.registryName!!): Item {
        val name = if (!registryName.resourcePath.startsWith(“block”)) {
            ResourceLocation(registryName.resourceDomain, “block/${registryName.resourcePath}”)
        } else {
            registryName
        }
        return ItemBlock(this).setRegistryName(name)
    }
```
これにより、GameRegistry.register(block.initItemBlock())と書いて登録、以降の呼び出しはblock.getItemBlock()でできます。
拡張関数って便利！！

## もっと拡張関数！
テンプレ処理はとりあえずTop-level Declarationsにしてしまう人

```kotlin
fun EntityPlayer.sendMessageOnlyServer(component: ITextComponent) {
    if (!world.isRemote) sendMessage(component)
}

fun EntityPlayer.addAndSpawnXpOrb(value: Long, world: World) {
    var over = if (value > Int.MAX_VALUE) {
        value - Int.MAX_VALUE
    } else {
        0
    }
    val xp = value - over
    over += Math.max(0, experienceTotal + xp - Int.MAX_VALUE)
    addExperience((value - over).toInt())
    if (over > 0 && !world.isRemote) {
        world.spawnXpOrb(over, posX, posY, posZ)
    }
}

fun BlockPos.serializeNBT(): NBTTagCompound {
    val tag = NBTTagCompound()
    tag.setInteger("x", x)
    tag.setInteger("y", y)
    tag.setInteger("z", z)
    return tag
}

fun BlockPos.deserializeNBT(tag: NBTTagCompound): BlockPos =
        BlockPos(tag.getInteger("x"), tag.getInteger("y"), tag.getInteger("z"))
```

ぶっちゃけ最後は拡張関数にする意味がほとんどない
まぁメソッド名汚染対策で拡張関数にしてるようなもの

# 最後に
MCDevNightで講演した内容は以上になります。
Kotlin、使いこなせればすごく便利なので是非ともModdingにも活用してください。
Kotlinの機能で、こうすればModdingの便利なのでは？というものがあれば是非とも教えてください