2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Nukkit in Kotlin #3

Last updated at Posted at 2020-03-03

Nukkit in Kotlin #3

第三回目です。#2はこちら

やること

今回はAPIについて解説します。

外部APIの利用

他プラグインが提供しているAPIは多くありますが、今回はonebornさんのEconomyAPIを使用します。
多くのプラグインはServiceManagerというAPIを提供するための機能を使っておりません。もちろんもれなくEconomyAPIも例外ではなく、その上PluginBaseを継承したクラスにいろいろとメソッドを追加してしまっています。利用側としてはたまったものではありませんが、広く使われているらしいので我慢しましょう。
ServiceManagerについては自作APIの作り方で解説します。

まず、EconomyAPIをインストールするためにbuild.gradle.ktsの一部を以下のように変更してください。変更する個所は適宜変えてもらっても構いません。

build.gradle.kts
repositories {
    jcenter()
    maven {
        url = uri("https://repo.nukkitx.com/main/")
    }
    maven {
        url = uri("http://jenkins.onebone.me/plugin/repository/everything/")    //リポジトリがあるurl
    }
}

dependencies {
    implementation(kotlin("stdlib"))
    compileOnly("me.onebone", "economyapi", "2.0.1")
    compileOnly("cn.nukkit", "nukkit", "1.0-SNAPSHOT")    //グループID, アーティファクトID, バージョン
}

nukkit {
    main = "com.example.sample.SamplePlugin"
    api = listOf("1.0.9")
    authors = listOf("your name")
    depend = listOf("EconomyAPI")
}

build.gradle.ktsを変更する以外は大したことは行っていません。
リポジトリを指定し、dependenciesで対象のプロジェクトの情報をcompileOnlyの引数に指定します。

LoginBonusListener.kt
package com.example.sample

import cn.nukkit.event.EventHandler
import cn.nukkit.event.Listener
import cn.nukkit.event.player.PlayerJoinEvent

import me.onebone.economyapi.EconomyAPI

class LoginBonusListener : Listener {
    @EventHandler
    fun onPlayerJoin(event: PlayerJoinEvent) {
        EconomyAPI.getInstance().addMoney(event.player, 10);
    }
}

だいたいの使い方はGitHubなどに記載されています。

自作APIの作成

こちらが本記事のメインです。

API作成の手順は少し特殊です。
外部にはインターフェースのみ公開し、実装は漏らさないようにしましょう。

ディレクトリの構成は下記になります。

Sample/
  ├plugin/
  │  ├src/main/kotlin/com/example/sample/
  │  │  ├SamplePlugin.kt
  │  │  └SampleServiceImpl.kt
  │  └build.gradle.kts
  ├interface/
  │  ├src/main/kotlin/com/example/sample/SampleService.kt    //インターフェース
  │  └build.gradle.kts
  ├build.gradle.kts
  ├setting.gradle.kts
  ├...

複数のプロジェクトが一つにまとまっているマルチプロジェクト構成です。プロジェクトごとに分けても構いません。
基本的にbuild.gradle.ktsが存在するディレクトリの名前がサブプロジェクト名になります。

ServiceManagerを使って外部に公開する際は、インターフェースとそれを実装したクラスを用意します。利用側が使うのはインターフェースのみです。

settings.gradle.kts
rootProject.name = "Sample"

include("plugin", "interface")

includeにサブプロジェクトの名前を一つ一つ指定していきます。

Sample/build.gradle.kts
plugins {
    kotlin("jvm") version "1.3.61"
    id("com.github.johnrengelman.shadow") version "5.2.0"
}

allprojects {
    group = "com.example"

    repositories {
        jcenter()
        maven {
            url = uri("https://repo.nukkitx.com/main/")
        }
    }
}

tasks {
    compileKotlin {
        kotlinOptions.jvmTarget = "1.8"
    }
    compileTestKotlin {
        kotlinOptions.jvmTarget = "1.8"
    }
}

rootディレクトリ直下にあるbuild.gradle.ktsにはプロジェクト全体の設定などを主に書きます。

プラグイン本体をビルドするためのスクリプトです。

Sample/plugin/build.gradle.kts
plugins {
    kotlin("jvm")
    id("com.github.johnrengelman.shadow")
    id("net.minecrell.plugin-yml.nukkit") version "0.3.0"
}

version = "0.1.0"

tasks.shadowJar {
    archiveBaseName.set("Sample")    //成果物の名前 これがないとサブプロジェクトのディレクトリ名で名前が出力されてしまう
}

dependencies {
    implementation(kotlin("stdlib"))
    compileOnly(project(":interface"))    //サブプロジェクトへの依存関係。project(":<サブプロジェクト名>")で指定できる
    compileOnly("cn.nukkit", "nukkit", "1.0-SNAPSHOT")
}

nukkit {
    name = "Sample"
    main = "com.example.sample.SamplePlugin"
    api = listOf("1.0.9")
    author = "UramnOIL"
}

外部公開用のインターフェースをビルドするためのスクリプトです。

Sample/interface/build.gradle.kts
plugins {
    kotlin("jvm")
    id("com.github.johnrengelman.shadow")
}

version = "0.1.0"

tasks.shadowJar {
    archiveBaseName.set("SampleInterface")    //成果物の名前 これがないとサブプロジェクトのディレクトリ名で名前が出力されてしまう
}

dependencies {
    compileOnly(kotlin("stdlib"))    //外部に公開するプロジェクトのStdlibはcompileOnlyで指定する implementationで指定するとIDEで依存関係を解決できなくなる
    compileOnly("cn.nukkit", "nukkit", "1.0-SNAPSHOT")
}

ここまでがGradle側の設定です。
Gradleを初めて使う方には地獄の作業だと思います。コピペで構いません。

SampleService.kt
package com.example.sample

import cn.nukkit.Player

interface SampleService {
    fun sendHoge(player: Player)
}

外部公開用のインターフェースです。コンストラクタにリポジトリ等を渡しサービス上で操作するのが基本ですが、今回は簡単にPlayerString引数に取るメソッドを一つだけ用意します。

SampleServiceImpl.kt
package com.example.sample

import cn.nukkit.Player

class SampleServiceImpl : SampleService {
    override hoge(player: Player) {
        player.sendMessage("Hoge")
    }
}

インターフェースに対し実際に行う処理を書きます。

SamplePlugin.kt
package com.uramnoil.ktform

import cn.nukkit.plugin.PluginBase
import cn.nukkit.plugin.service.ServicePriority

class KtFormPlugin : PluginBase() {
    override fun onEnable() {
        server.serviceManager.register(SampleService::class.java, SampleServiceImpl(), this, ServicePriority.Normal)
    }
}

サービスを登録します。
ServiceManager#register()の第一引数にはインターフェースのClassクラス、第二引数にはインターフェースを実装したクラスのインスタンス、第三引数にはサービスを提供しているプラグインクラスのインスタンス、第四引数にはServicePriorityを渡します。

ビルド

  • ./gradlew :plugin:shadowJar
  • ./gradlew :interface:shadowJar

それぞれpluginサブプロジェクトとinterfaceサブプロジェクトの成果物を生成します。


これでAPIを提供するプラグインは完成です。

ServiceManagerを使いサービスを取得する

新しくプラグインを作ります。

####ディレクトリ構成

Sample2/
  ├libs/compile_only/Sample-1.0.0-all.jar
  ├...

Sample2ディレクトリにlibs/compile_onlyディレクトリを作成し、interfaceサブプロジェクトで生成した成果物を配置します。

build.gradle.kts
plugins {
	kotlin("jvm") version "1.3.60"
	id("net.minecrell.plugin-yml.nukkit") version "0.3.0"
	id("com.github.johnrengelman.shadow") version "5.2.0"
}

group = "com.example"
version = "1.0-SNAPSHOT"

repositories {
    jcenter()
    maven {
        url = uri("https://repo.nukkitx.com/main/")
    }
}

dependencies {
    implementation(kotlin("stdlib"))
    compileOnly("cn.nukkit:nukkit:1.0-SNAPSHOT")
    compileOnly(fileTree("libs/compile_only"))
}

tasks {
    compileKotlin {
        kotlinOptions.jvmTarget = "1.8"
    }
    compileTestKotlin {
        kotlinOptions.jvmTarget = "1.8"
    }
    
}

nukkit {
    name = "Sample2"
    main = "com.example.sample2.Sample2Plugin"
    api = listOf("1.0.9")
    authors = listOf("your name")
    depend = listOf("Sample")
}

#1で記述したbuild.gradle.ktsを新しいプラグイン用に調整したものです。
compileOnly(fileTree("libs/compile_only"))で依存関係に含めるディレクトリを指定します。

GreetListener.kt
package com.example.sample2

import cn.nukkit.event.EventHandler
import cn.nukkit.event.Listener
import cn.nukkit.event.player.PlayerJoinEvent
import cn.nukkit.plugin.service.RegisteredServiceProvider

import com.example.sample.SampleService

class HogeListener() : Listener {
    val sampleService = Server.getInstance().serviceManager.getProvider(SampleService::class.java)

    @EventHandler
    fun onPlayerJoin(event: PlayerJoinEvent) {
        sampleService.provider.sendHoge(event.player)
    }
}

ServiceManager#getProvider()の第一引数にインターフェースのClassクラスを渡すとそのインターフェースに対応したインスタンスを保持したRegisteredServiceProviderが返ってきます。RegisteredServiceProvider#getProvider()でサービスクラスを取得し操作を行ってください。

Sample2Plugin.kts
package com.example.sample2

import import cn.nukkit.plugin.PluginBase

class Sample2Plugin() : PluginBase() {
    override fun onEnable() {
        server.pluginManager.registerEvents(HogeListener(), this)
    }
}

./gradlew shadowJarを実行すると成果物が生成されます。

まとめ

結構ボリュームはありますが、この手順を踏むことで利用者へ簡単に機能を提供することができます。

宣伝

これを使えばNukkitプラグインをもっと楽に、Kotlinらしく記述することができます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?