sbt(Activator)でMinecraftのmodを開発する


とりあえず(gradleのrunClientまで)動くようになったので知見として書き残しておきます。
gradleでもscalaを使って開発できないことはないですが(ForgeGradleのソース見た感じGroovyでも作れるっぽい？)、毎回gradleを起動することになるのでインタラクティブモードが使えるsbtを使ったほうが開発を手早く行うことができます。

サンプルではありませんが、実際にActivatorを使ってmodを開発しているので良くわからなければこちらも参考にしてください =&gt; [Pctg-x8/FluidMechanics](https://github.com/Pctg-x8/FluidMechanics)

## 対象

Javaは書きたくないけどMinecraftのmodを作ってみたいという人
Typesafe Activatorはバージョン1.3.7を使用します

## 大まかな手順

1. gradleを使ってデコンパイル及び難読化解除を行う(`./gradlew setupDecompWorkspace`)
2. ForgeGradleのGradleStart及びその関連クラスを移植してくる(かそのままsrc/main/javaフォルダに持ってくる)
3. build.sbtに必要な設定を記述する
4. sbt runで起動できるようになる

起動クラス移植は本当にそのまま持ってくるだけで動くと思います(はずです)。私はScalaの復習も兼ねて書き直しましたが。modのソースファイルと一緒の階層に置いておくとごっちゃになると思うので専用フォルダを作って隔離しておくといいと思います。
今回はクライアントのみ対応していますが、同じ手順でGradleStartServerも持ってくればサーバー側の起動も可能になると思います。

## 必要なライブラリ郡

libraryDependenciesに追加するものは以下の通りになります(足りない分を足し続けただけなのでもう少し削れるかもしれないです)。

```scala:build.sbt(一部)
	"org.apache.logging.log4j" % "log4j-api" % "2.0-beta9",
	"org.apache.logging.log4j" % "log4j-core" % "2.0-beta9",
	"com.google.code.gson" % "gson" % "2.2.4",
	"org.lwjgl.lwjgl" % "lwjgl" % "2.9.1",
	"org.lwjgl.lwjgl" % "lwjgl_util" % "2.9.1",
	"org.apache.commons" % "commons-compress" % "1.8.1",
	"org.apache.commons" % "commons-lang3" % "3.3.2",
	"commons-logging" % "commons-logging" % "1.1.3",
	"com.mojang" % "authlib" % "1.5.16",
	"tv.twitch" % "twitch" % "5.16",
	"com.ibm.icu" % "icu4j-core-mojang" % "51.2",
	"io.netty" % "netty-all" % "4.0.10.Final",
	"com.paulscode" % "soundsystem" % "20120107",
	"com.paulscode" % "libraryjavasound" % "20101123",
	"com.paulscode" % "librarylwjglopenal" % "20100824",
	"com.paulscode" % "codecjorbis" % "20101023",
	"com.paulscode" % "codecwav" % "20101023",
	"java3d" % "vecmath" % "1.3.1",
	"net.minecraft" % "launchwrapper" % "1.12",
	"net.sf.trove4j" % "trove4j" % "3.0.3",
	"lzma" % "lzma" % "0.0.1",
	"com.google.code.findbugs" % "jsr305" % "1.3.+"
```

com.google.code.findbugs:jsr305はScala側のバグ回避用です。
個人的にlzma:lzmaが必要だと気づくまでにハマりました。
resolverにはminecraftのmavenリポジトリ(`https://libraries.minecraft.net/`)のみ追加します。

また、コンパイルを正常に完了するためにMinecraftForgeのソースライブラリが必要です。こちらはgradleのキャッシュを引っ張ってきて使用します。

```scala:build.sbt(一部)
 val srcJarVersionSignature = Def.setting { Seq(minecraftVersion.value, forgeVersion.value, minecraftVersion.value).mkString("-") }
 val gradleCaches = file(System.getProperty("user.home")) / ".gradle" / "caches"
 val forgeSources = Def.setting { gradleCaches / "minecraft" / "net" / "minecraftforge" / "forge" / srcJarVersionSignature.value }
 unmanagedJars in Compile += forgeSources.value / ("forgeSrc-" + srcJarVersionSignature.value + ".jar")
```

## 起動周りの設定

別にそのままでも構いませんが(ルートフォルダがMinecraftの生成フォルダで汚染されます)、gradleに倣ってeclipseフォルダ内で起動させるほうがいいと思います。

```scala:build.sbt(一部)
fork := true
baseDirectory in run := (baseDirectory in Compile).value / "eclipse"
mainClass in Compile := Some("GradleStart")
```

jvmの作業ディレクトリを変更するにはforkを有効にしないといけないので注意です(sbtのドキュメントに書いてあります)。
また、起動クラスとしてGradleStartを指定しているためclasspathから直接見える位置にGradleStart.classがないといけません。一緒にコンパイルさせるのが簡単かつ確実ですが、classpathから見えさえすれば事前にコンパイルしたclassファイルを置いておくだけでも動くと思います(試していないのでわからないのと、sbtならインクリメンタルコンパイルが使えるので変更しない限り再コンパイルされません。ので事前にコンパイルしておく利点はあんまりないかなと)。

### おまけ: Scala版modエントリクラスについて

公式Wiki: http://www.minecraftforge.net/wiki/Scala
Java版と少しだけ変わっているので注意です。

- `Mod`アノテーションのパラメータに`modLanguage="scala"`を追加する
- `class`ではなく`object`で宣言する
    - クラス名がそのままインスタンスを表すのでチュートリアル中の`エントリクラス名.instance`に準ずる部分では`.instance`を省略して書く