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

@ -0,0 +1,90 @@
using AcDream.App.Physics;
using AcDream.App.World;
using AcDream.Core.Physics;
using AcDream.Core.World;
namespace AcDream.App.Rendering;
/// <summary>
/// Commits a changed root produced by retail's
/// <c>CPhysicsObj::animate_static_object</c> to the retained effect pose and
/// collision-shadow projections of the same live object incarnation.
/// </summary>
internal sealed class StaticLiveRootCommitter
{
private readonly Func<LiveEntityRuntime?> _runtime;
private readonly ShadowObjectRegistry _shadows;
private readonly Func<(int X, int Y)> _liveCenter;
private readonly Action<WorldEntity> _updateEffectRoot;
public StaticLiveRootCommitter(
Func<LiveEntityRuntime?> runtime,
ShadowObjectRegistry shadows,
Func<(int X, int Y)> liveCenter,
Action<WorldEntity> updateEffectRoot)
{
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
_shadows = shadows ?? throw new ArgumentNullException(nameof(shadows));
_liveCenter = liveCenter
?? throw new ArgumentNullException(nameof(liveCenter));
_updateEffectRoot = updateEffectRoot
?? throw new ArgumentNullException(nameof(updateEffectRoot));
}
public bool Commit(WorldEntity entity, PhysicsBody body)
{
ArgumentNullException.ThrowIfNull(entity);
ArgumentNullException.ThrowIfNull(body);
LiveEntityRuntime? runtime = _runtime();
if (runtime is null
|| !runtime.TryGetRecord(entity.ServerGuid, out LiveEntityRecord record)
|| !ReferenceEquals(record.WorldEntity, entity)
|| !ReferenceEquals(record.PhysicsBody, body))
{
return false;
}
// The retained pose owner follows the CPhysicsObj root even while its
// mesh is hidden. Hidden is presentation state, not logical teardown.
_updateEffectRoot(entity);
// EffectPoseChanged observers run synchronously and may delete this
// incarnation (or replace the GUID) before returning. Do not let the
// stale root restore collision shadows for an owner which no longer
// exists. This is the same callback-boundary lifetime rule used by the
// live schedulers and movement controllers.
if (!ReferenceEquals(_runtime(), runtime)
|| !runtime.TryGetRecord(entity.ServerGuid, out LiveEntityRecord current)
|| !ReferenceEquals(current, record)
|| !ReferenceEquals(current.WorldEntity, entity)
|| !ReferenceEquals(current.PhysicsBody, body))
{
return false;
}
// CObjCell::hide_object removes shadows. Never let a later static
// animation tick re-register collision for a hidden, withdrawn, or
// pending projection; UnHide/re-entry owns restoration.
if (record.ProjectionKind is not LiveEntityProjectionKind.World
|| !record.IsSpatiallyProjected
|| !record.IsSpatiallyVisible
|| runtime.IsHidden(entity.ServerGuid))
{
return true;
}
uint cellId = body.CellPosition.ObjCellId != 0
? body.CellPosition.ObjCellId
: record.FullCellId;
(int centerX, int centerY) = _liveCenter();
ShadowPositionSynchronizer.Sync(
_shadows,
entity.Id,
body.Position,
body.Orientation,
cellId,
centerX,
centerY);
return true;
}
}