# Mod API reference The contract between Horizon and its mods: the WIT package `horizon:mods@0.1.0`, every interface, type, function and hook in it, and the capabilities a manifest may list. The guide is [mods.md](../mods.md); stability and versions: [stability.md](stability.md). Manifest `api` lines this game serves: `0.1`, `0.2`, `0.3`. ## Capabilities | In `capabilities` | Grants | The player reads "May …" | | --- | --- | --- | | `game-read` | [`game`](#game) | see your position, health and the time | | `chat` | [`chat`](#chat) | show you chat messages | | `hud` | [`hud`](#hud) | draw text on your screen | | `storage` | [`storage`](#storage) | save its own settings to disk | | `command` | [`command`](#command) | run commands as you, with your permissions | | `registries` | data files (`assets/`, `data/`), no interface | add blocks and items to your worlds | | `ui` | [`ui`](#ui) | show its own screens and a settings page | | `render` | [`render`](#render) | draw on your screen, shake and zoom the camera | | `input` | [`input`](#input) | see the keys you hold and your clicks, take over the use button on its own items and the mouse wheel, and add keys | | `players` | [`players`](#players) | see who is on the server and message them | | `world` | [`world`](#world) | read and change the world's blocks | | `recipes` | [`recipes`](#recipes) | read the game's recipes and items and which recipes you know, place recipes in your crafting grid and, in creative mode, give you items | ## World `horizon-mod` A client mod: runs in the player's game. - Imports (what the game offers): `log`, `game`, `chat`, `hud`, `storage`, `command` - Exports (what the mod provides): `events` ## World `horizon-server-mod` A server mod (manifest `side = "server"`, `api = "0.3"`, experimental): runs where the world is simulated, on the dedicated server, a LAN host and single player's own server. - Imports (what the game offers): `log`, `storage`, `players`, `world` - Exports (what the mod provides): `server-events` ## World `horizon-ui-mod` A mod with widgets (capability `ui`): the SDK builds every mod against this world. Mods built for `horizon-mod` alone (no `ui-events` export) still load and simply hear nothing of their widgets. - Imports (what the game offers): `ui`, `render`, `input`, `recipes`, `browser`, `log`, `game`, `chat`, `hud`, `storage`, `command` - Exports (what the mod provides): `ui-events`, `events` ## World `ui-host` The game's side of `horizon-ui-mod` beyond `horizon-mod`. - Imports (what the game offers): `ui` - Exports (what the mod provides): `ui-events` ## World `render-host` The game's side of capability `render` (imports only). - Imports (what the game offers): `render`, `input` - Exports (what the mod provides): ## World `recipes-host` The game's side of capability `recipes` (imports only). - Imports (what the game offers): `recipes`, `browser` - Exports (what the mod provides): ## `log` Access: always granted. The mod's log, in the game's log under the mod's id. Always granted. ### enum `level` | Case | | | --- | --- | | `debug` | | | `info` | | | `warn` | | | `error` | | | Function | Signature | What it does | | --- | --- | --- | | `write` | `func(level: level, message: string)` | One line in the game's log as `[mod ] message`, at that level. | ## `game` Access: capability `game-read`. Capability `game-read`: the local player, the world clock and the block the player looks at. ### record `player` | Field | Type | | | --- | --- | --- | | `x` | `f64` | Feet position, in blocks. | | `y` | `f64` | | | `z` | `f64` | | | `yaw` | `f32` | Degrees, as F3 shows them. | | `pitch` | `f32` | Degrees: -90 looks straight up, 90 straight down. | | `health` | `f32` | Hit points as vanilla counts them: 20 is full health without effects. | | `food` | `u32` | 0 to 20, as the hunger bar. | | `dimension` | `string` | `minecraft:overworld`, `minecraft:the_nether`, ... | ### record `clock` | Field | Type | | | --- | --- | --- | | `game-time` | `u64` | Ticks since the world was created. | | `day-time` | `u64` | 24000 ticks per day; 0 is 6:00 on the first day. | ### record `hit` A block along the player's view. | Field | Type | | | --- | --- | --- | | `block-x` | `s32` | The block hit. | | `block-y` | `s32` | | | `block-z` | `s32` | | | `x` | `f64` | The point on its face where the view meets it. | | `y` | `f64` | | | `z` | `f64` | | | `block` | `string` | `minecraft:stone`, ... | | Function | Signature | What it does | | --- | --- | --- | | `get-player` | `func() -> option` | None on the title screen. | | `get-clock` | `func() -> option` | None on the title screen. | | `world-id` | `func() -> option` | Which world is open (AirDef fork): `world:` for a saved world, `server:
` on a server; none on the title screen. Mods keep their state apart by it. | | `raycast` | `func(distance: f32) -> option` | The first block along the player's view (fluids pass), up to `distance` blocks (at most 1024; past the loaded chunks, across the distant terrain when LOD Distance is on), as of this tick. None when nothing is hit, and on the title screen. | ## `chat` Access: capability `chat`. Capability `chat`: messages only the local player sees. | Function | Signature | What it does | | --- | --- | --- | | `show` | `func(text: string)` | Shown in chat after the mod's name, like a system message. | ## `hud` Access: capability `hud`. Capability `hud`: one panel of text lines per mod, drawn by the game every frame until the mod changes or clears it (no call per frame). ### enum `corner` | Case | | | --- | --- | | `top-left` | | | `top-right` | | | `bottom-left` | | | `bottom-right` | | ### record `line` | Field | Type | | | --- | --- | --- | | `text` | `string` | Plain text, up to 256 characters. | | `color` | `u32` | 0xRRGGBB. | | Function | Signature | What it does | | --- | --- | --- | | `set-panel` | `func(corner: corner, lines: list)` | Replaces the mod's panel: up to 16 lines in that corner of the screen. | | `clear` | `func()` | Removes the mod's panel. | ## `storage` Access: capability `storage`. Capability `storage`: a per-mod folder of small key/value files, quota'd (docs/mods.md). Keys are `[a-z0-9_.-]{1,64}`, one path segment. | Function | Signature | What it does | | --- | --- | --- | | `get` | `func(key: string) -> option>` | None if `key` has never been `set`, or the key is not well-formed. | | `set` | `func(key: string, value: list) -> result<_, string>` | Errs on a malformed key or over quota (bytes for the whole mod). | | `delete` | `func(key: string)` | No error if `key` was already absent. | ## `command` Access: capability `command`. Capability `command`: vanilla commands run as the player, with the player's permissions, after the hook call returns (8 per call). A mod's own commands are not run this way. ### variant `value-kind` What one word of a command line takes (AirDef fork, level 19). | Case | Payload | | | --- | --- | --- | | `word` | `list` | One word, anything up to the next space. The words given are only what the chat box suggests; any word is accepted. | | `integer` | `tuple, option>` | A whole number, within the bounds given (either may be none). | | `number` | `tuple, option>` | A number with or without decimals, within the bounds given. | | `choice` | `list` | One of these words and no other. | | `names` | `string` | One word, suggested from the list `set-suggestions` last gave under this name; any word is accepted, so a list that is out of date refuses nothing. | | `block-pos` | | Three coordinates: whole numbers, each one also `~` or `~n`. | ### record `tree-option` One `key=value` option, or a bare flag (`noaudio`) when `value` is none. | Field | Type | | | --- | --- | --- | | `key` | `string` | | | `value` | `option` | | ### record `tree-options` The words that follow, in any order, to the end of the line. | Field | Type | | | --- | --- | --- | | `entries` | `list` | The options and flags that may appear, each at most once. | | `free` | `option>` | Words that are no option are free words (a name): none refuses them, some accepts them and suggests the words given. | ### variant `node-kind` | Case | Payload | | | --- | --- | --- | | `value` | `value-kind` | One word, as `value-kind` says. | | `text` | | The rest of the line, spaces included: only a last node. | | `options` | `tree-options` | | ### record `tree-node` A node of a command's tree. Nodes are listed flat; a node's children come after it in the list (a tree, never a loop). | Field | Type | | | --- | --- | --- | | `name` | `string` | A literal's word, or an argument's name (the chat box's hint shows it as ``). | | `kind` | `option` | None: a literal that must be typed as it is. | | `children` | `list` | The nodes that may follow, by their place in the list. A word no literal child is, goes to the argument children, in order. | | `runs` | `bool` | The line may end here (a branch with optional words after it runs without them). | | Function | Signature | What it does | | --- | --- | --- | | `run` | `func(line: string)` | `line` without its slash; its feedback goes to the chat as if typed. | | `run-quiet` | `func(lines: list)` | `lines` (each without its slash, at most 256 a call) as `run` does, but with nothing in the chat: no feedback and no failure. In single player and on Horizon's servers, each runs exactly as the player typing it would (the same rights), in order, 64 a tick, and the server logs one line a batch; one that carries a chat message (`say`, `msg`) is refused. On any other server they go as chat commands, and there their feedback shows: a mod running many should say so. | | `set-tree` | `func(name: string, nodes: list) -> result<_, string>` | Describes the arguments of a command `name` the mod registered (AirDef fork, level 19), replacing the free text it takes otherwise: the chat box colours the line, shows the error and the next word before Enter, and completes it with Tab; a line that does not parse never reaches the mod's `command` hook (the player gets the error). A line that parses reaches it as it was typed. `nodes[0]` is the node right after the command's name (its literal children are the subcommands; `runs` says whether the bare command runs). At most 512 nodes, 256 words in a list, 64 characters in a word, no loops, no two children of a node with one name. Errs, and changes nothing, on a tree that breaks these rules; a tree for a name the mod did not register in `init` is ignored. Calling again replaces the tree. | | `set-suggestions` | `func(topic: string, values: list)` | Sets the words a `names` node suggests under `topic` (at most 256 of 64 characters, kept as given, the rest dropped). Call it again whenever the words change (a saved scenario, a deleted one): the chat box follows. | ## `ui` Access: capability `ui`. Capability `ui`: widgets the game draws with its own menus' widgets, for a screen the mod owns and a settings page in Options > Mods. Layout and drawing are the game's: a mod says what its widgets hold, never where they sit or how they look, and the game clips everything it is handed. ### record `text-widget` A line of text. | Field | Type | | | --- | --- | --- | | `id` | `u32` | The mod's own name for the widget; `ui-events` calls echo it. | | `text` | `string` | Up to 256 characters. | | `color` | `u32` | 0xRRGGBB; 0 is the menus' white. | ### record `item-widget` An item's icon and a name beside it, as recipe viewers show them. | Field | Type | | | --- | --- | --- | | `id` | `u32` | The mod's own name for the widget. | | `item` | `string` | `minecraft:diamond`, `:`; an unknown id draws a "?". | | `name` | `string` | The name shown beside the icon (empty: the item's own). | ### record `list-widget` Rows the player picks one of, as an options page lists choices: a pick is `ui-events::select`, `selected` is the row to mark now. | Field | Type | | | --- | --- | --- | | `id` | `u32` | The mod's own name for the widget; `ui-events::select` echoes it. | | `rows` | `list` | The rows' texts, at most 512, each up to 256 characters. | | `selected` | `option` | The row to mark as the current choice, if any. | ### record `button-widget` A thing on a mod's screen or settings page. A button as the options pages have them (200 × 20): a click is `ui-events::select(id, 0)`. Mods cycle its label for a choice ("Corner: Top Left"). | Field | Type | | | --- | --- | --- | | `id` | `u32` | The mod's own name for the widget. | | `label` | `string` | Up to 256 characters. | ### variant `widget` | Case | Payload | | | --- | --- | --- | | `text` | `text-widget` | Text the player reads. | | `button` | `button-widget` | A button. | | `item` | `item-widget` | An item's icon and name. | | `list` | `list-widget` | Rows the player picks from. | ### record `screen` A screen the mod owns: a title over widgets stacked down the middle, the Done button the game adds, and Esc to leave it. | Field | Type | | | --- | --- | --- | | `title` | `string` | Its title, up to 256 characters. | | `widgets` | `list` | Its widgets, in order, at most 64. | | Function | Signature | What it does | | --- | --- | --- | | `open-screen` | `func(screen: screen)` | Show `screen` over the game; a second call while it is open replaces it. Only the last call of one hook counts. | | `close-screen` | `func()` | Close the mod's screen when it is open (the mod hears no `closed` for this: it was its own doing). | | `set-settings` | `func(widgets: list)` | The mod's page in Options > Mods, reached from a settings button on its row: the widgets it shows, an empty list for no page. The player opening the page is `ui-events::settings-opened`: push fresh widgets then; later calls update the page while it shows. | ## `ui-events` Access: exported by the mod: the game calls it. What the game calls on a mod that shows widgets (capability `ui`): what the player did with them. | Function | Signature | What it does | | --- | --- | --- | | `select` | `func(id: u32, row: u32)` | A row of one of the mod's `list` widgets was picked: the widget's `id` and the row, on the screen or the settings page that showed it. | | `closed` | `func()` | The player left the mod's screen (Esc or Done); not for a screen the mod closed itself or one that went away with the mod. | | `settings-opened` | `func()` | The player opened the mod's settings page in Options > Mods: call `ui::set-settings` now to fill it with fresh widgets. | ## `input` Access: capability `input`. Capability `input` (experimental: the manifest needs `experimental = ["input"]`): the use button on items the mod claims, and the mod's own keys. Events wait for `poll`, at most 256. ### record `held-item` The main hand's stack. | Field | Type | | | --- | --- | --- | | `id` | `string` | `minecraft:spyglass`, `:`. | | `count` | `u32` | | | `slot` | `u32` | The hotbar slot, 0 to 8. | ### record `item-use` | Field | Type | | | --- | --- | --- | | `item` | `held-item` | | | `ticks` | `u32` | Game ticks the button was held (0 on `use-start`). | ### variant `event` | Case | Payload | | | --- | --- | --- | | `use-start` | `item-use` | The use button went down with a claimed item in the main hand. The game does nothing else with the click: no placing, no eating, no swing. | | `use-stop` | `item-use` | It came up again, or the item left the hand, or a screen opened. | | `key-down` | `string` | A key `register-key` named went down or up while playing (no screen open). The game's own mapping on that key still runs. A screen opening lets every held key up. | | `key-up` | `string` | | | `wheel` | `f32` | Mouse wheel steps while the mod holds the wheel (`claim-wheel`): positive away from the player, as the hotbar reads them. | ### record `mapping-state` A mapping as the player has bound it. | Field | Type | | | --- | --- | --- | | `label` | `string` | The bound key's name as Controls shows it: `W`, `Space`, `LMB`. | | `down` | `bool` | Held down now (as of this frame for a mod with a `frame` hook, else this tick). | | Function | Signature | What it does | | --- | --- | --- | | `claim-items` | `func(ids: list)` | The items whose use the mod handles instead of the game, by id (at most 64). Replaces the previous list. | | `register-key` | `func(name: string, label: string, key: string) -> result<_, string>` | A key of the mod's: `name` comes back in `key-down` and `key-up`, `label` says what it does, `key` is the key as options.txt writes it (`key.keyboard.r`), the default: the player can bind another one on the mod's page in Options > Mods, and that one wins. At most 16; an unknown key is an error. | | `held` | `func() -> option` | The main hand's stack, as of this tick; none when it is empty. | | `poll` | `func() -> list` | The events since the last call, oldest first. | | `claim-wheel` | `func(on: bool)` | Takes the mouse wheel while playing (true) or gives it back: while taken, its steps are the mod's `wheel` events and the hotbar doesn't move. The game gives it back when the mod stops. | | `mapping` | `func(name: string) -> option` | A movement or click mapping as the player has bound it: `key.forward`, `key.left`, `key.back`, `key.right`, `key.jump`, `key.sneak`, `key.sprint`, `key.attack` or `key.use`. None for another name. | | `clicks-per-second` | `func() -> tuple` | Clicks of the attack and use buttons in the last second. | ## `render` Access: capability `render`. Capability `render` (experimental: the manifest needs `experimental = ["render"]`): shapes the game draws for the mod with its own renderer, kept until the mod replaces them (no call per frame). A mod never supplies shader code. ### enum `corner` A corner of the screen, as `hud`'s panels use them. | Case | | | --- | --- | | `top-left` | | | `top-right` | | | `bottom-left` | | | `bottom-right` | | ### record `rect` A rectangle in GUI pixels: the top left corner and the size. | Field | Type | | | --- | --- | --- | | `x` | `f32` | | | `y` | `f32` | | | `w` | `f32` | | | `h` | `f32` | | ### record `point` A point in GUI pixels from the screen's top left. | Field | Type | | | --- | --- | --- | | `x` | `f32` | | | `y` | `f32` | | ### record `fill-shape` | Field | Type | | | --- | --- | --- | | `rect` | `rect` | | | `color` | `u32` | 0xAARRGGBB. | ### record `quad-shape` Any four-cornered shape (a wheel's segment, an arrow): the corners in order round it. | Field | Type | | | --- | --- | --- | | `corners` | `tuple` | | | `color` | `u32` | 0xAARRGGBB. | ### record `sprite-shape` | Field | Type | | | --- | --- | --- | | `sprite` | `string` | One of the game's GUI sprites, as `gui/sprites/` names them (`hud/heart/full`, `widget/button`), the resource packs' versions included. An unknown one draws nothing. | | `rect` | `rect` | | | `color` | `u32` | A tint, 0xAARRGGBB: 0xFFFFFFFF draws it as it is. | ### record `item-shape` | Field | Type | | | --- | --- | --- | | `item` | `string` | `minecraft:diamond`, `:`; an unknown one draws nothing. | | `at` | `point` | The icon's top left. | | `scale` | `f32` | 1.0 is the hotbar's 16 × 16. | ### record `text-shape` | Field | Type | | | --- | --- | --- | | `text` | `string` | Up to 256 characters, one line. | | `at` | `point` | | | `color` | `u32` | 0xRRGGBB, as `hud`'s lines. | | `centered` | `bool` | Centred on `at.x` instead of starting there. | | `shadow` | `bool` | | ### record `view-shape` A picture of one of the mod's `set-views` cameras (AirDef fork). | Field | Type | | | --- | --- | --- | | `view` | `u32` | The camera's id; an unknown one draws nothing. | | `rect` | `rect` | | | `mode` | `u32` | 0 as seen, 1 thermal (white hot), 2 night vision (green). | ### variant `shape` | Case | Payload | | | --- | --- | --- | | `fill` | `fill-shape` | | | `quad` | `quad-shape` | | | `sprite` | `sprite-shape` | | | `item` | `item-shape` | | | `text` | `text-shape` | | | `view` | `view-shape` | | ### record `frame-rate` | Field | Type | | | --- | --- | --- | | `fps` | `u32` | Frames drawn in the last second. | | `min` | `u32` | The slowest frame's rate, and the 1% low (the 99th percentile frame time's rate), over the last 5 seconds. | | `low` | `u32` | | ### record `zoom` | Field | Type | | | --- | --- | --- | | `factor` | `f32` | The field of view is divided by it: 1 is none, at most 10. | | `smooth` | `bool` | Ease in and out through the game's field-of-view easing, as the spyglass does, instead of at once. | | `turn` | `f32` | Mouse turning is multiplied by it while zoomed: 1 keeps the player's sensitivity, at least 0.05. | ### record `minimap` | Field | Type | | | --- | --- | --- | | `corner` | `corner` | | | `size` | `f32` | Its side in GUI pixels, 16 to 256. | | `blocks-per-pixel` | `f32` | World blocks per map pixel, 0.5 to 4; the map is 128 × 128 pixels. | | `rotate` | `bool` | The map turns with the player, who faces up, and an N on its rim marks north. Otherwise north is up and the player's arrow turns. | | `round` | `bool` | Round, the corners left clear; otherwise square. | ### record `far-terrain` | Field | Type | | | --- | --- | --- | | `distance` | `u32` | How far the distant terrain reaches, in chunks: 64 to 1024. On integrated graphics other than Apple's the game caps it lower. | | `reduced` | `bool` | Coarser far tiles, as the Far Detail setting's Reduced: 2 to 4 ms less GPU time per frame on integrated graphics. | ### record `model-instance` A block model drawn in the world (AirDef fork). | Field | Type | | | --- | --- | --- | | `id` | `u32` | Stays the same while the object lives: the game moves an id smoothly from where the last `set-models` put it. | | `model` | `string` | A block state as commands write it (`airdef:shahed`, `minecraft:stone`, `minecraft:furnace[facing=east]`): its block model is drawn. An unknown one draws nothing. | | `x` | `f64` | Where the model's centre is, in blocks. | | `y` | `f64` | | | `z` | `f64` | | | `rotation` | `tuple` | Rotation about the model's centre: a quaternion (x, y, z, w). | | `scale` | `f32` | Uniform scale, 1.0 a block (0 to 64). | | `glow` | `bool` | Drawn at full brightness, whatever the light where it is. | ### record `model-instance-id` `model-instance` with the model as a handle. | Field | Type | | | --- | --- | --- | | `id` | `u32` | | | `model` | `u32` | From `model-id`. | | `x` | `f64` | | | `y` | `f64` | | | `z` | `f64` | | | `rotation` | `tuple` | | | `scale` | `f32` | | | `glow` | `bool` | | ### record `particle-spawn` Particles to spawn on this client (AirDef fork). | Field | Type | | | --- | --- | --- | | `particle` | `string` | As `/particle` names the type: `minecraft:flame`, `minecraft:dust`. | | `x` | `f64` | | | `y` | `f64` | | | `z` | `f64` | | | `spread` | `tuple` | Gaussian spread round the point, per axis, in blocks. | | `velocity` | `tuple` | Every particle's starting velocity, blocks a tick. | | `speed` | `f32` | Gaussian speed added to it per axis. | | `count` | `u32` | 1 to 256. | | `color` | `u32` | 0xRRGGBB for dust and coloured types (0: white). | | `scale` | `f32` | Dust size (0.01 to 4). | ### enum `source-op-kind` What a `sound-sources` entry does. | Case | | | --- | --- | | `start` | Starts source `id` (one that is already running restarts: the old one releases, the new one attacks). | | `update` | Sets everything of a running source: pose, velocity, gain, pitch, looping and the rest. An id that is not running is ignored. | | `stop` | Releases source `id` (fading over its `release`). Only `id` is read. | ### record `source-op` One change of a persistent sound source (AirDef fork, API level 20): a sound that goes on, follows a pose and has a lifetime, over the game's own mixer. Unlike `play-sound` events they are not repeated; a source is started, updated as often as the mod likes (a tick) and stopped. The ids are the mod's own numbers (per mod, up to 256 running). | Field | Type | | | --- | --- | --- | | `kind` | `source-op-kind` | | | `id` | `u32` | | | `event` | `string` | The sound event (`:`), read by `start`: the same variant is chosen for the whole life of the source. | | `x` | `f64` | Where it is, in the world as the listener hears it (the listener stays at the player; a mod that shows another place moves the source by the offset itself). | | `y` | `f64` | | | `z` | `f64` | | | `vx` | `f32` | Blocks a second. The mixer carries the source on along it between calls (at most 0.15 s), so a source updated once a tick moves without steps. Also what Doppler reads. | | `vy` | `f32` | | | `vz` | `f32` | | | `gain` | `f32` | 0 to 16, times the event's volume and the category sliders; above 1 it only widens the distance it is heard from, as in vanilla. | | `pitch` | `f32` | 0.5 to 2, the sound's own pitch (Doppler is added by `doppler`). | | `looping` | `bool` | A looping source goes on until stopped; a source that is not looping plays once and is gone. | | `priority` | `f32` | More important sources keep their voice when there are not enough (0.1 to 10, 1 usually). Sources are ranked by priority times what the listener would hear, 24 get a voice; the others stay virtual (silent but alive, a loop only) and are heard when they rank again. | | `attack` | `f32` | Seconds to fade in (0 to 5; 0 is 5 ms, so there is no click). | | `release` | `f32` | Seconds to fade out after `stop` (0 to 5; 0 is 5 ms). | | `doppler` | `f32` | How much of the Doppler shift of the velocity is applied, 0 (none, the usual choice) to 1, bounded to a change of pitch of 12%. The velocity is taken against a listener at rest. Leave 0 when the pitch already follows the speed. | | `loop-from` | `f32` | A looping source repeats the part of its sound from `loop-from` to `loop-to` seconds into it (both 0: all of it), the join crossfaded over 50 ms: for sounds that fade in and die away at their ends. | | `loop-to` | `f32` | | ### enum `policy` What a block is to a query (AirDef fork): the three policies differ on purpose, so a sensor and a missile may disagree about a pane of glass. | Case | | | --- | --- | | `collision` | Projectile collision: every block with a collision shape stops it (stone, leaves, glass, slabs, fences); air, fluids, plants and other shapeless blocks let it pass. | | `sight` | Sensor line of sight: opaque blocks (those that stop light) only; glass, leaves and fluids are seen through. | | `sound` | Sound occlusion: blocks with a full-cube collision shape. | ### enum `verdict` How a query ended. Never `clear` for a chunk that isn't loaded. | Case | | | --- | --- | | `clear` | Nothing on the way, everything checked. | | `hit` | A block stops it; see the other fields. | | `unknown` | A part of the way is in a chunk the game doesn't have (far terrain, not yet loaded) or the budget ran out: no verdict. A `hit` found before the unknown part still comes back as `hit`. | ### record `ray` A segment to trace through the loaded blocks. | Field | Type | | | --- | --- | --- | | `id` | `u32` | Echoed in the answer. | | `start` | `tuple` | | | `end` | `tuple` | | | `policy` | `policy` | | ### record `ray-answer` | Field | Type | | | --- | --- | --- | | `id` | `u32` | | | `verdict` | `verdict` | | | `fraction` | `f64` | For a `hit`: how far along the segment, 0 to 1, where it enters the shape. | | `normal` | `tuple` | For a `hit`: the face normal (the axis it entered along). | | `block` | `tuple` | For a `hit`: the block hit. | | `known-to` | `f64` | For an `unknown`: how far along the segment the unknown part begins. | ### enum `section-fill` What a 16 x 16 x 16 section of the world holds, for a policy. | Case | | | --- | --- | | `unknown` | The game has no such chunk loaded: nothing is known. | | `empty` | Nothing in it stops a query. | | `full` | Every block in it does. | | `mixed` | Some do: `bits` tells which. | ### record `section` | Field | Type | | | --- | --- | --- | | `x` | `s32` | Section coordinates: block x, y, z divided by 16, rounded down. | | `y` | `s32` | | | `z` | `s32` | | | `fill` | `section-fill` | | | `bits` | `list` | For `mixed`: 512 bytes, one bit per block, set where it stops the policy's query. The index is (y & 15) * 256 + (z & 15) * 16 + (x & 15); bit i is bit (i & 7) of byte i / 8. Empty otherwise. | ### record `changes` Changes to the world since the last tick, for a mod's cache. | Field | Type | | | --- | --- | --- | | `epoch` | `u32` | A number that changes when another world or dimension opens: everything cached before is void. | | `revision` | `u64` | Counts block changes in the loaded world, ever (wraps). | | `overflow` | `bool` | More sections changed than fit: treat everything as changed. | | `sections` | `list>` | Sections (as in `section`) where a block changed, at most 256. | ### record `camera-view` A camera the game renders the world from besides the player's eye (AirDef fork): the land, the sky and the entities near the player. | Field | Type | | | --- | --- | --- | | `id` | `u32` | Stays the same while the camera lives: the game turns it smoothly between the last two `set-views`. | | `x` | `f64` | | | `y` | `f64` | | | `z` | `f64` | | | `yaw` | `f32` | Degrees, as the player's: yaw 0 looks south (+z), pitch 90 down. | | `pitch` | `f32` | | | `fov` | `f32` | Vertical field of view, degrees (1 to 110). | | `width` | `u32` | The picture's size in pixels (16 to 3840 × 16 to 2160). | | `height` | `u32` | | ### record `point-light` A light the mod's things give off (AirDef fork): rocket flames, flares, blasts. Drawn by the HD lighting (`set-lighting`). | Field | Type | | | --- | --- | --- | | `id` | `u32` | Stays the same while the light lives: the game moves it smoothly. | | `x` | `f64` | | | `y` | `f64` | | | `z` | `f64` | | | `color` | `tuple` | Linear RGB, 0 to 1. | | `intensity` | `f32` | Brightness: about what it lights one block away (0 to 100000). | | `radius` | `f32` | Nothing past this many blocks (1 to 256). | ### record `lighting` The HD lighting (AirDef fork): the world lit per pixel by the mods' lights, in high dynamic range, with bloom and tone mapping. | Field | Type | | | --- | --- | --- | | `hd` | `bool` | | | `exposure` | `f32` | 1 as is; more brightens. | | `bloom` | `f32` | How much bright things glow (0 none, 1 strong). | ### record `smoke-segment` A piece of smoke or exhaust trail (AirDef fork): a tube from `a` to `b` (equal ends: a puff) that the HD renderer ray-marches as a volume, lit by the sun, the sky and the mod's lights, and that thins the fog and clouds it passes through. The game derives everything from the age (`clock - born`): the radius grows from `radius-start` to `radius-end`, the density fades to nothing at `life`, the smoke drifts with the wind and rises by `rise`, so a segment is sent once per change of the set, not per frame, and a replay that seeks sends the segments of that moment. | Field | Type | | | --- | --- | --- | | `id` | `u32` | Seeds the smoke's noise. | | `ax` | `f64` | | | `ay` | `f64` | | | `az` | `f64` | | | `bx` | `f64` | | | `by` | `f64` | | | `bz` | `f64` | | | `born` | `f64` | When it was made, in the `clock` of `set-smoke` (ticks). | | `life` | `f32` | How long it lasts, in the same ticks (1 to 72000). | | `radius-start` | `f32` | Radius of the tube in blocks at birth and at the end of its life. | | `radius-end` | `f32` | | | `density` | `f32` | How thick it is at the core at birth: extinction per block (0 to 8). | | `heat` | `f32` | How hot it glows at birth, 0 soot to 1 white hot; it cools in seconds. | | `soot` | `f32` | How dark it is, 0 white vapour to 1 black soot. | | `rise` | `f32` | Blocks per second it floats up (negative sinks). | | `clear` | `f32` | Radius in blocks of the clear tunnel it leaves in fog and cloud, filling back in over `life` (0 none). | ### record `spot-light` A directional light with a visible beam (AirDef fork): a searchlight, a headlight. The HD lighting lights the surfaces and flying things inside its cone (smoothly from the inner to the outer angle, down to nothing at `range`) and draws the beam as a translucent volume lit from inside: it scatters in the air (more in fog and smoke, a faint artistic one in clear air), the world hides it where it is in front, and the camera may stand inside it. The light does not look for walls: send `range` as far as the beam is free (a cast along the axis) so it stops where the land stops it. | Field | Type | | | --- | --- | --- | | `id` | `u32` | Stays the same while the light lives: the game moves it smoothly. | | `x` | `f64` | The apex, in the lens. | | `y` | `f64` | | | `z` | `f64` | | | `dx` | `f32` | Where it points (any length but zero; it is normalised). | | `dy` | `f32` | | | `dz` | `f32` | | | `color` | `tuple` | Linear RGB, 0 to 1. | | `intensity` | `f32` | Brightness: about what it lights one block away (0 to 100000). | | `range` | `f32` | Nothing past this many blocks (1 to 1024). | | `inner-angle` | `f32` | Half angle in radians where the light is at full strength (0.005 to 1.2). | | `outer-angle` | `f32` | Half angle in radians where it has faded to nothing (past the inner one, to 1.4). | | `beam` | `f32` | How visible the beam is in the air, 0 (the light only lights things) to 1. | ### record `capture-options` What `capture-start` records (AirDef fork, API level 17). | Field | Type | | | --- | --- | --- | | `folder` | `string` | A folder under the game's home (letters, digits, `-`, `_` and `/`; no `..`); made if missing. The result lands in it. | | `name` | `string` | The file name without its extension (letters, digits, `-`, `_`, `.`). | | `fps` | `u32` | Frames a second of the picture (1 to 120). | | `width` | `u32` | The picture's size in pixels; 0 keeps the window's. An offscreen game is resized to it, a window asked to be (what it gets is in the state). | | `height` | `u32` | | | `crf` | `u32` | x264 quality, 0 (lossless) to 51; 14 is visually clean. | | `audio` | `bool` | Mix the sounds the mod plays (`play-sound`) into the file's audio track, heard from the main view's camera at the capture's own time. | | `warmup` | `u32` | Frames drawn and thrown away after a cut (a new `set-main-view` id) so the anti-aliasing and the volumes settle in the new shot (0 to 32). | ### enum `capture-phase` Where a capture is. | Case | | | --- | --- | | `idle` | Nothing running (or never started). | | `arming` | Asked; waiting for the picture to reach the size. | | `recording` | Frames are being rendered and written. | | `encoding` | The frames are written; the encoder is closing the file. | | `done` | Finished: `file` is the result. | | `failed` | Stopped by an error or cancelled: `message` says why. | ### record `capture-state` | Field | Type | | | --- | --- | --- | | `phase` | `capture-phase` | | | `frames` | `u32` | Frames written so far. | | `width` | `u32` | The size of the frames, once known. | | `height` | `u32` | | | `file` | `string` | The result: the MP4, or the folder of the PNG sequence when no `ffmpeg` was found. Empty until `done`. | | `message` | `string` | An error, or a note about the result (no encoder, no audio). | | Function | Signature | What it does | | --- | --- | --- | | `set-hud` | `func(shapes: list)` | Replaces the mod's HUD shapes: up to 1024, drawn every frame in order over the HUD (after the hotbar, under chat and screens), hidden with the HUD (F1). An empty list removes them. | | `gui-size` | `func() -> tuple` | The screen's size in GUI pixels (width, height), as of this tick. | | `shake` | `func(strength: f32, seconds: f32)` | Shakes the camera: a jolt of `strength` (0 to 1, 1 the hardest) that fades out over `seconds` (at most 10). Jolts add up to 1. The view turns, the player doesn't, and the player's Distortion Effects option scales it (0 turns it off). | | `set-corner` | `func(corner: corner, width: f32, height: f32, shapes: list)` | Replaces the mod's corner panel: shapes in GUI pixels from the top left of a `width` × `height` box that the game places in `corner`, stacked with the game's overlays and the other mods' panels (under the status effects at the top right), as `hud`'s panels are. | | `clear-corner` | `func()` | Removes the mod's corner panel. | | `text-width` | `func(text: string) -> f32` | The width of one line of text in GUI pixels, in the HUD's font: exact for Latin letters (U+0000 to U+024F), 6 per other character. | | `frame-stats` | `func() -> frame-rate` | The game's frame counter, recomputed twice a second. | | `set-zoom` | `func(zoom: zoom)` | Zooms the camera until the next call (`factor` 1 ends it), only while playing: a screen opening or the spyglass's own view (which wins) leave the camera vanilla's. With several mods, the strongest zoom counts. | | `set-minimap` | `func(minimap: option)` | Shows a map of the world around the player in `corner`, stacked with the game's overlays and the mods' panels, until the next call (none hides it). It looks like a map item: vanilla's map colours with their height shading and water depth, and the player's arrow. Underground, under a roof and in the Nether it maps the floor around the player's height. The game keeps the map's tiles and refreshes them within its own frame budget; hidden, they are dropped. One map shows at a time: the first loaded mod's. | | `set-far-terrain` | `func(far-terrain: option)` | Draws the world beyond the render distance as distant terrain, out to `distance` chunks, until the next call (none ends it). The game builds it on its own workers, from the chunks already loaded or saved and, where nobody has been, from the world's generator; the simulation distance doesn't change. Only worlds whose generator the game has (single player and a LAN world you host) show it. The player's own LOD Distance setting, when on, wins. One far terrain shows at a time: the first loaded mod's. | | `set-models` | `func(models: list)` | Replaces the mod's models: up to 4096, drawn with the world's entities until replaced. Call it once a tick: the game interpolates each id between its last two positions and rotations every frame. An empty list removes them. | | `model-id` | `func(model: string) -> option` | A handle for a block state name, for `set-models-by-id` (AirDef fork, API level 11): the name crosses once, not every tick. None for a name that is no block state of the game's registry (nothing is remembered for it). Handles are valid until `model-generation` changes (the registry changed: a resource reload, another world's data); ask again for every name then. | | `model-generation` | `func() -> u32` | Moves on whenever the handles `model-id` gave stop being valid. A mod reads it before `set-models-by-id` and re-asks when it changed. | | `set-models-by-id` | `func(generation: u32, models: list) -> bool` | `set-models` with handles instead of names: the same models, limits and interpolation, fewer bytes across the boundary. `generation` is the `model-generation` the handles were asked under; if it is not the current one the call is ignored and returns false (re-ask the handles and call again). Handles the game doesn't know draw nothing. | | `spawn-particles` | `func(particles: list)` | Spawns particles on this client only: no command, no server. At most 512 spawns a call. | | `play-sound` | `func(event: string, x: f64, y: f64, z: f64, volume: f32, pitch: f32)` | Plays a sound event (`minecraft:entity.generic.explode`, `:`) at a point on this client. | | `sound-source-generation` | `func() -> u32` | Moves on whenever the game released every source of every mod (the world closed, the session ended, a film began or ended, the sounds reloaded). A mod reads it before `sound-sources` and starts its sources again when it changed. | | `acoustics` | `func() -> bool` | Whether the game muffles sounds by what lies between them and the listener (AirDef fork, API level 21: `HORIZON_ACOUSTICS`). A mod that muffles by itself (a `_far` variant for a distant blast) plays the near one instead when this is true, so nothing is muffled twice. | | `sound-sources` | `func(generation: u32, ops: list) -> bool` | Starts, updates and stops persistent sound sources. `generation` is the `sound-source-generation` the mod's sources belong to; if it is not the current one the call is ignored and returns false (read the generation, forget the sources, start them again). Stops are never dropped by a limit: only new sources beyond 256 running are, and at most 512 entries a call count. Every source of a mod that stops or is reloaded is released by the game. | | `query-ground` | `func(columns: list>)` | Asks for the ground height of these columns (x, z): at most 512 a tick, answered by `ground` from the next tick on. | | `ground` | `func() -> list>` | The answers to the last tick's `query-ground`: (x, z, y), y one above the highest block that blocks motion or holds a fluid, as the motion blocking heightmap; -64 where the chunk isn't loaded. | | `query-rays` | `func(rays: list)` | Traces segments through the loaded blocks (AirDef fork): each is walked block by block on the host, against the real collision shapes (`policy`), and answered by `rays` from the next tick on. At most 256 segments a tick, each at most 512 blocks long (longer ones are answered `unknown` past that); over the budget they are answered `unknown` too, never `clear`. A game with no chunk at a place answers `unknown` for it. | | `rays` | `func() -> list` | The answers to the last tick's `query-rays`, in the order asked. | | `query-sections` | `func(policy: policy, sections: list>)` | Asks what these sections hold (AirDef fork), for a mod that traces its own paths against a cache of them: at most 64 a tick, answered by `sections` from the next tick on. Over the budget they are not answered: ask again. | | `sections` | `func() -> list
` | The answers to the last tick's `query-sections`. | | `world-changes` | `func() -> changes` | The block changes of the last tick (placed, broken, exploded) and the world's epoch. Only changes in chunks the game has loaded. | | `set-post` | `func(wgsl: string)` | A full-screen post pass (AirDef fork): WGSL defining `fn post(uv: vec2, pixel: vec2) -> vec4`, after a prelude with `frame` (camera matrices, time, `params`), the world's colour and depth and helpers (`scene`, `depth-at`, `view-dir`, `scene-distance`, `project`). It runs after the world, before the hand and the HUD. Compile errors come back in the chat and the log; the last good pass keeps running. | | `clear-post` | `func()` | Removes this mod's post pass: the world is shown as the game draws it. | | `set-params` | `func(params: list)` | The pass's `frame.params` (up to 1024 floats, as 256 vec4), kept until replaced; call it once a tick (the pass gets the partial tick in `frame.time.y`). | | `set-views` | `func(views: list)` | Replaces the mod's cameras: up to 8. A `view` shape shows one; the game renders up to three a frame, in turn. | | `set-main-view` | `func(view: option)` | Renders the screen's world from one of the `set-views` cameras (AirDef fork), with everything the player's own view has: the HD lighting, sky, clouds, fog, smoke, TAA and the post passes, at the window's resolution. The picture size of that camera is ignored, the player's hand, block outline and the game's HUD are not drawn (the mod's own `set-hud` shapes are); a `view` shape of that camera draws nothing. For a full-screen replay or a director's camera; the small pictures of monitors stay `view` shapes. None (or an unknown id) is the player's own view. The chunks stay those round the player. | | `set-lights` | `func(lights: list)` | Replaces the mod's lights: up to 512, interpolated per id between the last two calls. | | `set-lighting` | `func(lighting: lighting)` | Switches the HD lighting on or off for this mod's world, with its exposure and bloom; off, the world is drawn as the game draws it. | | `set-time-override` | `func(day-time: option)` | Shows the world at this time of day (AirDef fork): the sky, the sun and moon, the sky light and the fog, as `day-time` ticks (24000 a day, as `clock`). Only what the player sees changes: the world's own clock, its saves and its rules do not. None shows the world's own time. | | `set-smoke` | `func(clock: f64, segments: list) -> bool` | Replaces the mod's smoke: up to 4096 segments, `clock` being now in ticks (the game shows the age between the last two calls). Returns whether the HD renderer draws them; when it does not (the effects preset, HD smoke off), the mod draws its own smoke. | | `set-spot-lights` | `func(lights: list) -> bool` | Replaces the mod's spot lights: up to 32 (the nearest 16 are drawn), interpolated per id between the last two calls. Returns whether the HD renderer draws them with their beams; when it does not (the effects preset, HD off), the mod draws its own beam if it wants one. | | `capture-start` | `func(options: capture-options) -> result<_, string>` | Records the screen frame by frame (AirDef fork), at a fixed time step whatever the speed of the machine: while it records, the game clock advances exactly `1 / fps` seconds a frame (ticks, interpolation, the mod's `tick`s and the sounds with it), so a slow frame is a longer wait and not a skipped one. The frames are read back from the GPU and encoded to H.264 MP4 by `ffmpeg` (found on the `PATH`, or named by `HORIZON_FFMPEG`) through a bounded queue, never held in memory; with no `ffmpeg` they are written as a PNG sequence. Returns an error for a capture already running, a bad name, or an invalid size. Only the game's own files are written, under its home. | | `capture-stop` | `func(cancel: bool)` | Ends the capture: finishes the file, or (`cancel`) throws it away. | | `capture-status` | `func() -> capture-state` | The capture's state; `done` and `failed` stay until the next start. | | `set-film-quality` | `func(on: bool)` | Asks for the best picture the renderer can give (AirDef fork, level 17): the HD world at the full size of the window (no resolution scale, so no upscaling), and the volumetric clouds and fog marched with more steps. Meant for replays and films; false restores the player's settings. A capture turns it on for its duration. | | `world-log-start` | `func() -> u32` | Starts a log of the world's block changes as the game's client sees them (AirDef fork, level 18): each change keeps its position, the state before and after, and the game tick (the number `tick` is called with). It is bounded (about a million changes): the oldest go first. Returns the log's id, which stays the same while it runs (a second call returns it). Only the world open now has it: a new world (leaving, rejoining) loses it. Not kept when the world runs in the game's own frame (`--in-frame`), where `world-log-range` stays none. | | `world-log-stop` | `func(id: u32)` | Ends the log and forgets what it kept (a different id is ignored). | | `world-log-range` | `func(id: u32) -> option>` | What the log covers, as (the earliest tick whose world it can show, the tick it is at now); none for an unknown id, a log of another world, or one that has not been taken up yet (the tick after `world-log-start`). Asking for a time before the earliest shows the earliest. | | `set-world-time-view` | `func(view: option>)` | Shows the world as it stood at a game tick, (log id, tick), to the player's eyes only (AirDef fork, level 18): the changes logged after it are undone in the game's own copy of the blocks, a little each frame, and redone as the tick moves on; none (or the mod stopping, the world changing) puts every block back. The server, the saves, the player's own collision and the blocks next to the player are untouched, and changes that arrive meanwhile are kept and shown when the view ends. Chunks that are not loaded are skipped. | ## `events` Access: exported by the mod: the game calls it. What the game calls. Only the hooks `init` asks for are ever called. ### record `registration` | Field | Type | | | --- | --- | --- | | `commands` | `list` | Chat commands, without the slash. A name vanilla already uses is refused (vanilla's command runs). | | `tick` | `bool` | Call `tick` every game tick (20 per second while playing). | | `frame` | `bool` | Call `frame` every rendered frame. Budget: 0.1 ms. | | `chat` | `bool` | Call `chat` for each chat line the player receives. | | Function | Signature | What it does | | --- | --- | --- | | `init` | `func() -> result` | Once, after loading. An error keeps the mod off and is shown. | | `tick` | `func(tick: u64)` | Each game tick while in a world, with the game's tick counter. | | `frame` | `func(seconds: f32)` | Each rendered frame, with the seconds since the last one. | | `command` | `func(name: string, args: string) -> result, string>` | `/name args`: ok(some(text)) is shown as feedback, err(text) in red. | | `chat` | `func(text: string)` | Each chat line the player receives, as plain text. | ## `recipes` Access: capability `recipes`. Capability `recipes` (experimental): the recipes and items the game knows, for recipe viewers. In single player and on Horizon's servers they are the world's; on any other server they are vanilla 26.3's (a client is sent no others), while `known` follows that server's book. ### record `stack` An item of `items`, by its index there, with a count and the components it has beyond the item's own, in vanilla's item syntax (`[potion_contents={potion:"minecraft:swiftness"}]`), or "". | Field | Type | | | --- | --- | --- | | `item` | `u32` | | | `count` | `u32` | | | `components` | `string` | | ### enum `kind` The station a recipe is made at. | Case | | | --- | --- | | `crafting-shaped` | | | `crafting-shapeless` | | | `smelting` | | | `blasting` | | | `smoking` | | | `campfire` | | | `stonecutting` | | | `smithing` | | | `brewing` | | ### record `recipe` | Field | Type | | | --- | --- | --- | | `id` | `string` | `minecraft:oak_planks`. | | `kind` | `kind` | | | `category` | `string` | The recipe book's tab (`crafting_building_blocks`, `furnace_food`), or "". | | `group` | `string` | Recipes the book shows on one button share a group, or "". | | `width` | `u32` | A shaped recipe's grid: `slots` holds its cells row by row. 0 for the other kinds. | | `height` | `u32` | | | `slots` | `list>` | Each slot's stacks, any one of which fits; an empty list is an empty cell. Cooking and stonecutting: the input. Smithing: the template, the base, the addition. Brewing: the bottle, the reagent. | | `results` | `list` | What it makes; several when that varies (a trim on any armour). | | `cooking-time` | `u32` | Cooking: game ticks and experience; 0 for the other kinds. | | `experience` | `f32` | | | `book-id` | `s32` | The recipe book's id for it (one per way the book shows it), as `known` lists them; -1 for the kinds the book doesn't show (campfire, stonecutting, smithing, brewing). | ### record `item` | Field | Type | | | --- | --- | --- | | `id` | `string` | `minecraft:diamond`, `:`. | | `name` | `string` | In the player's language. | | `tags` | `list` | Its item tags, without the `#`. | | Function | Signature | What it does | | --- | --- | --- | | `items` | `func() -> list` | Every item, in the registry's order: `stack.item` indexes this list. | | `all` | `func() -> list` | Every recipe: the book's in its order, then campfire, stonecutting, smithing and brewing. They don't change while a world is open. | | `known` | `func() -> list` | The `book-id`s of the recipes the player's recipe book holds now. | | `place` | `func(book-id: s32, all: bool) -> result<_, string>` | Places a recipe in the open screen's grid as a click in the recipe book does (`ServerboundPlaceRecipePacket`): the inventory's grid or a crafting table. `all` places as many as the items allow, as with Shift. Refused while no such screen is open, for a recipe the player's book doesn't hold and for one that can't be placed; one missing items shows as a ghost in the grid, as in the book. | | `give` | `func(stack: stack) -> result<_, string>` | Creative mode only: gives the player the stack, as taking it from the creative inventory does (`ServerboundSetCreativeModeSlot`): the selected slot first, then the hotbar, then the inventory. The count is cut to one stack of the item (64, 16 or 1). | ## `browser` Access: not granted by any capability. Capability `ui` (experimental): a recipe viewer's panel beside the game's inventory and container screens (not the creative inventory), on the right of the screen. The game draws it with its own widgets and slots, pages it and keeps its search box; the mod says what it holds. The first loaded mod with a panel shows. ### record `stack` An item to show: its id, a count (1 draws none) and its components beyond the item's own, in vanilla's item syntax, or "". | Field | Type | | | --- | --- | --- | | `item` | `string` | | | `count` | `u32` | | | `components` | `string` | | ### record `click` A click on an item of the panel, by its index in `set-items`'s list. | Field | Type | | | --- | --- | --- | | `index` | `u32` | | | `right` | `bool` | | ### record `item-key` A key the mod registered (`input.register-key`) went down while the pointer was on an item: a slot of the screen's, or the panel's. | Field | Type | | | --- | --- | --- | | `key` | `string` | | | `stack` | `stack` | | ### record `slot-click` A click on a slot of a recipe the panel shows (`set-recipes`). | Field | Type | | | --- | --- | --- | | `recipe` | `u32` | The recipe's index in `set-recipes`'s list and the slot's in it; the results come after the slots. | | `slot` | `u32` | | | `stack` | `stack` | The stack the slot showed at that moment. | | `right` | `bool` | | ### variant `event` | Case | Payload | | | --- | --- | --- | | `clicked` | `click` | | | `search` | `string` | The search box's text, each time the player changes it. | | `key` | `item-key` | | | `slot-clicked` | `slot-click` | | | `action` | `u32` | The action button of the recipe at this index in `set-recipes`'s list. | | `page` | `u32` | The player turned the recipes' page (`set-recipes`): the index of the first recipe now shown. | | `back` | | The back button of `set-recipes`. | | `tab` | `u32` | The player picked the tab at this index of `set-recipes`' tabs. | ### enum `layout` How the game lays out a recipe of `set-recipes`. | Case | | | --- | --- | | `grid` | A crafting grid of `width` x `height` slots, an arrow and the result. | | `cooking` | One input over a flame, an arrow and the result (furnaces, smokers, campfires). | | `single` | One input, an arrow and the result (the stonecutter). | | `smithing` | Template, base and addition, an arrow and the result. | | `brewing` | A potion and an ingredient, an arrow and the result. | ### record `recipe-view` A recipe to show. Each slot, and the result, cycles through its stacks, one a second; an empty slot shows nothing. | Field | Type | | | --- | --- | --- | | `layout` | `layout` | | | `width` | `u32` | The grid's size (`grid` only), at most 3 x 3. | | `height` | `u32` | | | `slots` | `list>` | | | `results` | `list` | | | `note` | `string` | A short line under the recipe (a cooking time), or "". | | `action` | `option` | The action button beside it: none, enabled, or shown greyed. | | Function | Signature | What it does | | --- | --- | --- | | `set-items` | `func(items: list)` | The panel's items, at most 8192 (an empty list hides the panel). | | `set-recipes` | `func(title: string, tabs: list, tab: u32, recipes: list)` | Shows these recipes in the panel instead of its items, as many as fit a page, under `title` and a back button, with a row of `tabs` (each an icon; `tab` is the selected one) between them; an empty list of recipes shows the items again. At most 256 recipes and 16 tabs. | | `poll` | `func() -> list` | What the player did on the panel since the last poll, at most 256 kept. | ## `players` Access: capability `players`. Capability `players`, server mods only (manifest `api = "0.3"`, experimental): the players on the server, and messages to them. ### enum `game-mode` | Case | | | --- | --- | | `survival` | | | `creative` | | | `adventure` | | | `spectator` | | ### record `player` | Field | Type | | | --- | --- | --- | | `id` | `string` | The player's UUID, `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`. | | `name` | `string` | | | `x` | `f64` | Feet position, in blocks. | | `y` | `f64` | | | `z` | `f64` | | | `dimension` | `string` | `minecraft:overworld`, `minecraft:the_nether`, ... | | `game-mode` | `game-mode` | | | `health` | `f32` | Hit points as vanilla counts them: 20 is full health without effects. | | Function | Signature | What it does | | --- | --- | --- | | `online` | `func() -> list` | The players online, in the order they joined. | | `tell` | `func(player: string, text: string) -> bool` | A system message to one player, after the mod's name; false when no player with that id is online. Up to 8 messages per call. | | `broadcast` | `func(text: string)` | A system message to every player and the server's log, after the mod's name. Counts toward the same 8 per call. | ## `world` Access: capability `world`. Capability `world`, server mods only (manifest `api = "0.3"`, experimental): the blocks of the server's loaded chunks, and changes to them through the server's own block updates. ### record `block-change` | Field | Type | | | --- | --- | --- | | `x` | `s32` | | | `y` | `s32` | | | `z` | `s32` | | | `state` | `string` | `minecraft:oak_log[axis=y]`; properties left out take the block's defaults, `minecraft:stone` alone is the default state. | | Function | Signature | What it does | | --- | --- | --- | | `get-block` | `func(dimension: string, x: s32, y: s32, z: s32) -> option` | The block state at a position in a dimension (`minecraft:overworld`, ...), as `minecraft:oak_log[axis=y]`, as of this call: none where the server has no chunk loaded there. | | `get-biome` | `func(dimension: string, x: s32, y: s32, z: s32) -> option` | The biome at a position (`minecraft:plains`, ...), as the server stores it (one per 4 x 4 x 4 cell): none where no chunk is loaded. | | `set-blocks` | `func(dimension: string, blocks: list) -> result<_, string>` | Sets blocks as `/setblock` does: the old block entity removed, neighbours and shapes updated, light, and every player told. Every state is checked now (an error queues nothing); the changes are made in order after this call returns, before that dimension's next tick; one in a chunk not loaded then is skipped. At most 4096 per call. | ## `server-events` Access: exported by the mod: the game calls it. What the server calls in a server mod (manifest `api = "0.3"`, experimental). Only the hooks `init` asks for are ever called. The player in each event is as the server saw them when it happened. ### type `player` `type player = player` ### record `block-pos` | Field | Type | | | --- | --- | --- | | `x` | `s32` | | | `y` | `s32` | | | `z` | `s32` | | ### variant `argument-kind` What an argument of a command takes. The server sends each command and its arguments to players' games, whose chat box completes and colours them as vanilla's; the server parses them before the mod's `command` hook gets their values. | Case | Payload | | | --- | --- | --- | | `word` | | One word: letters, digits and `_ - . +`. | | `text` | | The rest of the line, spaces included: only last. | | `integer` | `tuple, option>` | A whole number, within the bounds given. | | `number` | | A number with or without decimals. | | `boolean` | | `true` or `false`. | | `player` | | One online player: a name, or `@s` or `@p` (whoever typed it, as vanilla picks for a player). | | `block-pos` | | A block position: three whole numbers, each one also `~` or `~n`, relative to whoever typed it. | ### record `argument` | Field | Type | | | --- | --- | --- | | `name` | `string` | Shown in the chat box's hint (``) and the key of its value. | | `kind` | `argument-kind` | | | `optional` | `bool` | Optional arguments come after every required one. | ### record `server-command` | Field | Type | | | --- | --- | --- | | `name` | `string` | Without the slash. A name vanilla already uses is refused (vanilla's command runs). | | `arguments` | `list` | In order; none: the command takes nothing after its name. Free text is one optional `text` argument. | ### variant `argument-value` An argument's value, parsed as its kind says. | Case | Payload | | | --- | --- | --- | | `word` | `string` | | | `text` | `string` | | | `integer` | `s32` | | | `number` | `f64` | | | `boolean` | `bool` | | | `player` | `player` | | | `block-pos` | `block-pos` | | ### record `registration` | Field | Type | | | --- | --- | --- | | `commands` | `list` | Commands players may type. | | `tick` | `bool` | Call `tick` every server tick (20 per second). | | `joins` | `bool` | Call `player-joined` and `player-left`. | | `chat` | `bool` | Call `chat` for each chat message a player sends. | | `blocks` | `bool` | Call `block-broken` and `block-placed`. | | Function | Signature | What it does | | --- | --- | --- | | `init` | `func() -> result` | Once, after loading. An error keeps the mod off and is logged. | | `tick` | `func(tick: u64)` | Each server tick, with the server's tick counter. | | `player-joined` | `func(player: player)` | A player joined the server; they are in `players.online` already. | | `player-left` | `func(player: player)` | A player left the server; they are no longer in `players.online`. | | `chat` | `func(player: player, text: string)` | A chat message a player sent, as plain text. | | `command` | `func(player: player, name: string, args: string, values: list>) -> result, string>` | `/name args` typed by `player`: ok(some(text)) is their feedback, err(text) their error in red. `values` holds each argument given, by name, in order (an optional one left out isn't there); a line that doesn't parse never reaches the mod: the player gets vanilla's error. | | `block-broken` | `func(player: player, pos: block-pos, state: string)` | A player broke the block at `pos` in their dimension: `state` is what was there, as `minecraft:oak_log[axis=y]`. | | `block-placed` | `func(player: player, pos: block-pos, state: string)` | A player placed `state` at `pos` in their dimension. |