refactor(runtime): publish canonical entity object deltas
Issue stable Runtime identities at canonical registration, publish entity and inventory commits through one generation-stamped synchronous stream, and make graphical adapters borrow the same direct views and events as a no-window host. Preserve exact projection teardown and retail mutation order while removing App-side event reconstruction. Make the hard-recenter ordering fixture independent of the production two-millisecond frame budget so its injected-failure gate is deterministic. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
d3e96ff912
commit
ce3ac310d9
23 changed files with 2352 additions and 666 deletions
|
|
@ -80,7 +80,7 @@ public sealed class LiveEntityHydrationControllerTests
|
|||
|
||||
RuntimeEntityRecord canonical = fixture.Canonical;
|
||||
Assert.False(fixture.Runtime.TryGetRecord(Guid, out _));
|
||||
Assert.Null(canonical.LocalEntityId);
|
||||
Assert.NotNull(canonical.LocalEntityId);
|
||||
Assert.Equal(0, fixture.Resources.RegisterCount);
|
||||
Assert.Empty(fixture.Materializer.Calls);
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ public sealed class LiveEntityHydrationControllerTests
|
|||
fixture.Controller.OnCreate(spawn);
|
||||
RuntimeEntityRecord canonical = fixture.Canonical;
|
||||
Assert.False(fixture.Runtime.TryGetRecord(Guid, out _));
|
||||
Assert.Null(canonical.LocalEntityId);
|
||||
Assert.NotNull(canonical.LocalEntityId);
|
||||
|
||||
Assert.True(fixture.Controller.OnAppearance(ObjDesc(2, 0x04000022u)));
|
||||
|
||||
|
|
@ -522,7 +522,7 @@ public sealed class LiveEntityHydrationControllerTests
|
|||
|
||||
RuntimeEntityRecord canonical = fixture.Canonical;
|
||||
Assert.False(fixture.Runtime.TryGetRecord(Guid, out _));
|
||||
Assert.Null(canonical.LocalEntityId);
|
||||
Assert.NotNull(canonical.LocalEntityId);
|
||||
Assert.Equal(1, resources.RegisterCount);
|
||||
Assert.Equal(1, resources.UnregisterCount);
|
||||
|
||||
|
|
@ -852,7 +852,7 @@ public sealed class LiveEntityHydrationControllerTests
|
|||
RuntimeEntityRecord replacement = fixture.Canonical;
|
||||
Assert.Equal((ushort)2, replacement.Generation);
|
||||
Assert.Equal("replacement", replacement.Snapshot.Name);
|
||||
Assert.Null(replacement.LocalEntityId);
|
||||
Assert.NotNull(replacement.LocalEntityId);
|
||||
Assert.False(fixture.Runtime.TryGetRecord(Guid, out _));
|
||||
Assert.Equal(0, fixture.Runtime.PendingTeardownCount);
|
||||
}
|
||||
|
|
@ -1368,7 +1368,7 @@ public sealed class LiveEntityHydrationControllerTests
|
|||
fixture.Controller.OnCreate(Spawn(Generation: 1, PositionSequence: 1));
|
||||
|
||||
RuntimeEntityRecord canonical = fixture.Canonical;
|
||||
Assert.Null(canonical.LocalEntityId);
|
||||
Assert.NotNull(canonical.LocalEntityId);
|
||||
Assert.False(fixture.Runtime.TryGetRecord(Guid, out _));
|
||||
Assert.Equal(0, fixture.Resources.RegisterCount);
|
||||
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ public sealed class LiveEntityPhysicsHostOwnershipTests
|
|||
|
||||
Assert.True(runtime.TryGetCanonical(guid, out var replacement));
|
||||
Assert.Equal((ushort)2, replacement.Incarnation);
|
||||
Assert.Null(replacement.LocalEntityId);
|
||||
Assert.NotNull(replacement.LocalEntityId);
|
||||
Assert.Null(replacement.PhysicsBody);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.Null(retired.RemoteMotionRuntime);
|
||||
|
|
|
|||
|
|
@ -1656,7 +1656,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
|
||||
Assert.False(runtime.TryGetRecord(spawn.Guid, out _));
|
||||
RuntimeEntityRecord canonical = CurrentCanonical(runtime, spawn.Guid);
|
||||
Assert.Null(canonical.LocalEntityId);
|
||||
Assert.NotNull(canonical.LocalEntityId);
|
||||
Assert.Equal(0, runtime.MaterializedCount);
|
||||
Assert.Empty(runtime.VisibleRecords);
|
||||
Assert.Empty(spatial.Entities);
|
||||
|
|
@ -1901,7 +1901,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
Assert.NotNull(replacement.PriorGenerationCleanupFailure);
|
||||
RuntimeEntityRecord canonical = CurrentCanonical(runtime, guid);
|
||||
Assert.Equal((ushort)2, canonical.Generation);
|
||||
Assert.Null(canonical.LocalEntityId);
|
||||
Assert.NotNull(canonical.LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
WorldEntity installed = runtime.MaterializeLiveEntity(
|
||||
guid,
|
||||
|
|
@ -2459,7 +2459,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
&& exception.Message.Contains("active logical-lifetime transition", StringComparison.Ordinal));
|
||||
RuntimeEntityRecord current = CurrentCanonical(runtime, guid);
|
||||
Assert.Equal((ushort)2, current.Generation);
|
||||
Assert.Null(current.LocalEntityId);
|
||||
Assert.NotNull(current.LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.Empty(spatial.Entities);
|
||||
Assert.DoesNotContain(
|
||||
|
|
@ -2493,7 +2493,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
Assert.False(outer.LogicalRegistrationCreated);
|
||||
RuntimeEntityRecord current = CurrentCanonical(runtime, guid);
|
||||
Assert.Equal((ushort)3, current.Generation);
|
||||
Assert.Null(current.LocalEntityId);
|
||||
Assert.NotNull(current.LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.Empty(spatial.Entities);
|
||||
Assert.Equal(1, resources.RegisterCount);
|
||||
|
|
@ -2524,8 +2524,8 @@ public sealed class LiveEntityRuntimeTests
|
|||
Assert.True(outer.LogicalRegistrationCreated);
|
||||
RuntimeEntityRecord replacement = CurrentCanonical(runtime, guid);
|
||||
Assert.Equal((ushort)2, replacement.Generation);
|
||||
Assert.Null(replacement.LocalEntityId);
|
||||
Assert.Null(CurrentCanonical(runtime, unrelatedGuid).LocalEntityId);
|
||||
Assert.NotNull(replacement.LocalEntityId);
|
||||
Assert.NotNull(CurrentCanonical(runtime, unrelatedGuid).LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.False(runtime.TryGetRecord(unrelatedGuid, out _));
|
||||
Assert.Equal(2, runtime.Count);
|
||||
|
|
@ -2587,7 +2587,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
Assert.False(outer.LogicalRegistrationCreated);
|
||||
RuntimeEntityRecord current = CurrentCanonical(runtime, guid);
|
||||
Assert.Equal((ushort)3, current.Generation);
|
||||
Assert.Null(current.LocalEntityId);
|
||||
Assert.NotNull(current.LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.Equal(1, resources.RegisterCount);
|
||||
Assert.Equal(1, resources.UnregisterCount);
|
||||
|
|
@ -2619,7 +2619,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
&& exception.Message == "old incarnation cleanup failed");
|
||||
RuntimeEntityRecord current = CurrentCanonical(runtime, guid);
|
||||
Assert.Equal((ushort)3, current.Generation);
|
||||
Assert.Null(current.LocalEntityId);
|
||||
Assert.NotNull(current.LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.Equal(1, resources.RegisterCount);
|
||||
Assert.Equal(1, resources.UnregisterCount);
|
||||
|
|
@ -2671,7 +2671,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
Assert.Contains("atomic resource registration", error.Message, StringComparison.Ordinal);
|
||||
RuntimeEntityRecord current = CurrentCanonical(runtime, guid);
|
||||
Assert.Equal((ushort)1, current.Generation);
|
||||
Assert.Null(current.LocalEntityId);
|
||||
Assert.NotNull(current.LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.Equal(1, resources.RegisterCount);
|
||||
Assert.Equal(1, resources.UnregisterCount);
|
||||
|
|
@ -2695,7 +2695,7 @@ public sealed class LiveEntityRuntimeTests
|
|||
|
||||
Assert.Contains("atomic resource registration", error.Message, StringComparison.Ordinal);
|
||||
RuntimeEntityRecord current = CurrentCanonical(runtime, guid);
|
||||
Assert.Null(current.LocalEntityId);
|
||||
Assert.NotNull(current.LocalEntityId);
|
||||
Assert.False(runtime.TryGetRecord(guid, out _));
|
||||
Assert.Equal(1, resources.RegisterCount);
|
||||
Assert.Equal(1, resources.UnregisterCount);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@ using AcDream.App.Rendering;
|
|||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Vfx;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Runtime;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Runtime;
|
||||
using AcDream.Runtime.Entities;
|
||||
|
||||
namespace AcDream.App.Tests.World;
|
||||
|
|
@ -230,6 +233,46 @@ public sealed class RuntimeEntityOwnershipTests
|
|||
property => IsPresentationType(property.PropertyType));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CurrentRuntimeAdapters_DoNotRetainEntityOrInventoryMirrors()
|
||||
{
|
||||
FieldInfo[] viewFields = typeof(CurrentGameRuntimeViewAdapter)
|
||||
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.Contains(
|
||||
viewFields,
|
||||
field => field.FieldType == typeof(IRuntimeEntityView));
|
||||
Assert.Contains(
|
||||
viewFields,
|
||||
field => field.FieldType == typeof(IRuntimeInventoryView));
|
||||
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,
|
||||
field => field.FieldType == typeof(LiveEntityRuntime)
|
||||
|| field.FieldType == typeof(ClientObjectTable)
|
||||
|| field.FieldType == typeof(RuntimeEventSequencer));
|
||||
Assert.Contains(
|
||||
eventFields,
|
||||
field => field.FieldType
|
||||
== typeof(RuntimeEntityObjectLifetime));
|
||||
|
||||
string root = FindRepositoryRoot();
|
||||
string liveSource = File.ReadAllText(Path.Combine(
|
||||
root,
|
||||
"src",
|
||||
"AcDream.App",
|
||||
"World",
|
||||
"LiveEntityRuntime.cs"));
|
||||
Assert.DoesNotContain("_entityObjects.PublishEntity", liveSource);
|
||||
Assert.DoesNotContain("_directory.TryApply", liveSource);
|
||||
Assert.DoesNotContain("_directory.RemoveActive", liveSource);
|
||||
}
|
||||
|
||||
private static bool IsPresentationType(Type type)
|
||||
{
|
||||
string? ns = type.Namespace;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue