WebAssembly
Limits
On this page
What bounds a plugin, and what happens when it reaches the bound. Every value here is a constant in the code cited beside it.
Fuel
A Wasm guest runs on fuel: a count of instructions per call. A guest that runs out traps, however deep in a loop it is, so a runaway guest is stopped rather than taking the tick with it. Fuel is refilled before every call. A native Rust plugin has no fuel: it is compiled into the server and trusted.
One ordinary call's fuel is 10,000,000 instructions. The server picks a budget for each call, as a multiple of that.
| Budget | Multiple | Fuel | Given to |
|---|---|---|---|
| Normal | ×1 | 10,000,000 | events in the game loop, every hook (packets included), news on a topic |
| Large | ×20 | 200,000,000 | command_run, server_started, server_stopping and settings_changed; calls from another plugin; menu clicks; the plugin's start |
| Extended | ×100 | 1,000,000,000 | the handlers the manifest names in extended |
| Stop | ×5 by default | 50,000,000 | rustmc_stop. Set with plugins.stop-fuel-multiple in rustmc.yml |
Fuel counts the guest's own instructions. The work a host call does on the server's side is not
counted, but reading and writing the JSON of a call is the guest's own work, so a big answer
costs fuel to parse. The guest SDK's host::calls_made() and host::bytes_received() let a
plugin measure what it has asked for.
When a call fails
A call that traps, runs out of fuel or panics (a Rust plugin) is skipped. The plugin is not unloaded.
- An event's failed handler counts as having passed, unless the manifest lists the event in
fail_closed: then it counts as a cancel. - A hook's failed handler passes: the game's own step stands.
- A plugin-bus call to a failed handler answers the caller with an error.
- A trapped Wasm instance is thrown away and replaced with a fresh one of the same module,
because a trap does not unwind and its memory may be half-changed. The fresh instance runs
rustmc_startagain (on the Large budget) before it is next asked anything, so it reads what the plugin saved. What the old instance held only in memory is lost. - A handler that fails 5 times in 1200 ticks (a minute) is paused for 1200 ticks. While
paused it is answered as a failure, so a
fail_closedevent stays cancelled. The plugin's other handlers keep running. - A plugin whose
startfails is not run at all, and/pluginsshows it stopped.
Sizes and counts
| Limit | Value | What happens past it | Source |
|---|---|---|---|
| Bytes in one transfer, either way (an event, a hook call, a host call's answer) | 4 MiB | cut to the limit and logged | rustmc-plugin-wasm/src/lib.rs:127, 604-613 |
| A plugin-bus request | 2 MiB | refused with an error | rustmc-plugin-wasm/src/call.rs:1626-1630 |
| Events a guest caused, waiting for its own call to return | 1024 | the newest is dropped and logged | rustmc-plugin-wasm/src/lib.rs:177, 840-854 |
| Events, hooks and bus calls nested inside each other | 8 deep | the chain is stopped: the event passes (or fails closed if its plugin says so), the hook runs the game's own step, the bus call is refused | rustmc-server/src/plugins.rs:107-117, 486, 663, 854, 902 |
| Wait for a guest busy on another thread, from inside another plugin | 250 ms | the inner call is skipped and logged | rustmc-plugin-wasm/src/lib.rs:89-92 |
Blocks in one set_blocks, entries in one set_blocks_data | 4096 | error | rustmc-plugin/src/value.rs:519 |
Rows in one storage_put_many | 16,384 | error | rustmc-plugin/src/value.rs:529 |
Entities in one move_entities | 1024 | error | rustmc-plugin-wasm/src/call.rs:31 |
| Entries in one tab-list, nametag or score batch | 1000 | error | rustmc-plugin/src/tab.rs:13 |
Viewers in one set_hidden | 4096 | error | rustmc-plugin-wasm/src/call.rs:43 |
entities_in radius | 128 blocks | error | rustmc-plugin/src/value.rs:638 |
read_blocks box | 16 chunk columns; 512 KiB of block-entity data | error | rustmc-plugin/src/value.rs:533-537 |
raycast distance | 512 blocks | error | the raycast doc comment |
| Chunk coordinates in a request | within 1,875,000 of the origin | error | rustmc-plugin-wasm/src/call.rs:1866-1894 |
| A plugin's name | 64 characters | not loaded | rustmc-server/src/plugins.rs:1169 |
| A setting's text | 16,384 characters | refused | rustmc-plugin/src/setting.rs:72 |
Each host call's own limits are in its doc comment on its reference page, with the router's error messages for its fields.
Concurrency
One call at a time per guest: a Wasm instance is one store behind one lock. A guest is never entered again while it runs on the same thread. Events it causes are queued until its call returns; a hook call or a bus message that would re-enter it is skipped and logged.
A Rust plugin's Plugin methods take &self and may be called from several threads at once. It
keeps its own state behind its own lock.
From docs/api/wasm/limits.md in the RustMC repository at bbd81209ad.