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

@ -18,7 +18,8 @@ namespace AcDream.App.Rendering.Vfx;
public sealed class EntityEffectPoseRegistry :
IEntityEffectPoseSource,
IEntityEffectCellSource,
IEntityEffectPoseChangeSource
IEntityEffectPoseChangeSource,
IEntityEffectPoseLifetimeSource
{
private sealed class PoseRecord
{
@ -26,9 +27,11 @@ public sealed class EntityEffectPoseRegistry :
public Matrix4x4[] PartLocal = Array.Empty<Matrix4x4>();
public bool[] PartAvailable = Array.Empty<bool>();
public uint CellId;
public ulong LifetimeVersion;
}
private readonly Dictionary<uint, PoseRecord> _poses = new();
private ulong _nextLifetimeVersion;
public event Action<uint>? EffectPoseChanged;
@ -68,7 +71,7 @@ public sealed class EntityEffectPoseRegistry :
bool changed;
if (!_poses.TryGetValue(entity.Id, out PoseRecord? record))
{
record = new PoseRecord();
record = new PoseRecord { LifetimeVersion = NextLifetimeVersion() };
_poses.Add(entity.Id, record);
changed = true;
}
@ -126,7 +129,7 @@ public sealed class EntityEffectPoseRegistry :
bool changed;
if (!_poses.TryGetValue(localEntityId, out PoseRecord? record))
{
record = new PoseRecord();
record = new PoseRecord { LifetimeVersion = NextLifetimeVersion() };
_poses.Add(localEntityId, record);
changed = true;
}
@ -176,7 +179,9 @@ public sealed class EntityEffectPoseRegistry :
uint[] removedOwners = _poses.Keys.ToArray();
_poses.Clear();
foreach (uint owner in removedOwners)
{
EffectPoseChanged?.Invoke(owner);
}
}
public bool TryGetRootPose(uint localEntityId, out Matrix4x4 rootWorld)
@ -248,6 +253,19 @@ public sealed class EntityEffectPoseRegistry :
return false;
}
public ulong GetPoseOwnerLifetimeVersion(uint localEntityId) =>
_poses.TryGetValue(localEntityId, out PoseRecord? record)
? record.LifetimeVersion
: 0UL;
private ulong NextLifetimeVersion()
{
_nextLifetimeVersion++;
if (_nextLifetimeVersion == 0UL)
_nextLifetimeVersion++;
return _nextLifetimeVersion;
}
private static bool CopyParts(
PoseRecord record,
IReadOnlyList<Matrix4x4> partLocal,
@ -277,3 +295,8 @@ public sealed class EntityEffectPoseRegistry :
return changed;
}
}
public interface IEntityEffectPoseLifetimeSource
{
ulong GetPoseOwnerLifetimeVersion(uint localEntityId);
}