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:
parent
f7ff9f4eea
commit
d9446030e6
32 changed files with 2777 additions and 92 deletions
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using AcDream.App.Streaming;
|
||||
|
|
@ -94,6 +95,254 @@ public sealed class LandblockPhysicsPublisherTests
|
|||
Assert.Equal(0x01000077u, building.ModelId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DualPublication_InstallsExactPreparedClosureAndReleasesCellOwner()
|
||||
{
|
||||
const uint envCellId = 0xA9B40100u;
|
||||
const uint gfxObjId = 0x01000077u;
|
||||
var fixture = Fixture();
|
||||
PhysicsDatBundle baseBundle =
|
||||
CellPortalAndBuildingBundle(envCellId);
|
||||
DatReaderWriter.DBObjs.Environment environment =
|
||||
Assert.Single(baseBundle.Environments.Values);
|
||||
CellStruct cellStruct = Assert.Single(environment.Cells.Values);
|
||||
cellStruct.PhysicsBSP = new PhysicsBSPTree
|
||||
{
|
||||
Root = new PhysicsBSPNode
|
||||
{
|
||||
Type = DatReaderWriter.Enums.BSPNodeType.Leaf,
|
||||
BoundingSphere = new Sphere
|
||||
{
|
||||
Origin = Vector3.Zero,
|
||||
Radius = 10f,
|
||||
},
|
||||
},
|
||||
};
|
||||
cellStruct.CellBSP = new CellBSPTree
|
||||
{
|
||||
Root = new CellBSPNode
|
||||
{
|
||||
Type = DatReaderWriter.Enums.BSPNodeType.Leaf,
|
||||
LeafIndex = 1,
|
||||
},
|
||||
};
|
||||
EnvCell envCell = baseBundle.EnvCells[envCellId];
|
||||
var setup = new Setup();
|
||||
setup.CylSpheres.Add(new CylSphere
|
||||
{
|
||||
Origin = new Vector3(0f, 0f, 0.6f),
|
||||
Radius = 0.4f,
|
||||
Height = 1.2f,
|
||||
});
|
||||
GfxObj gfx = PhysicsGfx();
|
||||
var bundle = new PhysicsDatBundle(
|
||||
baseBundle.Info,
|
||||
baseBundle.EnvCells,
|
||||
baseBundle.Environments,
|
||||
new Dictionary<uint, Setup> { [SetupId] = setup },
|
||||
new Dictionary<uint, GfxObj> { [gfxObjId] = gfx });
|
||||
FlatGfxObjCollisionAsset flatGfx =
|
||||
FlatCollisionAssetBuilder.FlattenGfxObj(gfx);
|
||||
FlatSetupCollision flatSetup =
|
||||
FlatCollisionAssetBuilder.FlattenSetup(setup);
|
||||
FlatCellStructureCollisionAsset flatCell =
|
||||
FlatCollisionAssetBuilder.FlattenCellStructure(cellStruct);
|
||||
FlatEnvCellTopology flatTopology =
|
||||
FlatCollisionAssetBuilder.FlattenEnvCellTopology(
|
||||
envCellId,
|
||||
envCell,
|
||||
flatCell.PortalPolygons);
|
||||
var collisions = new LandblockCollisionBuild(
|
||||
ImmutableDictionary<uint, FlatGfxObjCollisionAsset>.Empty
|
||||
.Add(gfxObjId, flatGfx),
|
||||
ImmutableDictionary<uint, FlatSetupCollision>.Empty
|
||||
.Add(SetupId, flatSetup),
|
||||
ImmutableDictionary<uint, FlatCellStructureCollisionAsset>.Empty
|
||||
.Add(envCellId, flatCell),
|
||||
ImmutableDictionary<uint, FlatEnvCellTopology>.Empty
|
||||
.Add(envCellId, flatTopology),
|
||||
[gfxObjId],
|
||||
[SetupId],
|
||||
[envCellId]);
|
||||
WorldEntity entity = new()
|
||||
{
|
||||
Id = 0x80A9B401u,
|
||||
SourceGfxObjOrSetupId = SetupId,
|
||||
Position = new Vector3(12f, 12f, 0f),
|
||||
Rotation = Quaternion.Identity,
|
||||
MeshRefs = [new MeshRef(gfxObjId, Matrix4x4.Identity)],
|
||||
};
|
||||
|
||||
Publish(
|
||||
fixture.Publisher,
|
||||
Build(
|
||||
FirstLandblock,
|
||||
[entity],
|
||||
bundle,
|
||||
collisions: collisions));
|
||||
|
||||
Assert.Same(flatGfx, fixture.Cache.GetFlatGfxObj(gfxObjId));
|
||||
Assert.Same(
|
||||
flatGfx.PhysicsBsp,
|
||||
fixture.Cache.GetGfxObj(gfxObjId)!.FlatPhysicsBsp);
|
||||
Assert.Same(flatSetup, fixture.Cache.GetFlatSetup(SetupId));
|
||||
Assert.Same(
|
||||
flatSetup,
|
||||
fixture.Cache.GetSetup(SetupId)!.FlatCollision);
|
||||
Assert.Same(flatCell, fixture.Cache.GetFlatCellStruct(envCellId));
|
||||
Assert.Same(flatTopology, fixture.Cache.GetFlatEnvCell(envCellId));
|
||||
Assert.Same(
|
||||
flatCell.PhysicsBsp,
|
||||
fixture.Cache.GetCellStruct(envCellId)!.FlatPhysicsBsp);
|
||||
|
||||
fixture.Publisher.RemoveLandblock(FirstLandblock);
|
||||
|
||||
Assert.Equal(0, fixture.Cache.CellStructCount);
|
||||
Assert.Equal(0, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(0, fixture.Cache.FlatEnvCellCount);
|
||||
Assert.Equal(0, fixture.Engine.ShadowObjects.RetainedRegistrationCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DualPublication_DemotionRehydrateRevisitAndRemovalOwnFlatCellsExactlyOnce()
|
||||
{
|
||||
const uint firstCellId = 0xA9B40100u;
|
||||
const uint adjacentCellId = 0xAAB40100u;
|
||||
var fixture = Fixture();
|
||||
PhysicsDatBundle firstBundle =
|
||||
CellPortalAndBuildingBundle(firstCellId);
|
||||
PhysicsDatBundle adjacentBundle =
|
||||
CellPortalAndBuildingBundle(adjacentCellId);
|
||||
LandblockCollisionBuild firstCollision =
|
||||
FlatCellClosure(firstBundle, firstCellId);
|
||||
LandblockCollisionBuild adjacentCollision =
|
||||
FlatCellClosure(adjacentBundle, adjacentCellId);
|
||||
|
||||
Publish(
|
||||
fixture.Publisher,
|
||||
Build(
|
||||
FirstLandblock,
|
||||
bundle: firstBundle,
|
||||
collisions: firstCollision));
|
||||
Publish(
|
||||
fixture.Publisher,
|
||||
Build(
|
||||
AdjacentLandblock,
|
||||
bundle: adjacentBundle,
|
||||
collisions: adjacentCollision));
|
||||
|
||||
Assert.Same(
|
||||
firstCollision.CellStructures[firstCellId],
|
||||
fixture.Cache.GetFlatCellStruct(firstCellId));
|
||||
Assert.Same(
|
||||
adjacentCollision.CellStructures[adjacentCellId],
|
||||
fixture.Cache.GetFlatCellStruct(adjacentCellId));
|
||||
Assert.Equal(2, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(2, fixture.Cache.FlatEnvCellCount);
|
||||
|
||||
fixture.Publisher.DemoteToTerrain(FirstLandblock);
|
||||
|
||||
Assert.Null(fixture.Cache.GetFlatCellStruct(firstCellId));
|
||||
Assert.Null(fixture.Cache.GetFlatEnvCell(firstCellId));
|
||||
Assert.Same(
|
||||
adjacentCollision.CellStructures[adjacentCellId],
|
||||
fixture.Cache.GetFlatCellStruct(adjacentCellId));
|
||||
Assert.Equal(1, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(1, fixture.Cache.FlatEnvCellCount);
|
||||
|
||||
LandblockCollisionBuild rehydratedCollision =
|
||||
FlatCellClosure(firstBundle, firstCellId);
|
||||
Publish(
|
||||
fixture.Publisher,
|
||||
Build(
|
||||
FirstLandblock,
|
||||
bundle: firstBundle,
|
||||
collisions: rehydratedCollision));
|
||||
|
||||
Assert.Same(
|
||||
rehydratedCollision.CellStructures[firstCellId],
|
||||
fixture.Cache.GetFlatCellStruct(firstCellId));
|
||||
Assert.Equal(2, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(2, fixture.Cache.FlatEnvCellCount);
|
||||
|
||||
LandblockCollisionBuild revisitCollision =
|
||||
FlatCellClosure(firstBundle, firstCellId);
|
||||
Publish(
|
||||
fixture.Publisher,
|
||||
Build(
|
||||
FirstLandblock,
|
||||
bundle: firstBundle,
|
||||
collisions: revisitCollision));
|
||||
|
||||
Assert.Same(
|
||||
revisitCollision.CellStructures[firstCellId],
|
||||
fixture.Cache.GetFlatCellStruct(firstCellId));
|
||||
Assert.Equal(2, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(2, fixture.Cache.FlatEnvCellCount);
|
||||
|
||||
fixture.Publisher.RemoveLandblock(FirstLandblock);
|
||||
fixture.Publisher.RemoveLandblock(AdjacentLandblock);
|
||||
|
||||
Assert.Equal(0, fixture.Cache.CellStructCount);
|
||||
Assert.Equal(0, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(0, fixture.Cache.FlatEnvCellCount);
|
||||
Assert.Equal(0, fixture.Engine.LandblockCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DualPublication_CancelledPreparedReceiptPublishesNeitherView()
|
||||
{
|
||||
const uint envCellId = 0xA9B40100u;
|
||||
var fixture = Fixture();
|
||||
PhysicsDatBundle bundle =
|
||||
CellPortalAndBuildingBundle(envCellId);
|
||||
CellStruct sourceCell =
|
||||
bundle.Environments.Values.Single().Cells.Values.Single();
|
||||
sourceCell.PhysicsBSP = new PhysicsBSPTree
|
||||
{
|
||||
Root = new PhysicsBSPNode
|
||||
{
|
||||
Type = DatReaderWriter.Enums.BSPNodeType.Leaf,
|
||||
BoundingSphere = new Sphere
|
||||
{
|
||||
Origin = Vector3.Zero,
|
||||
Radius = 10f,
|
||||
},
|
||||
},
|
||||
};
|
||||
LandblockCollisionBuild collision =
|
||||
FlatCellClosure(bundle, envCellId);
|
||||
|
||||
LandblockPhysicsPublication cancelled =
|
||||
fixture.Publisher.PreparePublication(
|
||||
RenderReceipt(Build(
|
||||
FirstLandblock,
|
||||
bundle: bundle,
|
||||
collisions: collision)));
|
||||
|
||||
Assert.Equal(0, fixture.Engine.LandblockCount);
|
||||
Assert.Equal(0, fixture.Cache.CellStructCount);
|
||||
Assert.Equal(0, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(0, fixture.Cache.FlatEnvCellCount);
|
||||
Assert.Equal(0, fixture.Engine.ShadowObjects.RetainedRegistrationCount);
|
||||
|
||||
LandblockPhysicsPublication accepted =
|
||||
fixture.Publisher.PreparePublication(
|
||||
RenderReceipt(Build(
|
||||
FirstLandblock,
|
||||
bundle: bundle,
|
||||
collisions: collision)));
|
||||
fixture.Publisher.BeginPublication(accepted);
|
||||
fixture.Publisher.CompletePublication(accepted);
|
||||
|
||||
Assert.Same(
|
||||
collision.CellStructures[envCellId],
|
||||
fixture.Cache.GetFlatCellStruct(envCellId));
|
||||
Assert.Equal(1, fixture.Cache.CellStructCount);
|
||||
Assert.Equal(1, fixture.Cache.FlatCellStructCount);
|
||||
Assert.Equal(1, fixture.Cache.FlatEnvCellCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompletePublication_PhysicsBspSuppressesSetupCylinderFallback()
|
||||
{
|
||||
|
|
@ -592,6 +841,25 @@ public sealed class LandblockPhysicsPublisherTests
|
|||
Resolved = new Dictionary<ushort, ResolvedPolygon>(),
|
||||
};
|
||||
|
||||
private static GfxObj PhysicsGfx() => new()
|
||||
{
|
||||
Flags = DatReaderWriter.Enums.GfxObjFlags.HasPhysics,
|
||||
PhysicsBSP = new PhysicsBSPTree
|
||||
{
|
||||
Root = new PhysicsBSPNode
|
||||
{
|
||||
Type = DatReaderWriter.Enums.BSPNodeType.Leaf,
|
||||
BoundingSphere = new Sphere
|
||||
{
|
||||
Origin = Vector3.Zero,
|
||||
Radius = 1.25f,
|
||||
},
|
||||
},
|
||||
},
|
||||
PhysicsPolygons = new Dictionary<ushort, Polygon>(),
|
||||
VertexArray = new VertexArray(),
|
||||
};
|
||||
|
||||
private static PhysicsDatBundle CellPortalAndBuildingBundle(uint envCellId)
|
||||
{
|
||||
var cellStruct = new CellStruct
|
||||
|
|
@ -658,6 +926,33 @@ public sealed class LandblockPhysicsPublisherTests
|
|||
new Dictionary<uint, GfxObj>());
|
||||
}
|
||||
|
||||
private static LandblockCollisionBuild FlatCellClosure(
|
||||
PhysicsDatBundle bundle,
|
||||
uint envCellId)
|
||||
{
|
||||
EnvCell envCell = bundle.EnvCells[envCellId];
|
||||
DatReaderWriter.DBObjs.Environment environment =
|
||||
bundle.Environments.Values.Single();
|
||||
CellStruct cellStruct = environment.Cells[envCell.CellStructure];
|
||||
FlatCellStructureCollisionAsset flatCell =
|
||||
FlatCollisionAssetBuilder.FlattenCellStructure(cellStruct);
|
||||
FlatEnvCellTopology topology =
|
||||
FlatCollisionAssetBuilder.FlattenEnvCellTopology(
|
||||
envCellId,
|
||||
envCell,
|
||||
flatCell.PortalPolygons);
|
||||
return new LandblockCollisionBuild(
|
||||
ImmutableDictionary<uint, FlatGfxObjCollisionAsset>.Empty,
|
||||
ImmutableDictionary<uint, FlatSetupCollision>.Empty,
|
||||
ImmutableDictionary<uint, FlatCellStructureCollisionAsset>.Empty
|
||||
.Add(envCellId, flatCell),
|
||||
ImmutableDictionary<uint, FlatEnvCellTopology>.Empty
|
||||
.Add(envCellId, topology),
|
||||
[],
|
||||
[],
|
||||
[envCellId]);
|
||||
}
|
||||
|
||||
private static WorldEntity CylinderEntity(
|
||||
uint id,
|
||||
Vector3 position,
|
||||
|
|
@ -674,7 +969,8 @@ public sealed class LandblockPhysicsPublisherTests
|
|||
uint landblockId,
|
||||
IReadOnlyList<WorldEntity>? entities = null,
|
||||
PhysicsDatBundle? bundle = null,
|
||||
LandblockBuildOrigin? origin = null)
|
||||
LandblockBuildOrigin? origin = null,
|
||||
LandblockCollisionBuild? collisions = null)
|
||||
{
|
||||
var landblockInfo = new LandBlockInfo();
|
||||
PhysicsDatBundle physics = bundle ?? new PhysicsDatBundle(
|
||||
|
|
@ -689,7 +985,8 @@ public sealed class LandblockPhysicsPublisherTests
|
|||
FlatHeightmap(),
|
||||
entities ?? Array.Empty<WorldEntity>(),
|
||||
physics),
|
||||
Origin: origin ?? new LandblockBuildOrigin(0xA9, 0xB4));
|
||||
Origin: origin ?? new LandblockBuildOrigin(0xA9, 0xB4),
|
||||
Collisions: collisions);
|
||||
}
|
||||
|
||||
private static LandBlock FlatHeightmap() => new()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue