feat(streaming): shadow-publish flat collision assets

Carry one immutable prepared collision closure with each accepted near-tier generation and install graph plus flat views through the same retained publication receipt. Apply the same strict package-only rule to live entities, add exact sampled graph-authoritative comparison artifacts and lifecycle counters, and prove cancellation, demotion, rehydrate, revisit, teardown, reconnect, and the nine-stop route with 14,064 zero-mismatch samples.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-25 16:38:54 +02:00
parent f7ff9f4eea
commit d9446030e6
32 changed files with 2777 additions and 92 deletions

View file

@ -46,6 +46,8 @@ public sealed class LandblockPhysicsPublication
internal int BuildingCursor { get; set; }
internal bool BaseCommitted { get; set; }
internal int GfxCursor { get; set; }
internal uint[] SetupObjectIds { get; set; } = Array.Empty<uint>();
internal int SetupCursor { get; set; }
internal int PriorStaticCursor { get; set; }
internal int StaticCursor { get; set; }
internal int BspOwnerCount { get; set; }
@ -199,7 +201,7 @@ public sealed class LandblockPhysicsPublisher
build.Landblock.PhysicsDats ?? PhysicsDatBundle.Empty;
BuildingInfo[] buildings = datBundle.Info?.Buildings.ToArray()
?? Array.Empty<BuildingInfo>();
return new LandblockPhysicsPublication(
var publication = new LandblockPhysicsPublication(
_receiptOwner,
build,
origin,
@ -207,6 +209,10 @@ public sealed class LandblockPhysicsPublisher
buildings,
_physicsEngine.ShadowObjects.CaptureStaticOwnersForLandblock(
build.Landblock.LandblockId));
publication.SetupObjectIds = build.Collisions is { } collisions
? [.. collisions.SetupIds]
: datBundle.Setups.Keys.Order().ToArray();
return publication;
}
/// <summary>
@ -223,6 +229,14 @@ public sealed class LandblockPhysicsPublisher
IReadOnlyList<WorldEntity> entities =
publication.Build.Landblock.Entities;
if (publication.Build.Collisions is { } collisions)
{
publication.GfxObjectIds = [.. collisions.GfxObjIds];
publication.PreparationCursor = entities.Count;
publication.PreparationCommitted = true;
return true;
}
if (publication.PreparationCursor < entities.Count)
{
WorldEntity entity = entities[publication.PreparationCursor];
@ -389,10 +403,33 @@ public sealed class LandblockPhysicsPublisher
long cacheStarted = Stopwatch.GetTimestamp();
uint gfxObjectId = publication.GfxObjectIds[publication.GfxCursor];
if (datBundle.GfxObjs.TryGetValue(gfxObjectId, out var gfx))
_physicsDataCache.CacheGfxObj(gfxObjectId, gfx);
{
FlatGfxObjCollisionAsset? prepared = null;
publication.Build.Collisions?.GfxObjs.TryGetValue(
gfxObjectId,
out prepared);
_physicsDataCache.CacheGfxObj(
gfxObjectId,
gfx,
prepared);
}
publication.GfxCursor++;
_gfxCacheTicks += Stopwatch.GetTimestamp() - cacheStarted;
}
else if (publication.SetupCursor < publication.SetupObjectIds.Length)
{
uint setupId =
publication.SetupObjectIds[publication.SetupCursor];
if (datBundle.Setups.TryGetValue(setupId, out var setup))
{
FlatSetupCollision? prepared = null;
publication.Build.Collisions?.Setups.TryGetValue(
setupId,
out prepared);
_physicsDataCache.CacheSetup(setupId, setup, prepared);
}
publication.SetupCursor++;
}
else if (publication.PriorStaticCursor
< publication.PriorStaticOwnerIds.Length)
{
@ -486,11 +523,21 @@ public sealed class LandblockPhysicsPublisher
Matrix4x4.CreateFromQuaternion(rotation)
* Matrix4x4.CreateTranslation(cellOriginWorld);
FlatCellStructureCollisionAsset? preparedStructure = null;
FlatEnvCellTopology? preparedTopology = null;
publication.Build.Collisions?.CellStructures.TryGetValue(
envCellId,
out preparedStructure);
publication.Build.Collisions?.EnvCells.TryGetValue(
envCellId,
out preparedTopology);
_physicsDataCache.CacheCellStruct(
envCellId,
envCell,
cellStruct,
physicsCellTransform);
physicsCellTransform,
preparedStructure,
preparedTopology);
var worldVertices = new Dictionary<ushort, Vector3>(
cellStruct.VertexArray.Vertices.Count);