Reference

Manifest

On this page

A plugin's manifest tells the server who the plugin is and what it wants. The server reads it once, at load, so it can decide whether to call a plugin without calling it. A Rust plugin returns a Manifest from Plugin::manifest. A Wasm guest emits it as JSON from rustmc_manifest (the ABI).

Every field but name may be left out. Unknown JSON fields are ignored.

FieldRustWasm JSONDefaultMeaning
nameManifest::new(name, ..)"name"requiredunique among loaded plugins; letters, digits, _ and -, 1 to 64 characters. It is the key for the plugin's stored data, its folder plugins/<name>/, its log tag, and the from other plugins see on the bus
versionManifest::new(.., version)"version""0"shown by /plugins
events.hearing(&[..])"events": [..]all eventsevent names this plugin hears
priority.at(Priority::..)"priority"normalfirst, early, normal, late or observer: the order handlers run in, for events and hooks
commands.with_command(Command::new(name, description, level)), .governed_by(node)"commands": [{"name", "description", "level", "node"}]nonecommands this plugin answers to, without the slash (below)
hooks.hooking(point, Mode::..)"hooks": [{"point", "mode"}]nonehook points and the mode for each: prepend, override or append
listens.listening(&[topic])"listens": [topic]noneplugin-bus topics heard: exact, family.* or * (the bus)
settings.with_setting(Setting::new(..))"settings": [{..}]nonesettings the server keeps and the web portal edits (below)
pages.with_page(Page::new(id, title))"pages": [{"id", "title"}]nonepages this plugin adds to the web portal (below)
disables.disabling(&[system])"disables": [system]nonevanilla systems switched off while the plugin is loaded (below)
fail_closed.failing_closed(&[event])"fail_closed": [event]nonecancellable events whose handler, if it fails or is paused, cancels rather than passes
extended.extending(&[key])"extended": [key]nonehandlers given the extended fuel budget (limits)

events and hooks default in opposite directions on purpose. Being told about something costs a plugin nothing it did not ask for. Being run instead of the game is something a plugin has to ask for.

No manifest field grants access to host calls: every plugin may make every call. What reaches outside the server (outbound HTTP to a fixed URL, listening on a port) needs the server owner's grant in rustmc.yml instead (host calls).

Load-time checks

A manifest is refused, and the plugin not loaded, when:

  • the name is empty, longer than 64 characters, or holds anything but letters, digits, _ and -;
  • a plugin of the same name is already loaded;
  • a setting's key is not [a-z0-9_]{1,64}, is declared twice, is an enum with no values, or has a default its own type refuses;
  • a page id is not [a-z0-9_-]{1,32};
  • (Wasm) a hook names no point, or a mode other than prepend, override or append;
  • (Wasm) a command names no name, or a setting has an unknown type;
  • (Wasm) hooks is not empty and the module does not export rustmc_on_hook, or listens is not empty and it does not export rustmc_on_message.

A plugin that names extended handlers is logged once at load, so the owner can see it.

Subscription rules

  • Events. Exact names from the event list. No families, no wildcards. An empty or missing list hears every event. A name that is no event matches nothing, so ["none"] hears nothing.
  • Hooks. A point is an exact name (block.hardness) or a family ending in .* (packet.play.*, command.*), or * for every hook point. One plugin may hook the same point in several modes.
  • Topics (listens) take exact names, family.*, or *.
  • Handler keys (extended) are an event name, hook:<point> (a family as hooks takes it), menu (clicks in this plugin's menus), call (other plugins' calls) or topic:<topic>.
  • fail_closed names events. Only a cancellable event can fail closed; any other name is ignored. A Wasm guest that was skipped because it was busy on another thread also counts as failed here.

Commands

FieldMeaning
namewithout the slash
descriptionone line, for /help
levelthe operator level needed, 0 to 4, on the game's scale. Default 0
nodethe permission node that governs it, passed to a permissions plugin's permission.commands hook. Optional

A command is sent to clients in the command tree, so it appears in tab completion. When a player runs it, the plugin hears command_run with command (the name) and rest (the line after it, unparsed). Completions while typing are asked of the plugin through rustmc_on_message (the ABI). Each command is also a hook point, command.<name> (commands).

Settings

Each entry of settings is {"key", "type", "default", "title"?, "help"?, "group"?, "min"?, "max"?, "max_length"?, "max_items"?, "values"?, "secret"?}.

typeValueLimits
booleantrue / false
integerwhole numbermin, max
numbernumbermin, max
stringone line, no control charactersmax_length (at most 16384 characters)
textseveral lines; tabs and line breaks allowedmax_length (at most 16384 characters)
enumone of valuesvalues must not be empty
listlines of textmax_items; 16384 characters in all
durationwhole seconds, 0 or moremax is the most seconds
iteman item id; stone becomes minecraft:stone
blocka block id, read the same way
playera player name: 1 to 16 letters, digits or _

title is the form's label (the key when empty), help one line under it, and settings with the same group are drawn together. A secret setting (a password, a token) is never sent back to a browser in full. sql_submit reads a database URL only from a secret setting.

The server keeps the values in plugins/<plugin>/settings.json. The plugin reads them with the settings host call and changes its own with setting_set. When anybody changes them, the plugins that hear settings_changed are told, except a plugin changing its own.

Pages

A page adds a tab to the web portal. id is [a-z0-9_-]{1,32}, unique in the plugin, and title is what the tab says. The portal asks for a page over the plugin bus, as a call: {"op": "portal.page", "page", "viewer"}, and sends a page's actions the same way: {"op": "portal.action", "page", "action", "values", "viewer"}. The plugin answers {"components": [...]}: data the portal draws, never markup or script.

The call comes from rustmc:portal, a name no plugin can have, and viewer is the portal user looking. An answer with more than 200 components, or more than 256 KiB of JSON, is refused, as is one that is not {"components": [...]}. An answer {"error": "..."} is shown as an error.

Each component is an object with a type. Every string is drawn as text, never as markup.

typeFieldsDrawn as
headingtexta heading
texttexta paragraph
noticetext, tone? (good or bad)a status line; bad is drawn as an error
statsitems: [{label, value}]a row of cards
tablecolumns: [string], rows: [{key?, cells: [value]}], row_actions?: [{action, label, danger?}], empty?a table; empty (default "Nothing yet.") when there are no rows. A row action sends {key} as its values
formaction, submit? (default "Save"), fields: [{key, type?, label?, value?, values?}]a form. A field's type is boolean (a checkbox), enum (a list of values), integer or number; anything else is a text box. Submitting sends the fields' values
buttonaction, label, danger?a button that sends no values

A component of another type is not drawn. A component that fails to draw is replaced by a message in its place only. Whatever an action answers replaces the whole page. The guest SDK's portal module (sdk/src/portal.rs) builds these components.

Switching off vanilla systems

A gamemode that replaces one of the game's own systems names it in disables, and the server stops running it for as long as the plugin is loaded and alive. A plugin that panics and is unloaded hands the system back.

NameWhat stops
hungerfood, saturation and exhaustion stop ticking, and with them starving and the healing food gives. The food bar is the plugin's to set
mob_spawningevery creature the world makes on its own: the natural spawner, a new chunk's animals, patrols, phantoms, wandering traders, a storm's skeleton trap horse, a portal's zombified piglins, zombie reinforcements, a pearl's endermite, a creaking heart's creaking, a shrieker's warden, villagers' iron golems and raids. What a player, a command, a plugin or a spawner block makes goes on. See below
natural_regenthe healing a full food bar gives and Peaceful's own, as the natural_health_regeneration game rule turned off does, without changing the rule

What mob_spawning stops

The rule: switched off, mob_spawning acts as if every game rule that gates the world's own spawning were off -- spawn_mobs (and with it spawn_monsters's spawns), spawn_patrols, spawn_phantoms, spawn_wandering_traders, spawn_wardens and raids -- and villagers summon no iron golems, which have no rule of their own. Everything those rules leave running still runs, and the two spawner blocks keep their own rules (spawner_blocks_work, and the trial spawner's spawn_mobs): they are blocks built to spawn, and a gamemode decides whether its world has any. The game rules themselves are not changed.

Stops:

SpawnWhy
The natural spawner, every category (monsters, animals, water and cave creatures, slimes, drowned)spawn_mobs
A new chunk's packs of animalsspawn_mobs (spawnMobsForChunkGeneration)
A thunderstorm's skeleton trap horsespawn_mobs (tickThunder); the bolt lands as an ordinary strike instead
A Nether portal's zombified piglinsspawn_mobs and spawn_monsters (isSpawningMonsters)
A hurt zombie's reinforcementsspawn_mobs and spawn_monsters
The endermite a landing ender pearl may leavespawn_mobs and spawn_monsters
A creaking heart's creakingspawn_mobs and spawn_monsters
Pillager patrolsspawn_patrols
Phantomsspawn_phantoms
The wandering trader and its llamasspawn_wandering_traders
A sculk shrieker's warden (the shrieker answers nobody)spawn_wardens
Raids: none opens, and one under way when the plugin loads is stoppedraids
Villagers summoning an iron golemthe village's own population, with no rule

Goes on:

SpawnWhy
Spawn eggs, used or dispensed; /summon; a plugin's spawn callsasked for by a player, a command or a plugin
Breeding, a turtle's eggs and frogspawn hatching, a tadpole growing up, an allay duplicatinga player's doing, or a creature's own offspring
A built iron golem, snow golem or wither; a creature from a bucketbuilt by a player
Spawner blocks and trial spawnersblocks built to spawn, with their own rules
A structure's fixed creatures (a monument's elder guardians, an igloo's villager) and the End's dragon and crystalspart of what was generated, as with spawn_mobs off
A creature changing or dividing: conversions, lightning's transformations, a slime splittingnot a new creature the world chose to add
A creature's own attack or trap: an evoker's vexes and fangs, a trap horse's riders when a player comes near, a hive's bees, infested and oozingthe creature already exists; spawn_mobs does not stop them either

The server reads all of these through one helper (spawn::world_spawns), and a test refuses any other code that reads the spawning rules directly, so a new spawn path cannot forget the switch.

Other names are ignored rather than refused, so a plugin written for a newer server still loads on an older one. It asks the disabled_systems host call which of its names were honoured. A server where no plugin asks pays one atomic read per check.

From docs/api/reference/manifest.md in the RustMC repository at bbd81209ad.