render.set-far-terrain moves to API level 7: main added levels 4 to 6
(world, recipes, browser) since this branch started.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mod can now turn on the game's distant terrain (LOD) with
render.set-far-terrain: a distance of 64 to 1024 chunks and Far Detail's
Reduced. The game builds and draws the tiles as it does for the LOD
Distance setting, which wins while it is on. One far terrain shows at a
time, the first loaded mod's. API level 4.
mods/official/far-view is the official mod on it: Distance (256 chunks by
default) and Far detail on its settings page, kept in its storage.
As the creative inventory does: a count over the item's stack size (64, 16
or 1) is cut to it, so a mod can ask for a full stack without knowing it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
recipes.place(book-id, all) is the recipe book's click: in the open
inventory's or crafting table's grid it sends ServerboundPlaceRecipePacket
on a server, or places it in this world, refused as handlePlaceRecipe
refuses it (no crafting screen, a recipe the player's book doesn't hold, an
impossible one); a recipe missing items shows as a ghost. recipes.give(stack)
gives a creative player the stack as the creative inventory does; a server
hears it as SetCreativeModeSlot through the inventory's sync. Both answer at
once from the tick's snapshot (placing, creative, known) and take at most 36
calls a hook call.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mod gives the recipes to show (a layout: crafting grid, cooking, single
input, smithing or brewing; the slots, each cycling through its stacks;
the results; a note; and an optional action button, enabled or greyed)
under a title; an empty list shows the items again. The game pages them
and sends the slot-clicked, action, page and back events.
At most 256 recipes, 9 slots each and 64 stacks a slot. Same level as the
rest of browser (5): none of it has shipped yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new interface under capability ui. browser.set-items lists the items of
a panel the game shows right of the inventory and container screens (an
empty list hides it); browser.poll returns what the player did since the
last poll: a click on an item, the search text, or one of the mod's own
keys pressed over an item (a slot's or the panel's), with that stack. The
slot-clicked, action, page and back events are there now for the recipe
pages to come, since adding variant cases later would break mods.
At most 8192 items and 256 waiting events. The game side is the next PR.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
recipes.items() lists every item in registry order, with its name in the
player's language and its tags. recipes.all() lists every recipe: crafting
and furnace ones from the recipe book's own entries (vanilla's displays and
display ids), then campfire, stonecutting, smithing and brewing (display
only). Stacks point into the item table by index. recipes.known() gives the
book ids the player's recipe book holds, from the client book in every mode,
so on a server it follows the server's book.
The game builds the tables once, the first time a running mod reads them.
Experimental, new functions only. Smithing::Transform now keeps its recipe
id.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mod hands `run-quiet` a list of lines (at most 256 a call). In single
player they run as the player typing them would, with the player's source
made silent; on a Horizon server (it announces horizon:quiet_commands) they
go as one payload that the server runs the same way; on any other server
they fall back to chat commands, whose feedback shows. command.run is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`game.raycast(distance)` returns the first block along the player's view
(fluids pass), up to 1024 blocks: through the loaded chunks as the
crosshair's ray goes, then across the distant terrain's ground heights
(`Lod::ground`, from Stink-O's fork). The game casts it only while a running
mod imports the function. It is API level 2, so older Horizons refuse a mod
that calls it with "update Horizon".
Co-authored-by: Stink-O <180130752+Stink-O@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What the official Minimap mod needs: a mod picks the corner, side, scale,
shape and turning, and the game draws the map as the minimap extra does,
from the same tiles (vanilla's map colours, the floor under roofs) within
its own frame budget. One map at a time, the first loaded mod's. A new
function and type, so mods built before it keep loading.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new case in ui's widget variant stops every ui mod built before it from
loading: the runtime links a component only when each imported
function's types match exactly ("expected variant of 5 cases, found
4"), so the published FPS Counter and Keystrokes would break. Instead,
the game lists a mod's keys first on its page in Options > Mods, as Key
Binds rows, and a mod with keys gets a page even without ui. No WIT
type changes. docs/api/stability.md said a new case the mod sends isn't
breaking; it is, and now says so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A ui page's key widget shows one of the mod's keys as Key Binds does
("Zoom: Z"). A click waits for the next key or mouse button, which
becomes the key; Esc binds none. The game keeps the choice in
horizon-mods.txt (key.<mod>.<name>:key.keyboard.z), applies it when
the mods load and to keys registered later, and lets a held key go when
it is rebound. register-key's key is now the default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
render.set-zoom divides the field of view by up to 10, eased through the
game's FOV easing or at once, with a turning scale (at least 0.05); only
while playing, and the spyglass's own view wins. The strongest of several
mods' zooms counts, and the built-in zoom keeps working beside it.
input.claim-wheel takes the wheel from the hotbar while playing: its steps
are the mod's wheel events. That is the one input a mod consumes; its keys
never do (docs/api/stability.md). A screen opening now lets the mod's held
keys up, and mod keys bound to mouse buttons (key.mouse.4) are heard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Capability::ALL is 9 with Input; the SDK re-exports command and input;
the API reference is regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both add a Capability variant: the list holds 8 now. The SDK re-exports
both interfaces; the API reference is regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts kept both sides: the capability list (now 7), the WIT, the
SDK's re-exports and docs, the mods state's fields, the workspace
members and AGENTS.md's macOS --exclude list. The API reference is
regenerated, and docs/api/stability.md lists `command` with the 0.1
preview interfaces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The built-in extras become official mods; these are the calls the FPS
counter and keystrokes overlay need to look and behave the same:
- render.set-corner(corner, w, h, shapes): a box the game places with
its own corner stacking (under the status effects, with the overlays
and hud panels), clear-corner; text-width(text) in the HUD font
(advances for U+0000..U+024F, 6 otherwise); frame-stats(): the game's
FPS counter (fps, min, 1% low).
- input.mapping(name): a movement or click mapping's key label and
whether it is held; clicks-per-second() for attack and use.
- ui: a button widget (200 x 20, select(id, 0)) for settings pages.
- The game keeps its FPS and click counters running while a mod runs,
and refreshes the snapshot every frame for mods with a frame hook.
- The ui test guest gains /corner and /readings, with a sandbox test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- WIT `input`: claim-items(ids) makes the use button on those items the
mod's: use-start, then use-stop with the ticks held (button up, item
out of the hand, or a screen opening); the game does nothing else
with the click. register-key(name, label, key) for up to 16 keys
(key.keyboard.r), heard as key-down / key-up while playing; the
game's own mapping on the key still runs. held(), poll() (256 kept).
- game/mod_input.rs: play_mouse asks mod_use first (local and remote
windows), the tick ends a use whose item left the hand, the keyboard
and horizon-ctl's key send the physical key to mod_key.
- No claim and no key, no cost: one check each.
- Experimental: the manifest needs experimental = ["input"].
- The ui test guest gains /input_claim, /input_poll and /input_held,
with a sandbox test. Docs: mods.md, stability.md, the changelog, the
regenerated reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mods draw through the game's own renderer, never their own shaders:
- WIT `render`: set-hud(shapes) with fill, quad (any four corners),
sprite (the game's GUI sprites), item (scaled) and text shapes, up
to 1024, kept until replaced; gui-size(); shake(strength, seconds).
- Drawn over the HUD after the extras, under chat and screens, hidden
with F1 (game/mod_render.rs). Colours are 0xAARRGGBB.
- shake turns the view, not the player: jolts fade out squared, add up
to 1, at most 3 degrees of yaw and 2 of pitch, scaled by Distortion
Effects (screenEffectScale; 0 turns it off).
- Experimental: the manifest needs experimental = ["render"].
- No mod drawing, no cost: an empty iterator and an is_empty check.
- cargo horizon-mod check reports shakes and HUD shapes. The ui test
guest gains /render_hud, /render_clear and /render_shake, with a
sandbox test; HudBatch gets fill_quad.
- Docs: mods.md, stability.md, the changelog, the regenerated reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A server mod's registration now lists each command's arguments: a name, a
kind (word, text, integer with bounds, number, boolean, player, block-pos)
and whether it is optional. The server adds them to the command tree every
player receives, after vanilla's commands, so a player's chat box completes
and colours them as vanilla's. When a player sends one, the server parses
the line as vanilla's parsers do (horizon-mods' commands.rs: ~ coordinates
from the player, @s/@p, integer bounds) and calls the mod's command hook
with the values by name, or answers with vanilla's error without calling
the mod.
The hub keeps the server mods' commands as a root in the commands report's
format and resends players' trees when they change (CommandTree::
packet_with); the game sets it when its mods start, stop or reload, or a
hub opens. In-frame, the window's own chat box lists them by name.
SDK: Registration::command_with(name, [Argument::...]), Argument builders,
Args (text(), integer(), player(), block_pos() ...); command(name) is free
text, an optional text argument. Examples: welcome's /mined [player],
beacons' /beacon [height] and /ground [pos], with scripts for values and
errors.
Docs: docs/mods.md (typed commands, kinds table), docs/api/mods.md and
the regenerated reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Server mods get interface world: get-block(dimension, x, y, z) reads a
loaded chunk's block state as "minecraft:oak_log[axis=y]", get-biome its
biome ("minecraft:plains"), and
set-blocks(dimension, changes) sets up to 4096 blocks per call, checked at
once, made as /setblock makes them (place_state: neighbours, light, block
entities, every client told through the block log) after the call, before
that dimension's next tick.
Reads cost no copy: for a mod call the game lends Mods the levels' chunk
maps (moved out with mem::take and put back after), only while a running
mod has world. Writes for a level that isn't ticking (another dimension,
in-frame) wait for it, up to 65,536.
Test scripts get blocks (setup) and expect.blocks; cargo horizon-mod check
reports a trial run's block writes. Example beacons: /beacon builds a
glowstone pillar beside the player, /ground reads the block underfoot and the biome.
parse_state is now a free function so the mod path shares it.
Docs: docs/mods.md (the capability, script keys), docs/api/mods.md and the
regenerated reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New WIT interface `ui` (text, item and list widgets; open-screen,
close-screen, set-settings) plus a `ui-events` export (select, closed,
settings-opened), in a second world `horizon-ui-mod` so mods built before
it keep loading. The game lays out and draws the widgets with its own
menus' code: a screen the mod owns (Esc/Done to leave) and a Settings
button on the mod's Options > Mods row that opens its page.
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>