feat(physics): port retail complete object frame pipeline
Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates. Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
31a0889f08
commit
f961d70023
77 changed files with 12513 additions and 1871 deletions
|
|
@ -119,6 +119,143 @@ public sealed class EntityEffectPoseRegistryTests
|
|||
Assert.Equal(0, queue.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnimationHookQueue_DeleteAndLocalIdReuse_DropsOldIncarnationHooks()
|
||||
{
|
||||
var poses = new EntityEffectPoseRegistry();
|
||||
WorldEntity first = Entity(9u, Vector3.Zero);
|
||||
poses.Publish(first, Array.Empty<Matrix4x4>());
|
||||
var router = new AnimationHookRouter();
|
||||
var sink = new RecordingSink();
|
||||
router.Register(sink);
|
||||
var queue = new AnimationHookFrameQueue(router, poses);
|
||||
var sequencer = new AnimationSequencer(
|
||||
new Setup(),
|
||||
new MotionTable(),
|
||||
new NullAnimationLoader());
|
||||
|
||||
queue.Capture(9u, sequencer, new AnimationHook[] { new SoundHook() });
|
||||
Assert.True(poses.Remove(9u));
|
||||
WorldEntity replacement = Entity(9u, new Vector3(40f, 50f, 60f));
|
||||
poses.Publish(replacement, Array.Empty<Matrix4x4>());
|
||||
|
||||
queue.Drain();
|
||||
|
||||
Assert.Empty(sink.Calls);
|
||||
Assert.Equal(0, queue.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnimationDoneReuseDuringCapture_DropsOldIncarnationHooks()
|
||||
{
|
||||
var poses = new EntityEffectPoseRegistry();
|
||||
poses.Publish(Entity(9u, Vector3.Zero), Array.Empty<Matrix4x4>());
|
||||
var router = new AnimationHookRouter();
|
||||
var sink = new RecordingSink();
|
||||
router.Register(sink);
|
||||
var queue = new AnimationHookFrameQueue(router, poses);
|
||||
var sequencer = new AnimationSequencer(
|
||||
new Setup(),
|
||||
new MotionTable(),
|
||||
new NullAnimationLoader());
|
||||
sequencer.Manager.AddToQueue(0x10000001u, ticks: 1u);
|
||||
sequencer.MotionDoneTarget = (_, success) =>
|
||||
{
|
||||
Assert.True(success);
|
||||
Assert.True(poses.Remove(9u));
|
||||
poses.Publish(
|
||||
Entity(9u, new Vector3(40f, 50f, 60f)),
|
||||
Array.Empty<Matrix4x4>());
|
||||
};
|
||||
|
||||
queue.Capture(
|
||||
9u,
|
||||
sequencer,
|
||||
new AnimationHook[]
|
||||
{
|
||||
new AnimationDoneHook(),
|
||||
new SoundHook(),
|
||||
});
|
||||
queue.Drain();
|
||||
|
||||
Assert.Empty(sink.Calls);
|
||||
Assert.Equal(0, queue.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleAnimationDoneHooks_StopAdvancingDisplacedSequencerAfterReuse()
|
||||
{
|
||||
var poses = new EntityEffectPoseRegistry();
|
||||
poses.Publish(Entity(9u, Vector3.Zero), Array.Empty<Matrix4x4>());
|
||||
var queue = new AnimationHookFrameQueue(
|
||||
new AnimationHookRouter(),
|
||||
poses);
|
||||
var sequencer = new AnimationSequencer(
|
||||
new Setup(),
|
||||
new MotionTable(),
|
||||
new NullAnimationLoader());
|
||||
sequencer.Manager.AddToQueue(0x10000001u, ticks: 1u);
|
||||
sequencer.Manager.AddToQueue(0x10000002u, ticks: 1u);
|
||||
int completions = 0;
|
||||
sequencer.MotionDoneTarget = (_, success) =>
|
||||
{
|
||||
Assert.True(success);
|
||||
completions++;
|
||||
Assert.True(poses.Remove(9u));
|
||||
poses.Publish(
|
||||
Entity(9u, new Vector3(40f, 50f, 60f)),
|
||||
Array.Empty<Matrix4x4>());
|
||||
};
|
||||
|
||||
queue.Capture(
|
||||
9u,
|
||||
sequencer,
|
||||
new AnimationHook[]
|
||||
{
|
||||
new AnimationDoneHook(),
|
||||
new AnimationDoneHook(),
|
||||
});
|
||||
|
||||
Assert.Equal(1, completions);
|
||||
Assert.Single(sequencer.Manager.PendingAnimations);
|
||||
queue.Drain();
|
||||
Assert.Equal(0, queue.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HookSinkReuseDuringDrain_StopsRemainingOldIncarnationHooks()
|
||||
{
|
||||
var poses = new EntityEffectPoseRegistry();
|
||||
poses.Publish(Entity(9u, Vector3.Zero), Array.Empty<Matrix4x4>());
|
||||
var router = new AnimationHookRouter();
|
||||
bool replaced = false;
|
||||
var sink = new CallbackSink(() =>
|
||||
{
|
||||
if (replaced)
|
||||
return;
|
||||
replaced = true;
|
||||
Assert.True(poses.Remove(9u));
|
||||
poses.Publish(
|
||||
Entity(9u, new Vector3(40f, 50f, 60f)),
|
||||
Array.Empty<Matrix4x4>());
|
||||
});
|
||||
router.Register(sink);
|
||||
var queue = new AnimationHookFrameQueue(router, poses);
|
||||
var sequencer = new AnimationSequencer(
|
||||
new Setup(),
|
||||
new MotionTable(),
|
||||
new NullAnimationLoader());
|
||||
|
||||
queue.Capture(
|
||||
9u,
|
||||
sequencer,
|
||||
new AnimationHook[] { new SoundHook(), new SoundHook() });
|
||||
queue.Drain();
|
||||
|
||||
Assert.Equal(1, sink.Calls);
|
||||
Assert.Equal(0, queue.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AnimationHookQueue_CompletesMotionStateAtCaptureEvenWhenPoseWasRemoved()
|
||||
{
|
||||
|
|
@ -217,6 +354,17 @@ public sealed class EntityEffectPoseRegistryTests
|
|||
Calls.Add((entityId, entityWorldPosition));
|
||||
}
|
||||
|
||||
private sealed class CallbackSink(Action callback) : IAnimationHookSink
|
||||
{
|
||||
public int Calls { get; private set; }
|
||||
|
||||
public void OnHook(uint entityId, Vector3 entityWorldPosition, AnimationHook hook)
|
||||
{
|
||||
Calls++;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NullAnimationLoader : IAnimationLoader
|
||||
{
|
||||
public Animation? LoadAnimation(uint animationId) => null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue