arch(runtime): establish presentation-independent boundary
Create the dependency-only Runtime project before moving any gameplay owner, enforce its direct, transitive, source, and load-time closure, and pin coherent lifetime-group extraction plus retryable teardown ordering. No production behavior changes in J0. Validated by the four focused Runtime boundary tests, a zero-error Release solution build, and 8,406 passing Release tests with five pre-existing skips. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
9f37d17936
commit
b632672e5c
10 changed files with 457 additions and 2 deletions
|
|
@ -202,6 +202,11 @@ src/
|
|||
WorldSession.cs -> done (wire-compatible with ACE)
|
||||
NetClient.cs -> done
|
||||
Messages/ -> done (CreateObject, MoveToState, etc.)
|
||||
|
||||
AcDream.Runtime/ presentation-independent client kernel boundary
|
||||
RuntimeAssemblyMarker.cs -> J0 dependency boundary; owner moves start after I6/I7
|
||||
-> may reference Core, Core.Net, Content, and Plugin.Abstractions only
|
||||
-> must never reference App, UI, Silk.NET, OpenAL, Arch, or ImGui
|
||||
|
||||
AcDream.Plugin.Abstractions/ Layer 5: plugin interfaces
|
||||
IAcDreamPlugin.cs -> done
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ Today:
|
|||
|
||||
- `tests/AcDream.Core.Tests/` ← `src/AcDream.Core/`
|
||||
- `tests/AcDream.Core.Net.Tests/` ← `src/AcDream.Core.Net/`
|
||||
- `tests/AcDream.Runtime.Tests/` ← `src/AcDream.Runtime/`
|
||||
- `tests/AcDream.UI.Abstractions.Tests/` ← `src/AcDream.UI.Abstractions/`
|
||||
- `tests/AcDream.App.Tests/` ← `src/AcDream.App/`
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
**Date:** 2026-07-24
|
||||
|
||||
**Status:** Slices A–H and I0–I3 are complete. Slices F–L were explicitly
|
||||
approved 2026-07-24; Slice I4 is active.
|
||||
**Status:** Slices A–H are complete. Slice I6's flat-authoritative production
|
||||
cutover is committed and awaits the final user collision/visual gate before
|
||||
parsed-graph deletion. Slices F–L were explicitly approved 2026-07-24. Slice
|
||||
J0's dependency-boundary preparation is complete without moving gameplay
|
||||
owners. J1 remains behind the I6/I7 user collision/visual closeout.
|
||||
|
||||
**Scope:** Reconcile and sequence the existing Modern Pipeline (`MP`) and
|
||||
Linux/headless (`LH`) tracks using the 2026-07-24 connected performance audit.
|
||||
|
|
@ -499,6 +502,41 @@ It exposes:
|
|||
- Deterministic `Tick`.
|
||||
- Retryable, complete shutdown.
|
||||
|
||||
#### 9.2.1 Structural teardown protocol
|
||||
|
||||
Every graphical or headless host follows one retryable, generation-scoped
|
||||
teardown transaction. New runtime owners register through the same
|
||||
`CompositionAcquisitionScope` lease discipline already used by the graphical
|
||||
composition root; release order is encoded by leases and acknowledgements,
|
||||
never remembered by callers.
|
||||
|
||||
1. **Cancel scheduler generations.** Make commands inert, cancel the active
|
||||
session/runtime generation, stop new receive publication, and prevent any
|
||||
timer, bot, plugin, or worker callback from creating more work for it.
|
||||
2. **Drain or poison journals and queues.** Detach inbound producers, drain
|
||||
already-accepted ordered events through the generation boundary, then
|
||||
poison every command/event/delta queue so delayed callbacks fail against
|
||||
their exact generation instead of entering a replacement session.
|
||||
3. **Unpublish presentation.** The graphical host consumes the final ordered
|
||||
withdrawals and acknowledges that the render scene, retained gameplay UI,
|
||||
audio/effect projections, selection, radar, and private viewports no longer
|
||||
expose the runtime generation. Headless hosts acknowledge this stage
|
||||
immediately because they construct no presentation owners.
|
||||
4. **Release residency after fences.** Withdraw content owners, finish
|
||||
streaming/landblock retirement, and allow GPU-backed stores to retire only
|
||||
after their existing frame-flight fences signal. A failed fence or release
|
||||
retains the exact unfinished lease and resumes without replaying completed
|
||||
stages.
|
||||
5. **Release the shared `ContentStore` last.** Session-scoped collision/content
|
||||
leases go away before the runtime instance. The immutable mapped content
|
||||
store outlives every runtime and presentation host and is disposed only by
|
||||
the process host after all registered sessions have acknowledged teardown.
|
||||
|
||||
Logout, session reset, mid-portal disconnect, reconnect replacement, ordinary
|
||||
window close, and construction rollback all use this protocol. No stage may
|
||||
clear another generation's state, and no teardown failure may be converted
|
||||
into a best-effort purge.
|
||||
|
||||
### 9.3 Hosts
|
||||
|
||||
`GraphicalGameHost` provides input, camera, rendering, UI, audio, frame pacing,
|
||||
|
|
|
|||
230
docs/plans/2026-07-25-modern-runtime-slice-j.md
Normal file
230
docs/plans/2026-07-25-modern-runtime-slice-j.md
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
# Modern runtime Slice J — presentation-independent runtime
|
||||
|
||||
**Status:** J0 COMPLETE — lifetime plan, project boundary, and dependency
|
||||
guards; J1 waits for the Slice I6/I7 user collision/visual closeout
|
||||
**Parent:** `2026-07-24-modern-runtime-architecture.md`, Slice J
|
||||
**Authorization:** the user approved Slices F–L, including the otherwise-frozen
|
||||
Slice J gameplay-owner moves, on 2026-07-24
|
||||
**Purpose:** move the existing authoritative client kernel into a
|
||||
presentation-independent assembly without creating a second world, changing
|
||||
retail behavior, or making the graphical host a special case.
|
||||
|
||||
Slice J is a lifetime-group campaign, not one large move. Each sub-slice keeps
|
||||
the graphical client on the same owner instance, establishes host parity, and
|
||||
deletes the replaced App production path before the next group moves.
|
||||
|
||||
## 1. Fixed invariants
|
||||
|
||||
1. `LiveEntityRuntime` remains the sole server-GUID/incarnation owner. There is
|
||||
never a second GUID map or synchronized gameplay world.
|
||||
2. Runtime types do not reference `AcDream.App`, either UI assembly, Silk.NET,
|
||||
OpenAL, Arch, ImGui, a native window, or an OpenGL resource.
|
||||
3. App projection state is keyed by runtime-issued local entity identity plus
|
||||
incarnation. It may not recover or mirror server GUID ownership.
|
||||
4. The accepted update order, packet ordering, timestamp gates, outbound
|
||||
cadence, and retail-shaped gameplay algorithms do not change during the
|
||||
extraction.
|
||||
5. Contracts expose borrowed/immutable views, typed commands, and ordered
|
||||
deltas. They do not expose App render components or deep per-frame object
|
||||
graphs.
|
||||
6. All clocks, queues, random sources, diagnostics identity, plugin behavior,
|
||||
and mutable caches that can differ between sessions are instance-scoped.
|
||||
7. Immutable prepared content may be shared. Session state and mutable query
|
||||
scratch may not.
|
||||
8. Every new owner participates in the structural teardown protocol in parent
|
||||
plan §9.2.1 through acquisition leases and exact acknowledgements.
|
||||
9. A headless construction path is proved after every coherent move. “No
|
||||
window” means no App, Silk, OpenAL, retained UI, particle, or render assembly
|
||||
is loaded—not a hidden graphical process.
|
||||
10. The user collision/visual gate for Slice I6 still controls parsed-graph
|
||||
deletion. J0 may land independently; gameplay-owner moves begin only after
|
||||
the active I6/I7 closeout has reconciled the final collision owner shape.
|
||||
|
||||
## 2. Target dependency direction
|
||||
|
||||
```text
|
||||
AcDream.App ───────────────► AcDream.Runtime
|
||||
AcDream.Headless (Slice K) ─► AcDream.Runtime
|
||||
├──► AcDream.Core.Net
|
||||
├──► AcDream.Core
|
||||
├──► AcDream.Content
|
||||
└──► AcDream.Plugin.Abstractions
|
||||
|
||||
AcDream.Runtime -X► AcDream.App / AcDream.UI.* / Silk.NET / OpenAL / Arch
|
||||
```
|
||||
|
||||
Runtime-emitted entity events contain incarnation, transform, flags, and
|
||||
property deltas only. An App-side projection adapter translates them into
|
||||
`IRenderScene` journal operations. Render vocabulary never enters Runtime.
|
||||
|
||||
## 3. Teardown transaction
|
||||
|
||||
The exact order is:
|
||||
|
||||
1. cancel scheduler/session generations and make commands inert;
|
||||
2. detach producers, drain accepted ordered events, and poison queues;
|
||||
3. withdraw and acknowledge graphical presentation;
|
||||
4. retire session residence and GPU-backed owners through existing fences;
|
||||
5. dispose the process-owned immutable `ContentStore` only after every runtime
|
||||
and host has completed.
|
||||
|
||||
Completed stages never replay. Failure retains the exact remaining suffix.
|
||||
Logout, reset, mid-portal disconnect, reconnect replacement, construction
|
||||
rollback, and native close all traverse the same transaction.
|
||||
|
||||
## 4. Execution slices
|
||||
|
||||
### J0 — boundary, plan, and dependency enforcement
|
||||
|
||||
**Completed 2026-07-25.**
|
||||
|
||||
- Add `AcDream.Runtime` and `AcDream.Runtime.Tests`.
|
||||
- Permit references only to Core, Core.Net, Content, and Plugin.Abstractions.
|
||||
- Add App → Runtime as the future host dependency; Runtime never references
|
||||
App.
|
||||
- Add direct-reference, dependency-closure, source-project, and assembly-load
|
||||
guards that fail if a presentation/backend dependency enters Runtime.
|
||||
- Add only an assembly boundary marker; do not introduce a facade
|
||||
`GameRuntime`, mirror state, or move behavior in J0.
|
||||
- Pin this lifetime-group plan and parent-plan teardown protocol.
|
||||
|
||||
Gate: Release build and full tests pass; the Runtime test process loads no App,
|
||||
UI, Silk, OpenAL, Arch, or ImGui assembly; graphical behavior is byte-for-byte
|
||||
unchanged because no runtime path changed.
|
||||
|
||||
Evidence: four focused dependency tests pass, the Release solution builds with
|
||||
zero errors, and the complete Release suite passes 8,406 tests with five
|
||||
pre-existing skips. The exact rollback commit is recorded below after landing.
|
||||
|
||||
### J1 — read, command, event, clock, and lifecycle contracts
|
||||
|
||||
- Define `IGameRuntimeView`, typed command interfaces, ordered event/delta
|
||||
records, instance clock, lifecycle states, generation tokens, and teardown
|
||||
acknowledgements.
|
||||
- Build App adapters over the current owners first. Adapters borrow; they do
|
||||
not copy mutable collections or own lifetime.
|
||||
- Add a normalized parity trace of accepted inbound events, runtime commands,
|
||||
state revisions, and lifecycle edges.
|
||||
- Prove press-time graphical input reaches the same current owners through the
|
||||
command contract with no extra frame of latency.
|
||||
|
||||
Gate: adapter and direct-host traces are identical over deterministic session,
|
||||
entity, inventory, chat, movement, and portal fixtures; no canonical owner has
|
||||
moved yet.
|
||||
|
||||
### J2 — session lifetime and ordered transport group
|
||||
|
||||
- Remove App presentation dependencies from `LiveSessionController`,
|
||||
`LiveSessionHost`, lifecycle host, event router, and command router.
|
||||
- Move the same owner instances into Runtime with Core.Net transport and
|
||||
instance-scoped clocks/queues.
|
||||
- Preserve bind-before-connect, publish-after-EnterWorld, exact generation
|
||||
rechecks, receive ordering, ack placement, graceful F653/disconnect, and
|
||||
retryable replacement.
|
||||
- Let App provide only endpoint credentials, presentation bindings, and
|
||||
graphical lifecycle acknowledgements.
|
||||
|
||||
Gate: connected login/logout/reconnect and malformed/reentrant lifecycle suites
|
||||
produce the same packet/order trace; no-window construction can connect and
|
||||
disconnect without loading App or a backend.
|
||||
|
||||
### J3 — canonical identity, properties, and object-table group
|
||||
|
||||
- Strip renderer, particle, Wb, and streaming components from the canonical
|
||||
record into an App projection store keyed only by local identity/incarnation.
|
||||
- Move `LiveEntityRuntime`, accepted spawn/state/property timestamps,
|
||||
`ClientObjectTable`, container/inventory indices, vitals, enchantments,
|
||||
spell state, target/combat state, and their exact teardown into Runtime.
|
||||
- Emit ordered create/update/rebucket/hidden/withdraw/delete deltas.
|
||||
- Keep DAT-backed visual hydration, render registrations, effects, lights,
|
||||
paperdoll, selection markers, and radar projections in App.
|
||||
|
||||
Gate: GUID reuse, equal-generation updates, parent/child identity,
|
||||
inventory/container mutations, Hidden, delete/recreate, and portal teardown
|
||||
match current traces; App has no server-GUID ownership.
|
||||
|
||||
### J4 — chat, inventory, magic, and gameplay-state services
|
||||
|
||||
- Move canonical chat history/channel state, inventory transactions, spell and
|
||||
component state, enchantments, vitals, skills/attributes, and gameplay
|
||||
cooldown state by coherent owner groups.
|
||||
- Keep retained panel layout, text shaping, icons, paperdoll rendering, and
|
||||
status-bar presentation in App.
|
||||
- Route plugins and future bots through the same typed command/event surface
|
||||
as graphical UI.
|
||||
|
||||
Gate: deterministic command/response traces and graphical ViewModel revisions
|
||||
match; no Runtime type references UI abstractions.
|
||||
|
||||
### J5 — movement, physics, interaction, and combat group
|
||||
|
||||
- Move presentation-free movement interpretation, authoritative/predicted
|
||||
physics state, selection identity, approach/use transactions, combat intent,
|
||||
projectiles, and outbound movement cadence.
|
||||
- Replace input and camera dependencies with typed commands and runtime views.
|
||||
- Keep camera, mouse look, selection markers/highlight, combat/spell bars,
|
||||
animated pose composition, sounds, and particles in App.
|
||||
- Share immutable flat collision content while retaining per-session transition
|
||||
scratch.
|
||||
|
||||
Gate: bit-identical collision/trajectory fixtures, target-facing and
|
||||
auto-approach traces, outbound packet timing, combat/magic commands, and
|
||||
graphical feel gates remain unchanged.
|
||||
|
||||
### J6 — world, portal, environment, and projection handshake
|
||||
|
||||
- Move authoritative teleport/session cell identity, world clock, weather
|
||||
state, and reveal-generation state required by gameplay.
|
||||
- Keep streaming publication, terrain/EnvCell rendering, portal tunnel,
|
||||
materialization VFX, sky drawing, audio, and UI in App.
|
||||
- Define the destination-ready/withdrawal acknowledgements between Runtime and
|
||||
the graphical or headless host without making visual readiness gameplay
|
||||
authority.
|
||||
|
||||
Gate: fresh login, repeated recall/portal, world edge, dungeon, reconnect, and
|
||||
mid-portal disconnect traces match; the graphical screenshots remain accepted.
|
||||
|
||||
### J7 — one `GameRuntime` composition root
|
||||
|
||||
- Compose all moved lifetime groups into one instance-scoped `GameRuntime`.
|
||||
- Make `GameWindow`/App own exactly one Runtime instance plus presentation
|
||||
adapters.
|
||||
- Delete temporary direct-owner adapters and every superseded App production
|
||||
path.
|
||||
- Register all Runtime and App projection owners with structural acquisition
|
||||
leases and the exact teardown transaction.
|
||||
|
||||
Gate: graphical connected lifecycle/resource routes pass with no mirrored
|
||||
state, no extra update latency, and no performance regression.
|
||||
|
||||
### J8 — no-window integration and Slice J closeout
|
||||
|
||||
- Build a transport/content test host that constructs Runtime directly.
|
||||
- Connect, enter world, receive chat/inventory/world updates, move, use,
|
||||
fight/cast through deterministic fixtures, portal, log out, reconnect, and
|
||||
tear down without loading App/Silk/OpenAL/UI.
|
||||
- Add cancellation, failure injection, same-process multiple-instance
|
||||
isolation, credential-safe diagnostics, and zero-presentation-allocation
|
||||
gates.
|
||||
- Update architecture, milestone, roadmap, issue/divergence ledgers, and
|
||||
durable memory.
|
||||
|
||||
Final gate: Release build/full tests, connected graphical route, no-window
|
||||
integration, resource teardown, packet/order/timing parity, and the user visual
|
||||
matrix all pass. Slice K may then build the Linux multi-session host without
|
||||
another gameplay extraction.
|
||||
|
||||
## 5. Commit order
|
||||
|
||||
1. `arch(runtime): establish presentation-independent boundary`
|
||||
2. `feat(runtime): define borrowed views commands and ordered events`
|
||||
3. `refactor(runtime): move session lifetime and ordered transport`
|
||||
4. `refactor(runtime): move canonical entity and property ownership`
|
||||
5. `refactor(runtime): move gameplay state services`
|
||||
6. `refactor(runtime): move movement physics interaction and combat`
|
||||
7. `refactor(runtime): separate world authority from presentation`
|
||||
8. `refactor(runtime): compose one graphical game runtime`
|
||||
9. `test(runtime): close no-window parity and teardown`
|
||||
|
||||
Each commit is independently buildable and retains an exact rollback command
|
||||
in this plan before its connected gate.
|
||||
Loading…
Add table
Add a link
Reference in a new issue