refactor(streaming): compose landblock presentation transaction
Own render, physics, DAT-static presentation, and retained static-resource rebinds behind retryable receipts so a failed publication resumes its exact unfinished suffix without replaying retail create-time defaults. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
e1110f7e54
commit
ea0da8c8ae
15 changed files with 1744 additions and 89 deletions
|
|
@ -20,17 +20,32 @@ public sealed class LandblockRenderPublication
|
|||
internal LandblockRenderPublication(
|
||||
object owner,
|
||||
LandblockBuild build,
|
||||
LandblockMeshData meshData,
|
||||
Vector3 origin,
|
||||
Dictionary<uint, LoadedCell> visibilityCells)
|
||||
Dictionary<uint, LoadedCell> visibilityCells,
|
||||
Vector3 aabbMin,
|
||||
Vector3 aabbMax)
|
||||
{
|
||||
Owner = owner;
|
||||
Build = build;
|
||||
MeshData = meshData;
|
||||
Origin = origin;
|
||||
_visibilityCells = new ReadOnlyDictionary<uint, LoadedCell>(visibilityCells);
|
||||
AabbMin = aabbMin;
|
||||
AabbMax = aabbMax;
|
||||
}
|
||||
|
||||
internal object Owner { get; }
|
||||
internal LandblockBuild Build { get; }
|
||||
internal LandblockMeshData MeshData { get; }
|
||||
internal Vector3 AabbMin { get; }
|
||||
internal Vector3 AabbMax { get; }
|
||||
internal bool TerrainCommitted { get; set; }
|
||||
internal bool VisibilityCommitted { get; set; }
|
||||
internal bool AabbCommitted { get; set; }
|
||||
internal bool BeginCommitted { get; set; }
|
||||
internal bool BuildingRegistryCommitted { get; set; }
|
||||
internal bool EnvCellsCommitted { get; set; }
|
||||
internal bool CompletionCommitted { get; set; }
|
||||
|
||||
public uint LandblockId => Build.Landblock.LandblockId;
|
||||
|
|
@ -137,6 +152,22 @@ public sealed class LandblockRenderPublisher
|
|||
public LandblockRenderPublication BeginPublication(
|
||||
LandblockBuild build,
|
||||
LandblockMeshData meshData)
|
||||
{
|
||||
LandblockRenderPublication publication = PreparePublication(
|
||||
build,
|
||||
meshData);
|
||||
BeginPublication(publication);
|
||||
return publication;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates and captures the immutable render transaction before its
|
||||
/// first external mutation. A coordinator retains this receipt when an
|
||||
/// exact-replacement callback fails, then resumes the same publication.
|
||||
/// </summary>
|
||||
public LandblockRenderPublication PreparePublication(
|
||||
LandblockBuild build,
|
||||
LandblockMeshData meshData)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(build);
|
||||
ArgumentNullException.ThrowIfNull(meshData);
|
||||
|
|
@ -145,7 +176,6 @@ public sealed class LandblockRenderPublisher
|
|||
"Render publication requires the build's captured origin.",
|
||||
nameof(build));
|
||||
|
||||
long beginStarted = Stopwatch.GetTimestamp();
|
||||
uint landblockId = build.Landblock.LandblockId;
|
||||
if (build.EnvCells is { } candidateEnvCells &&
|
||||
candidateEnvCells.LandblockId != landblockId)
|
||||
|
|
@ -157,28 +187,64 @@ public sealed class LandblockRenderPublisher
|
|||
|
||||
Vector3 origin = ComputeOrigin(landblockId, build.Origin);
|
||||
|
||||
long terrainStarted = Stopwatch.GetTimestamp();
|
||||
_publishTerrain(landblockId, meshData, origin);
|
||||
_terrainPublishTicks += Stopwatch.GetTimestamp() - terrainStarted;
|
||||
|
||||
var committedCells = new Dictionary<uint, LoadedCell>();
|
||||
var visibilityCells = new Dictionary<uint, LoadedCell>();
|
||||
if (build.EnvCells is { } envCells)
|
||||
{
|
||||
_cellVisibility.CommitLandblock(landblockId, envCells.VisibilityCells);
|
||||
foreach (LoadedCell cell in envCells.VisibilityCells)
|
||||
committedCells[cell.CellId] = cell;
|
||||
visibilityCells[cell.CellId] = cell;
|
||||
}
|
||||
|
||||
(Vector3 aabbMin, Vector3 aabbMax) = ComputeAabb(meshData, origin);
|
||||
_worldState.SetLandblockAabb(landblockId, aabbMin, aabbMax);
|
||||
|
||||
_beginCount++;
|
||||
_beginPublishTicks += Stopwatch.GetTimestamp() - beginStarted;
|
||||
return new LandblockRenderPublication(
|
||||
_receiptOwner,
|
||||
build,
|
||||
meshData,
|
||||
origin,
|
||||
committedCells);
|
||||
visibilityCells,
|
||||
aabbMin,
|
||||
aabbMax);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances the render prefix from a retained receipt. Every operation is
|
||||
/// exact replacement, so a callback failure can safely retry this stage.
|
||||
/// </summary>
|
||||
public void BeginPublication(LandblockRenderPublication publication)
|
||||
{
|
||||
ValidateReceipt(publication);
|
||||
if (publication.BeginCommitted)
|
||||
return;
|
||||
|
||||
long beginStarted = Stopwatch.GetTimestamp();
|
||||
LandblockBuild build = publication.Build;
|
||||
uint landblockId = publication.LandblockId;
|
||||
|
||||
if (!publication.TerrainCommitted)
|
||||
{
|
||||
long terrainStarted = Stopwatch.GetTimestamp();
|
||||
_publishTerrain(landblockId, publication.MeshData, publication.Origin);
|
||||
_terrainPublishTicks += Stopwatch.GetTimestamp() - terrainStarted;
|
||||
publication.TerrainCommitted = true;
|
||||
}
|
||||
|
||||
if (!publication.VisibilityCommitted)
|
||||
{
|
||||
if (build.EnvCells is { } envCells)
|
||||
_cellVisibility.CommitLandblock(landblockId, envCells.VisibilityCells);
|
||||
publication.VisibilityCommitted = true;
|
||||
}
|
||||
|
||||
if (!publication.AabbCommitted)
|
||||
{
|
||||
_worldState.SetLandblockAabb(
|
||||
landblockId,
|
||||
publication.AabbMin,
|
||||
publication.AabbMax);
|
||||
publication.AabbCommitted = true;
|
||||
}
|
||||
|
||||
publication.BeginCommitted = true;
|
||||
_beginCount++;
|
||||
_beginPublishTicks += Stopwatch.GetTimestamp() - beginStarted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -188,28 +254,35 @@ public sealed class LandblockRenderPublisher
|
|||
/// </summary>
|
||||
public void CompletePublication(LandblockRenderPublication publication)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(publication);
|
||||
if (!ReferenceEquals(publication.Owner, _receiptOwner))
|
||||
throw new ArgumentException(
|
||||
"The render publication receipt belongs to another publisher.",
|
||||
nameof(publication));
|
||||
ValidateReceipt(publication);
|
||||
if (!publication.BeginCommitted)
|
||||
throw new InvalidOperationException(
|
||||
"Render publication cannot complete before its prefix commits.");
|
||||
if (publication.CompletionCommitted)
|
||||
return;
|
||||
|
||||
long started = Stopwatch.GetTimestamp();
|
||||
LandblockBuild build = publication.Build;
|
||||
uint landblockId = publication.LandblockId;
|
||||
if (build.Landblock.PhysicsDats?.Info is { } info)
|
||||
if (!publication.BuildingRegistryCommitted)
|
||||
{
|
||||
uint registryKey = landblockId & 0xFFFF0000u;
|
||||
_buildingRegistries[registryKey] = BuildingLoader.Build(
|
||||
info,
|
||||
landblockId,
|
||||
publication.VisibilityCells);
|
||||
if (build.Landblock.PhysicsDats?.Info is { } info)
|
||||
{
|
||||
uint registryKey = landblockId & 0xFFFF0000u;
|
||||
_buildingRegistries[registryKey] = BuildingLoader.Build(
|
||||
info,
|
||||
landblockId,
|
||||
publication.VisibilityCells);
|
||||
}
|
||||
publication.BuildingRegistryCommitted = true;
|
||||
}
|
||||
|
||||
if (build.EnvCells is { } envCells)
|
||||
_commitEnvCells?.Invoke(envCells);
|
||||
if (!publication.EnvCellsCommitted)
|
||||
{
|
||||
if (build.EnvCells is { } envCells)
|
||||
_commitEnvCells?.Invoke(envCells);
|
||||
publication.EnvCellsCommitted = true;
|
||||
}
|
||||
|
||||
publication.CompletionCommitted = true;
|
||||
_completeCount++;
|
||||
|
|
@ -287,4 +360,15 @@ public sealed class LandblockRenderPublisher
|
|||
new Vector3(origin.X, origin.Y, zMin),
|
||||
new Vector3(origin.X + 192f, origin.Y + 192f, zMax));
|
||||
}
|
||||
|
||||
private void ValidateReceipt(LandblockRenderPublication publication)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(publication);
|
||||
if (!ReferenceEquals(publication.Owner, _receiptOwner))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The render publication receipt belongs to another publisher.",
|
||||
nameof(publication));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue