Files
last-hearth/docs/architecture.md
T
2026-11-03 12:41:09 -05:00

15 KiB
Executable File
Raw Blame History

Architecture and module contracts

Updated: 2026-11-02. Describes the actual M0–M3 source organization, not proof of human acceptance. See M0 decisions/evidence, M1 evidence, M2 evidence, M3 evidence, roadmap and operator procedures.

Bootstrap and dependency direction

mods/hearth/init.lua creates hearth.players (validated needs) and hearth.sessions (ephemeral UI/movement/timers), loads the pure model, sets texture/notification helpers, obtains mod storage, loads the schema and constructs the persistence adapter. It loads validated claims and wraps core.is_protected while retaining the prior engine/mod protection function.

The actual top-level order is:

  1. model → schema → persistence factory.
  2. Claims/protection and /claim in init.
  3. content, which constructs construction, interactions and processes helper factories.
  4. interface → presentation → feedback → clock → environment → players → runtime → encounters → course.

UI/content callbacks reference later-installed APIs at invocation time. Do not start scheduling needs before clock/environment/player lifecycle is installed.

Responsibility and API map

Module Contract / boundary
init Bootstrap, shared globals, claim/home/access registries, role-aware protection wrapper, /claim membership and visible bounds; one 25 × 25 ±24-Y claim per owner.
schema Pure decode(kind, value) returns schema-1 normalized envelope and migration boolean; defaults() returns a fresh needs copy. Kinds include needs, claims, homes, access, location and spawn. Access grants are limited to 16 valid player names per owner and roles resident/storekeeper. No JSON, I/O or engine. Reject unknown fields/kinds/versions and malformed/out-of-range data.
persistence Factory (schema, core, storage) → load_world, save_world, load_player, save_player. Owns serialization, legacy byte backups, readback checks, best-effort rollback/quarantine. World failures are fatal; callers must stop/kick on player failures. Access is an additive schema-1 world record; missing access defaults empty.
model Pure location, solar, weather, needs, fuel_remaining, clamp. Time is an input. Needs return a new table and danger contribution; elapsed needs step clamps to 0–2 seconds, not an offline catch-up engine.
content Declarative nodes/items/tools/recipes, terrain aliases/biomes/decorations, fire/bedroll callbacks, M3 building palette, claim-protected storage/drying rack and wearable cloak.
construction Factory (hearth, core) returns near, action-aware allowed, can_dig, place, place_building, owner, preview, removal_safe. Six-node reach; support graph bounded to two horizontal links and sixteen vertical levels. Removal is refused if connected structures would lose support; unknown/unloaded and cross-household dependencies deny. No cascading collapse.
interactions Factory (hearth) returns eat: consume/replace a stack only when an applicable need can improve, update bounded session-owned needs. Not a new inventory persistence layer.
processes Factory (hearth, core, construction) returns use_fire, fire_tick, refresh_fire, fuel_left, ignite_fire and use_firestarter. Access checks, real-time fuel/job deadlines, queue/reload reconciliation, one pending output and safe inventory collection. Cooking is six seconds; boiling is four; their heat cost is reserved on start.
interface Inventory, searchable recipe/process guide, location forms, field handlers and HUD via inventory, guide, recipes, location_form, make_hud, update_hud. UI is not a substitute for server-side validation.
clock Validate environment and preview settings, load/validate stored location even if overridden, enforce dedicated coordinates. Exports set_location, clock_update, clock, weather, has_location. Configured location takes precedence; singleplayer may use stored choice; unchosen local preview is disclosed.
environment environment(pos) returns roof/shelter/enclosed/ventilated/open_sides/heat/smoke/rain/temperature. Flood-fill is bounded to 7 × 7 × 5 cells, cached for one second, and invalidated by room-geometry edits. Open door is an opening; a nearby roof vent exhausts smoke. Unknown/unloaded probes fail exposed. It is an approximation, not persistent smoke simulation.
players player_join, player_leave, player_save, player_respawn, arrive, safe_arrival: bounded emergence/recovery checks (fallback scans ±64 nodes), versioned homes, safe /sethome inside the owner's claim, home revalidation, safe-spawn scan and retained-inventory partial-needs death recovery. Clear sessions before kicking on load/save failure.
runtime Globalsteps coordinate sprint/brace speed, safe bedroll rest, cloak warmth/rain protection, sheltered heated/rack drying, 1-Hz needs/HUD/damage, ~10-second clock/save cadence and bounded local rain. Waiting/dead sessions do not accrue needs.
movement Pure bounded-dt movement/stamina model; smooth walk/sprint/swim/brace speed and exhaustion release lock. Engine retains physical collision/camera/prediction.
combat Pure server attack/guard validation: weapon-specific cadence, reach, aim, claims, stamina, line-of-sight and short displacement plausibility. Not a complete anti-cheat or lag-compensation system.
presentation Ephemeral whitelisted held-tool meshes attached to right hand; cleanup on weapon change, death, disconnect and shutdown. Cosmetic entities are not saved.
feedback Animated player state, material step/dig/landing and attack feedback, 10-Hz contextual target/placement HUD; static FOV is opt-in.
encounters Stationary reference and bounded opt-in moving sparring; deterministic wind-up/strike/recovery, safe steps, claim leash and no drops/lethal hits. Not wildlife AI or competitive netcode.
course Consent-gated local/staging-only fixed-volume movement route; mock generation/checkpoint tests and native no-consent preflight. /course report gives server-step/callback time, not FPS.

Save and migration contract

Hearth schema 1 is an envelope {schema = 1, kind = ..., data = ...} for needs, claims, homes, access, location and spawn. Needs contain exactly food/water/warmth/ wet/stamina, each 0–100. Coordinates are finite bounded values; claim positions are integer nodes. Complete valid legacy unversioned records migrate; malformed or unknown future records fail rather than resetting silently.

Storage owner Current key / payload
Engine mod storage claims, homes, access, location, spawn schema envelopes; legacy originals at m0_backup:<kind>.
Engine player metadata hearth:needs envelope; immutable legacy bytes at hearth:m0_backup:needs; hearth:arrived remains a separate engine metadata flag.
Engine node metadata Fire absolute fuel_until, job fields and pending output; structure owner and protected storage inventories; player metadata hearth:cloak stores cloak-worn state. These node/player fields are engine-owned and not envelope transactions.
Engine SQLite backends World/map, player/auth/inventory data remain engine-managed; full world backup copies them rather than reimplementing their formats.
Memory only Session HUD IDs, sprint/brace state, damage/save timers and arrival waiters; not durable schema records.

Adapter load/save validates actual JSON serialization, suppresses raw parser logging, saves original legacy bytes before migration and verifies writes by readback. Failed commit attempts byte-exact rollback; failed rollback quarantines that store/key for the adapter lifetime. Player metadata wrappers are cached to prevent bypassing quarantine. These are not transactions, CAS, or guaranteed crash-durable writes. Missing needs alone get defaults; absent claims get an empty registry; absent location/spawn remain unset. Invalid existing records do not become defaults. Startup world errors abort; player errors clear the session, emit sanitized diagnostics and kick rather than write over corrupt state.

Full offline world snapshots complement per-key backups. Restore requires a new destination and all writers stopped. No live-world migration/recovery success is asserted without the evidence ledger.

Clock, fuel and current behavior preservation

model.solar() derives UTC/date, local solar hour, seasonal temperature and a disclosed engine-orbit approximation. Synthetic weather is deterministically indexed to six-hour UTC fronts; it is not a remote forecast and can change abruptly. Production follows host time. Fixed preview dates/weather are local or staging-only; explicit dedicated coordinates are required even if storage has a location. The 45°N, 0°E check fixture never selects a production location.

Fuel and queued jobs use real os.time() rather than the preview clock: sticks add 45 seconds, logs 180, capped reserve 600. Fire requires a reusable firestarter; fuel does not self-ignite. Cooking takes six seconds and reserves 20 seconds of fuel; boiling takes four and reserves 10. M4 smelting takes 60 seconds, firing pottery 120, drying food 300, and making charcoal 60; each reserves 30 fuel seconds before consuming its charge. Absolute deadlines continue during server absence, so fuel may expire, while completed output remains queued until collected. Deadlines are validated decimal strings; invalid job metadata fails closed and timer/LBM reconciliation commits overdue output once. Offline player needs pause. M4 crop growth samples historical local solar climate in six-hour intervals, pauses below 3°C, slows below 10°C and caps replay at 30 days. Berry bushes renew after 24 hours; planted saplings attempt bounded, claim-safe tree growth after three days. Hares are passive, non-dropping, nonpersistent and capped. Food has no general spoilage; dried rations are shelf-stable. Death preserves inventory and most needs, limiting stamina/warmth on respawn. M3 room checks are bounded to 7 × 7 × 5 voxels, cached one second and invalidated by room-node edits; support removal refuses dependent work rather than cascading. Shared claims add 16-member resident/storekeeper roles, protected storage, and structural dependencies cannot cross household boundaries. See M2 verification and M3 verification.

Tooling boundaries

Script Contract
run CLI dispatch; validate exact engine before staging/world access; launcher lease; temporary isolated LUANTI_GAME_PATH; persistent SP world with prelaunch snapshot or disposable tests. Not production deployment.
launcher Platform roots/absolute overrides, engine-version check, coordinate/fixture/environment validation, prelaunch snapshot. HEARTH_HOME preserves the old default root unless explicitly overridden.
package Read release contract; copy fixed config/license/mod payload, generate assets, copy JSON fixtures, write/verify exact SHA-256 manifest. New directory only; no engine or saves.
assets Original deterministic procedural PNG/OBJ generation into package payload, not downloaded media.
files Absolute safe paths, regular-file inventories including empty directories, link/junction/case-collision rejection, trusted-parent exclusive staging/rename, manifest and launcher leases. No hostile-parent/portable ACL guarantee.
backup Offline world snapshot/verification/restore with disjoint new destinations and lock/SQLite sidecar refusal; caller must stop remote/external writers. SHA-256 integrity, not authenticity.
recovery Private old-save copy-only exercise: retained snapshot, two restores/engine boots, synthetic staging probe and sanitized report. Source world is not booted. No connected-client/public-readiness certification.

Package/backup formats are separate from save schema. The generated manifest covers exact files/directories, not cryptographic publisher identity. Temporary logs/config/worlds are cleaned up, retained snapshots are not automatically pruned. Production data and backup custody remain operator responsibilities.

Verification boundaries and next work

Node tooling tests and Lua schema, persistence, clock, model and content, access policy, room model, claim service, M2 timed processes, M2 rest and recipe index harnesses exist. The engine smoke mod executes isolated Lua specs and real registry/crafting/emergence checks; it does not play as a human client. Legacy fixture, scenario definitions and recovery probe support evidence collection, not automatic milestone approval.

Known limitations are deliberately assigned, not reported fixed: M1 human feel/repeat/performance gate; M2 unfamiliar-player and operator acceptance, and community-location selection before shared climate staging; M3 two-player cabin, comfort and grief-resistance acceptance; M4 renewal/preservation and other offline simulation; M5 operational hardening. M3 room and support rules are bounded prototypes, not general physics, and operational/public-service readiness remains open.