diff --git a/docs/aetheria-current-codebase-model.md b/docs/aetheria-current-codebase-model.md new file mode 100644 index 000000000..31fd7bc93 --- /dev/null +++ b/docs/aetheria-current-codebase-model.md @@ -0,0 +1,301 @@ +# Aetheria Current Codebase Model + +Date: 2026-06-22 + +This is a modeling pass over the live codebase as it exists now. It is not the +ideal architecture; it is the current control-flow map, with migration pressure +points called out where Unity still acts like more than a thin renderer/input +lowerer. + +## Topology + +- `Aetheria.State` owns the CultCache/CultNet typed state node, document + registry, Eve surface state, provider advertisement projection, command + acceptance, and durable Aetheria state documents. +- `Aetheria.State.Daemon` is the local Verse member. It opens an + `AetheriaStateNode`, starts the CultMesh server, publishes discovery, accepts + typed Eve commands, reads typed daemon commands, ticks the authoritative run + document, and republishes daemon witnesses. +- `Packages/org.gamecult.aetheria.state/Runtime` is the embedded Unity-facing + runtime package. It contains the shared daemon frame documents, typed command + clients, render query helpers, stat recipe surfaces, replica/discovery code, + and the SoA publication schema that Unity can map. +- `Assets/Scripts` is the Unity client. It still contains the gameplay facade + graph, behavior simulation classes, zone renderer, UI shells, and Ymir query + bridge. During the migration it is partly renderer/input client and partly + legacy simulation host. +- `Aetheria.State.Verify` is the migration fence. It asserts that deleted + authority paths stay dead: stringly public command APIs, queues as public API, + Unity physics authority, direct Unity-side zone rendering ownership, old + serializer/database paths, and several daemon publication invariants. + +## Boot And Daemon Tick + +The daemon entry point is `Aetheria.State.Daemon/Program.cs`. + +Current daemon control flow: + +1. Parse daemon options: state path, daemon id, session id, Verse id, CultMesh + address, tick interval, fixed delta. +2. Open `AetheriaStateNode` with `startServer: true`. +3. Start `AetheriaVerseDiscoveryHost`. +4. Ensure world, Verse host settings, runtime session, compatibility surfaces, + and first daemon tick. +5. On each interval: + - accept observed Eve commands through `AetheriaEveCommandBridge`; + - read the current daemon frame from the state path; + - collect typed loadout templates from the node cache; + - collect observed daemon command documents from the state graph; + - call `AetheriaRuntimeDaemonTickRunner.Tick`; + - publish the resulting frame, SoA view, provider advertisement, health, + command boundary, game GUI/TUI surface, and editor GUI/TUI surface back + into the typed state node. + +`AetheriaRuntimeDaemonTickRunner.Tick` is the authoritative tick boundary. It +filters already-accounted commands, executes new daemon commands through +`AetheriaRuntimeDaemonOperations.Execute`, stamps zone simulation time, builds +`AetheriaRuntimeDaemonFrameDocument`, writes the frame witness, writes the +current-zone entity SoA slab, then publishes command boundary, provider +advertisement, health, and Eve surfaces. + +Important nuance: the current tick applies command effects to a typed run +checkpoint. It is authoritative for many menu, inventory, targeting, movement +intent, docking, loot, trade, loadout, and action-bar operations, but it is not +yet a complete standalone simulation engine for every behavior that still lives +under Unity `Assets/Scripts/ServerShared/Behaviors`. + +## Command Flow + +Unity public gameplay calls mostly route through typed operation wrappers now: + +- `AetheriaDaemonObserver` polls observed daemon state and owns + `AetheriaDaemonOperations`. +- `AetheriaDaemonOperations` exposes typed methods such as `SetTarget`, + `SetMoveVector`, `TransferCargoItem`, `TradePurchase`, `RestoreLoadout`, + `ToggleHullConductivity`, and `SetBehaviorActive`. +- `AetheriaRuntimeDaemonOperationClient` builds + `AetheriaRuntimeDaemonCommandDocument` values and submits them through + `AetheriaRuntimeCommandSubmitter.TrySubmitDaemonCommand`. +- The daemon reads those typed command documents and applies them in + `AetheriaRuntimeDaemonOperations`. + +This is much healthier than the earlier `Apply(command, payload)` shape. The +remaining compromise is that the command document is one broad record with many +optional fields, selected by `AetheriaRuntimeDaemonCommandKinds`. That is typed +at the document boundary, but not as strong as distinct command body types per +operation. The verifier now prevents public Unity-facing queue/string APIs from +returning, but the command shape is still a union-ish document rather than +fully discriminated typed bodies. + +Eve commands are a parallel typed edge for operator/runtime UI surfaces. The +daemon accepts observed Eve commands, applies known surface commands, and +publishes acceptance status. Daemon-published game/editor surfaces should keep +moving toward command templates that point at typed daemon command documents or +typed state refs, not generic payload maps. + +## Observation And Render Flow + +Unity observes daemon state through `AetheriaDaemonObserver`: + +1. Poll `AetheriaRuntimeStateReader.TryReadObservedDaemonState`. +2. Feed the result through `AetheriaRuntimeDaemonObservationCursor`. +3. If the SoA view changed, map daemon memory through + `AetheriaDaemonSoaMemoryMap`. +4. Lower the mapped columns to `AetheriaDaemonRenderNativeView`, which exposes + `NativeArray` columns for Burst/Jobs-friendly rendering. + +The daemon SoA hot slab is written by +`AetheriaRuntimeDaemonSoaFramePublisher.PublishCurrentZoneEntities`. It +currently publishes entity index, position, rotation, velocity, physics body +radius/mass/inverse mass, render scale, visibility, LOD, and render group. This +is the right direction for a thin Unity client: daemon state appears as a native +view instead of a serialized object graph. + +Current limitation: physics body radius, mass, inverse mass, render scale, LOD, +and render group are still generic defaults in the publisher. Entity identity +and position are real; body semantics are not yet rich enough to replace all +renderer/gameplay guesses. + +`ActionGameManager.ApplyLatestAuthoritativeDaemonFrame` is the main Unity frame +consumer. It reads the newest authoritative daemon frame, restores or updates +the Unity observed facade graph, calls `ZoneRenderer.ApplyDaemonFrame`, and +loads the daemon zone view when the zone changes. + +`ZoneRenderer` is currently a projection cache. It lowers daemon zone snapshots +into Unity GameObjects for bodies, belts, wormholes, entities, compass markers, +loot visuals, and gravity presentation. Per frame, it asks +`AetheriaRuntimeDaemonRenderQueries` for body poses, asteroid poses, visible +entity indices, compass markers, wormhole exits, gravity terrain height, and +gravity terrain bands. + +This means `ZoneRenderer` is no longer the canonical level hierarchy owner, but +it still exists as a persistent Unity hierarchy cache. The migration target is +smaller: a per-frame query/lowering layer that feeds native render buffers and +presentation objects only where GameObject views are still unavoidable. + +## Physics And Ymir + +Unity physics is already fenced as forbidden gameplay authority by +`Aetheria.State.Verify`. + +Current Ymir control flow: + +- Projectiles ask `AetheriaYmirPhysicsBridge.TryStepProjectile`. +- Target/zone hit queries call `TryOverlapTargetHulls`, `TryCastTargetHulls`, + `TryOverlapZoneHulls`, or `TryCastZoneHulls`. +- `TryBuildDaemonWorld` builds Ymir bodies from `AetheriaDaemonRenderNativeView` + rather than from Unity `HullCollider` geometry. +- Query results are resolved back to Unity presentation hulls through daemon + entity ids, so visual effects still have a GameObject to attach to. + +This is the right authority direction: Ymir receives bodies derived from daemon +SoA, and Unity hulls are presentation targets. Remaining debt: + +- `AetheriaYmirPhysicsBridge` still has a fallback from missing daemon + `EntityIndex` to row index. That should be removed; stable entity identity is + required for daemon/Ymir correspondence. +- Ymir query requests are still JSON-over-local-HTTP from Unity. That can be a + temporary transport, but the durable model should be typed CultNet/CultMesh + command/query boundaries. +- Clickable raycasts still construct query bodies from Unity click bounds. That + is presentation picking, not simulation authority, but it should stay clearly + labeled as renderer/UI picking. + +## Gravity Terrain + +There are now shared daemon render queries for gravity influence brushes and +terrain sampling. `ZoneRenderer` uses +`AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight` and +`QueryGravityTerrainBand` for rendering. + +The old `Assets/Scripts/Gravity.cs` still exists. It samples Unity +`GravityObject` materials and shader names directly. That path is a legacy +Unity-local gravity evaluator. It should not own gameplay gravity or canonical +terrain facts. The desirable shape is: + +- daemon zone state defines body/orbit/gravity influence data; +- shared runtime query code answers viewport/intersection questions such as + "which influence brushes intersect this XZ area?"; +- Unity receives only the brushes and terrain samples needed for the current + camera/minimap/render pass; +- Ymir receives physics-relevant fields from the same daemon state. + +`Aetheria.State.Verify` already checks for shared gravity influence query +symbols and pushes `ZoneRenderer` away from mirrored Unity `Zone` math. + +## Stats And Behaviors + +Stats are mid-migration. + +Designer-facing recipe state exists in +`AetheriaRuntimeStatRecipes` and `AetheriaRuntimeStatRecipeSurfaceBuilder`. +Runtime item stat inspection exists in +`AetheriaRuntimeDaemonItemStatQueries`. Recipes are explicit: base value plus +enabled condition modifiers for quality, durability, heat, charge, ammo, range, +integrity, pilot skill, and environment. The recipe evaluator caches enabled +modifiers and dependency masks on the Unity-side `StatRecipe`. + +The live behavior system still uses `PerformanceStat` heavily. Every behavior +constructs stat objects, registers them, and calls `Evaluate(...)` during +runtime. If a recipe exists, it uses the recipe evaluator. If not, it falls back +to the legacy exponent model: + +- quality exponent; +- durability exponent multiplier; +- heat exponent multiplier; +- curve sampling for behavior-specific effects. + +`StatModifier` still mutates target `PerformanceStat` modifier dictionaries at +runtime by behavior and entity. This is a partial modifier system, but it is +attached to the old `PerformanceStat` object graph rather than a compact +blueprint/instance stat set. + +Migration target: + +- blueprint/catalog rows define base stat ids and default values; +- designer recipes define explicit condition dependencies per stat; +- item/entity instances carry only condition values and sparse modifier state; +- each stat has a dependency mask so hot paths sample only required conditions; +- current stat queries are daemon-owned and can answer "what is this stat now?" + without Unity spelunking behavior objects; +- Unity behavior code consumes resolved current stat values or cached stat + handles, not raw `PerformanceStat` exponent objects. + +## UI Surfaces + +The current UI direction is Eve/CultUI: + +- daemon publishes game/editor GUI and TUI surfaces; +- compatibility state surfaces exist for operations, player settings, catalog, + loadout templates, zone details, trade item details, and stat recipes; +- Unity lowers local surfaces through UI Toolkit for many menu/inventory/trade + panels; +- Brokkr should be considered just another daemon publishing Unity editor Eve + surfaces, not a special Codex socket. + +The architectural note in `Aetheria.State/docs/verse-daemon-shape.md` is still +the right boundary: Odin discovers/indexes providers, Bifrost hosts MCP tools +for xeno agents, Eve/CultUI defines presentation, and the daemon owns side +effects. + +Remaining UI debt is not mostly visual. It is resolution semantics: if an Eve +surface contains a typed pointer to daemon state, the runtime should resolve it +automatically. Current surfaces still often duplicate display values into +component props. That works for rendering, but it is weaker than state-ref +driven UI where agents and clients can inspect or invoke the same typed daemon +state. + +## Remaining Unity Responsibility + +Unity is already less authoritative than it was, but not detached. + +Still in Unity: + +- observed `Galaxy`, `Zone`, `Entity`, `Ship`, `EquippedItem`, and behavior + facade graph projection; +- live behavior classes and much stat evaluation; +- `ActionGameManager` as a large input/UI/projection coordinator; +- `ZoneRenderer` persistent GameObject projection cache; +- prefab/hull visual instantiation and effect attachment; +- some presentation picking and clickable queries; +- old gravity material evaluator in `Gravity`; +- Unity-specific rendering settings and asset palette lowering. + +Moved or moving out: + +- durable world/catalog/player/run/zone/entity state; +- typed daemon command acceptance and operation application; +- provider advertisements, health, command boundaries, and surfaces; +- current-zone entity hot slab for native rendering and Ymir body construction; +- shared render queries for body poses, gravity terrain, wormholes, markers, + visible entities, and asteroid poses; +- Unity physics authority. + +## High-Value Next Cuts + +1. Remove the Ymir row-index fallback. `TryBuildDaemonWorld` should require + daemon `EntityIndex`; Ymir body ids must never derive from SoA row order. +2. Enrich daemon SoA physics columns. Publish body radius/mass/inverse mass + from typed entity/hull state instead of `1.0` placeholders. +3. Add daemon current-stat query documents/surfaces. Designers and clients + should query current resolved stats through daemon-owned state refs, not + inspect Unity behavior objects. +4. Convert hot behavior reads from `PerformanceStat` to stat handles with + dependency masks and cached condition sampling. Leave legacy exponent + evaluation only as migration import/readback. +5. Shrink `ZoneRenderer` toward stateless per-frame render lowering. Persistent + GameObjects can remain for prefabs, but zone/body/entity discovery should be + query-driven from daemon state. +6. Replace local HTTP Ymir calls with typed CultNet/CultMesh query boundaries + when the Ymir daemon interface is ready. +7. Make Eve state refs first-class in the UI runtime so surfaces can point to + daemon state and clients resolve it automatically. + +## Mental Model + +The current machine is no longer "Unity owns the game and exports some state." +It is now "the Aetheria daemon owns typed state and publications, while Unity is +a powerful but still-too-large observer/projection runtime." The main migration +task is to keep deleting places where Unity turns observed daemon facts back +into local authority. +