# Writing and symbol data Milestone 4 stores what a player wrote without deciding what that writing does to an Age. Milestone 5 owns grammar validation and symbol interaction, and translation into biome, noise, dimension-type, and other world-generation configuration. Milestone 6 owns instability effects. See the [Legacy grammar audit](LEGACY_AGE_GRAMMAR_AUDIT.md) for the source-backed Milestone 5 contract. ## Symbol catalog The authoritative server catalog loads JSON resources from `data//rivencraft/symbols/.json`. The resource ID is the symbol ID. For example, `data/rivencraft/rivencraft/symbols/terrain/standard.json` defines `rivencraft:terrain/standard`. Schema version 1 contains display metadata only: ```json { "schema_version": 1, "name": "symbol.rivencraft.terrain.standard", "description": "symbol.rivencraft.terrain.standard.description", "category": "rivencraft:terrain" } ``` `name` and `description` are translation keys, not arbitrary formatted text or executable behavior. `category` is a namespaced organizational identifier. Milestone 5 adds [server-authoritative schema v2 semantics](SYMBOL_SEMANTICS.md) with a typed role and bounded value. Existing v1 definitions remain readable but display-only; updating an ID to v2 never rewrites saved pages or books. Invalid definitions are logged and excluded by the server. Menus and future page synchronization must obtain resolved display metadata from the server; clients never make a symbol valid by naming it. Rivencraft ships a small catalog covering terrain, biomes, lighting, weather, celestial behavior, and features. Its v2 semantics prepare for the full Legacy-inspired grammar; writing still uses random Age fallback until the parser and compiler are implemented. ## Persistent writing `SymbolReference` stores only a namespaced symbol ID. `WritingEntry` is either a symbol reference or a baseline link-panel marker. `OrderedWriting` retains up to 256 entries in exact player-authored order. All three contracts have both persistent codecs and bounded network codecs. Assembled Descriptive Books store `OrderedWriting` schema version 1 alongside their Age reference. Books assembled in 0.3.7.2 saved writing as a bare list; that form still loads as version 1 without reordering or dropping unknown IDs. Older page-free books without writing continue to work unchanged. Copying a book copies its writing and Age reference; merely copying it never creates a new Age. The Book Binder creates the Age when its finished output is taken. The Descriptive Book screen can inspect the ordered pages by turning them from the bottom corners and identifies unresolved IDs using server-provided symbol metadata. Until Milestone 5, the saved symbols are informational only: Age creation uses the existing random fallback and does not apply them to generation. Removing a data pack never removes its IDs from saved writing. A missing ID is resolved as client-safe **Unresolved Symbol** display metadata while retaining the original namespaced ID for diagnostics and later restoration. This is deliberately different from treating an unknown symbol as valid grammar. ## Pages The single stackable `rivencraft:page` item has a bounded, persistent `rivencraft:page_content` component. Its explicit states are blank, symbol, unresolved symbol, and baseline link panel. Symbol and unresolved-symbol pages retain their namespaced symbol reference; unresolved pages can be inspected and removed but are rejected as usable writing until their data pack returns. Only server-side writing operations may create a known symbol page or link panel from vanilla paper. The shared `PageItem` creation and validation gates reject new pages for identifiers absent from the server catalog. The Writing Desk refuses to copy an unresolved page, and the Book Binder keeps such a page removable but will not assemble a book from it until its symbol resolves again. The item model uses server-assigned custom-model data derived from the symbol identifier. Built-in pages reproduce Mystcraft Legacy's four-word poem composition from its page background and symbol-component sheet. Data-pack symbols without a bundled resource-pack model use the legacy question-mark fallback while keeping their real identifier and display metadata. The Notebook surface displays pages by their persistent slot index instead of packing occupied pages together. Empty positions through the last placed page appear darkened. While carrying a Page, all available positions are shown for placement. The surface draws the native 30×40 portrait page region from Legacy's book UI atlas; symbols are layered at a uniform scale, while the square item models remain unchanged in inventories. A Notebook holds 27 ordered positions. Its pages, names, gaps, and unresolved IDs are saved on the item, so moving the Notebook between an inventory, Desk, and Binder does not rearrange them. A loose Page in a Desk tab exposes just one position; a Notebook exposes all 27. The Desk has 88 persistent collection tabs, shown four at a time. Use the up/down tab controls to move by four, put paper and Ink Vials in their input slots, select a stored page, and take the copy from the virtual output. Taking it spends one paper and the configured symbol-copy ink cost. The `AZ`, `ALL`, search, and category controls only change which stored pages are displayed; the server rechecks the actual source before copying. Moving a page within the Desk or dragging it into the inventory removes the original only when the destination accepts it. The Book Binder takes a leather or empty-Notebook cover, an Age title, one Link Panel, and optionally up to 27 ordered written Pages. A filled Notebook can be imported at a chosen strip position; the empty Notebook remains recoverable. The preview result is virtual. Taking the completed Descriptive Book commits the inputs once, creates its Age identity, and saves schema-versioned `OrderedWriting` plus the Age reference on the book. Link-Panel-only books create random Ages. A written symbol does **not** yet change that generation. Inputs, title, and pending Age identity survive Binder save/load; breaking the Binder returns stored inputs. The completed book retains both its writing and Age identity through item save/load and copying. ## Ink and baseline link panels An Ink Vial is crafted from two black dye, a glass bottle, and a water bucket. The bucket remains after crafting. Each vial holds 16 ink units, stored in a persistent, network-synchronized component; only vials with identical remaining ink stack together. When depleted, a vial returns a glass bottle that can be used to craft another. The animated item texture comes from Mystcraft Legacy. The server config sets ink costs independently for symbol copying (default one unit) and baseline link-panel preparation (default two units). To prepare a panel, hold vanilla paper in one hand and use an Ink Vial in the other. The server checks the held items and sufficient ink before consuming anything. A stack of paper yields exactly one Link Panel Page per use; if the inventory is full, the result is dropped instead of deleted. Symbol copying is implemented by the Writing Desk: it previews a copy of the selected resolved page, then spends paper and ink only when that copy is taken. The desk's 16-unit ink meter refills from an Ink Vial when needed and returns a vanilla glass bottle. Link-property modifiers are deferred beyond this baseline flow. The Ink Mixer is the station version of Link Panel preparation. Put paper and Ink Vials in its inputs and take the Link Panel from its virtual output. Its 16-unit basin refills only when needed and only if the glass-bottle output has space. The basin and inputs persist through save/load; breaking the Mixer returns stored items. A Link Panel plus leather also crafts an unwritten Linking Book. JEI, when installed, displays the Mixer operation and the writing, binding, and Linking Book crafting flows; it is not required to play. ## Migration and validation The 0.3.7.2 bare-list `OrderedWriting` form reads as schema version 1 without changing entry order. Descriptive Books with no writing component keep their existing Age behavior. Old eight-slot Desks migrate their four collections, ink supply, paper, bottle return, and legacy output into the 88-tab layout; saved inputs are never silently discarded. Missing symbol IDs survive page, Notebook, Binder, and assembled-book persistence instead of being substituted or erased. The core and archived dedicated-server [GameTests](GAMETESTS.md) together exercise codec and item round-trips, legacy data, workstation NBT reload, break drops, full-inventory refusal, forged menu IDs, and replayed output requests. World saves and chunk unloads use the same block entity NBT persistence path as those reload tests. This does not replace a manual smoke test of loading an older world before updating a production save. Milestone 4 stops at preserving author intent and offering a playable random Age fallback. Milestone 5 will define symbol grammar and validation, translate valid writing into actual Age generation, and introduce symbol and ink modifiers.