Aetheria/docs/aetheria-current-codebase-model.md
2026-06-22 01:11:56 +01:00

14 KiB

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<T> 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.