プラグイン API(共有データ)
Storia Cluster のすべてのワーカーで共有するデータとメッセージ。プラグイン開発者向け。単体のサーバーでも動きます。
Storia Cluster では、各 Storia Worker がそれぞれプラグインを動かすので、プラグインのファイルやメモリは ワーカーごとに別々です。Storia のプラグイン API は、どのワーカーでも同じであるべきものを扱います。
- プラグインごとの キーと値のストア。Storia Relay が保管し、比較して入れ替え(CAS)、カウンター、すべてのワーカーへの 変更通知があります。
- すべてのワーカーの同じプラグインへの メッセージ。
単体の Storia サーバー(Cluster なし)でも同じ API が動きます。データはサーバーフォルダの storia-shared/ に保存され、
メッセージはそのサーバーに届きます。プラグインを 2 通り書き分ける必要はありません。
プレイヤーデータ(インベントリ・位置・効果・プレイヤーの PersistentDataContainer)、進捗、統計、地図、スコアボードは、
すでに Cluster が共有しています。API はプラグイン自身のデータに使ってください。
API を追加する#
リリース から storia-api-26.2-5.jar をダウンロードし、compileOnly の依存に
追加します(実行時のクラスは Storia サーバーに入っています)。
// build.gradle.kts
dependencies {
compileOnly("io.papermc.paper:paper-api:26.2.build.+") // または Folia の API
compileOnly(files("libs/storia-api-26.2-5.jar"))
}
ほかの Folia 用プラグインと同じく、plugin.yml に folia-supported: true を書きます。
共有データ#
import dev.storia.api.SharedStore;
import dev.storia.api.StoriaShared;
SharedStore coins = StoriaShared.get().store("myplugin"); // プラグインごとに 1 つの名前空間
coins.increment("coins." + uuid, 50) // どのワーカーからでも正確に加算
.thenAccept(total -> player.getScheduler().run(this,
task -> player.sendMessage("所持コイン:" + total), null));
coins.setString("motd", "ようこそ!");
coins.getString("motd").thenAccept(text -> getLogger().info(text));
| メソッド | 内容 |
|---|---|
get(key) / getString(key) |
値を読みます(なければ null)。 |
set(key, value) / setString(key, text) |
値を保存します。null ならキーを削除します。 |
delete(key) |
キーを削除します。 |
compareAndSet(key, expected, value) |
キーが expected(null = 存在しない)のときだけ変更します。成功するのは 1 台だけです。 |
increment(key, delta) |
10 進数の文字列で保存したカウンター(なければ 0)に加算し、新しい値を返します。 |
keys(prefix) |
prefix で始まるキーを並べて返します。 |
listen(listener) |
この名前空間のキーが、どのワーカーで(自分も含めて)変わっても呼ばれます。 |
- 名前空間:
a-z 0-9 _ . -の 1〜64 文字。プラグイン名を使ってください。 - キー:UTF-8 で 1〜100 バイト。値:1 MiB まで。
- 変更は Relay が受け取った順に反映され、通知もどのワーカーでもその順に届きます。
1 台だけが取れるロック#
store.compareAndSet("event-running", null, nodeName.getBytes())
.thenAccept(won -> { if (won) startEvent(); });
// ... あとで
store.delete("event-running");
メッセージ#
StoriaShared shared = StoriaShared.get();
shared.subscribe("myplugin:announce", message ->
Bukkit.getGlobalRegionScheduler().run(this, task ->
Bukkit.broadcast(Component.text(message.text()))));
shared.publish("myplugin:announce", "5 分後にイベントが始まります!");
メッセージは、すべてのワーカーのそのチャンネルの購読者に、送ったワーカー自身も含めて、Relay が受け取った順に届きます。
チャンネルは a-z 0-9 _ . : - の 1〜64 文字。message.node() は送り主です。
スレッド#
どの呼び出しも CompletableFuture を返し、待たされることはありません。結果・リスナー・メッセージの処理は Storia の
スレッドで動き、リージョンのスレッドではありません。ワールドやプレイヤーを触るときは、正しいリージョンに処理を
予約してください(player.getScheduler()、Bukkit.getRegionScheduler()、Bukkit.getGlobalRegionScheduler())。
リージョンのスレッドで join() を呼ばないでください。
名前空間・キー・値が不正なときは、その場で IllegalArgumentException になります。Relay に届かないときは future が
失敗で終わります。自動で溜めて送り直すことはしないので、失敗を扱ってください(再試行やプレイヤーへの案内など)。
データの保存場所#
| 構成 | 保存場所 |
|---|---|
| Cluster | Storia Relay の cluster-world/storia-shared/<名前空間>/(ワールドと一緒にバックアップ)。 |
| 単体のサーバー | サーバーフォルダの storia-shared/<名前空間>/。 |
キー 1 つが 1 ファイルで、ファイル名はキーを 16 進数にしたものです。単体サーバーのデータを Cluster に移すときは、
storia-shared/ フォルダを Relay の cluster-world/ にコピーしてください。