5.7 KiB
Executable File
Woodland traveler character creator
Use /character at any time while connected, or open Character appearance
from the inventory. Creation is opt-in: joining restores the saved appearance
without opening the creator. Existing location selection and field-note join
forms retain priority.
A quiet traveler, not a costume catalogue
All choices are inclusive and independent, with no gender, identity or stat fields. Compact, lean, balanced, broad and sturdy silhouettes have identical mechanics, height, camera, collision, reach, movement and held-tool anchors. Hair, layered field clothes, cloaks, belt gear and packs use original low-poly geometry with hand-drawn, muted woodland pixel art.
Four tabs keep the 14 × 11 form manageable:
| Tab | Choices |
|---|---|
| Face | 5 body silhouettes, 6 skin tones, 5 face-line patterns, 5 eye colors |
| Hair | 10 styles, 6 shared hair/facial-hair colors, 7 facial-hair styles |
| Clothing | 7 garments, 8 cloth colors, 4 trousers, 3 boot leathers |
| Finishing | 9 accents, 8 accent colors, 5 face-detail patterns |
Dropdowns use readable names, not gender categories or free-form colors. Cloth
and accent palettes are sage, teal, slate, mauve, ochre, rust, linen and charcoal.
Transparent none layers and colors under absent hair/accents mean the raw
selection combinations are not all visually distinct; no uniqueness count is
claimed.
Preview and explicit consent
The form contains a real Luanti model[] preview, not a screenshot, external
browser or URL. It uses one composited 64 × 64 texture and the selected B3D.
The preview turns continuously and supports mouse dragging; Front, Profile and
Back reset its camera angle. These controls never rotate the actual player.
Changing a dropdown, switching tabs, Randomize and Reset draft affect
only a private session draft. Randomize chooses from every fixed allowlist using
bounded math.random; it is not security randomness and introduces no secrets.
Reset copies the defaults rather than modifying shared state. Cancel or
Escape discards the draft, preserving both the real appearance and saved bytes.
Only Save / Apply commits. No need, inventory, identity or physics values
are changed. Drafts disappear when the session ends.
Version-1 storage and recovery
The only new persistent value is the engine-owned player metadata string
hearth:appearance. It is independent of the existing global save schema and
world/mod storage. A complete flat JSON object contains exactly version:1 and
these string selections:
body, skin, face, eyes, hair, hairColor, beard, garment, cloth,
trousers, boots, accent, accentColor, detail.
Defaults match the authoritative art contract: balanced, sand, calm, hazel, sidepart, chestnut, none, tunic, teal, earth, walnut, scarf, sage, none. An absent value uses defaults without writing metadata. Invalid, unknown-version, missing/extra-field, malformed, nested or over-2048-byte values show safe defaults and a fixed warning. Original corrupt bytes are not overwritten, and appearance errors never kick a player. Only an explicit successful Apply replaces that value. Back up the world before manual recovery.
Encoding and decoding use core.write_json / core.parse_json(raw,nil,true);
parser errors do not log player payloads. There is no executable deserialization,
custom path, uploaded texture or free-form hexadecimal color. Every mesh and
layer filename is built solely from fixed IDs. Compositing order is skin, face,
eyes, garment, trousers, boots, hair, beard, accent, detail; accents layer above
hair so a selected hood frames and covers it. The model includes an original
draped mantle, belt, pack, projecting nose and ears. Face overlays are
skin-neutral; there is no global color tint.
The held-tool material remains the existing compatibility texture.
Apply serializes and checks the roundtrip, then writes metadata before
publishing mesh and textures. A thrown/false metadata write or serialization
failure keeps the previous published appearance and saved session selection,
leaving the draft available to retry. Engine metadata durability still follows
normal engine player saving; this is not a new disk transaction service.
Integration and validation boundary
The factory receives (hearth, api), registers
its own command/receive-fields callback, and exports apply, on_join, show,
handle, defaults, options, validate, decode, encode, preview and
snapshot-only state. Bootstrap instantiates it before the interface and
players; the player join handler calls it after base property initialization.
Only the current engine-supplied connected player object and its own session can
edit an open draft. Target names, arbitrary fields, hidden-tab selections and
invalid dropdown indices are rejected as a whole event. All displayed data is
formspec-escaped; text and input sizes are bounded.
CodeGuard input/storage guidance was applied: allowlisted keys and selections, bounded parsing, session ownership, preserved corrupt data and sanitized errors. No credentials, certificate handling, cryptography, network calls or dependencies are introduced. No authentication or database infrastructure is changed.
Focused offline Lua harnesses cover character behavior, player join integration, inventory navigation and bootstrap order. Mocks model native JSON through registered exact fixture strings, not executable Lua. Native engine rendering, mouse behavior and visual fit still require client review; no full-engine or live-world test is claimed by this change.