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

@ -12,7 +12,10 @@ public sealed record ScriptActivationInfo(
uint ScriptId,
IReadOnlyList<Matrix4x4> PartTransforms,
EntityEffectProfile? EffectProfile = null,
IReadOnlyList<bool>? PartAvailability = null);
IReadOnlyList<bool>? PartAvailability = null,
DatReaderWriter.DBObjs.Setup? Setup = null,
uint DefaultAnimationId = 0,
bool UsesStaticAnimationWorkset = false);
/// <summary>
/// Owns create-time Setup script activation and its symmetric teardown for
@ -25,13 +28,29 @@ public sealed record ScriptActivationInfo(
/// </remarks>
public sealed class EntityScriptActivator
{
private sealed class StaticOwnerState
{
public required WorldEntity Entity;
public required ScriptActivationInfo Info;
public bool PosePublished;
public bool EffectOwnerRegistered;
public bool AnimationOwnerRegistered;
public bool ScriptStarted;
public bool ReleaseStarted;
public bool ScriptsStopped;
public bool EmittersStopped;
public bool PoseRemoved;
}
private readonly PhysicsScriptRunner _scriptRunner;
private readonly ParticleHookSink _particleSink;
private readonly EntityEffectPoseRegistry _poses;
private readonly Func<WorldEntity, ScriptActivationInfo?> _resolver;
private readonly Action<uint, WorldEntity, EntityEffectProfile>? _registerDatStaticEffectOwner;
private readonly Action<uint>? _unregisterDatStaticEffectOwner;
private readonly Dictionary<uint, WorldEntity> _activeStaticOwners = new();
private readonly Action<WorldEntity, ScriptActivationInfo>? _registerDatStaticAnimationOwner;
private readonly Action<uint>? _unregisterDatStaticAnimationOwner;
private readonly Dictionary<uint, StaticOwnerState> _staticOwners = new();
public EntityScriptActivator(
PhysicsScriptRunner scriptRunner,
@ -39,7 +58,9 @@ public sealed class EntityScriptActivator
EntityEffectPoseRegistry poses,
Func<WorldEntity, ScriptActivationInfo?> resolver,
Action<uint, WorldEntity, EntityEffectProfile>? registerDatStaticEffectOwner = null,
Action<uint>? unregisterDatStaticEffectOwner = null)
Action<uint>? unregisterDatStaticEffectOwner = null,
Action<WorldEntity, ScriptActivationInfo>? registerDatStaticAnimationOwner = null,
Action<uint>? unregisterDatStaticAnimationOwner = null)
{
_scriptRunner = scriptRunner ?? throw new ArgumentNullException(nameof(scriptRunner));
_particleSink = particleSink ?? throw new ArgumentNullException(nameof(particleSink));
@ -47,6 +68,8 @@ public sealed class EntityScriptActivator
_resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
_registerDatStaticEffectOwner = registerDatStaticEffectOwner;
_unregisterDatStaticEffectOwner = unregisterDatStaticEffectOwner;
_registerDatStaticAnimationOwner = registerDatStaticAnimationOwner;
_unregisterDatStaticAnimationOwner = unregisterDatStaticAnimationOwner;
}
public void OnCreate(WorldEntity entity)
@ -57,33 +80,93 @@ public sealed class EntityScriptActivator
return;
if (entity.ServerGuid == 0
&& _activeStaticOwners.TryGetValue(key, out WorldEntity? existing))
&& _staticOwners.TryGetValue(key, out StaticOwnerState? retained))
{
if (ReferenceEquals(existing, entity))
return;
throw new InvalidOperationException(
$"Dat-static WorldEntity id 0x{key:X8} is already active; static allocators must be globally unique.");
if (!ReferenceEquals(retained.Entity, entity))
{
throw new InvalidOperationException(
$"Dat-static WorldEntity id 0x{key:X8} is already active; static allocators must be globally unique.");
}
if (retained.ReleaseStarted)
{
throw new InvalidOperationException(
$"Dat-static WorldEntity id 0x{key:X8} cannot reactivate while teardown is pending.");
}
ActivateStaticOwner(retained);
return;
}
ScriptActivationInfo? info = _resolver(entity);
if (info is null)
return;
// Particle::Init (0x0051C930) samples the current root/part frames.
// Publish before the Setup default script can execute; effects never
// fall back to the camera or a cached spawn anchor.
_poses.Publish(entity, info.PartTransforms, info.PartAvailability);
if (entity.ServerGuid == 0)
_activeStaticOwners.Add(key, entity);
if (entity.ServerGuid == 0 && info.EffectProfile is { } profile)
_registerDatStaticEffectOwner?.Invoke(key, entity, profile);
{
var state = new StaticOwnerState
{
Entity = entity,
Info = info,
};
_staticOwners.Add(key, state);
ActivateStaticOwner(state);
return;
}
// Live registration is an atomic logical-lifetime edge owned by
// LiveEntityRuntime and is never retried at this activator in isolation.
_poses.Publish(entity, info.PartTransforms, info.PartAvailability);
if (info.UsesStaticAnimationWorkset
&& info.DefaultAnimationId != 0)
{
_registerDatStaticAnimationOwner?.Invoke(entity, info);
}
if (info.ScriptId != 0)
_scriptRunner.Play(info.ScriptId, key, entity.Position);
}
private void ActivateStaticOwner(StaticOwnerState state)
{
WorldEntity entity = state.Entity;
ScriptActivationInfo info = state.Info;
uint key = entity.Id;
// Particle::Init (0x0051C930) samples the current root/part frames.
// Commit each acquisition only after its callback returns. A later
// failure can then retry without replaying completed create-time work.
if (!state.PosePublished)
{
_poses.Publish(entity, info.PartTransforms, info.PartAvailability);
state.PosePublished = true;
}
if (!state.EffectOwnerRegistered
&& info.EffectProfile is { } profile
&& _registerDatStaticEffectOwner is not null)
{
_registerDatStaticEffectOwner(key, entity, profile);
state.EffectOwnerRegistered = true;
}
// TS-51: script-only statics still use the shared script runner. The
// dedicated static animation scheduler owns only real DefaultAnimation
// PartArray work, so script-only owners incur no empty frame scan.
if (!state.AnimationOwnerRegistered
&& info.UsesStaticAnimationWorkset
&& info.DefaultAnimationId != 0
&& _registerDatStaticAnimationOwner is not null)
{
_registerDatStaticAnimationOwner(entity, info);
state.AnimationOwnerRegistered = true;
}
if (!state.ScriptStarted && info.ScriptId != 0)
{
_scriptRunner.Play(info.ScriptId, key, entity.Position);
state.ScriptStarted = true;
}
}
public void OnRemove(WorldEntity entity)
{
ArgumentNullException.ThrowIfNull(entity);
@ -91,17 +174,48 @@ public sealed class EntityScriptActivator
if (key == 0)
return;
if (entity.ServerGuid == 0
&& (!_activeStaticOwners.TryGetValue(key, out WorldEntity? existing)
|| !ReferenceEquals(existing, entity)))
if (entity.ServerGuid == 0)
{
if (!_staticOwners.TryGetValue(key, out StaticOwnerState? state)
|| !ReferenceEquals(state.Entity, entity))
{
return;
}
state.ReleaseStarted = true;
if (!state.ScriptsStopped)
{
_scriptRunner.StopAllForEntity(key);
state.ScriptsStopped = true;
}
if (!state.EmittersStopped)
{
_particleSink.StopAllForEntity(key, fadeOut: false);
state.EmittersStopped = true;
}
if (!state.PoseRemoved)
{
_poses.Remove(key);
state.PoseRemoved = true;
}
if (state.AnimationOwnerRegistered)
{
_unregisterDatStaticAnimationOwner?.Invoke(key);
state.AnimationOwnerRegistered = false;
}
if (state.EffectOwnerRegistered)
{
_unregisterDatStaticEffectOwner?.Invoke(key);
state.EffectOwnerRegistered = false;
}
_staticOwners.Remove(key);
return;
}
_unregisterDatStaticAnimationOwner?.Invoke(key);
_scriptRunner.StopAllForEntity(key);
_particleSink.StopAllForEntity(key, fadeOut: false);
_poses.Remove(key);
if (entity.ServerGuid == 0 && _activeStaticOwners.Remove(key))
_unregisterDatStaticEffectOwner?.Invoke(key);
}
}