前までは、Minecraftと外部をやりとりするためにプラグインとAPIサーバ、Webサーバに分けるアプローチをとっていたが、やっぱりこれは
良くないように思える。WebSocketなどを用いた通信なども考えたが、単なる通信のみならそれでも出来そうだが、そのほかのことをするとなると
自分の技術力で実装できるかがわからない。
そこで、既存のフレームワークを用いることにした。
前にSpringBootをプラグインに埋め込むことをもくろんだが、ファイル構成が違ったりしてビルドに難ありという感じだった。
Kotlinという言語特性を活かせるKtorであれば、もしかしたら埋め込めるかもしれないと思った。
ということで実際に試してみる。
すでにMinecraftでは1.19がリリースされているが、今回想定するMinecraftバージョンは1.18.1とする。
とりあえず、いつものようにひな形をつくる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.jetbrains.kotlin.jvm") version "1.6.10"
id ("com.github.johnrengelman.shadow") version "7.1.2"
application
}
group = "io.github.rokuosan"
version = "1.0.0"
java.sourceCompatibility = JavaVersion.VERSION_17
val mcVersion = "1.18.1"
repositories {
mavenCentral()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
}
dependencies {
compileOnly("org.spigotmc:spigot-api:${mcVersion}-R0.1-SNAPSHOT")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
application {
mainClass.set("io.github.rokuosan.ktorintegration.AppKt")
}
tasks.withType<KotlinCompile>{
kotlinOptions{
jvmTarget = "17"
}
}
tasks.withType<ProcessResources>{
val props = mapOf("version" to version)
inputs.properties(props)
filteringCharset = "UTF-8"
filesMatching("plugin.yml"){
expand(props)
}
}
|
ここにKtor の依存関係を追加する。
1
2
3
4
5
6
7
|
dependencies {
// Ktor
implementation("io.ktor:ktor-server-core:2.0.3")
implementation("io.ktor:ktor-server-netty:2.0.3")
/* 以下省略 */
}
|
そして、applicationからメインクラスをセットする。
1
2
3
|
application {
mainClass.set("io.github.rokuosan.ktorintegration.AppKt")
}
|
次に実際にビルドができるかHello Worldで試してみる。
メインクラスに以下のように書き込んで確認する。
1
2
3
4
5
6
7
8
9
10
|
fun main(){
embeddedServer(Netty, 8080){
routing {
get("/"){
call.respondText("Hello World")
println("Connection OK")
}
}
}.start(true)
}
|
これはうまく動いた。
しかし起動時にSLF4Jがないというような警告を受けた。
これは依存関係を追加することで解決できた。
参考: https://ktor.io/docs/logging.html
dependenciesに追記して、
1
|
implementation("ch.qos.logback:logback-classic:1.2.11")
|
logback.xmlをresourcesに書く。
1
2
3
4
5
6
7
8
9
10
11
|
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="io.netty" level="INFO"/>
</configuration>
|
再度起動すると、このような表示に変わっている。
Ktorをビルドすることができたので、次はプラグインに埋め込む。
さっきかいたmain()があるファイルにJavaPluginクラスを継承したクラスを作成する。
onEnable()をオーバーライドする。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.bukkit.plugin.java.JavaPlugin
class App: JavaPlugin() {
override fun onEnable() {
embeddedServer(Netty, 8080){
routing {
get("/"){
call.respondText("Hello World")
println("Connection OK")
}
}
}.start(true)
}
|
最後にプラグインに必要なplugin.ymlをresourcesに作成した。
あとはfatJarとしてビルドする。
最後にMinecraftサーバーを起動する。
パット見成功したように思ったが、これ以降のログがKtorのみになった。
それに加えてMinecraftサーバーが動作しない。
もしかしたら、非同期のスケジューラを使用すれば起動できるかもしれない。
予想通り、非同期のスケジューラとして起動することで並行して処理ができる。
無名クラスとしてスケジューラを実装した。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
override fun onEnable() {
object: BukkitRunnable(){
override fun run(){
embeddedServer(Netty, 8080){
routing {
get("/"){
call.respondText("Hello World")
println("Connection OK")
}
}
}.start(true)
}
}.runTaskAsynchronously(this)
}
|
デメリットとしては、非同期のスケジューラではSpigotの機能を使用することができないので、Ktor側からMinecraft側の処理は直接行えない。
SpigotプラグインにKtorを埋め込んでみたが、思っていたより簡単だった。
今回作成したプログラムはGitHubに公開した。