refactor(runtime): own per-session physics simulation

Move the sole PhysicsEngine, production cache, collision admissions, canonical bodies and hosts, remote components, ordinary/remote worksets, simulation, cell commits, and shadow synchronization under RuntimeEntityObjectLifetime. Keep App as the prepared-asset, animation-input, and render-projection adapter while preserving the named-retail update and collision order.

Add exact-incarnation, object-clock, callback-reentrancy, GUID-reuse, two-runtime isolation, source ownership, collision publication, and graphical projection coverage. Release build and the complete 8,588-test solution pass.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-26 13:39:57 +02:00
parent 0dc3bfdeff
commit 7e6033d0ad
39 changed files with 3685 additions and 1722 deletions

View file

@ -2,6 +2,7 @@ using System.Diagnostics;
using System.Numerics;
using AcDream.Core.Physics;
using AcDream.Core.World;
using AcDream.Runtime.Physics;
using DatReaderWriter.Types;
namespace AcDream.App.Streaming;
@ -19,7 +20,8 @@ public sealed class LandblockPhysicsPublication
Vector3 origin,
uint currentCellId,
BuildingInfo[] buildings,
uint[] priorStaticOwnerIds)
uint[] priorStaticOwnerIds,
RuntimeCollisionAdmission collisionAdmission)
{
Owner = owner;
Build = build;
@ -27,6 +29,7 @@ public sealed class LandblockPhysicsPublication
CurrentCellId = currentCellId;
Buildings = buildings;
PriorStaticOwnerIds = priorStaticOwnerIds;
CollisionAdmission = collisionAdmission;
}
internal object Owner { get; }
@ -34,6 +37,7 @@ public sealed class LandblockPhysicsPublication
internal uint CurrentCellId { get; }
internal BuildingInfo[] Buildings { get; }
internal uint[] PriorStaticOwnerIds { get; }
internal RuntimeCollisionAdmission CollisionAdmission { get; }
internal SortedSet<uint> GfxObjectIdSet { get; } = new();
internal uint[] GfxObjectIds { get; set; } = Array.Empty<uint>();
internal int PreparationCursor { get; set; }
@ -102,6 +106,7 @@ public readonly record struct LandblockPhysicsPublisherDiagnostics(
public sealed class LandblockPhysicsPublisher
{
private readonly object _receiptOwner = new();
private readonly RuntimePhysicsState _physics;
private readonly PhysicsEngine _physicsEngine;
private readonly PhysicsDataCache _physicsDataCache;
private readonly float[] _heightTable;
@ -121,21 +126,19 @@ public sealed class LandblockPhysicsPublisher
private long _fullRemovalCount;
public LandblockPhysicsPublisher(
PhysicsEngine physicsEngine,
RuntimePhysicsState physics,
float[] heightTable)
{
ArgumentNullException.ThrowIfNull(physicsEngine);
ArgumentNullException.ThrowIfNull(physics);
ArgumentNullException.ThrowIfNull(heightTable);
if (heightTable.Length < 256)
throw new ArgumentException(
"The retail terrain height table must contain at least 256 entries.",
nameof(heightTable));
_physicsEngine = physicsEngine;
_physicsDataCache = physicsEngine.DataCache
?? throw new ArgumentException(
"The physics engine must own its canonical data cache before publication wiring.",
nameof(physicsEngine));
_physics = physics;
_physicsEngine = physics.Engine;
_physicsDataCache = physics.DataCache;
_heightTable = (float[])heightTable.Clone();
}
@ -208,6 +211,8 @@ public sealed class LandblockPhysicsPublisher
_physicsDataCache.CellGraph.CurrCell?.Id ?? 0u,
buildings,
_physicsEngine.ShadowObjects.CaptureStaticOwnersForLandblock(
build.Landblock.LandblockId),
_physics.BeginCollisionAdmission(
build.Landblock.LandblockId));
publication.SetupObjectIds = build.Collisions is { } collisions
? [.. collisions.SetupIds]
@ -332,18 +337,16 @@ public sealed class LandblockPhysicsPublisher
}
else if (!publication.BaseCommitted)
{
_physicsEngine.AddLandblock(
landblock.LandblockId,
publication.TerrainSurface,
publication.CellSurfaces,
publication.PortalPlanes,
origin.X,
origin.Y);
if ((publication.CurrentCellId & 0xFFFF0000u)
== (landblock.LandblockId & 0xFFFF0000u))
{
_physicsEngine.UpdatePlayerCurrCell(publication.CurrentCellId);
}
_physics.AdmitCollisionAssets(
publication.CollisionAdmission,
new RuntimeLandblockCollisionAssets(
landblock.LandblockId,
publication.TerrainSurface,
publication.CellSurfaces.ToArray(),
publication.PortalPlanes.ToArray(),
origin.X,
origin.Y,
publication.CurrentCellId));
publication.BaseCommitted = true;
}
else
@ -481,6 +484,8 @@ public sealed class LandblockPhysicsPublisher
}
else
{
_physics.CompleteCollisionAdmission(
publication.CollisionAdmission);
_staticBspOwnerCount += publication.BspOwnerCount;
_staticCylinderOwnerCount += publication.CylinderOwnerCount;
publication.CompletionCommitted = true;
@ -493,13 +498,13 @@ public sealed class LandblockPhysicsPublisher
public void DemoteToTerrain(uint landblockId)
{
_physicsEngine.DemoteLandblockToTerrain(landblockId);
_physics.DemoteCollisionToTerrain(landblockId);
_demotionCount++;
}
public void RemoveLandblock(uint landblockId)
{
_physicsEngine.RemoveLandblock(landblockId);
_physics.WithdrawCollision(landblockId);
_fullRemovalCount++;
}