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:
Erik 2026-07-21 21:45:16 +02:00
parent e1110f7e54
commit ea0da8c8ae
15 changed files with 1744 additions and 89 deletions

View file

@ -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));
}
}
}