読者です 読者をやめる 読者になる 読者になる

Minecraft サーバ Spigot のプラグインを Kotlin で書いてみたときのメモ

  • 個人の作業メモになるので、内容の保証は一切できないけれど、誰かの参考になれば。
  • 各レイヤーで何をやっているか知りたかったので、低レベルなところをコマンドラインでやってみている。
  • 実際に開発する場合はもうちょっとちゃんと環境を整えた方が良いように思う。
  • 私自身そんなに詳しいわけではないので突っ込み歓迎。

環境

このメモの手順通りに作業した場合には以下のようなディレクトリ構造になる。

.
|- kotlin            (Kotlin コンパイラ)
|- kikori-plugin     (木こりプラグイン)
|- kotlin-plugin     (Kotlin サンプルプラグイン)
|- hello-plugin     (Pure Java サンプルプラグイン)
|- spigot           (Spigot 自体のビルド作業用)
`- spigot-server    (Spigot サーバを動かす用)

Spigot とは

https://www.spigotmc.org/

Minecraft のサーバ。プラグインを入れられる。Java で動く。

Spigot の導入

大人の事情により Spigot はバイナリ配布されていない。代わりに簡単にビルドできるビルドツールがあるので、これでビルドする。

https://www.spigotmc.org/wiki/buildtools/

↑に各環境の詳細な手順が書かれているので、うまく行かない場合は読むとよい。

まずはビルドツールを取得する。

$ mkdir spigot
$ cd spigot
$ curl https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -o BuildTools.jar

そしてこれを使ってビルド。今回は 1.9 を使う。今のところ、指定なしだと 1.8 がビルドされる模様。

ちなみにビルドツールを動かすには、Java の他に Git も必要。ない場合はインストールしておく。

$ java -jar BuildTools.jar --rev 1.9

めっちゃがんばってビルドしてくれる。しばし待つと、カレントディレクトリに spigot-1.9.jar ができる。これがサーバになる。

実際に起動してみる

https://www.spigotmc.org/wiki/spigot-installation/

まずはプラグインなしでとりあえず動かしてみる。実行は spigot-1.9.jar だけがあればできる。

こいつは起動時にカレントディレクトリにあれこれファイルやディレクトリを生成するので、新しいディレクトリで作業するのがよい。

$ mkdir ../spigot-server
$ cp spigot-1.9.jar ../spigot-server
$ cd ../spigot-server

引数が長いので、簡単に起動用のシェルスクリプトを作っておくと便利。

$ echo 'java -Xms512M -Xmx1024M -XX:MaxPermSize=128M -jar spigot-1.9.jar' > start.sh
$ chmod +x start.sh

起動する。

$ ./start.sh
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128M; support was removed in 8.0
Loading libraries, please wait...
[19:04:47 INFO]: Starting minecraft server version 1.9
[19:04:48 INFO]: Loading properties
[19:04:48 WARN]: server.properties does not exist
[19:04:48 INFO]: Generating new properties file
[19:04:48 WARN]: Failed to load eula.txt
[19:04:48 INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
[19:04:48 INFO]: Stopping server
>

$ ls
eula.txt  logs/  server.properties  spigot-1.9.jar

$

初回は起動に失敗する。使用許諾契約に同意する必要がある。eula.txt というファイルが生成されているので、これを Vim などの適当なエディタで開く。

#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).
#Sun Mar 13 19:04:48 JST 2016
eula=false

EULA の URL があるので開いて目を通し、問題がなければ eula=falseeula=true に書き換えて保存。再び起動する。

$ ./start.sh
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128M; support was removed in 8.0
Loading libraries, please wait...
[21:22:23 INFO]: Starting minecraft server version 1.9
[21:22:23 INFO]: Loading properties
[21:22:23 INFO]: Default game type: SURVIVAL
[21:22:24 INFO]: This server is running CraftBukkit version git-Spigot-6f291ea-55a8535 (MC: 1.9) (Implementing API version 1.9-R0.1-SNAPSHOT)
[21:22:24 INFO]: Unable to find file banned-players.json, creating it.
[21:22:24 INFO]: Unable to find file banned-ips.json, creating it.
[21:22:24 INFO]: Unable to find file ops.json, creating it.
[21:22:24 INFO]: Unable to find file whitelist.json, creating it.

...(中略)...

[21:23:31 INFO]: Preparing start region for level 2 (Seed: 5519750147800174725)
[21:23:32 INFO]: Preparing spawn area: 11%
[21:23:33 INFO]: Preparing spawn area: 25%
[21:23:34 INFO]: Preparing spawn area: 39%
[21:23:35 INFO]: Preparing spawn area: 51%
[21:23:36 INFO]: Preparing spawn area: 62%
[21:23:37 INFO]: Preparing spawn area: 75%
[21:23:38 INFO]: Preparing spawn area: 90%
[21:23:39 INFO]: Done (73.641s)! For help, type "help" or "?"
>

無事起動した。最後の > はプロンプトで、管理用のコマンドなどが実行できる。

この状態で Minecraft 1.9 のクライアントから Multiplayer でサーバに接続すれば、ゲームが遊べる。サーバとクライアントが同じマシンなら、接続先のサーバアドレスに localhost と入力して接続すれば OK。

サーバを終了させるには stop コマンドを実行すればよい。

Pure Javaプラグインを作ってみる

Kotlin に入る前に、まずは Pure Javaプラグインを作ってみる。改めて断っておくと、とりあえず動くことを確認するための作業なので、構成はものすごく適当。

まずはプラグインのプロジェクト用にディレクトリを作る。

$ mkdir ../hello-plugin
$ cd ../hello-plugin

プラグインをビルドするには、Spigot のライブラリが必要になる。最初にビルドした中にあるので、コピーしてくる。

$ cp ../spigot/Spigot/Spigot-API/target/spigot-api-1.9-R0.1-SNAPSHOT.jar spigot-api.jar

HelloPlugin.java として、以下のようなファイルを作る。

import org.bukkit.plugin.java.JavaPlugin;

public class HelloPlugin extends JavaPlugin {
  @Override
  public void onEnable() {
    getLogger().info("Hello, world!");
  }
}

プラグインがロードされた際に、ログに Hello, world! と表示するだけのプラグインだ。

これをコンパイルする。

$ javac -cp spigot-api.jar HelloPlugin.java

次に、プラグインの構成情報のためのファイル、plugin.yml を作成する。プラグインにはこれが必要になる。

# プラグイン名
name: hello
# プラグインのメインクラス。パッケージ付きのフルネームを書く
main: HelloPlugin
# プラグインのバージョン
version: 1.0

ここまででファイルは以下のような感じ。

.
|- HelloPlugin.class
|- HelloPlugin.java
|- hello.jar
|- plugin.yml
`- spigot-api.jar

プラグインに必要な plugin.ymlHelloPlugin.class を jar で固めて、サーバの plugins ディレクトリに放り込む。

$ jar cf hello.jar HelloPlugin.class plugin.yml
$ cp hello.jar ../spigot-server/plugins

あとはサーバを起動すれば、プラグインが読み込まれる。

$ cd ../spigot-server
$ ./start.sh
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128M; support was removed in 8.0
Loading libraries, please wait...
[22:18:07 INFO]: Starting minecraft server version 1.9
[22:18:07 INFO]: Loading properties
[22:18:07 INFO]: Default game type: SURVIVAL
[22:18:08 INFO]: This server is running CraftBukkit version git-Spigot-6f291ea-55a8535 (MC: 1.9) (Implementing API version 1.9-R0.1-SNAPSHOT)
[22:18:08 INFO]: Server Ping Player Sample Count: 12
[22:18:08 INFO]: Using 4 threads for Netty based IO
[22:18:08 INFO]: Debug logging is disabled
[22:18:08 INFO]: Generating keypair
[22:18:09 INFO]: Starting Minecraft server on *:25565
[22:18:09 INFO]: Using epoll channel type
[22:18:09 INFO]: Set PluginClassLoader as parallel capable
[22:18:09 INFO]: [hello] Loading hello v1.0
[22:18:09 INFO]: **** Beginning UUID conversion, this may take A LONG time ****

...(中略)...

[22:10:53 INFO]: Preparing spawn area: 66%
[22:10:53 INFO]: Preparing start region for level 2 (Seed: 5519750147800174725)
[22:10:54 INFO]: [hello] Enabling hello v1.0
[22:10:54 INFO]: [hello] Hello, world!
[22:10:54 INFO]: Server permissions file permissions.yml is empty, ignoring it
[22:10:54 INFO]: Done (9.339s)! For help, type "help" or "?"
>

ログに Hello, world! と表示されている。[hello]プラグイン名だ。

Kotlin とは

https://kotlinlang.org/

JVM で動く実用的な言語。Android とかで動かすことも考えて作られてる。 先月の 2/15 に待望のバージョン 1.0 がリリースされた。

まとめると、私もよくわかってない。わかってないので今回触ってみることにした感じ。

インストール

今回はコマンドラインで動くコンパイラを入れる。

$ cd ..
$ curl -sL https://github.com/JetBrains/kotlin/releases/download/build-1.0.0/kotlin-compiler-1.0.0.zip -o kotlin-compiler-1.0.0.zip
$ unzip -q kotlin-compiler-1.0.0.zip
$ PATH=$PATH:$PWD/kotlinc/bin

$PATH はとりあえず設定したけど、必要なら .bachrc などできちんと設定すること。

Kotlin でプラグインを作ってみる

Kotlin もインストールできたので、プラグインを作ってみる。まずはディレクトリの作成。

$ mkdir ../kotlin-plugin
$ cd ../kotlin-plugin

ライブラリと plugin.yml も先に用意しちゃう。

$ cp ../spigot/Spigot/Spigot-API/target/spigot-api-1.9-R0.1-SNAPSHOT.jar spigot-api.jar
name: kotlin
main: KotlinPlugin
version: 1.0

そして Kotlin のソースコードkotlin.kt を作る。

import org.bukkit.plugin.java.JavaPlugin

class KotlinPlugin : JavaPlugin() {
    override fun onEnable() {
        getLogger().info("Hello, Kotlin!")
    }
}

コンパイルする。

$ kotlinc -cp spigot-api.jar kotlin.kt -include-runtime -d kotlin.jar

-include-runtime は、Kotlin のランタイムを jar に同梱して standalone にするためのオプション。実を言うと現段階のコードでは Kotlin のランタイムに依存していないので、これがなくても動くのだけど、書いていったらどうせ依存すると思うので今から入れておく。

さて、jar はできたけど、これには plugin.yml が含まれていないので入れる。

$ jar uf kotlin.jar plugin.yml

そしてサーバの plugins へ放り込んで動作確認。

$ cp kotlin.jar ../spigot-server/plugins
$ cd ../spigot-server
$ ./start.sh
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128M; support was removed in 8.0
Loading libraries, please wait...
[05:24:36 INFO]: Starting minecraft server version 1.9
[05:24:36 INFO]: Loading properties
[05:24:36 INFO]: Default game type: SURVIVAL
[05:24:37 INFO]: This server is running CraftBukkit version git-Spigot-6f291ea-55a8535 (MC: 1.9) (Implementing API version 1.9-R0.1-SNAPSHOT)
[05:24:37 INFO]: Server Ping Player Sample Count: 12
[05:24:37 INFO]: Using 4 threads for Netty based IO
[05:24:37 INFO]: Debug logging is disabled
[05:24:37 INFO]: Generating keypair
[05:24:37 INFO]: Starting Minecraft server on *:25565
[05:24:37 INFO]: Using epoll channel type
[05:24:37 INFO]: Set PluginClassLoader as parallel capable
[05:24:38 INFO]: [kotlin] Loading kotlin v1.0
[05:24:38 INFO]: [hello] Loading hello v1.0
[05:24:38 INFO]: **** Beginning UUID conversion, this may take A LONG time ****

...(中略)...

[05:24:46 INFO]: Preparing spawn area: 68%
[05:24:47 INFO]: Preparing start region for level 2 (Seed: 5519750147800174725)
[05:24:48 INFO]: [kotlin] Enabling kotlin v1.0
[05:24:48 INFO]: [kotlin] Hello, Kotlin!
[05:24:48 INFO]: [hello] Enabling hello v1.0
[05:24:48 INFO]: [hello] Hello, world!
[05:24:48 INFO]: Server permissions file permissions.yml is empty, ignoring it
[05:24:48 INFO]: Done (10.087s)! For help, type "help" or "?"
>

ちゃんと動いてる。

もうちょっと実践的なやつを作ってみる

せっかくなので何か簡単に作ろうと思う。というわけで、木を破壊したらその木より上にある木も全部破壊してくれる、いわゆる木こり機能を作ってみる。

コードは Gist に貼っておく。以下。Kotlin のコードをまともに書いたのは今回が初めてなので、Kotlin らしくなっていいるかはわからない。良ければアドバイスください(Gistにコメントなど歓迎)。

kikori mod for Spigot by Kotlin

Spigot では様々なイベントに対して処理を行える。Listener インターフェースを実装したクラスで @EventHandler アノテーションをつけたメソッドを定義し、そこでイベントを受け取る。引数のイベントの型で受け取れるイベントが決まり、イベントのオブジェクトからイベントに関する情報が取れる。今回使った BlockBreakEvent なら、壊れたブロックや壊したプレイヤーの情報が取得できる。あとはこのクラスをプラグインマネージャに登録すればよい。

今まで通りビルドすれば動かせる。

$ kotlinc -cp spigot-api.jar kikori.kt -include-runtime -d kikori.jar
$ jar uf kikori.jar plugin.yml
$ cp kikori.jar ../spigot-server/plugins

結果は自分の手でご確認ください。(動画とか用意するの面倒)

参考リンク