fix(physics): activate collision generations atomically
This commit is contained in:
parent
3e0f3b6206
commit
be94bc9b06
18 changed files with 1402 additions and 80 deletions
|
|
@ -153,13 +153,106 @@ public sealed class PhysicsEngine
|
|||
/// </summary>
|
||||
public ClientObjectTable? Objects { get; set; }
|
||||
|
||||
private sealed record LandblockPhysics(
|
||||
internal sealed record LandblockPhysics(
|
||||
TerrainSurface Terrain,
|
||||
IReadOnlyList<CellSurface> Cells,
|
||||
IReadOnlyList<PortalPlane> Portals,
|
||||
float WorldOffsetX,
|
||||
float WorldOffsetY);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an off-side collision world from the last complete generation.
|
||||
/// Streaming modifies this copy only; the active engine and its borrowed
|
||||
/// cache/registry identities remain stable until Runtime commits.
|
||||
/// </summary>
|
||||
internal PhysicsEngine CreateCollisionStagingCopy(
|
||||
PhysicsDataCache stagingCache)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(stagingCache);
|
||||
var staging = new PhysicsEngine
|
||||
{
|
||||
DataCache = stagingCache,
|
||||
Objects = Objects,
|
||||
};
|
||||
foreach ((uint id, LandblockPhysics landblock) in _landblocks)
|
||||
staging._landblocks[id] = landblock;
|
||||
staging.ShadowObjects.CopyCollisionStateFrom(
|
||||
ShadowObjects,
|
||||
stagingCache);
|
||||
return staging;
|
||||
}
|
||||
|
||||
internal PreparedPhysicsEngineLandblock PrepareLandblockReplacement(
|
||||
PhysicsEngine staging,
|
||||
uint landblockId,
|
||||
ReadOnlySpan<uint> gfxObjectIds,
|
||||
ReadOnlySpan<uint> setupIds,
|
||||
IReadOnlyDictionary<uint, ulong> expectedDynamicVersions)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(staging);
|
||||
uint canonical = (landblockId & 0xFFFF0000u) | 0xFFFFu;
|
||||
if (!staging._landblocks.TryGetValue(
|
||||
canonical,
|
||||
out LandblockPhysics? landblock))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Staging collision generation has no landblock 0x{canonical:X8}.");
|
||||
}
|
||||
PhysicsDataCache stagingCache = staging.DataCache
|
||||
?? throw new InvalidOperationException(
|
||||
"Staging collision engine has no data cache.");
|
||||
return new PreparedPhysicsEngineLandblock(
|
||||
canonical,
|
||||
landblock,
|
||||
stagingCache.PrepareLandblockReplacement(
|
||||
canonical,
|
||||
gfxObjectIds,
|
||||
setupIds),
|
||||
ShadowObjects.PrepareLandblockReplacement(
|
||||
staging.ShadowObjects,
|
||||
canonical,
|
||||
expectedDynamicVersions));
|
||||
}
|
||||
|
||||
internal bool ValidateLandblockReplacement(
|
||||
PreparedPhysicsEngineLandblock replacement) =>
|
||||
ShadowObjects.ValidateLandblockReplacement(replacement.Shadows);
|
||||
|
||||
internal void CommitLandblockReplacement(
|
||||
PreparedPhysicsEngineLandblock replacement)
|
||||
{
|
||||
if (!ValidateLandblockReplacement(replacement))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Collision generation changed after it was sealed.");
|
||||
}
|
||||
(DataCache ?? throw new InvalidOperationException(
|
||||
"Active collision engine has no data cache."))
|
||||
.CommitLandblockReplacement(replacement.DataCache);
|
||||
_landblocks[replacement.LandblockId] = replacement.Landblock;
|
||||
ShadowObjects.CommitLandblockReplacement(replacement.Shadows);
|
||||
}
|
||||
|
||||
internal sealed class PreparedPhysicsEngineLandblock
|
||||
{
|
||||
internal PreparedPhysicsEngineLandblock(
|
||||
uint landblockId,
|
||||
LandblockPhysics landblock,
|
||||
PreparedPhysicsDataCacheLandblock dataCache,
|
||||
ShadowObjectRegistry.PreparedLandblockShadowReplacement shadows)
|
||||
{
|
||||
LandblockId = landblockId;
|
||||
Landblock = landblock;
|
||||
DataCache = dataCache;
|
||||
Shadows = shadows;
|
||||
}
|
||||
|
||||
internal uint LandblockId { get; }
|
||||
internal LandblockPhysics Landblock { get; }
|
||||
internal PreparedPhysicsDataCacheLandblock DataCache { get; }
|
||||
internal ShadowObjectRegistry.PreparedLandblockShadowReplacement Shadows { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a landblock with its terrain surface, indoor cells, portal
|
||||
/// planes, and world-space origin offset.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue