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:
Erik 2026-07-20 09:10:31 +02:00
parent 31a0889f08
commit f961d70023
77 changed files with 12513 additions and 1871 deletions

View file

@ -260,6 +260,193 @@ public sealed class EntityScriptActivatorTests
Assert.Equal(new Vector3(5, 5, 5), p.Recording.Calls[0].Pos);
}
[Fact]
public void DatStaticAnimationOwner_RegistersAndUnregistersExactlyOnce()
{
var p = BuildPipeline();
int registerCount = 0;
int unregisterCount = 0;
WorldEntity? registeredEntity = null;
ScriptActivationInfo info = new(
ScriptId: 0,
PartTransforms: Array.Empty<Matrix4x4>(),
DefaultAnimationId: 0x03000001u,
UsesStaticAnimationWorkset: true);
var activator = new EntityScriptActivator(
p.Runner,
p.Sink,
p.Poses,
_ => info,
registerDatStaticAnimationOwner: (entity, actual) =>
{
registerCount++;
registeredEntity = entity;
Assert.Same(info, actual);
},
unregisterDatStaticAnimationOwner: ownerId =>
{
unregisterCount++;
Assert.Equal(0x40A9B420u, ownerId);
});
WorldEntity entity = MakeDatStatic(0x40A9B420u, Vector3.One);
activator.OnCreate(entity);
activator.OnCreate(entity);
activator.OnRemove(entity);
activator.OnRemove(entity);
Assert.Equal(1, registerCount);
Assert.Equal(1, unregisterCount);
Assert.Same(entity, registeredEntity);
}
[Fact]
public void LiveNonStaticEntity_DoesNotEnterStaticAnimationWorkset()
{
var p = BuildPipeline();
int registerCount = 0;
var activator = new EntityScriptActivator(
p.Runner,
p.Sink,
p.Poses,
_ => new ScriptActivationInfo(
ScriptId: 0,
PartTransforms: Array.Empty<Matrix4x4>(),
DefaultAnimationId: 0x03000001u),
registerDatStaticAnimationOwner: (_, _) => registerCount++);
activator.OnCreate(MakeEntity(0x70000420u, Vector3.Zero));
Assert.Equal(0, registerCount);
}
[Fact]
public void LivePhysicsStaticEntity_EntersAndLeavesStaticAnimationWorkset()
{
var p = BuildPipeline();
int registerCount = 0;
int unregisterCount = 0;
var activator = new EntityScriptActivator(
p.Runner,
p.Sink,
p.Poses,
_ => new ScriptActivationInfo(
ScriptId: 0,
PartTransforms: Array.Empty<Matrix4x4>(),
DefaultAnimationId: 0x03000001u,
UsesStaticAnimationWorkset: true),
registerDatStaticAnimationOwner: (_, _) => registerCount++,
unregisterDatStaticAnimationOwner: _ => unregisterCount++);
WorldEntity entity = MakeEntity(0x70000421u, Vector3.Zero);
activator.OnCreate(entity);
activator.OnRemove(entity);
Assert.Equal(1, registerCount);
Assert.Equal(1, unregisterCount);
}
[Fact]
public void DatStaticActivation_RetriesOnlyIncompleteAcquisitionStages()
{
var p = BuildPipeline(
(0xAAu, BuildScript((10.0, new CreateParticleHook { EmitterInfoId = 100 }))));
WorldEntity entity = MakeDatStatic(0x40A9B421u, Vector3.Zero);
var setup = new DatReaderWriter.DBObjs.Setup();
ScriptActivationInfo info = new(
ScriptId: 0xAAu,
PartTransforms: Array.Empty<Matrix4x4>(),
EffectProfile: EntityEffectProfile.CreateDatStatic(setup),
Setup: setup,
DefaultAnimationId: 0x03000001u,
UsesStaticAnimationWorkset: true);
int effectAttempts = 0;
int animationAttempts = 0;
var activator = new EntityScriptActivator(
p.Runner,
p.Sink,
p.Poses,
_ => info,
registerDatStaticEffectOwner: (_, _, _) =>
{
effectAttempts++;
if (effectAttempts == 1)
throw new InvalidOperationException("effect acquire fault");
},
registerDatStaticAnimationOwner: (_, _) =>
{
animationAttempts++;
if (animationAttempts == 1)
throw new InvalidOperationException("animation acquire fault");
});
Assert.Throws<InvalidOperationException>(() => activator.OnCreate(entity));
Assert.Equal(1, effectAttempts);
Assert.Equal(0, animationAttempts);
Assert.Equal(0, p.Runner.ActiveOwnerCount);
Assert.Throws<InvalidOperationException>(() => activator.OnCreate(entity));
Assert.Equal(2, effectAttempts);
Assert.Equal(1, animationAttempts);
Assert.Equal(0, p.Runner.ActiveOwnerCount);
activator.OnCreate(entity);
activator.OnCreate(entity);
Assert.Equal(2, effectAttempts);
Assert.Equal(2, animationAttempts);
Assert.Equal(1, p.Runner.ActiveOwnerCount);
}
[Fact]
public void DatStaticRemoval_RetriesOnlyIncompleteReleaseStages()
{
var p = BuildPipeline();
WorldEntity entity = MakeDatStatic(0x40A9B422u, Vector3.Zero);
var setup = new DatReaderWriter.DBObjs.Setup();
ScriptActivationInfo info = new(
ScriptId: 0,
PartTransforms: Array.Empty<Matrix4x4>(),
EffectProfile: EntityEffectProfile.CreateDatStatic(setup),
Setup: setup,
DefaultAnimationId: 0x03000001u,
UsesStaticAnimationWorkset: true);
int animationReleaseAttempts = 0;
int effectReleaseAttempts = 0;
var activator = new EntityScriptActivator(
p.Runner,
p.Sink,
p.Poses,
_ => info,
registerDatStaticEffectOwner: (_, _, _) => { },
unregisterDatStaticEffectOwner: _ =>
{
effectReleaseAttempts++;
if (effectReleaseAttempts == 1)
throw new InvalidOperationException("effect release fault");
},
registerDatStaticAnimationOwner: (_, _) => { },
unregisterDatStaticAnimationOwner: _ =>
{
animationReleaseAttempts++;
if (animationReleaseAttempts == 1)
throw new InvalidOperationException("animation release fault");
});
activator.OnCreate(entity);
Assert.Throws<InvalidOperationException>(() => activator.OnRemove(entity));
Assert.Equal(1, animationReleaseAttempts);
Assert.Equal(0, effectReleaseAttempts);
Assert.Throws<InvalidOperationException>(() => activator.OnRemove(entity));
Assert.Equal(2, animationReleaseAttempts);
Assert.Equal(1, effectReleaseAttempts);
activator.OnRemove(entity);
activator.OnRemove(entity);
Assert.Equal(2, animationReleaseAttempts);
Assert.Equal(2, effectReleaseAttempts);
}
[Fact]
public void CollidingDatEntityIds_FailFastAtAllocatorBoundary()
{