refactor(runtime): extract the live object frame

This commit is contained in:
Erik 2026-07-22 00:42:26 +02:00
parent 99a3e819c4
commit 4e4aac2c5a
27 changed files with 1217 additions and 371 deletions

View file

@ -17,6 +17,20 @@ namespace AcDream.App.Tests.World;
public sealed class LiveEntityRuntimeTests
{
[Fact]
public void RuntimeSlot_BindsExactlyOnceWithoutOwningEntityState()
{
var spatial = new GpuWorldState();
var runtime = new LiveEntityRuntime(spatial, new RecordingResources());
var slot = new LiveEntityRuntimeSlot();
Assert.Null(slot.Current);
slot.Bind(runtime);
Assert.Same(runtime, slot.Current);
Assert.Throws<InvalidOperationException>(() => slot.Bind(runtime));
}
private sealed class RecordingResources : ILiveEntityResourceLifecycle
{
public int RegisterCount { get; private set; }
@ -245,11 +259,18 @@ public sealed class LiveEntityRuntimeTests
landblockId: 0x01010000u,
seedCellId: 0x01010001u);
int poseUpdates = 0;
var runtimeSlot = new LiveEntityRuntimeSlot();
runtimeSlot.Bind(runtime);
var origin = new LiveWorldOriginState();
origin.Recenter(1, 1);
var poses = new EntityEffectPoseRegistry();
poses.PublishMeshRefs(entity);
poses.EffectPoseChanged += _ => poseUpdates++;
var committer = new StaticLiveRootCommitter(
() => runtime,
runtimeSlot,
shadows,
() => (1, 1),
_ => poseUpdates++);
origin,
poses);
body.SetFrameInCurrentCell(
body.Position,
@ -282,13 +303,14 @@ public sealed class LiveEntityRuntimeTests
[Fact]
public void StaticRootCommit_BeforeLiveRuntimeCompositionIsSafeNoOp()
{
LiveEntityRuntime? runtime = null;
int poseUpdates = 0;
var poses = new EntityEffectPoseRegistry();
poses.EffectPoseChanged += _ => poseUpdates++;
var committer = new StaticLiveRootCommitter(
() => runtime,
new LiveEntityRuntimeSlot(),
new ShadowObjectRegistry(),
() => (0, 0),
_ => poseUpdates++);
new LiveWorldOriginState(),
poses);
Assert.False(committer.Commit(
Entity(0x7F000001u, 0x70000001u),
@ -347,26 +369,33 @@ public sealed class LiveEntityRuntimeTests
worldOffsetY: 0f,
landblockId: 0x01010000u,
seedCellId: 0x01010001u);
var runtimeSlot = new LiveEntityRuntimeSlot();
runtimeSlot.Bind(runtime);
var origin = new LiveWorldOriginState();
origin.Recenter(1, 1);
var poses = new EntityEffectPoseRegistry();
poses.PublishMeshRefs(oldEntity);
poses.EffectPoseChanged += _ =>
{
Assert.True(shadows.Suspend(oldEntity.Id));
Assert.True(runtime.UnregisterLiveEntity(
new DeleteObject.Parsed(guid, InstanceSequence: 1),
isLocalPlayer: false));
runtime.RegisterLiveEntity(Spawn(
guid,
instance: 2,
positionSequence: 1,
cell: 0x01010001u));
runtime.MaterializeLiveEntity(
guid,
0x01010001u,
id => Entity(id, guid));
};
var committer = new StaticLiveRootCommitter(
() => runtime,
runtimeSlot,
shadows,
() => (1, 1),
_ =>
{
Assert.True(shadows.Suspend(oldEntity.Id));
Assert.True(runtime.UnregisterLiveEntity(
new DeleteObject.Parsed(guid, InstanceSequence: 1),
isLocalPlayer: false));
runtime.RegisterLiveEntity(Spawn(
guid,
instance: 2,
positionSequence: 1,
cell: 0x01010001u));
runtime.MaterializeLiveEntity(
guid,
0x01010001u,
id => Entity(id, guid));
});
origin,
poses);
oldBody.SetFrameInCurrentCell(
oldBody.Position,
@ -453,7 +482,7 @@ public sealed class LiveEntityRuntimeTests
id => Entity(id, guid))!;
var animation = new AnimationRuntime(entity);
runtime.SetAnimationRuntime(guid, animation);
var view = new LiveEntityAnimationRuntimeView<AnimationRuntime>(() => runtime);
var view = new LiveEntityAnimationRuntimeView<AnimationRuntime>(BoundSlot(runtime));
Assert.Equal(1, view.Count);
Assert.Equal(entity.Id, Assert.Single(view.Keys));
@ -489,7 +518,7 @@ public sealed class LiveEntityRuntimeTests
id => Entity(id, guid))!;
runtime.SetAnimationRuntime(guid, new AnimationRuntime(entity));
}
var view = new LiveEntityAnimationRuntimeView<AnimationRuntime>(() => runtime);
var view = new LiveEntityAnimationRuntimeView<AnimationRuntime>(BoundSlot(runtime));
var visited = new List<uint>();
foreach (KeyValuePair<uint, AnimationRuntime> pair in view)
@ -519,7 +548,7 @@ public sealed class LiveEntityRuntimeTests
id => Entity(id, guid))!;
runtime.SetAnimationRuntime(guid, new AnimationRuntime(entity));
var view = new LiveEntityAnimationRuntimeView<AnimationRuntime>(
() => runtime);
BoundSlot(runtime));
var ids = new HashSet<uint>();
view.CopySpatialIdsTo(ids);
@ -2803,6 +2832,13 @@ public sealed class LiveEntityRuntimeTests
private static LoadedLandblock EmptyLandblock(uint canonicalId) =>
new(canonicalId, new LandBlock(), Array.Empty<WorldEntity>());
private static LiveEntityRuntimeSlot BoundSlot(LiveEntityRuntime runtime)
{
var slot = new LiveEntityRuntimeSlot();
slot.Bind(runtime);
return slot;
}
private static void ResolveParent(LiveEntityRuntime runtime, uint childGuid) =>
runtime.ParentAttachments.Resolve(
childGuid,