fix(physics): seal collision generations before activation

This commit is contained in:
Erik 2026-07-31 15:53:05 +02:00
parent be94bc9b06
commit d94145e6b8
15 changed files with 1556 additions and 410 deletions

View file

@ -21,6 +21,7 @@ public sealed class LandblockPhysicsPublication : IDisposable
uint currentCellId,
BuildingInfo[] buildings,
uint[] priorStaticOwnerIds,
RuntimePhysicsState physics,
RuntimeCollisionAdmission collisionAdmission,
PreparedLandblockCollisionGeneration preparedGeneration)
{
@ -30,6 +31,7 @@ public sealed class LandblockPhysicsPublication : IDisposable
CurrentCellId = currentCellId;
Buildings = buildings;
PriorStaticOwnerIds = priorStaticOwnerIds;
Physics = physics;
CollisionAdmission = collisionAdmission;
PreparedGeneration = preparedGeneration;
}
@ -39,6 +41,7 @@ public sealed class LandblockPhysicsPublication : IDisposable
internal uint CurrentCellId { get; }
internal BuildingInfo[] Buildings { get; }
internal uint[] PriorStaticOwnerIds { get; }
internal RuntimePhysicsState Physics { get; }
internal RuntimeCollisionAdmission CollisionAdmission { get; }
internal PreparedLandblockCollisionGeneration PreparedGeneration { get; }
internal PhysicsDataCache StagingCache => PreparedGeneration.DataCache;
@ -63,16 +66,19 @@ public sealed class LandblockPhysicsPublication : IDisposable
internal int CylinderOwnerCount { get; set; }
internal int NoCollisionCount { get; set; }
internal int SceneryTried { get; set; }
internal uint[]? RefloodOwnerIds { get; set; }
internal IReadOnlyList<uint>? RefloodOwnerIds { get; set; }
internal int RefloodCursor { get; set; }
internal bool RefloodCommitted { get; set; }
internal bool SealCommitted { get; set; }
internal bool BeginCommitted { get; set; }
internal bool CompletionCommitted { get; set; }
public void Dispose()
{
if (!CompletionCommitted)
PreparedGeneration.Dispose();
Physics.CancelCollisionGeneration(
CollisionAdmission,
PreparedGeneration);
}
public uint LandblockId => Build.Landblock.LandblockId;
@ -217,23 +223,34 @@ public sealed class LandblockPhysicsPublisher
?? Array.Empty<BuildingInfo>();
RuntimeCollisionAdmission collisionAdmission =
_physics.BeginCollisionAdmission(build.Landblock.LandblockId);
var publication = new LandblockPhysicsPublication(
_receiptOwner,
build,
origin,
_physicsDataCache.CellGraph.CurrCell?.Id ?? 0u,
buildings,
_physicsEngine.ShadowObjects.CaptureStaticOwnersForLandblock(
build.Landblock.LandblockId),
collisionAdmission,
_physics.PrepareCollisionGeneration(collisionAdmission));
publication.SetupObjectIds = build.Collisions is { } collisions
? [.. collisions.SetupIds]
: datBundle.Setups.Keys.Order().ToArray();
publication.PreparedGeneration.SetAssetClosure(
publication.GfxObjectIds,
publication.SetupObjectIds);
return publication;
PreparedLandblockCollisionGeneration? prepared = null;
try
{
prepared = _physics.PrepareCollisionGeneration(collisionAdmission);
var publication = new LandblockPhysicsPublication(
_receiptOwner,
build,
origin,
_physicsDataCache.CellGraph.CurrCell?.Id ?? 0u,
buildings,
_physicsEngine.ShadowObjects.CaptureStaticOwnersForLandblock(
build.Landblock.LandblockId),
_physics,
collisionAdmission,
prepared);
publication.SetupObjectIds = build.Collisions is { } collisions
? [.. collisions.SetupIds]
: datBundle.Setups.Keys.Order().ToArray();
publication.PreparedGeneration.SetAssetClosure(
publication.GfxObjectIds,
publication.SetupObjectIds);
return publication;
}
catch
{
_physics.CancelCollisionGeneration(collisionAdmission, prepared);
throw;
}
}
/// <summary>
@ -481,15 +498,20 @@ public sealed class LandblockPhysicsPublisher
}
else if (publication.RefloodOwnerIds is null)
{
publication.RefloodOwnerIds =
_physics.CaptureCollisionDynamicOwners(
RuntimeCollisionOwnerCaptureStep capture =
_physics.AdvanceCollisionRetainedOwnerCapture(
publication.CollisionAdmission,
publication.PreparedGeneration);
if (capture.Completed)
{
publication.RefloodOwnerIds =
publication.PreparedGeneration.RetainedOwnerIds;
}
}
else if (publication.RefloodCursor
< publication.RefloodOwnerIds.Length)
< publication.RefloodOwnerIds.Count)
{
_physics.RefreshCollisionDynamicOwner(
_physics.RefreshCollisionRetainedOwner(
publication.CollisionAdmission,
publication.PreparedGeneration,
publication.RefloodOwnerIds[publication.RefloodCursor]);
@ -507,6 +529,25 @@ public sealed class LandblockPhysicsPublisher
LogMissingSceneryBounds(landblock, publication.StagingCache);
publication.RefloodCommitted = true;
}
else if (!publication.SealCommitted)
{
RuntimeCollisionSealStep seal =
_physics.AdvanceCollisionGenerationSeal(
publication.CollisionAdmission,
publication.PreparedGeneration);
if (seal.WorkUnits > 1)
{
throw new InvalidOperationException(
"Collision seal exceeded its one-unit publication budget.");
}
publication.SealCommitted = seal.Completed;
if (seal.Restarted)
{
publication.RefloodOwnerIds = null;
publication.RefloodCursor = 0;
publication.RefloodCommitted = false;
}
}
else
{
RuntimeCollisionGenerationCommit commit =
@ -515,9 +556,13 @@ public sealed class LandblockPhysicsPublisher
publication.PreparedGeneration);
if (!commit.Committed)
{
publication.RefloodOwnerIds = commit.DirtyDynamicOwnerIds;
_physics.RestartCollisionRetainedOwnerCapture(
publication.CollisionAdmission,
publication.PreparedGeneration);
publication.RefloodOwnerIds = null;
publication.RefloodCursor = 0;
publication.RefloodCommitted = false;
publication.SealCommitted = false;
_completePublishTicks += Stopwatch.GetTimestamp() - started;
return false;
}
@ -1011,5 +1056,11 @@ public sealed class LandblockPhysicsPublisher
"The physics publication receipt belongs to another publisher.",
nameof(publication));
}
if (!publication.CompletionCommitted)
{
ObjectDisposedException.ThrowIf(
publication.PreparedGeneration.IsDisposed,
publication);
}
}
}

View file

@ -731,13 +731,15 @@ public sealed class LandblockPresentationPipeline
< transaction.Build.Landblock.Entities.Count
? 1
: transaction.PhysicsPublication
.RefloodOwnerIds is not null
&& transaction.PhysicsPublication
.RefloodOwnerIds is null
|| transaction.PhysicsPublication
.RefloodCursor
< transaction.PhysicsPublication
.RefloodOwnerIds.Length
? 1
: 0;
.RefloodOwnerIds.Count
|| !transaction.PhysicsPublication
.SealCommitted
? 1
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),