refactor(runtime): own canonical entity identity and incarnations
Introduce the presentation-free RuntimeEntityDirectory and RuntimeEntityRecord as the sole owners of server GUIDs, INSTANCE_TS incarnations, accepted snapshots, authority versions, retryable tombstones, session epochs, and local-ID allocation. Convert LiveEntityRuntime into an App projection sidecar host resolved through canonical record identity without a second GUID map. Preserve the existing synchronous and reentrant projection lifecycle, including retryable registration/delete/session teardown. Add Runtime-only identity/lifetime tests and App ownership guards. Validated by the Release solution build and 8,441 complete Release tests with five existing skips. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
f7442d13e9
commit
f46ddb5cdb
5 changed files with 1218 additions and 225 deletions
76
tests/AcDream.App.Tests/World/RuntimeEntityOwnershipTests.cs
Normal file
76
tests/AcDream.App.Tests/World/RuntimeEntityOwnershipTests.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using System.Reflection;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Runtime.Entities;
|
||||
|
||||
namespace AcDream.App.Tests.World;
|
||||
|
||||
public sealed class RuntimeEntityOwnershipTests
|
||||
{
|
||||
[Fact]
|
||||
public void LiveEntityRuntime_ComposesCanonicalRuntimeDirectory()
|
||||
{
|
||||
FieldInfo directory = typeof(LiveEntityRuntime).GetField(
|
||||
"_directory",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("Missing canonical entity directory.");
|
||||
FieldInfo sidecars = typeof(LiveEntityRuntime).GetField(
|
||||
"_activeRecords",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("Missing App projection sidecar store.");
|
||||
|
||||
Assert.Equal(typeof(RuntimeEntityDirectory), directory.FieldType);
|
||||
Assert.Equal(typeof(RuntimeEntityRecord), sidecars.FieldType.GetGenericArguments()[0]);
|
||||
Assert.Equal(typeof(LiveEntityRecord), sidecars.FieldType.GetGenericArguments()[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppCompatibilityLookup_IsNotASecondGuidDictionary()
|
||||
{
|
||||
FieldInfo compatibilityView = typeof(LiveEntityRuntime).GetField(
|
||||
"_recordsByGuid",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new InvalidOperationException("Missing migration compatibility view.");
|
||||
|
||||
Assert.False(compatibilityView.FieldType.IsGenericType);
|
||||
Assert.DoesNotContain(
|
||||
compatibilityView.FieldType.GetInterfaces(),
|
||||
type => type.IsGenericType
|
||||
&& type.GetGenericTypeDefinition() == typeof(IDictionary<,>));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanonicalRuntimeRecord_HasNoProjectionOrBackendSurface()
|
||||
{
|
||||
string[] forbiddenPropertyNames =
|
||||
[
|
||||
"WorldEntity",
|
||||
"AnimationRuntime",
|
||||
"RemoteMotionRuntime",
|
||||
"ProjectileRuntime",
|
||||
"EffectProfile",
|
||||
"ResourcesRegistered",
|
||||
"IsSpatiallyProjected",
|
||||
"IsSpatiallyVisible",
|
||||
];
|
||||
PropertyInfo[] properties = typeof(RuntimeEntityRecord).GetProperties(
|
||||
BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
Assert.DoesNotContain(
|
||||
properties,
|
||||
property => forbiddenPropertyNames.Contains(
|
||||
property.Name,
|
||||
StringComparer.Ordinal));
|
||||
Assert.DoesNotContain(
|
||||
properties,
|
||||
property => IsPresentationType(property.PropertyType));
|
||||
}
|
||||
|
||||
private static bool IsPresentationType(Type type)
|
||||
{
|
||||
string? ns = type.Namespace;
|
||||
return ns?.StartsWith("AcDream.App", StringComparison.Ordinal) == true
|
||||
|| ns?.StartsWith("AcDream.UI", StringComparison.Ordinal) == true
|
||||
|| ns?.StartsWith("Silk.NET", StringComparison.Ordinal) == true
|
||||
|| ns?.StartsWith("ImGuiNET", StringComparison.Ordinal) == true;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue