refactor(runtime): cut graphical host over to canonical root

Make every App composition phase borrow one GameRuntime, retire the duplicate view/event adapters, and dispose the root only after its graphical borrowers release. This preserves synchronous UI commands while giving shutdown one exact ownership ledger.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-26 19:06:09 +02:00
parent ecb9f79444
commit ce41efb9e5
29 changed files with 613 additions and 778 deletions

View file

@ -10,6 +10,7 @@ using AcDream.App.World;
using AcDream.Core.Combat;
using AcDream.Core.Items;
using AcDream.Core.Spells;
using AcDream.Runtime;
using AcDream.Runtime.Gameplay;
namespace AcDream.App.Tests.Composition;
@ -33,7 +34,7 @@ public sealed class InteractionRetainedUiCompositionTests
[Fact]
public void EnabledUiPublishesOneExactResultAfterFrozenConstructionOrder()
{
var fixture = new Fixture(retailUi: true);
using var fixture = new Fixture(retailUi: true);
InteractionRetainedUiResult result = fixture.Compose();
@ -56,7 +57,7 @@ public sealed class InteractionRetainedUiCompositionTests
[Fact]
public void DisabledUiAcquiresNoRetainedFrontendResource()
{
var fixture = new Fixture(retailUi: false);
using var fixture = new Fixture(retailUi: false);
InteractionRetainedUiResult result = fixture.Compose();
@ -81,11 +82,13 @@ public sealed class InteractionRetainedUiCompositionTests
int pointValue)
{
var point = (InteractionRetainedUiCompositionPoint)pointValue;
var fixture = new Fixture(retailUi: true, failurePoint: point);
using var fixture = new Fixture(retailUi: true, failurePoint: point);
Assert.Throws<InvalidOperationException>(fixture.Compose);
Assert.Equal(point, fixture.Points[^1]);
Assert.False(
fixture.Dependencies.Runtime.CaptureOwnership().IsDisposeRequested);
if (point == InteractionRetainedUiCompositionPoint.ResultPublished)
{
Assert.Empty(fixture.Factory.Releases);
@ -129,9 +132,11 @@ public sealed class InteractionRetainedUiCompositionTests
[Fact]
public void PublicationFailureRollsBackCompleteUnpublishedPrefix()
{
var fixture = new Fixture(retailUi: true, publicationFailure: true);
using var fixture = new Fixture(retailUi: true, publicationFailure: true);
Assert.Throws<InvalidOperationException>(fixture.Compose);
Assert.False(
fixture.Dependencies.Runtime.CaptureOwnership().IsDisposeRequested);
Assert.Equal(
[
@ -161,7 +166,7 @@ public sealed class InteractionRetainedUiCompositionTests
Assert.DoesNotContain("private uint? PickWorldGuidAtCursor(", source);
}
private sealed class Fixture
private sealed class Fixture : IDisposable
{
private readonly InteractionRetainedUiCompositionPoint? _failurePoint;
@ -175,9 +180,7 @@ public sealed class InteractionRetainedUiCompositionTests
Publication = new Publication(publicationFailure);
RuntimeOptions options = RuntimeOptions.Parse("dat", static _ => null)
with { RetailUi = retailUi };
var actions = new RuntimeActionState(
new InventoryTransactionState(new ClientObjectTable()),
new Spellbook(),
GameRuntime runtime = GameRuntimeTestFactory.Create(
new NoopCombatOperations(),
new NoopCombatTargetOperations(),
new NoopCombatModeOperations(),
@ -196,19 +199,15 @@ public sealed class InteractionRetainedUiCompositionTests
RetainedInputCapture: null!,
InputDispatcher: null,
Settings: null!,
Actions: actions,
Runtime: runtime,
CombatAttackOperations: new NoopCombatOperations(),
CombatTargetOperations: new RuntimeCombatTargetOperationsSlot(),
SpellCastOperations: new RuntimeSpellCastOperationsSlot(),
Inventory: null!,
MagicCatalog: null!,
Character: null!,
Communication: null!,
StackSplitQuantity: null!,
UiRegistry: null,
CombatModeCommands: null!,
PlayerIdentity: null!,
PlayerController: null!,
PlayerMode: null!,
SelectionCameraFactory: static _ => Stub<SelectionCameraSource>(),
FrameDiagnostics: new DeferredRenderFrameDiagnosticsSource(),
@ -235,6 +234,8 @@ public sealed class InteractionRetainedUiCompositionTests
if (_failurePoint == point)
throw new InvalidOperationException($"fault at {point}");
}).Compose();
public void Dispose() => Dependencies.Runtime.Dispose();
}
private sealed class FakeFactory : IInteractionRetainedUiCompositionFactory