The compiled definition is the typed contract between the rpp plugin and the server runtime/codegen. The rpp plugin now
emits Kotlin sources that instantiate this schema directly (WindowPackData, WindowFonts, and friends are internal;
WindowDefinitions, WindowHudDefinitions, and WindowSprite are public); it does not write a JSON manifest into the
pack output.
The JSON below is the schema's compatibility representation, used by legacy tools and parser tests. Schema changes bump
version and must update window-core/src/manifest.rs, the runtime DTOs, and codegen together.
Resource packs do contain assets/<namespace>/window/debug.json, but that schema-v1 document is a client diagnostics
descriptor, not this runtime manifest. It intentionally contains semantic expectations, pack/resource fingerprints, and
isolation masks while omitting server bindings and control routing. See docs/INSPECTOR.md.
All coordinates are GUI-space pixels (origin = container/HUD top-left). All codepoints are serialized as the actual
characters inside JSON strings, except in spacers where they are integers for readability.
The definition holds only primitives: baked art (static), text (slots), images (sprite_slots), inventory
regions, items, collections, inputs, and switches whose cases hold any of these, including other switches.
Authoring components compile into these: a control with states, such as an industrial Button with enabled, is a
switch on its handle with one region per state.
Notes:
windows.*.staticalready starts and ends attitle_origin.x(net-zero) — consumers concatenate segments without bookkeeping.font_metricsis the preferred measurement table for styled runtime text.text_advancesandtext_glyph_widthsremain for compatibility and are the same vanilla default-font metrics used by generated shifted fonts.- Shader HUDs render from the actionbar center:
huds.*.staticand every slot segment are net-zero, and the generated shader moves the local HUD coordinate system into place. Non-shader HUD fallbacks keep a fixed-width static segment so vanilla actionbar/bossbar/sidebar centering stays stable. - A slot's vertical placement is fully encoded in its
font;yis informational (and used by codegen/tooling). A shader HUD's slot fonts sitshader.row_offsetrows belowy. - Runtime sprite slot vertical placement is likewise encoded in
sprite_slots.*.font. The sprite glyph comes fromsprites.<id>.glyph, and the generatedsprite_y...font repeats that glyph at the slot's vertical ascent. - A sprite font holds only the sprites its slots can show: a slot's
only(its handle'sonlylist), a collection selection'ssprite, or the whole catalog for a slot withoutonly. The runtime rejects binding a sprite outside a slot'sonly, since its glyph is missing from the font. spritesholds thedefineWindowssprite catalog and inline art that the runtime draws (a collection's selected sprite). Inline art is keyedart/{name}-{hash}, orart/{hash}without a name, wherehashis a hex prefix of the art's content hash; codegen gives these noWindowSpriteconstant.boldchanges text advance bybold_advanceper rendered character. Italic, underline, strikethrough, and obfuscated are style defaults but do not change cursor measurement.regions.*.slots,items.*.slots,collections.*.slots, andinputs.*.slotare typed slot references.area = "container"addresses the opened inventory.area = "player"addresses the viewing player's inventory, with Minestom's slot indices (0..8hotbar,9..35main inventory). Runtime player slots are snapshotted before Window writes them and restored when the menu closes.- Regions take the slots their laid-out rectangle overlaps: their parent box's, or their section area's. If no slot
overlaps, the build fails. Items and collections take slots resolved from authoring
slots,pattern,transform, their section area, or the slots their parent box covers. - Slot ownership and slot routing are separate contracts. Exactly one active control fills any given slot:
regions.*.fill_slots(defaulting toregions.*.slots),items.*.slots,collections.*.slots, andinputs.*.slotmust be disjoint. Independently, theslotsof regions with anactionmust be disjoint, because a slot can only route its clicks to one region. Regions, items, and collections in mutually exclusive switch cases are exempt from both: two entries are exclusive when their case paths (the cases enclosing them, outermost first) pick different cases of the same switch. A region over a slot an item, collection, or anvil input fills relies on the ownership/routing split: it routes the slot while the other control fills it, andfill_slotsleaves the slot out (it may be empty). The runtime writes the hitbox intofill_slotsof every active region and routesslots; a click on an action collection's cell goes to the collection even when a region also routes that slot. regions.*.actionnames the handler a click runs. The runtime runs the handler bound to that id, elsedefault_action; a region withoutactionignores clicks. Ids in thewindow:namespace (window:close) are runtime actions: codegen declares no member for them, and they always carry the samedefault_action, so they work unbound. A region'sactionis its entry name, so regions of the same action handle have one action each (buy,buy~2), all bound to the handle's member.regions.*.hitboxis the item the runtime writes into the region's fill slots:item_model(default: the emitted invisible{namespace}:gui/hitboxmodel) and an optionaltooltip. Withouthitboxthe slots stay empty.inputsdescribes native inventory input metadata. It is currently valid only forsurface.container = "anvil"; exactly one input owns container slot0, whose item name drives the vanilla edit field.handlesis optional metadata for codegen on windows and HUDs; runtimes ignore it.kindisflag,toggle,value,selection,text,sprite,tint,fraction,items,collection,action,input, orbuiltin.valueslists a value or selection's values or a tint's entries,colorsa tint's color per entry,shapean indexed handle's extents,initiala toggle's ("true"/"false") or selection's initial value,selectablewhether a collection marks a selected cell, andonlythe sprites a sprite handle returns, which codegen generates as an enum of thoseWindowSpriteconstants. Each use names the entry it binds: aslot,sprite_slot,item,collection,input,switch, a region'sclick, the palette atinthandle picks from, or thebarafractionhandle fills.atis the index read from an indexed handle, andvaluethe value a condition compares with or a click sets. A use is keyed{id}, then[{i}]per index,={value}for a click that sets a value, or?{value}for a condition, with~2,~3, … appended to later uses of the same key. Codegen generates members only from handles and binds every use to its handle's member; an entry no handle uses gets no member.switchesis optional on windows and HUDs. At most one case of a switch is active: the case whosevaluethe switch's binding returns, or none until it is bound. A switch named in a case'sswitchesis nested: it and everything in its cases are active only while that case is. A case lists only the entries directly inside it. The runtime draws the active cases'staticat the switch's position inlayers, leaves out the slots, sprite slots, and collection selections of inactive cases, and claims only the regions,items, andcollectionsof active cases, so changing a case swaps the items and click routes of its slots. A click routes by the cases selected when it arrives: the runtime reads each enclosing switch's binding at click time rather than waiting for the next render. Each case'sstaticis net-zero: window cases start and end attitle_origin.x, HUD cases at the HUD's left edge. Codegen binds aBooleanwhen the case values are exactlytrueandfalse, otherwise an enum of the values.layerslists every slot, sprite slot, switch, art layer, and collection with a selected sprite exactly once, in authored tree order. The runtime composesstaticfirst, then these layers in order, skipping entries in inactive cases. A switch layer draws its active case'sstatic; an art layer draws itsstatic; a collection layer draws its selected cell'sselectionsprite.artandtintsare optional on windows and HUDs, and a definition without them draws as before.tintsmaps each palette id to its entries'#rrggbbcolors.tinton anartentry, a slot, or a sprite slot names a palette: the runtime sends that entry in the color of the palette entry the tint's binding returns, which the client multiplies into art and sprites. Every palette must be bound. A tinted slot'scoloris its palette's first authored entry, and runtimes ignore it. Art and sprites withouttintare sent in white. A case lists the art layers directly inside it inart, drawn only while the case is active.art.*.barmarks the fill of a bar, which the runtime draws up to the fraction bound tobar.nameinstead ofstatic(the whole fill). The columns drawn areround(clamp(fraction, 0, 1) * width).levels[k]holds one glyph offontper clip of2^kcolumns: its characteridraws the fill's columns fromi << (k + 1), and a space is a clip with nothing to draw. To drawncolumns, take the set bits ofnfrom the highest: each draws the clip of that size starting where the previous one ended, placedxplus that start right of wherestaticstarts, and advances the cursor by its glyph'sadvancesentry. The runtime returns the cursor to the start afterwards. Every bar must be bound.- In a shader HUD,
art.*.tint_markersandslots.*.tint_markersmap each palette entry to the marker color the runtime sends in place of the palette color; the generated shader restores that entry's color. Untinted art layers useshader.static_marker. Outside shader HUDstint_markersis absent and the runtime sends the palette color. slots.*.overflowandslots.*.linesappear only on dynamic slots. Withoverflow: "ellipsis", runtimes truncate rendered content whose visible width exceedswidthand append "…", or "..." when the slot's font metrics have no advance for "…", keeping the styling of the kept text. Withlines, runtimes wrap content at spaces onto at mostcountlines, ellipsizing a word wider thanwidthand the last line when text is left over (overflowis then always set). Whenklines are used, linej(0-based) draws infonts[count - k + 2j], whose top isy + floor((count - k + 2j) * line_height / 2), which centers the used lines in the box.fontshas2 * count - 1entries andfonts[0]equalsfont.tooltipmay be absent, or an object withtitleand optionallines. Authoring accepts a string shorthand, but the compiled definition always uses the object form.sourceon regions names the authored element they come from, such asregion `category=all`orplayer section, for diagnostics. On slots, sprite slots, and switches it is thedebugNameof the nearest authored element that sets one.- Entry keys are unique per window or HUD across all maps. Handle ids match
^[a-z][a-z0-9_]*$; entries derived from them add[,],?,=, or~, and runtime actions and section claims use:and_section. shaderis metadata for generated core-shader packs.origin_*is a normalized GUI target point,anchor_*is the normalized point inside the HUD surface placed on that target, andoffset_*is a GUI-pixel nudge.source_bottomis the nominal bottom of the source text surface measured up from the GUI bottom; generated shaders apply Minecraft's fixed actionbar source inset, independent of the authored HUD height.row_offsetis how many rows below that source the compiler baked the HUD's art, switch cases, and slot fonts so that its text rows share fonts with other surfaces; the generated shaders subtract it again, so it does not move the HUD. Runtime display still uses the HUD's fallbackchannel.- Shader HUDs include generated marker colors in the compiled definition:
shader.static_markerfor baked chrome,slots.*.shader_markerfor slot text, andtint_markersfor tinted art and text. These marker colors reserve green channel00; red+blue encode a generated 16-bit segment id. Generated shaders only move exact ids in their table, then restore the authored display color, so unrelated green-zero text does not relocate. - Generated Kotlin is deterministic: files use stable iteration order, fixed headers, and
\nendings. Legacy JSON serializers should keep sorted keys so identical inputs stay byte-identical.
{ "version": 9, "namespace": "window", "font": "window:ui", // font id of the main (static + spacer) font // Spacer table: codepoint (integer) → horizontal advance in pixels. // Values are ±1, ±2, ±4 … ±1024. Compose arbitrary offsets from these. "spacers": { "983040": -1024, "983041": -512, /* … */ "983061": 1024, }, // Vanilla default-font advance widths (pixels per character: cursor movement // after rendering). Applies to every shifted label font too. Consumers use // this for net-zero spacer math instead of bundling their own table. "text_advances": { " ": 4, "!": 2, /* … */ "~": 7 }, // Vanilla default-font visible glyph widths (pixels per character, excluding // the inter-glyph gap). Consumers use this for visual alignment. "text_glyph_widths": { " ": 0, "!": 1, /* … */ "~": 6 }, // Per-font metrics keyed by font id. Generated definitions include // "minecraft:default", every generated shifted text font, and every // text-font offset ("window:small_caps/y0"), whose sheet glyphs replace // the vanilla entries. "font_metrics": { "window:y0": { "advances": { " ": 4, "!": 2, /* … */ "~": 7 }, "glyph_widths": { " ": 0, "!": 1, /* … */ "~": 6 }, "bold_advance": 1, }, }, // Runtime sprite catalog. The same glyph is repeated into every generated // sprite_y... font needed by authored sprite slots; those font providers // reference the same texture file, so PNGs are not duplicated by height. // width/height are the authored visual canvas. x_offset/glyph_width are the // measured visible ink bounds used for alignment. advance is the Minecraft // cursor movement from the rightmost opaque pixel after bitmap scaling, plus // the one-pixel bitmap gap. "sprites": { "wooden_pickaxe": { "width": 32, "height": 32, "x_offset": 0, "glyph_width": 32, "advance": 33, "glyph": "", }, }, "windows": { "shop": { "surface": { "kind": "container", "container": "generic_9x6", // window-core surface kind id "size": [176, 222], // GUI texture size "title_origin": [8, 6], // where the title cursor starts }, // Net-zero baked chrome. Send as-is in font `font` before slot segments. "static": "…", // Dynamic text slots, including static labels (which carry "text"). "slots": { "title": { "x": 8, "y": 6, // top-left of the text line (8px tall) "width": 160, // reserved width for alignment/clipping "align": "center", // "left" | "center" | "right" "font": "window:y0", // shifted font for this y offset; "window:<font>/y0" for text fonts "color": "#404040", // default text color (hex, lowercase) "shadow": false, "bold": false, // optional, omitted when false "italic": false, "underlined": false, "strikethrough": false, "obfuscated": false, "overflow": "ellipsis", // optional: shorten content wider than `width` }, "holder": { "x": 8, "y": 20, // top of the slot's box, (count - 1) * line_height + 8 px tall "width": 46, "align": "center", "font": "window:y14", "color": "#ffffff", "shadow": false, "overflow": "ellipsis", // Optional: wrap onto at most `count` lines. fonts[s] draws a line at y + floor(s * line_height / 2). "lines": { "count": 2, "line_height": 7, "fonts": ["window:y14", "window:y17", "window:y21"] }, }, "buy_label": { "x": 58, "y": 196, "width": 60, "align": "center", "font": "window:y190", "color": "#ffffff", "shadow": false, "text": "Buy", // present ⇒ static label, auto-rendered }, }, // Runtime sprite regions. Runtime code binds a sprite catalog key; the // slot's font carries the correct ascent for this y offset. "sprite_slots": { "selected_tool_icon": { "x": 112, "y": 45, "width": 32, "height": 32, "align": "center", "font": "window:sprite_y39", "only": ["pickaxe", "shovel"], // optional: the catalog sprites the slot's font holds; absent for any "source": "tool-icon", // optional: the nearest authored `debugName`, for diagnostics }, }, // Inventory regions: the slots each claims and fills with its hitbox item, and the action its clicks name. // Regions directly in a switch case are claimed only while that case is active. "regions": { "buy": { "x": 52, "y": 190, "width": 72, "height": 20, // Slots whose clicks route to this region. "slots": [ { "area": "container", "index": 46 }, { "area": "container", "index": 47 }, { "area": "player", "index": 0 }, ], "action": "buy", // the handler key clicks name; absent ⇒ hover- or claim-only // Optional item filling the slots; absent leaves them empty. "hitbox": { "item_model": "example:gui/buy", // absent ⇒ the pack's invisible hitbox model "tooltip": { "title": "Buy", "lines": ["Spend coins"] }, }, // Optional hover outline (`coreShaders.hover`): the outline glyph and its net-zero spacers in the main // font, and the region's top-left relative to the GUI center. The runtime leads the hitbox tooltip's title // with it, filling the slots even without "hitbox". "hover": { "text": "\uE040\uDB80\uDC0A", "x": -36, "y": 79 }, "source": "region `buy`", // the authored element, for diagnostics }, // The disabled state of the same button: hover-only, so it has no action. "region~0": { "x": 52, "y": 190, "width": 72, "height": 20, "slots": [/* same as buy */], "hitbox": { "tooltip": { "title": "Need more coins" } }, "source": "region `region~0`", }, "window:close": { "x": 8, "y": 190, "width": 40, "height": 20, "slots": [{ "area": "container", "index": 45 }], "action": "window:close", "default_action": "window:close", // a runtime action: works unbound "source": "region `window:close`", }, "clear": { // A region over the anvil input's slot routes its clicks while the input keeps its seed item. "x": 27, "y": 47, "width": 16, "height": 16, "slots": [{ "area": "container", "index": 0 }], "fill_slots": [], // optional strict subset of "slots" filled with the hitbox; absent ⇒ every slot "action": "clear", "source": "region `clear`", }, "player_section": { // A claim-only region from a section's unowned slots: no action, no hitbox. "x": 8, "y": 140, "width": 160, "height": 16, "slots": [ { "area": "player", "index": 9 }, { "area": "player", "index": 10 }, ], "source": "player section", }, }, // Dynamic inventory item regions. Runtime code binds `item("featured")`. Items directly in a switch case // are filled only while that case is active. "items": { "featured": { "slots": [{ "area": "container", "index": 13 }], }, }, // Repeated dynamic item regions. Slots are ordered; collection click handlers // receive the clicked cell index. Set `"action": false` for display-only grids. // `selection` holds one sprite slot per cell, in cell order, drawn over the cell's // 18x18 slot box; it is omitted when the collection has no `selected_sprite`. "collections": { "entries": { "slots": [ { "area": "container", "index": 19 }, { "area": "container", "index": 20 }, { "area": "player", "index": 9 }, ], "action": true, "selection": [ { "x": 7, "y": 17, "width": 18, "height": 18, "align": "left", "font": "window:sprite_y11", "sprite": "art/industrial-slot-selected-2423c7", }, // …one entry per cell ], }, }, // Native text inputs. Anvil surfaces support one input, backed by slot 0. // Generated views expose an `onQueryChanged(value: String)` member. "inputs": { "query": { "slot": { "area": "container", "index": 0 }, "initial": "", "item_model": "example:gui/search_input", }, }, // Runtime-selected cases by key. Only the active case's net-zero `static` // art, slots, sprite slots, regions, items, collections, and switches are drawn or claimed. "switches": { "mode": { "cases": [ { "value": "buy", "static": "…", "slots": ["price"], "switches": ["stock"] }, { "value": "sell", "static": "…", "slots": ["label_3", "payout"], "items": ["featured"] }, ], }, // Nested in case `buy` of `mode`: active only while `mode` is `buy`. "stock": { "cases": [ { "value": "low", "slots": ["count"] }, { "value": "high", "static": "…", "slots": ["count~2"] }, ], }, // The state switch of a button with `enabled={canBuy}`, keyed by the flag's entry. "can_buy": { "source": "buy-button", // optional: the switch's `debugName`, for diagnostics "cases": [ { "value": "true", "static": "…", "slots": ["buy_label"], "regions": ["buy"] }, { "value": "false", "static": "…", "slots": ["buy_label~2"], "regions": ["region~0"] }, ], }, }, // Optional. Art drawn as a layer of its own: art tinted from a palette, // untinted art authored over it, and the fill of a bar. `static` is net-zero // like the window's. "art": { "art/0": { "static": "…", "tint": "tone" }, "art/1": { "static": "…" }, "art/2": { "static": "…", "bar": { "name": "health", "x": 8, "width": 5, "levels": ["\uE010\uE010\uE011", "\uE012", "\uE013"], "advances": { "\uE010": 2, "\uE011": 2, "\uE012": 3, "\uE013": 5 }, }, }, }, // Optional. The palettes that `tint` fields name: entry name to color. "tints": { "tone": { "danger": "#ff4d4d", "ok": "#3ddc84", "warn": "#ffb020" }, }, // Every slot, sprite slot, switch, art layer, and selectable collection in // authored tree order: the order runtime layers compose above `static`. "layers": [ { "kind": "art", "name": "art/0" }, { "kind": "art", "name": "art/1" }, { "kind": "slot", "name": "title" }, { "kind": "switch", "name": "mode" }, { "kind": "slot", "name": "price" }, { "kind": "switch", "name": "stock" }, { "kind": "slot", "name": "count" }, { "kind": "slot", "name": "count~2" }, { "kind": "slot", "name": "label_3" }, { "kind": "slot", "name": "payout" }, { "kind": "collection", "name": "entries" }, { "kind": "switch", "name": "can_buy" }, { "kind": "slot", "name": "buy_label" }, { "kind": "slot", "name": "buy_label~2" }, ], // Typed handles by id, with the entries that use them. "handles": { "category": { "kind": "selection", "values": ["all", "gear"], "initial": "gear", "uses": [ { "role": "click", "entry": "category=all", "value": "all" }, { "role": "switch", "entry": "category?gear", "value": "gear" }, ], }, "can_buy": { "kind": "flag", "uses": [{ "role": "switch", "entry": "can_buy" }] }, "buy_label": { "kind": "text", "uses": [ { "role": "slot", "entry": "buy_label" }, { "role": "slot", "entry": "buy_label~2" }, ], }, "names": { "kind": "text", "shape": [2], "uses": [{ "role": "slot", "entry": "names[0]", "at": [0] }] }, "tone": { "kind": "tint", "values": ["ok", "warn", "danger"], "colors": ["#3ddc84", "#ffb020", "#ff4d4d"], "uses": [{ "role": "tint", "entry": "tone" }], }, }, }, }, "huds": { "status": { "surface": { "kind": "hud", "channel": "actionbar", // "actionbar" | "bossbar" | "sidebar" "width": 160, // fixed line width in GUI pixels "height": 16, }, // Fixed-width baked chrome. Send as-is in font `font` before slot // overlays; it intentionally claims exactly `surface.width` pixels. "static": "…", // Same slot shape as windows. Static labels carry "text"; dynamic slots // are bound by generated HudView subclasses. "slots": { "coins": { "x": 54, "y": 4, "width": 98, "align": "right", "font": "window:y4", "color": "#ffffff", "shader_marker": "#120034", "shadow": false, }, }, // HUDs carry `switches`, `art`, `tints`, and `layers` like windows; HUD layers hold slots, switches, and // art. In a shader HUD a tinted slot or art layer also carries `tint_markers`. // Present only when the authored HUD requested core-shader relocation. "shader": { "static_marker": "#010002", "source_bottom": 59, // Omitted when 0. "row_offset": -3, "origin_x": 0.5, "origin_y": 0.08, "anchor_x": 0.5, "anchor_y": 0.0, "offset_x": 0, "offset_y": 0, }, }, }, }