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:
parent
e18df84437
commit
420e5eea70
73 changed files with 2939 additions and 1715 deletions
|
|
@ -33,10 +33,9 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
scale: 2f,
|
||||
itemType: (uint)ItemType.Creature,
|
||||
descriptionFlags: 0x28u);
|
||||
var record = new LiveEntityRecord(spawn)
|
||||
{
|
||||
FinalPhysicsState = PhysicsStateFlags.Hidden | PhysicsStateFlags.Static,
|
||||
};
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
record.FinalPhysicsState =
|
||||
PhysicsStateFlags.Hidden | PhysicsStateFlags.Static;
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
var builder = Builder();
|
||||
|
|
@ -65,7 +64,7 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
var setup = new Setup();
|
||||
setup.Parts.Add(part);
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1.5f);
|
||||
var record = new LiveEntityRecord(spawn);
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
var builder = new LiveEntityCollisionBuilder(
|
||||
|
|
@ -90,7 +89,7 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
var setup = new Setup();
|
||||
setup.Parts.Add(basePart);
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1f);
|
||||
var record = new LiveEntityRecord(spawn);
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
var builder = new LiveEntityCollisionBuilder(
|
||||
|
|
@ -115,7 +114,7 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
var setup = new Setup();
|
||||
setup.Parts.Add(basePart);
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1f);
|
||||
var record = new LiveEntityRecord(spawn);
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
var builder = new LiveEntityCollisionBuilder(
|
||||
|
|
@ -145,7 +144,7 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
var setup = new Setup();
|
||||
setup.Parts.Add(basePart);
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1f);
|
||||
var record = new LiveEntityRecord(spawn);
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
var builder = new LiveEntityCollisionBuilder(
|
||||
|
|
@ -178,7 +177,7 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
var setup = new Setup();
|
||||
setup.Parts.Add(basePart);
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1f);
|
||||
var record = new LiveEntityRecord(spawn);
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
var builder = new LiveEntityCollisionBuilder(
|
||||
|
|
@ -243,7 +242,7 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
var setup = new Setup();
|
||||
setup.Parts.Add(basePart);
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1f);
|
||||
var record = new LiveEntityRecord(spawn);
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
var builder = new LiveEntityCollisionBuilder(
|
||||
|
|
@ -277,7 +276,7 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
public void ShapelessSetup_ProducesNoRegistration()
|
||||
{
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1f);
|
||||
var record = new LiveEntityRecord(spawn);
|
||||
var record = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
WorldEntity entity = Entity();
|
||||
record.WorldEntity = entity;
|
||||
|
||||
|
|
@ -294,8 +293,9 @@ public sealed class LiveEntityCollisionBuilderTests
|
|||
public void Build_RejectsDifferentRecordIncarnation()
|
||||
{
|
||||
WorldSession.EntitySpawn spawn = Spawn(scale: 1f);
|
||||
var expected = new LiveEntityRecord(spawn);
|
||||
var other = new LiveEntityRecord(spawn with { InstanceSequence = 2 });
|
||||
var expected = LiveEntityTestFixture.CreateExactProjectionRecord(spawn);
|
||||
var other = LiveEntityTestFixture.CreateExactProjectionRecord(
|
||||
spawn with { InstanceSequence = 2 });
|
||||
WorldEntity entity = Entity();
|
||||
expected.WorldEntity = entity;
|
||||
other.WorldEntity = entity;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -26,10 +26,8 @@ public sealed class LiveEntityOrdinaryPhysicsUpdaterTests
|
|||
var live = new LiveEntityRuntime(
|
||||
spatial,
|
||||
new DelegateLiveEntityResourceLifecycle(_ => { }, _ => { }));
|
||||
LiveEntityRecord record = live.RegisterLiveEntity(Spawn()).Record!;
|
||||
WorldEntity entity = live.MaterializeLiveEntity(
|
||||
Guid,
|
||||
SourceCell,
|
||||
LiveEntityRecord record = live.RegisterAndMaterializeProjection(
|
||||
Spawn(),
|
||||
id => new WorldEntity
|
||||
{
|
||||
Id = id,
|
||||
|
|
@ -39,7 +37,8 @@ public sealed class LiveEntityOrdinaryPhysicsUpdaterTests
|
|||
Rotation = Quaternion.Identity,
|
||||
MeshRefs = Array.Empty<MeshRef>(),
|
||||
ParentCellId = SourceCell,
|
||||
})!;
|
||||
});
|
||||
WorldEntity entity = Assert.IsType<WorldEntity>(record.WorldEntity);
|
||||
|
||||
var remote = new AcDream.App.Physics.RemoteMotion
|
||||
{
|
||||
|
|
@ -94,10 +93,8 @@ public sealed class LiveEntityOrdinaryPhysicsUpdaterTests
|
|||
var live = new LiveEntityRuntime(
|
||||
spatial,
|
||||
new DelegateLiveEntityResourceLifecycle(_ => { }, _ => { }));
|
||||
LiveEntityRecord record = live.RegisterLiveEntity(Spawn()).Record!;
|
||||
WorldEntity entity = live.MaterializeLiveEntity(
|
||||
Guid,
|
||||
SourceCell,
|
||||
LiveEntityRecord record = live.RegisterAndMaterializeProjection(
|
||||
Spawn(),
|
||||
id => new WorldEntity
|
||||
{
|
||||
Id = id,
|
||||
|
|
@ -107,7 +104,8 @@ public sealed class LiveEntityOrdinaryPhysicsUpdaterTests
|
|||
Rotation = Quaternion.Identity,
|
||||
MeshRefs = Array.Empty<MeshRef>(),
|
||||
ParentCellId = SourceCell,
|
||||
})!;
|
||||
});
|
||||
WorldEntity entity = Assert.IsType<WorldEntity>(record.WorldEntity);
|
||||
var remote = new AcDream.App.Physics.RemoteMotion
|
||||
{
|
||||
CellId = SourceCell,
|
||||
|
|
|
|||
|
|
@ -243,18 +243,18 @@ public sealed class ProjectileControllerTests
|
|||
Assert.True(fixture.Controller.TryBind(record, ProjectileSetup(), 1.0, 1, 1));
|
||||
Assert.True(record.PhysicsBody!.InWorld);
|
||||
Assert.True(record.PhysicsBody.IsActive);
|
||||
Assert.Single(fixture.Live.SpatialProjectileRuntimes);
|
||||
Assert.Equal(1, fixture.Live.SpatialProjectileRuntimeCount);
|
||||
|
||||
fixture.Spatial.RemoveLandblock(0x0101FFFFu);
|
||||
|
||||
Assert.False(record.IsSpatiallyVisible);
|
||||
Assert.Empty(fixture.Live.SpatialProjectileRuntimes);
|
||||
Assert.Equal(0, fixture.Live.SpatialProjectileRuntimeCount);
|
||||
Assert.False(record.PhysicsBody.InWorld);
|
||||
Assert.False(record.PhysicsBody.IsActive);
|
||||
Assert.Equal(0, fixture.Engine.ShadowObjects.TotalRegistered);
|
||||
|
||||
fixture.Spatial.AddLandblock(EmptyLandblock(0x0101FFFFu));
|
||||
Assert.Single(fixture.Live.SpatialProjectileRuntimes);
|
||||
Assert.Equal(1, fixture.Live.SpatialProjectileRuntimeCount);
|
||||
Assert.True(record.PhysicsBody.InWorld);
|
||||
Assert.True(record.PhysicsBody.IsActive);
|
||||
Assert.Equal(1, fixture.Engine.ShadowObjects.TotalRegistered);
|
||||
|
|
@ -545,7 +545,8 @@ public sealed class ProjectileControllerTests
|
|||
Assert.Same(runtime, record.ProjectileRuntime);
|
||||
Assert.Same(body, record.PhysicsBody);
|
||||
Assert.True(body.IsActive); // State preserves an already-active body.
|
||||
Assert.Same(record.WorldEntity, fixture.Live.MaterializedWorldEntities[Guid]);
|
||||
Assert.True(fixture.Live.TryGetWorldEntity(Guid, out WorldEntity entity));
|
||||
Assert.Same(record.WorldEntity, entity);
|
||||
Assert.Equal(1, fixture.Resources.Registers);
|
||||
}
|
||||
|
||||
|
|
@ -1501,12 +1502,8 @@ public sealed class ProjectileControllerTests
|
|||
angularVelocity ?? Vector3.Zero,
|
||||
friction,
|
||||
elasticity);
|
||||
LiveEntityRegistrationResult registration = Live.RegisterLiveEntity(spawn);
|
||||
Assert.NotNull(registration.Record);
|
||||
registration.Record!.HasPartArray = true;
|
||||
WorldEntity? entity = Live.MaterializeLiveEntity(
|
||||
Guid,
|
||||
cellId,
|
||||
LiveEntityRecord record = Live.RegisterAndMaterializeProjection(
|
||||
spawn,
|
||||
localId => new WorldEntity
|
||||
{
|
||||
Id = localId,
|
||||
|
|
@ -1518,10 +1515,11 @@ public sealed class ProjectileControllerTests
|
|||
? new[] { new MeshRef(0x01000001u, Matrix4x4.Identity) }
|
||||
: Array.Empty<MeshRef>(),
|
||||
ParentCellId = cellId,
|
||||
});
|
||||
Assert.NotNull(entity);
|
||||
},
|
||||
initializeProjection: exact => exact.HasPartArray = true);
|
||||
WorldEntity entity = Assert.IsType<WorldEntity>(record.WorldEntity);
|
||||
Poses.PublishMeshRefs(entity);
|
||||
return registration.Record!;
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -697,7 +697,7 @@ public sealed class RemotePhysicsUpdaterTests
|
|||
new DelegateLiveEntityResourceLifecycle(_ => { }, _ => { }));
|
||||
BindHiddenRemote(live, firstGuid);
|
||||
BindHiddenRemote(live, secondGuid);
|
||||
Assert.Equal(2, live.SpatialRemoteMotionRuntimes.Count);
|
||||
Assert.Equal(2, live.SpatialRemoteMotionRuntimeCount);
|
||||
|
||||
var updater = new RemotePhysicsUpdater(
|
||||
new PhysicsEngine(),
|
||||
|
|
@ -721,7 +721,7 @@ public sealed class RemotePhysicsUpdaterTests
|
|||
|
||||
Assert.Single(published);
|
||||
Assert.Equal(published, partPoseDirty);
|
||||
Assert.Single(live.SpatialRemoteMotionRuntimes);
|
||||
Assert.Equal(1, live.SpatialRemoteMotionRuntimeCount);
|
||||
}
|
||||
|
||||
private static PhysicsEngine BuildBoundaryEngine()
|
||||
|
|
@ -908,14 +908,8 @@ public sealed class RemotePhysicsUpdaterTests
|
|||
Vector3 position,
|
||||
bool enqueueDestination)
|
||||
{
|
||||
LiveEntityRegistrationResult registration = Live.RegisterLiveEntity(
|
||||
Spawn(instanceSequence, position));
|
||||
LiveEntityRecord record = Assert.IsType<LiveEntityRecord>(
|
||||
registration.Record);
|
||||
WorldEntity entity = Assert.IsType<WorldEntity>(
|
||||
Live.MaterializeLiveEntity(
|
||||
Guid,
|
||||
SourceCell,
|
||||
LiveEntityRecord record = Live.RegisterAndMaterializeProjection(
|
||||
Spawn(instanceSequence, position),
|
||||
id => new WorldEntity
|
||||
{
|
||||
Id = id,
|
||||
|
|
@ -925,7 +919,8 @@ public sealed class RemotePhysicsUpdaterTests
|
|||
Rotation = Quaternion.Identity,
|
||||
MeshRefs = Array.Empty<MeshRef>(),
|
||||
ParentCellId = SourceCell,
|
||||
}));
|
||||
});
|
||||
WorldEntity entity = Assert.IsType<WorldEntity>(record.WorldEntity);
|
||||
var remote = new AcDream.App.Physics.RemoteMotion();
|
||||
remote.Body.Position = position;
|
||||
remote.Body.Orientation = Quaternion.Identity;
|
||||
|
|
|
|||
|
|
@ -1089,13 +1089,14 @@ public sealed class RemoteTeleportControllerTests
|
|||
const uint sourceCell = 0xA9B40039u;
|
||||
const uint destinationCell = 0xAAB40001u;
|
||||
RemoteTeleportController? controller = null;
|
||||
LiveEntityRecord? owner = null;
|
||||
bool cleanupAfterFailureRan = false;
|
||||
var resources = new DelegateLiveEntityResourceLifecycle(
|
||||
_ => { },
|
||||
_ => LiveEntityTeardown.Run(
|
||||
[
|
||||
() => throw new InvalidOperationException("effect teardown failed"),
|
||||
() => controller!.Forget(guid),
|
||||
() => controller!.Forget(owner!),
|
||||
() => cleanupAfterFailureRan = true,
|
||||
]));
|
||||
var spatial = new GpuWorldState();
|
||||
|
|
@ -1117,6 +1118,7 @@ public sealed class RemoteTeleportControllerTests
|
|||
var remote = new AcDream.App.Physics.RemoteMotion();
|
||||
remote.Body.SnapToCell(sourceCell, entity.Position, entity.Position);
|
||||
live.SetRemoteMotionRuntime(guid, remote);
|
||||
Assert.True(live.TryGetRecord(guid, out owner));
|
||||
controller = new RemoteTeleportController(
|
||||
BuildEngine(includeDestination: false),
|
||||
live,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue