Repository navigation
Extensions
Your plugin can share its data through WorldAPI without depending on it: no library to add, nothing of WorldAPI's on your classpath. Your plugin stays the source of truth. WorldAPI reads your resources when a client asks and keeps none of them; it records only the events you mark recorded, in the event history like its own.
Clients then read it at /api/v1/extensions and subscribe to your topics. Mo’Bosses works this way.
Declare WorldAPI as an optional dependency that loads before your plugin, in paper-plugin.yml:
dependencies:
server:
WorldAPI:
load: BEFORE
required: falseRegister in onEnable. If WorldAPI starts later, listen for ServiceRegisterEvent and register then.
WorldAPI registers a java.util.function.Function<Map<String, Any?>, Any?> in Bukkit's ServicesManager. Find the one whose plugin is WorldAPI:
@Suppress("UNCHECKED_CAST")
val bridge = Bukkit.getServicesManager().getRegistrations(java.util.function.Function::class.java)
.firstOrNull { it.plugin.name == "WorldAPI" }?.provider as? java.util.function.Function<Map<String, Any?>, Any?>Send it requests as maps of plain Java values. Every request carries v, the contract version (1 now). WorldAPI refuses a version newer than it knows, so check the answer's contract if you rely on something new. Answers are maps: ok, contract, version. A bad request throws IllegalArgumentException.
bridge?.apply(mapOf(
"v" to 1, "op" to "register", "plugin" to this, "namespace" to "mobosses", "description" to "Bosses and their hunters",
"resources" to listOf(
mapOf("path" to "hunters", "description" to "The best hunters", "public" to true,
"get" to java.util.function.Function<Map<String, String>, Any?> { topHunters() }),
mapOf("path" to "players/{player}", "description" to "A player's card", "public" to true,
"get" to java.util.function.Function<Map<String, String>, Any?> { card(it.getValue("player")) }),
mapOf("path" to "bosses", "description" to "Bosses alive, with coordinates", "mainThread" to true,
"get" to java.util.function.Function<Map<String, String>, Any?> { liveBosses() }),
),
"events" to listOf(
mapOf("name" to "boss.killed", "description" to "Players killed a boss", "public" to true, "recorded" to true),
),
))bridge?.apply(mapOf("v" to 1, "op" to "publish", "namespace" to "mobosses", "event" to "boss.killed",
"data" to mapOf("type" to "zombie", "level" to 40), "player" to player.uniqueId, "playerName" to player.name, "world" to world.name))| Op | Fields |
|---|---|
register |
plugin (your plugin), namespace (1 to 32 of a-z 0-9 _ -), description, resources, events. Registering again replaces the namespace; another plugin can't take it |
publish |
namespace, event, data (a map), and optionally player (a UUID), playerName, world. Safe from any thread; it reaches clients from the server thread |
unregister |
namespace. WorldAPI also drops a plugin's namespaces when it stops |
-
pathis segments ofa-z 0-9 _ -, or{name}for a parameter. -
gettakes the path parameters and the query parameters, as strings, and returns plain Java values: maps, lists, arrays, strings, numbers, booleans, and null. UUIDs, enums, and times turn into strings; anything else into itstoString(). -
getmay return aCompletableFutureof those too, which WorldAPI waits up to 10 seconds for. -
getruns on a worker thread, so it must be thread-safe, unless the resource setsmainThread: true. - Throw
IllegalArgumentExceptionfor 400,NoSuchElementExceptionfor 404.
Each becomes the topic ext.<namespace>.<name>, on the WebSocket and, when recorded, in GET /events. Their data loses location, position, and the other coordinate keys for visitors unless privacy.anonymous.coordinates is on, as with WorldAPI's own topics.
Everything a plugin shares needs a token, unless the plugin marks it public. Then visitors may read it too, unless the server's extensions.private lists its namespace (mobosses), its resource (mobosses/bosses), or its topic (ext.mobosses.boss.spawned). Visitors reach these through /extensions and /extensions/** in auth.anonymous.endpoints, which the default config has.
| Endpoint | Permission | What it does |
|---|---|---|
GET /extensions |
server:read |
Other plugins’ data: their namespaces, resources, and event topics |
GET /extensions/{namespace}/{path} |
server:read |
One resource of a plugin, read from the plugin as it is now |
-
GET /extensionslists the namespaces, with their resources and topics, that the client may see. -
GET /extensions/{namespace}/{path...}reads one resource, like/extensions/mobosses/players/Steve, straight from the plugin. - Subscribe to
ext.mobosses.boss.killedlike any topic;GET /events?type=ext.mobosses.boss.killedreturns the recorded ones.
Next: Java scripts.