refactor(app): complete session startup composition

Move the live-session reset and routing graph, combat and diagnostic command targets, and the sole gameplay input subscriber into Phase 7 before frame publication. Add exact retryable ownership for late bindings so partial startup cannot strand session or component teardown edges.

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-22 18:49:31 +02:00
parent 7fa60971e2
commit 826f9ea9b5
22 changed files with 924 additions and 412 deletions

View file

@ -47,7 +47,7 @@ public sealed class GameWindowSlice8BoundaryTests
{
string body = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
"private void OnUpdate(double dt)");
AssertAppearsInOrder(
body,
@ -64,16 +64,29 @@ public sealed class GameWindowSlice8BoundaryTests
"LivePresentationResult livePresentation =",
"new LivePresentationCompositionPhase(",
"this).Compose(",
"new SessionPlayerCompositionPhase(",
"var renderFrameOrchestrator =",
"var updateFrameOrchestrator = new AcDream.App.Update.UpdateFrameOrchestrator(",
"_frameGraphs.Publish(updateFrameOrchestrator, renderFrameOrchestrator);",
"_liveSessionHost = CreateLiveSessionHost();",
"_liveCombatModeCommands.Bind(combatCommand);",
"_runtimeDiagnosticCommands.Bind(runtimeDiagnostics);",
"_gameplayInputActions = AcDream.App.Input.GameplayInputActionRouter.Create(",
"_gameplayInputActions.Attach();",
"_liveSessionHost.Start(_options)");
Assert.Equal(1, CountOccurrences(body, "_liveSessionHost.Start(_options)"));
"sessionPlayer.SessionHost.Start(_options)");
string sessionPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
AssertAppearsInOrder(
sessionPhase,
"sessionRuntimeFactory.Create(liveSession)",
"d.CombatModeCommands.BindOwned(combatCommand)",
"d.RuntimeDiagnosticCommands.BindOwned(runtimeDiagnostics)",
"GameplayInputActionRouter.Create(",
"gameplayActions.Attach();",
"_publication.PublishSessionPlayer(result);");
Assert.Equal(1, CountOccurrences(
body,
"sessionPlayer.SessionHost.Start(_options)"));
string phaseOne = Slice(
File.ReadAllText(Path.Combine(
FindRepoRoot(),
@ -93,7 +106,9 @@ public sealed class GameWindowSlice8BoundaryTests
"if (firstKeyboard is not null && firstMouse is not null)",
phaseOne,
StringComparison.Ordinal);
int start = body.IndexOf("_liveSessionHost.Start(_options)", StringComparison.Ordinal);
int start = body.IndexOf(
"sessionPlayer.SessionHost.Start(_options)",
StringComparison.Ordinal);
string postStart = body[start..];
Assert.Contains("switch (liveStart.Status)", postStart, StringComparison.Ordinal);
Assert.Empty(System.Text.RegularExpressions.Regex.Matches(
@ -113,10 +128,17 @@ public sealed class GameWindowSlice8BoundaryTests
string source = GameWindowSource();
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
string sessionFactory = MethodBody(
"private AcDream.App.Net.LiveSessionEventRouter CreateLiveSessionEventRouter(",
"private AcDream.App.Net.LiveInventorySessionBindings");
"private void OnUpdate(double dt)");
string sessionFactorySource = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Net",
"LiveSessionRuntimeFactory.cs"));
string sessionFactory = Slice(
sessionFactorySource,
"private LiveSessionEventRouter CreateEventRouter(",
"private LiveInventorySessionBindings CreateInventoryBindings()");
string worldPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
@ -143,9 +165,9 @@ public sealed class GameWindowSlice8BoundaryTests
Assert.Contains("environment.Initialize(region);", worldPhase, StringComparison.Ordinal);
AssertAppearsInOrder(
sessionFactory,
"new AcDream.App.Net.LiveEnvironmentSessionSink(",
"_worldEnvironment.ApplyAdminEnvirons,",
"_worldEnvironment.SynchronizeFromServer)");
"new LiveEnvironmentSessionSink(",
"_world.Environment.ApplyAdminEnvirons,",
"_world.Environment.SynchronizeFromServer)");
Assert.DoesNotContain("_loadedSkyDesc", source, StringComparison.Ordinal);
Assert.DoesNotContain("_loadedSkyDayIndex", source, StringComparison.Ordinal);
Assert.DoesNotContain("private void RefreshSkyForCurrentDay()", source, StringComparison.Ordinal);
@ -164,10 +186,16 @@ public sealed class GameWindowSlice8BoundaryTests
"debugVm.CycleTimeOfDay = _dependencies.DiagnosticCommands.CycleTimeOfDay;",
"debugVm.CycleWeather = _dependencies.DiagnosticCommands.CycleWeather;",
"debugVm.ToggleCollisionWires =");
string sessionPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
AssertAppearsInOrder(
load,
"new AcDream.App.Diagnostics.RuntimeDiagnosticCommandController(",
"_runtimeDiagnosticCommands.Bind(runtimeDiagnostics);");
sessionPhase,
"new RuntimeDiagnosticCommandController(",
"d.RuntimeDiagnosticCommands.BindOwned(runtimeDiagnostics)");
}
[Fact]
@ -176,7 +204,13 @@ public sealed class GameWindowSlice8BoundaryTests
string source = GameWindowSource();
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
"private void OnUpdate(double dt)");
string sessionPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
string shutdown = MethodBody(
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
@ -187,9 +221,11 @@ public sealed class GameWindowSlice8BoundaryTests
Assert.DoesNotContain("private void OnUiDragReleasedOutside(", source, StringComparison.Ordinal);
Assert.DoesNotContain("DragReleasedOutsideUi +=", source, StringComparison.Ordinal);
Assert.Equal(1, CountOccurrences(
load,
"AcDream.App.Input.GameplayInputActionRouter.Create("));
Assert.Equal(1, CountOccurrences(load, "_gameplayInputActions.Attach();"));
sessionPhase,
"GameplayInputActionRouter.Create("));
Assert.Equal(1, CountOccurrences(
sessionPhase,
"gameplayActions.Attach();"));
Assert.Equal(1, CountOccurrences(
LivePresentationSource(),
"RetainedUiGameplayBinding.Create("));
@ -214,7 +250,7 @@ public sealed class GameWindowSlice8BoundaryTests
{
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
"private void OnUpdate(double dt)");
string body = MethodBody(
"private void OnFramebufferResize(",
"private void OnClosing()");
@ -251,7 +287,7 @@ public sealed class GameWindowSlice8BoundaryTests
string run = MethodBody("public void Run()", "private void OnLoad()");
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
"private void OnUpdate(double dt)");
string shutdown = MethodBody(
"private ResourceShutdownTransaction CreateShutdownTransaction()",
"private void OnFocusChanged(bool focused)");
@ -295,7 +331,7 @@ public sealed class GameWindowSlice8BoundaryTests
"this).Compose(platform, hostInputCamera, contentEffectsAudio);",
"new WorldRenderCompositionPhase(",
"new SessionPlayerCompositionPhase(",
"_liveSessionHost.Start(_options)");
"sessionPlayer.SessionHost.Start(_options)");
string sessionPhase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
@ -464,7 +500,7 @@ public sealed class GameWindowSlice8BoundaryTests
string source = GameWindowSource();
string load = MethodBody(
"private void OnLoad()",
"private AcDream.App.Net.LiveSessionHost");
"private void OnUpdate(double dt)");
string shutdown = Slice(
source,
"private ResourceShutdownTransaction CreateShutdownTransaction()",