Guides

Your first WebAssembly plugin

A WebAssembly plugin is a core module (not a component) placed in the server's plugins/ directory (getting started says where that is). The server loads it at start if it was built with --features wasm. To write one in Rust with the guest SDK, start with getting started instead. Any language that can export functions and use linear memory works. The motivating case is Kotlin/Wasm, which is why the ABI never asks the guest to keep memory alive between calls.

The smallest complete plugin is crates/rustmc-plugin-wasm/examples/hello.wat, written by hand in WebAssembly text. Build it with:

cargo run -p rustmc-plugin-wasm --example wat2wasm -- \
    crates/rustmc-plugin-wasm/examples/hello.wat <world>/plugins/hello.wasm
cargo run --release -p rustmc-server --features wasm -- --port 25570 ...

The shape of a guest

  1. Exports.
    • memory;
    • rustmc_manifest(): hands over the manifest JSON with emit;
    • rustmc_on_event(len) -> i32: called for events;
    • rustmc_on_hook(len) -> i32: called for hooks, and needed only if the manifest names hooks.
  2. Imports, from module rustmc:
    • emit(ptr, len): give the host bytes;
    • take(ptr): copy what is waiting (an event, a hook call, or a call's answer) into your memory;
    • call(ptr, len) -> i32: make a host call and get the answer's length back.

A hook, end to end

The manifest:

{"name": "loud", "version": "1",
 "events": ["none"],
 "hooks": [{"point": "command.say", "mode": "prepend"}]}

events: ["none"] names an event that does not exist, so the plugin hears no events. An empty or missing list means all events.

When someone runs /say hello, the server calls rustmc_on_hook(len). Your guest reserves len bytes, calls take(ptr), and reads:

{"hook": "command.say", "input": {"name": "say", "player": 7, "rest": "hello"}, "mode": "prepend"}

To change the input, emit the replacement and return 1:

{"name": "say", "player": 7, "rest": "HELLO"}

To leave it alone, return 0.

A complete, runnable version of a hook guest is crates/rustmc-plugin-wasm/examples/hook.wat. It overrides /seed, and a test in that crate loads it and checks its answer.

The full byte-level contract is in the ABI.

From docs/api/guide/wasm-plugin.md in the RustMC repository at bbd81209ad.