refactor(app): key live projections by runtime identity

Move materialized live-object sidecars and presentation worksets to exact RuntimeEntityKey ownership. Runtime remains the only GUID/incarnation/local-ID authority while hydration, animation, effects, lights, equipped children, renderer resources, visibility, liveness, and teardown resolve exact projection identities. Preserve synchronous callbacks, local-ID allocation order, and current rendering behavior.
This commit is contained in:
Erik 2026-07-25 21:50:58 +02:00
parent e18df84437
commit 420e5eea70
73 changed files with 2939 additions and 1715 deletions

View file

@ -17,7 +17,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Motion_StrictFreshnessAndWrapAreOwnedBeforePresentation()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 7, movement: 0xFFFE));
Register(runtime, Spawn(Guid, instance: 7, movement: 0xFFFE));
var published = new List<AcceptedPhysicsTimestamps>();
var gate = new LiveEntityInboundAuthorityGate(
runtime,
@ -52,7 +52,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void AutonomousLocalMotion_ConsumesTimestampButDoesNotPublishPayload()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 1));
Register(runtime, Spawn(Guid, instance: 1));
int publishCount = 0;
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => publishCount++);
@ -72,7 +72,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Vector_InvalidPayloadDoesNotConsumeSequenceAndDuplicateIsRejected()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 2));
Register(runtime, Spawn(Guid, instance: 2));
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => { });
var update = new VectorUpdate.Parsed(
Guid,
@ -94,7 +94,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Vector_WrappedSequenceIsAccepted()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 2, vector: 0xFFFE));
Register(runtime, Spawn(Guid, instance: 2, vector: 0xFFFE));
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => { });
Assert.True(gate.TryAcceptVector(
@ -112,7 +112,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void State_EqualIsRejectedAndWrappedSequenceIsAccepted()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 3, state: 0xFFFE));
Register(runtime, Spawn(Guid, instance: 3, state: 0xFFFE));
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => { });
Assert.False(gate.TryAcceptState(
@ -149,7 +149,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
Assert.Equal(0xAABB0001u, gate.LastLivePlayerLandblockId);
Assert.Equal(0, publishCount);
runtime.RegisterLiveEntity(Spawn(Guid, instance: 4, position: 1));
Register(runtime, Spawn(Guid, instance: 4, position: 1));
Assert.True(gate.TryAcceptPosition(
update,
Guid,
@ -169,7 +169,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Position_InvalidPayloadDoesNotConsumeSequence()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 5, position: 1));
Register(runtime, Spawn(Guid, instance: 5, position: 1));
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => { });
WorldSession.EntityPositionUpdate update = Position(Guid, 5, 2, 0x01010001u);
@ -185,7 +185,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Position_WrappedSequenceIsAccepted()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 5, position: 0xFFFE));
Register(runtime, Spawn(Guid, instance: 5, position: 0xFFFE));
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => { });
Assert.True(gate.TryAcceptPosition(
@ -201,7 +201,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Vector_NewerSameIncarnationPacketInvalidatesCapturedAuthority()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 5, vector: 1));
Register(runtime, Spawn(Guid, instance: 5, vector: 1));
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => { });
Assert.True(gate.TryAcceptVector(
new VectorUpdate.Parsed(Guid, Vector3.One, Vector3.Zero, 5, 2),
@ -220,7 +220,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void State_NewerSameIncarnationPacketInvalidatesCapturedAuthority()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 5, state: 1));
Register(runtime, Spawn(Guid, instance: 5, state: 1));
var gate = new LiveEntityInboundAuthorityGate(runtime, (_, _) => { });
Assert.True(gate.TryAcceptState(
new SetState.Parsed(Guid, (uint)PhysicsStateFlags.Hidden, 5, 2),
@ -238,7 +238,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Motion_PublisherReplacementInvalidatesCapturedIncarnation()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 6));
Register(runtime, Spawn(Guid, instance: 6));
var gate = new LiveEntityInboundAuthorityGate(
runtime,
(_, _) => runtime.RegisterLiveEntity(Spawn(Guid, instance: 7)));
@ -249,15 +249,17 @@ public sealed class LiveEntityInboundAuthorityGateTests
out _,
out bool timestampAccepted));
Assert.True(timestampAccepted);
Assert.True(runtime.TryGetRecord(Guid, out LiveEntityRecord replacement));
Assert.Equal((ushort)7, replacement.Generation);
Assert.True(runtime.TryGetCanonical(Guid, out RuntimeEntityRecord replacement));
Assert.Equal((ushort)7, replacement.Incarnation);
Assert.Null(replacement.LocalEntityId);
Assert.False(runtime.TryGetRecord(Guid, out _));
}
[Fact]
public void Position_PublisherNewerChannelInvalidatesCapturedAuthority()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 8, position: 1));
Register(runtime, Spawn(Guid, instance: 8, position: 1));
var nested = Position(Guid, 8, 3, 0x01010002u);
LiveEntityInboundAuthorityGate? gate = null;
gate = new LiveEntityInboundAuthorityGate(
@ -286,7 +288,7 @@ public sealed class LiveEntityInboundAuthorityGateTests
public void Position_PublisherNewerVectorPreservesIndependentPositionAuthority()
{
LiveEntityRuntime runtime = Runtime();
runtime.RegisterLiveEntity(Spawn(Guid, instance: 9, position: 1, vector: 1));
Register(runtime, Spawn(Guid, instance: 9, position: 1, vector: 1));
var gate = new LiveEntityInboundAuthorityGate(
runtime,
(_, _) => runtime.TryApplyVector(
@ -317,6 +319,11 @@ public sealed class LiveEntityInboundAuthorityGateTests
new GpuWorldState(),
new DelegateLiveEntityResourceLifecycle(_ => { }, _ => { }));
private static LiveEntityRecord Register(
LiveEntityRuntime runtime,
WorldSession.EntitySpawn spawn) =>
runtime.RegisterAndMaterializeProjection(spawn);
private static WorldSession.EntityMotionUpdate Motion(
ushort instance,
ushort movement,