Skip to Content
MoripaFishing

API利用ガイド

API利用ガイド(アドオン開発者向け)

MoripaFishingは、アドオン開発者がプラグインの機能を拡張できるAPIを提供しています。 このガイドでは、APIの導入方法から基本的な使い方までを解説します。

依存関係の追加

リポジトリの設定

MoripaFishing APIはMaven Centralで公開されています。 認証不要で利用できます。

Gradle (Kotlin DSL)

build.gradle.kts
repositories {
    mavenCentral()
}

dependencies {
    compileOnly("party.morino:moripafishing-api:<version>")
}

Gradle (Groovy)

build.gradle
repositories {
    mavenCentral()
}

dependencies {
    compileOnly 'party.morino:moripafishing-api:<version>'
}

Maven

pom.xml
<dependencies>
    <dependency>
        <groupId>party.morino</groupId>
        <artifactId>moripafishing-api</artifactId>
        <version>VERSION</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Integration 向けの SPI (WorldStateApplier / WorldLifecycle / WeatherSource) も同じ moripafishing-api に含まれます (party.morino.moripafishing.api.core.world.spi パッケージ)。Integration の作り方は Integration とは を参照してください。

プラグイン定義ファイルの設定

MoripaFishing 本体は Paper プラグイン (paper-plugin.yml) として配布しているため、アドオンも paper-plugin.yml で Core への必須依存を宣言します。こうすると Core が先に有効化されるため、onEnable からそのまま API を取得できます。

paper-plugin.yml
dependencies:
  server:
    MoripaFishing:
      load: BEFORE
      required: true

APIの取得

APIの取得口はMoripaFishingAPIProviderです。コア(MoripaFishing)は有効化時にAPIをBukkitのServicesManagerへ登録し、無効化時に解除します。MoripaFishingAPIProviderはその登録を引くだけの状態を持たないファサードです。 プラグイン名でのキャスト(getPlugin("MoripaFishing"))には依存しないでください。

Kotlin

import party.morino.moripafishing.api.MoripaFishingAPIProvider

// コア未初期化時は IllegalStateException
val api = MoripaFishingAPIProvider.get()

// null 許容で取得したい場合
val apiOrNull = MoripaFishingAPIProvider.getOrNull()

Java

import party.morino.moripafishing.api.MoripaFishingAPI;
import party.morino.moripafishing.api.MoripaFishingAPIProvider;

// @JvmStatic のため、Java からも静的メソッドとして呼び出せます
MoripaFishingAPI api = MoripaFishingAPIProvider.get();

ServicesManager 経由の取得

MoripaFishingAPIProviderと同じく、BukkitのServicesManagerから直接取得することもできます。

val api = Bukkit.getServicesManager().load(MoripaFishingAPI::class.java)
    ?: error("MoripaFishing is not loaded")

利用可能なマネージャー

APIインスタンスから以下のマネージャーを取得できます。

マネージャーメソッド説明
ConfigManagergetConfigManager()プラグイン設定(config.json)の取得
FishManagergetFishManager()魚の登録・取得
RarityManagergetRarityManager()レアリティの登録・取得
FishDrawergetFishDrawer()魚の抽選
WorldManagergetWorldManager()釣り場ワールドの管理
AnglerManagergetAnglerManager()つりびと(プレイヤー)の取得

主なマネージャーのメソッドは次のとおりです。

  • FishManager: getAll() / get(id) / getByRarity(rarity) / register(fish) / unregister(id)
  • RarityManager: getAll() / get(id) / register(rarity) / unregister(id)
  • FishDrawer: draw(worldId) / roll(fishData)
  • WorldManager: getDefaultWorldId() / getWorlds() / getWorld(id) / getWorldByKey(key) / getGeneratorIds() / createWorld(id, generatorId) / deleteWorld(id) / updateWorldConfig(config) / reload()
  • AnglerManager: getAngler(anglerId) / getOnlineAnglers()

FishManager / RarityManager の register は検証に失敗すると IllegalArgumentException を投げます。register で追加した定義は /mf reload で破棄されるため、必要なら MoripaFishingReloadedEvent で登録し直してください。

WorldManager の取得系メソッドは任意のスレッドから呼べます。変更系 (createWorld / deleteWorld / updateWorldConfig / reload) はメインスレッドから呼んでください。 独自の天候ソースは ServicesManager に登録します。詳細は WeatherSource の登録 を参照してください。

使用例

魚の一覧を取得する

val fishManager = api.getFishManager()
val allFish = fishManager.getAll()
allFish.forEach { fish ->
    // displayName は Map<Locale, String> なので、ロケールを指定して取り出す
    val name = fish.displayName[Locale.JAPAN] ?: fish.id.value
    println("${fish.id.value}: $name")
}

レアリティ別に魚を取得する

val fishManager = api.getFishManager()
val rarityId = RarityId("legendary")
val legendaryFish = fishManager.getByRarity(rarityId)

つりびとの情報を取得する

AnglerId はプレイヤーの Minecraft UUID そのもので、JSON では UUID 文字列になります。 getAngler は存在確認をせずに常に Angler を返します。オフラインのつりびとでは getWorld() と getLocation() が null になります。

val anglerManager = api.getAnglerManager()
val angler = anglerManager.getAngler(AnglerId(player.uniqueId))
println("Name: ${angler.getName()}")
println("World: ${angler.getWorld()?.getId()?.value}")

魚を抽選する

val fish = api.getFishDrawer().draw(FishingWorldId("default")) ?: return
println("${fish.data.id.value} (${fish.rarity.id.value}): ${fish.size}cm, worth ${fish.worth}")

ワールドの一覧を取得する

getWorlds() と getWorld(id) は、設定ファイルが存在する 定義済み の釣りワールドを返します。 対応する Bukkit ワールドがロードされているかどうかは Bukkit.getWorld(world.getWorldKey()) で確認します。

val worldManager = api.getWorldManager()
worldManager.getWorlds().forEach { world ->
    val loaded = Bukkit.getWorld(world.getWorldKey()) != null
    println("World: ${world.getId().value} / ${world.getWeather()} / loaded=$loaded")
}

Bukkit ワールドから釣りワールドを引く

Bukkit ワールドが釣りワールドかどうかは、キーで getWorldByKey を引いて判定します。

val fishingWorld = api.getWorldManager().getWorldByKey(player.world.key)
if (fishingWorld != null) {
    player.sendMessage("ここは釣りワールド ${fishingWorld.getId().value} です (天候: ${fishingWorld.getWeather()})")
}

ワールドの設定を変更する

FishingWorld は読み取り専用です。設定を変えるには、getConfig() の値をもとに新しい設定を作り、 メインスレッドから updateWorldConfig に渡します。保存とロード済みワールドへの適用が即座に行われます。

val worldManager = api.getWorldManager()
val world = worldManager.getWorld(FishingWorldId("default")) ?: return
val config = world.getConfig()
worldManager.updateWorldConfig(
    config.copy(
        spawn = SpawnPoint(0.0, 70.0, 0.0, 90f, 0f),
        border = BorderConfig(centerX = 0.0, centerZ = 0.0, size = 200.0),
    ),
)

ワールドを作成する

ワールド作成の実体は WorldLifecycle Integration が提供します。 未導入の環境では createWorld が false を返し、getGeneratorIds() は空を返します。 サーバーの起動完了 (ServerLoadEvent) 前に呼んだ createWorld も false を返すため、onEnable からではなく起動後に呼んでください。

val worldManager = api.getWorldManager()
val generatorIds = worldManager.getGeneratorIds()
// メインスレッドから呼ぶ
val created = worldManager.createWorld(FishingWorldId("my_world"), "terra")

WeatherSource の登録

アドオンは WeatherSource を実装して独自の天候ソースを提供できます。 Bukkit の ServicesManager に登録し、ワールドの気候で {"type": "external", "source": "<キー>"} を指定すると、 そのワールドの天候がアドオンに委ねられます。

import net.kyori.adventure.key.Key
import org.bukkit.plugin.ServicePriority
import party.morino.moripafishing.api.core.world.spi.WeatherSource
import party.morino.moripafishing.api.model.world.FishingWorldId
import party.morino.moripafishing.api.model.world.WeatherType

class MyWeatherSource : WeatherSource {
    override val key: Key = Key.key("myplugin", "realistic")

    // true: この天候をワールドへ適用させる (Weather Integration 導入時)。false: 魚の条件にのみ使う
    override val appliesToWorld: Boolean = true

    // 任意のスレッドから呼ばれる。Bukkit API を直接呼ばず、自前の状態だけを読むこと
    override fun getWeather(
        worldId: FishingWorldId,
        worldKey: Key,
    ): WeatherType = WeatherType.SUNNY
}

override fun onEnable() {
    server.servicesManager.register(WeatherSource::class.java, MyWeatherSource(), this, ServicePriority.Normal)
}
  • 登録はプラグインの無効化時に Bukkit が自動で解除します。
  • 同じ key の登録が複数ある場合は、優先度の高い登録が使われます。
  • Core は天候を決めるたびに ServicesManager を引き直すため、後から登録したソースも次の判定から使われます。
  • 一致するソースが登録されていない間は、内蔵の天候カレンダー (random の既定値) で天候を決め、警告を一度だけ出します。

イベント

MoripaFishingは、釣果・ワールド・天候・設定に関するBukkitイベントを発火します。 いずれもmoripafishing-apiに含まれ、通常のBukkitリスナーで購読できます。

イベントパッケージCancellable発火タイミング
AnglerFishCaughtEventapi.event.fishing✔釣り上げの確定直前。キャンセルするとバニラの釣り上げごと取り消される。setCaughtFishで釣果を差し替え可能
AnglerFishCaughtResultEventapi.event.fishing✘釣り上げ処理の完了後。通知・記録などの副作用はこちらを購読する
AnglerJoinTeleportEventapi.event.angler✔参加時テレポートの実行直前(WorldLifecycle Integration が発火)。setDestinationで移動先を変更可能
FishingWorldLoadEventapi.event.world✘定義済みの釣りワールドの Bukkit ワールドがロードされ、状態の適用を終えた後(Bukkit の WorldLoadEvent に追従。サーバー起動時点でロード済みのワールドと、/mf reload で新たに定義されたロード済みのワールドにも発火)
FishingWorldUnloadEventapi.event.world✘定義済みの釣りワールドの Bukkit ワールドのアンロード時(Bukkit の WorldUnloadEvent に追従。プラグイン無効化時には発火しない)
FishingWorldCreateEventapi.event.world✘createWorld の成功後(作成したワールドの FishingWorldLoadEvent の後)
FishingWorldDeleteEventapi.event.world✔deleteWorld による削除の直前。キャンセルで削除中止
FishingWorldDeletedEventapi.event.world✘削除の完了後(ロード済みだった場合は FishingWorldUnloadEvent の後)
FishingWorldWeatherChangeEventapi.event.world✘魚の抽選条件に使う天候 (getWeather()) の変化時。常にメインスレッドで発火し、リフレッシュ間隔ごとに検知する。初回の観測では発火しない
MoripaFishingReloadedEventapi.event✘/mf reloadで設定・ワールド・レアリティ・魚・翻訳の再読み込みがすべて完了した後。設定由来の値をキャッシュしている場合はここで再取得する

パッケージはいずれもparty.morino.moripafishing.配下です(例: party.morino.moripafishing.api.event.fishing.AnglerFishCaughtEvent)。

AnglerFishCaughtEvent

つりびとが魚を釣ったときに発生するイベントです。キャンセルや釣果の差し替えができます。 差し替えた釣果はアイテム化と後続のAnglerFishCaughtResultEventに反映されます。

import party.morino.moripafishing.api.event.fishing.AnglerFishCaughtEvent

class MyListener : Listener {
    @EventHandler(ignoreCancelled = true)
    fun onFishCaught(event: AnglerFishCaughtEvent) {
        val angler = event.getAngler()
        val caughtFish = event.getCaughtFish()

        // 釣れた魚の情報を取得
        val fishId = caughtFish.fish
        val size = caughtFish.size
        val rarity = caughtFish.rarity
        val worth = caughtFish.worth

        // つりびとの情報
        val playerName = angler.getName()

        // イベントのキャンセル(釣り上げ自体を取り消す)
        // event.isCancelled = true

        // 釣果の差し替え
        // event.setCaughtFish(caughtFish.copy(worth = worth * 2))
    }
}

AnglerFishCaughtResultEvent

釣り上げ処理が完了した後に発生するモニター用途のイベントです。 AnglerFishCaughtEventがキャンセルされず、釣果のアイテム化まで完了した場合にのみ発火します。 アナウンスやログ記録などの副作用はこちらを購読してください。

import party.morino.moripafishing.api.event.fishing.AnglerFishCaughtResultEvent

class MyListener : Listener {
    @EventHandler
    fun onFishCaughtResult(event: AnglerFishCaughtResultEvent) {
        val caughtFish = event.getCaughtFish()
        // 確定した釣果に対する通知・記録など
    }
}

CaughtFish のフィールドは CaughtFish を参照してください。

ワールド関連のイベント

釣りワールドのロード・アンロード・作成・削除・天候変化に対応するイベントです。 いずれも getWorldId() で対象の FishingWorldId を取得できます。FishingWorldDeleteEvent だけが Cancellable を実装しており、削除を中止できます。

import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import party.morino.moripafishing.api.model.world.FishingWorldId
import party.morino.moripafishing.api.event.world.FishingWorldDeleteEvent

class ProtectWorldListener : Listener {
    private val protected = setOf(FishingWorldId("protected_world"))

    @EventHandler(ignoreCancelled = true)
    fun onWorldDelete(event: FishingWorldDeleteEvent) {
        if (event.getWorldId() in protected) {
            event.isCancelled = true
        }
    }
}

FishingWorldWeatherChangeEvent は getOldWeather() と getNewWeather() で変化前後の WeatherType を取得できます。常にメインスレッドで発火するため、ハンドラから Bukkit API をそのまま呼べます。

AnglerJoinTeleportEvent

つりびとが参加して釣り場ワールドへテレポートする直前に発火します。Cancellable であるため テレポート自体を中止できるほか、setDestination(LocationData) で行き先を差し替えることもできます。

import party.morino.moripafishing.api.event.angler.AnglerJoinTeleportEvent

@EventHandler
fun onJoinTeleport(event: AnglerJoinTeleportEvent) {
    val angler = event.getAngler()

    // 行き先を変更する
    event.setDestination(event.getDestination().copy(x = 100.0, z = 100.0))

    // テレポート自体を中止する
    // event.isCancelled = true
}

MoripaFishingReloadedEvent

/mf reload で設定・ワールド・レアリティ・魚・翻訳の再読み込みがすべて完了した後に、メインスレッドで発火します。 設定由来の値をキャッシュしているアドオンや、API の register で魚・レアリティを追加しているアドオンは、このイベントで再取得・再登録してください。

Dokka APIリファレンス

詳細なAPIリファレンスはDokkaを参照してください。

注意事項

注意

現在MoripaFishingは開発中のため、APIに破壊的な変更が行われる可能性があります。 バージョンアップ時は、リリースノートを確認してください。

On this page