Aetheria/docs/unity-daemon-development.md
2026-08-08 08:45:14 +01:00

98 lines
5.3 KiB
Markdown

# Unity daemon development
Open `Aetheria.Unity` in Unity and choose **Aetheria > Daemon Development**.
This is the interactive development path. The released-package witness remains
the automated integration proof and is not required for ordinary iteration.
**Build daemon** explicitly builds `Aetheria.State.Daemon` in
`bin/Debug/net10.0`. **Reimport state & build** also replaces the isolated
development state under `Aetheria.Unity/Build`. **Start daemon** launches the
already-prepared Debug apphost immediately; it never performs a build. The editor owns
the daemon process, its PID, and its output streams. The generic EveUnity
client connects directly to the daemon's local CultMesh endpoint. Odin is not
part of this path.
The named daemon apphost is launched with both system .NET runtime-root
variables pinned explicitly rather than inheriting Unity's embedded runtime
environment. This keeps .NET 10 host resolution deterministic while preserving
the `Aetheria.State.Daemon` process identity used for ownership, Rider attach,
and domain-reload reattachment.
The daemon lifecycle is independent of Unity Play Mode. Play never builds,
starts, restarts, or stops the daemon. Entering Play connects a generic client;
leaving Play drops that client while the authoritative daemon remains alive and
ready for the next connection.
Starting the daemon does not create, load, or step a game world. It starts the
typed state boundary and CultMesh transport in `ready` mode and publishes the
`aetheria.hangar` Eve surface. The Hangar exposes the canonical saved loadout
and typed equip/remove operations. **Launch** commits a Terminus deployment
from the current Hangar revision and creates its run; **Continue** selects the
existing deployed Terminus checkpoint without creating a replacement run.
Typed mode selection chooses authority and policy; a committed
deployment/admission creates session state through the applicable provider or
Arena server. Only that activation boundary opens
simulation persistence and starts the fixed simulation clock. When the last
playable-world subscription is withdrawn,
the daemon commits the final public frame and matching private Ymir checkpoint,
releases the active physics world, and returns to `ready`. A dropped client
therefore leaves the daemon and saved world alive without making Unity Play
Mode a lifecycle authority.
The editor connects through the daemon's `cultnet+tcp` control endpoint. The
advertised session then selects the dedicated content and QUIC realtime planes;
the editor bootstrap does not choose those planes or retain an RUDP fallback.
The launcher displays and passes the exact source dependency roots used for the
Debug daemon build. CultLib prefers the active
`CultLib-codex-cultmesh-reliability` checkout, then `CultLib-release`, and
finally the canonical `CultLib` sibling.
Ymir still prefers the active `Ymir-aetheria-integration` sibling checkout and
falls back to the canonical `Ymir` sibling when that integration checkout is
absent. This prevents an older sibling assembly from impersonating the daemon
being debugged.
## Normal loop
1. Press **Build daemon** after changing daemon code. Use **Reimport state &
build** only when the isolated development state must be replaced.
2. Press **Start daemon** and wait for the window to report the live endpoint.
3. Enter Unity Play Mode. The generic client mounts `aetheria.hangar`.
4. Configure the saved loadout, then choose **Launch** for a new Terminus run
or **Continue** for the currently deployed saved run. The same mounted Eve
surface becomes the gameplay composition after activation.
5. Use Unity Pause to submit the advertised `simulation.pause` action to the
daemon. Unity unpause submits `simulation.rate.realtime`.
6. While paused, use **Advance one step** to commit exactly one fixed daemon
simulation step.
7. Stop or restart the daemon explicitly from the same
window.
Unity's editor clock is never gameplay authority. The pause and step controls
are ordinary advertised Eve operations accepted by the daemon. If the active
game mode does not advertise its simulation clock, the window reports that
contract failure instead of locally freezing world truth.
## Rider
The window displays the actual `Aetheria.State.Daemon` process ID. Press
**Copy daemon PID**, then in Rider choose **Run > Attach to Process** and select
that PID. **Open daemon source** opens `Aetheria.State.Daemon/Program.cs` through
Unity's configured external editor. The launched process uses Debug assemblies
from the current checkout, so breakpoints bind to the code being run.
Hitting a Rider breakpoint suspends the authoritative daemon process. The Unity
client continues to display its last committed generation until execution
resumes; it does not simulate through the breakpoint.
## Files
- State: `Aetheria.Unity/Build/aetheria-unity-dev.cc`
- Daemon stdout: `Aetheria.Unity/Build/DaemonDevelopment/daemon.log`
- Daemon stderr: `Aetheria.Unity/Build/DaemonDevelopment/daemon.error.log`
- Preparation logs: `Aetheria.Unity/Build/DaemonDevelopment/launcher*.log`
`Reimport state & build` deletes only the isolated development `.cc` file, its
`.cultmesh` (or legacy `.records`) sidecar, and the matching daemon-private
`.ymir.cc` journal store before importing it again. It does not touch
`GameData`, the source catalog, or another witness/run state.