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

@ -236,34 +236,18 @@ public sealed class RuntimeEntityOwnershipTests
[Fact]
public void CurrentRuntimeAdapters_DoNotRetainEntityOrInventoryMirrors()
{
FieldInfo[] viewFields = typeof(CurrentGameRuntimeViewAdapter)
FieldInfo[] adapterFields = typeof(CurrentGameRuntimeAdapter)
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
Assert.Contains(
viewFields,
field => field.FieldType == typeof(IRuntimeEntityView));
Assert.Contains(
viewFields,
field => field.FieldType == typeof(IRuntimeInventoryView));
adapterFields,
field => field.FieldType == typeof(GameRuntime));
Assert.DoesNotContain(
viewFields,
field => field.FieldType == typeof(LiveEntityRuntime)
|| field.FieldType == typeof(ClientObjectTable));
Assert.DoesNotContain(
typeof(CurrentGameRuntimeViewAdapter).GetNestedTypes(
BindingFlags.NonPublic),
type => type.Name is "EntityView" or "InventoryView");
FieldInfo[] eventFields = typeof(CurrentGameRuntimeEventAdapter)
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
Assert.DoesNotContain(
eventFields,
adapterFields,
field => field.FieldType == typeof(LiveEntityRuntime)
|| field.FieldType == typeof(ClientObjectTable)
|| field.FieldType == typeof(RuntimeEventSequencer));
Assert.Contains(
eventFields,
field => field.FieldType
== typeof(RuntimeEntityObjectLifetime));
|| field.FieldType == typeof(IRuntimeEntityView)
|| field.FieldType == typeof(IRuntimeInventoryView)
|| field.FieldType == typeof(RuntimeEntityObjectLifetime));
string root = FindRepositoryRoot();
string liveSource = File.ReadAllText(Path.Combine(
@ -276,19 +260,34 @@ public sealed class RuntimeEntityOwnershipTests
Assert.DoesNotContain("_directory.TryApply", liveSource);
Assert.DoesNotContain("_directory.RemoveActive", liveSource);
string viewSource = File.ReadAllText(Path.Combine(
string appRuntimeRoot = Path.Combine(
root,
"src",
"AcDream.App",
"Runtime",
"CurrentGameRuntimeViewAdapter.cs"));
"Runtime");
Assert.False(File.Exists(Path.Combine(
appRuntimeRoot,
"CurrentGameRuntimeViewAdapter.cs")));
Assert.False(File.Exists(Path.Combine(
appRuntimeRoot,
"CurrentGameRuntimeEventAdapter.cs")));
string rootSource = File.ReadAllText(Path.Combine(
root,
"src",
"AcDream.Runtime",
"GameRuntime.cs"));
Assert.Contains(
"_entityView.MaterializedCount",
viewSource,
"public RuntimeEntityObjectLifetime EntityObjects",
rootSource,
StringComparison.Ordinal);
Assert.DoesNotContain(
"_entities.MaterializedCount",
viewSource,
Assert.Contains(
"public IRuntimeEntityView Entities => EntityObjects.EntityView;",
rootSource,
StringComparison.Ordinal);
Assert.Contains(
"public IRuntimeInventoryView Inventory => EntityObjects.InventoryView;",
rootSource,
StringComparison.Ordinal);
}