158 lines
15 KiB
Markdown
Executable File
158 lines
15 KiB
Markdown
Executable File
# Architecture and module contracts
|
||
|
||
Updated: 2026-11-02. Describes the actual M0–M3 source organization, **not proof
|
||
of human acceptance**. See [M0 decisions/evidence](m0-decisions.md),
|
||
[M1 evidence](m1-verification.md), [M2 evidence](m2-verification.md), [M3 evidence](m3-verification.md),
|
||
[roadmap](roadmap.md) and [operator procedures](../README.md).
|
||
|
||
## Bootstrap and dependency direction
|
||
|
||
[mods/hearth/init.lua](../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](../mods/hearth/model.lua) → [schema](../mods/hearth/schema.lua) →
|
||
[persistence factory](../mods/hearth/persistence.lua).
|
||
2. Claims/protection and `/claim` in init.
|
||
3. [content](../mods/hearth/content.lua), which constructs
|
||
[construction](../mods/hearth/construction.lua),
|
||
[interactions](../mods/hearth/interactions.lua) and
|
||
[processes](../mods/hearth/processes.lua) helper factories.
|
||
4. [interface](../mods/hearth/interface.lua) → [presentation](../mods/hearth/presentation.lua)
|
||
→ [feedback](../mods/hearth/feedback.lua) → [clock](../mods/hearth/clock.lua)
|
||
→ [environment](../mods/hearth/environment.lua)
|
||
→ [players](../mods/hearth/players.lua)
|
||
→ [runtime](../mods/hearth/runtime.lua)
|
||
→ [encounters](../mods/hearth/encounters.lua) → [course](../mods/hearth/course.lua).
|
||
|
||
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](../mods/hearth/init.lua) | 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](../mods/hearth/schema.lua) | 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](../mods/hearth/persistence.lua) | 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](../mods/hearth/model.lua) | 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](../mods/hearth/content.lua) | Declarative nodes/items/tools/recipes, terrain aliases/biomes/decorations, fire/bedroll callbacks, M3 building palette, claim-protected storage/drying rack and wearable cloak. |
|
||
| [construction](../mods/hearth/construction.lua) | 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](../mods/hearth/interactions.lua) | 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](../mods/hearth/processes.lua) | 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](../mods/hearth/interface.lua) | 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](../mods/hearth/clock.lua) | 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](../mods/hearth/environment.lua) | `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](../mods/hearth/players.lua) | `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](../mods/hearth/runtime.lua) | 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](../mods/hearth/movement.lua) | Pure bounded-dt movement/stamina model; smooth walk/sprint/swim/brace speed and exhaustion release lock. Engine retains physical collision/camera/prediction. |
|
||
| [combat](../mods/hearth/combat.lua) | 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](../mods/hearth/presentation.lua) | Ephemeral whitelisted held-tool meshes attached to right hand; cleanup on weapon change, death, disconnect and shutdown. Cosmetic entities are not saved. |
|
||
| [feedback](../mods/hearth/feedback.lua) | Animated player state, material step/dig/landing and attack feedback, 10-Hz contextual target/placement HUD; static FOV is opt-in. |
|
||
| [encounters](../mods/hearth/encounters.lua) | 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](../mods/hearth/course.lua) | 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](m0-decisions.md#verification-and-acceptance-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](m2-verification.md) and [M3 verification](m3-verification.md).
|
||
|
||
## Tooling boundaries
|
||
|
||
| Script | Contract |
|
||
| --- | --- |
|
||
| [run](../scripts/run.mjs) | 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](../scripts/launcher.mjs) | Platform roots/absolute overrides, engine-version check, coordinate/fixture/environment validation, prelaunch snapshot. `HEARTH_HOME` preserves the old default root unless explicitly overridden. |
|
||
| [package](../scripts/package.mjs) | 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](../scripts/assets.mjs) | Original deterministic procedural PNG/OBJ generation into package payload, not downloaded media. |
|
||
| [files](../scripts/files.mjs) | 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](../scripts/backup.mjs) | 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](../scripts/recovery.mjs) | 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](../tests/tooling.test.mjs) and Lua
|
||
[schema](../tests/schema_spec.lua), [persistence](../tests/persistence_spec.lua),
|
||
[clock](../tests/clock_spec.lua), [model](../tests/model_spec.lua) and
|
||
[content](../tests/content_spec.lua), [access policy](../tests/access_spec.lua),
|
||
[room model](../tests/environment_spec.lua), [claim service](../tests/init_spec.lua),
|
||
[M2 timed processes](../tests/m2_processes_spec.lua), [M2 rest](../tests/m2_rest_spec.lua)
|
||
and [recipe index](../tests/recipe_spec.lua)
|
||
harnesses exist. The
|
||
[engine smoke mod](../tests/engine_smoke.lua) executes isolated Lua specs and
|
||
real registry/crafting/emergence checks; it does not play as a human client.
|
||
[Legacy fixture](../tests/fixtures/legacy-save.lua),
|
||
[scenario definitions](../tests/fixtures/scenarios.json) and
|
||
[recovery probe](../tests/recovery_probe.lua) 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.
|