using AcDream.App.Rendering.Wb;
using AcDream.App.Rendering.Scene;
using AcDream.Core.Terrain;
using AcDream.Core.World;
namespace AcDream.App.Streaming;
///
/// Read-only presentation state needed by render traversal and diagnostics.
/// Keeping this projection on the pipeline prevents the composition root from
/// retaining each concrete publication owner independently.
///
public readonly record struct LandblockPresentationDiagnostics(
LandblockRenderPublisherDiagnostics Render,
LandblockPhysicsPublisherDiagnostics Physics,
LandblockStaticPresentationDiagnostics Statics);
internal readonly record struct LandblockPublicationAdvance(
bool Completed,
bool Progressed);
///
/// Render-thread transaction coordinator for one accepted landblock streaming
/// result. Streaming policy (desired tier, generation, priority, and apply
/// budget) remains in ; spatial buckets remain
/// in . This owner fixes the presentation ordering
/// independently of those policies. The concrete render, physics, and static
/// publishers remain private implementation participants behind this boundary.
///
///
/// Retail establishes cells/buildings before static objects in
/// LScape::grab_visible_cells @ 0x00504EC0. Its release path separates
/// CLandBlock::release_all @ 0x0052FCF0 (objects and visible cells) from
/// later destruction in CLandBlock::Destroy @ 0x0052FAA0 (static objects
/// and buildings). Acdream's detach-first retry ledger is the existing
/// asynchronous lifetime adaptation. Production always uses the focused owner
/// graph; the internal delegate constructor exists only for hermetic policy and
/// retry characterization tests.
///
public sealed class LandblockPresentationPipeline
{
private enum PublicationKind : byte
{
Loaded,
PromoteExisting,
PromoteSelfContained,
Far,
}
private sealed class PublicationTransaction
{
public required PublicationKind Kind;
public required LandblockBuild Build;
public required LandblockMeshData MeshData;
public required uint LandblockId;
public required LandblockStreamCostEstimate Cost;
public LandblockStreamTier Tier;
public bool PresentationCommitted;
public LandblockRenderPublication? RenderPublication;
public LandblockPhysicsPublication? PhysicsPublication;
public LandblockStaticPresentationPublication? StaticPublication;
public bool SpatialCommitted;
public GpuLandblockSpatialPublication? SpatialPublication;
public bool SpatialPresentationCommitted;
public bool EnvCellReplayCommitted;
public bool LiveRecoveryCommitted;
}
private readonly Action?
_publishBeforeSpatialCommit;
private readonly LandblockRenderPublisher? _renderPublisher;
private readonly LandblockPhysicsPublisher? _physicsPublisher;
private readonly LandblockStaticPresentationPublisher? _staticPublisher;
private readonly GpuWorldState _state;
private readonly Action? _onLandblockLoaded;
private readonly Action? _ensureEnvCellMeshes;
private readonly IRenderStaticProjectionJournalSink? _staticProjectionSink;
private readonly LandblockRetirementCoordinator _retirements;
private readonly Dictionary _publications =
new(ReferenceEqualityComparer.Instance);
internal LandblockPresentationPipeline(
Action publishBeforeSpatialCommit,
GpuWorldState state,
Action? onLandblockLoaded = null,
Action? ensureEnvCellMeshes = null,
LandblockRetirementCoordinator? retirementCoordinator = null,
Action? removeTerrain = null,
Action? demoteNearLayer = null,
IRenderStaticProjectionJournalSink? staticProjectionSink = null)
{
ArgumentNullException.ThrowIfNull(publishBeforeSpatialCommit);
ArgumentNullException.ThrowIfNull(state);
_publishBeforeSpatialCommit = publishBeforeSpatialCommit;
_state = state;
_onLandblockLoaded = onLandblockLoaded;
_ensureEnvCellMeshes = ensureEnvCellMeshes;
_staticProjectionSink = staticProjectionSink;
if (retirementCoordinator is not null
&& !retirementCoordinator.MatchesState(_state))
{
throw new ArgumentException(
"The retirement coordinator must own the pipeline's world state.",
nameof(retirementCoordinator));
}
_retirements = retirementCoordinator
?? LandblockRetirementCoordinator.CreateLegacy(
state,
removeTerrain,
demoteNearLayer);
}
///
/// Production constructor. The pipeline retains each concrete owner
/// receipt before mutation and resumes only the unfinished exact stage
/// after a failure.
///
public LandblockPresentationPipeline(
LandblockRenderPublisher renderPublisher,
LandblockPhysicsPublisher physicsPublisher,
LandblockStaticPresentationPublisher staticPublisher,
GpuWorldState state,
LandblockPresentationRetirementOwner retirementOwner,
Action? onLandblockLoaded = null,
Action? ensureEnvCellMeshes = null)
: this(
renderPublisher,
physicsPublisher,
staticPublisher,
state,
retirementOwner,
onLandblockLoaded,
ensureEnvCellMeshes,
staticProjectionSink: null)
{
}
internal LandblockPresentationPipeline(
LandblockRenderPublisher renderPublisher,
LandblockPhysicsPublisher physicsPublisher,
LandblockStaticPresentationPublisher staticPublisher,
GpuWorldState state,
LandblockPresentationRetirementOwner retirementOwner,
Action? onLandblockLoaded,
Action? ensureEnvCellMeshes,
IRenderStaticProjectionJournalSink? staticProjectionSink)
{
_renderPublisher = renderPublisher
?? throw new ArgumentNullException(nameof(renderPublisher));
_physicsPublisher = physicsPublisher
?? throw new ArgumentNullException(nameof(physicsPublisher));
_staticPublisher = staticPublisher
?? throw new ArgumentNullException(nameof(staticPublisher));
_state = state ?? throw new ArgumentNullException(nameof(state));
if (!_renderPublisher.MatchesState(_state))
{
throw new ArgumentException(
"The render publisher must own the pipeline's world state.",
nameof(state));
}
ArgumentNullException.ThrowIfNull(retirementOwner);
if (!retirementOwner.Matches(
_renderPublisher,
_physicsPublisher,
_staticPublisher))
{
throw new ArgumentException(
"The retirement owner must reference the pipeline's concrete publishers.",
nameof(retirementOwner));
}
_onLandblockLoaded = onLandblockLoaded;
_ensureEnvCellMeshes = ensureEnvCellMeshes;
_staticProjectionSink = staticProjectionSink;
_retirements = LandblockRetirementCoordinator.CreateBudgeted(
state,
retirementOwner.AdvanceOne,
retirementOwner.Advance,
staticProjectionSink is null
? null
: staticProjectionSink.Retire);
}
///
/// Building visibility registries published by the render owner. Legacy
/// test pipelines have no render owner and therefore expose an empty view.
///
public IReadOnlyCollection BuildingRegistries =>
_renderPublisher?.BuildingRegistries ?? Array.Empty();
///
/// Cumulative diagnostics from the three concrete presentation owners.
///
public LandblockPresentationDiagnostics Diagnostics => new(
_renderPublisher?.Diagnostics ?? default,
_physicsPublisher?.Diagnostics ?? default,
_staticPublisher?.Diagnostics ?? default);
public int PendingRetirementCount => _retirements.PendingCount;
internal bool UsesBudgetedRetirementSteps => _retirements.UsesBudgetedSteps;
public int PendingPublicationCount => _publications.Count;
internal bool MatchesState(GpuWorldState state) =>
ReferenceEquals(_state, state);
public bool IsRetirementPending(uint landblockId) =>
_retirements.IsPending(landblockId);
public bool HasPendingPublication(LandblockStreamResult result)
{
ArgumentNullException.ThrowIfNull(result);
return _publications.ContainsKey(result);
}
public IReadOnlyList GetPendingPublicationResults() =>
_publications.Keys.ToArray();
public void ResumePublication(LandblockStreamResult result)
{
ArgumentNullException.ThrowIfNull(result);
if (!_publications.TryGetValue(result, out PublicationTransaction? transaction))
return;
Advance(result, transaction, meter: null, ensureProgress: false);
}
internal LandblockPublicationAdvance ResumePublication(
LandblockStreamResult result,
StreamingWorkMeter meter,
bool ensureProgress)
{
ArgumentNullException.ThrowIfNull(result);
ArgumentNullException.ThrowIfNull(meter);
if (!_publications.TryGetValue(result, out PublicationTransaction? transaction))
return new LandblockPublicationAdvance(Completed: true, Progressed: false);
return Advance(result, transaction, meter, ensureProgress);
}
public void AdvanceRetirements() => _retirements.Advance();
public void AdvanceRetirements(StreamingWorkMeter meter) =>
_retirements.Advance(meter);
internal void AdvancePriorityRetirement(
uint landblockId,
StreamingWorkMeter meter) =>
_retirements.AdvancePriority(landblockId, meter);
public void BeginFullRetirement(uint landblockId)
{
_retirements.BeginFull(landblockId);
if (_retirements.UsesBudgetedSteps)
_retirements.Advance();
}
public void BeginNearLayerRetirement(uint landblockId)
{
_retirements.BeginNearLayer(landblockId);
if (_retirements.UsesBudgetedSteps)
_retirements.Advance();
}
internal void EnqueueFullRetirement(uint landblockId) =>
_retirements.BeginFull(landblockId);
internal void EnqueueNearLayerRetirement(uint landblockId) =>
_retirements.BeginNearLayer(landblockId);
///
/// Withdraws the complete old spatial generation in one transaction, then
/// transfers its exact receipts to the ordinary budgeted retirement FIFO.
///
internal GpuWorldRecenterRetirement DetachAllForOriginRecenter()
{
GpuWorldRecenterRetirement detached =
_state.DetachAllForOriginRecenter();
Exception? adoptionFailure =
_retirements.AdoptDetachedFull(detached.Landblocks);
Exception? failure = (detached.ObserverFailure, adoptionFailure) switch
{
(null, null) => null,
({ } observer, null) => observer,
(null, { } adoption) => adoption,
({ } observer, { } adoption) => new AggregateException(
"Origin-recenter spatial observers and retirement adoption failed.",
observer,
adoption),
};
return detached with { ObserverFailure = failure };
}
public void PublishLoaded(LandblockStreamResult.Loaded loaded)
{
ArgumentNullException.ThrowIfNull(loaded);
PublicationTransaction transaction = GetOrCreate(
loaded,
PublicationKind.Loaded,
loaded.Build,
loaded.MeshData,
loaded.LandblockId,
loaded.Tier,
estimate: null);
Advance(loaded, transaction, meter: null, ensureProgress: false);
}
internal LandblockPublicationAdvance PublishLoaded(
LandblockStreamResult.Loaded loaded,
LandblockStreamCostEstimate estimate,
StreamingWorkMeter meter,
bool ensureProgress)
{
ArgumentNullException.ThrowIfNull(loaded);
ArgumentNullException.ThrowIfNull(meter);
PublicationTransaction transaction = GetOrCreate(
loaded,
PublicationKind.Loaded,
loaded.Build,
loaded.MeshData,
loaded.LandblockId,
loaded.Tier,
estimate);
return Advance(loaded, transaction, meter, ensureProgress);
}
public void PublishPromoted(
LandblockStreamResult.Promoted promoted,
bool mergeIntoExistingLandblock)
{
ArgumentNullException.ThrowIfNull(promoted);
PublicationKind kind = mergeIntoExistingLandblock
? PublicationKind.PromoteExisting
: PublicationKind.PromoteSelfContained;
PublicationTransaction transaction = GetOrCreate(
promoted,
kind,
promoted.Build,
promoted.MeshData,
promoted.LandblockId,
LandblockStreamTier.Near,
estimate: null);
Advance(promoted, transaction, meter: null, ensureProgress: false);
}
internal LandblockPublicationAdvance PublishPromoted(
LandblockStreamResult.Promoted promoted,
bool mergeIntoExistingLandblock,
LandblockStreamCostEstimate estimate,
StreamingWorkMeter meter,
bool ensureProgress)
{
ArgumentNullException.ThrowIfNull(promoted);
ArgumentNullException.ThrowIfNull(meter);
PublicationKind kind = mergeIntoExistingLandblock
? PublicationKind.PromoteExisting
: PublicationKind.PromoteSelfContained;
PublicationTransaction transaction = GetOrCreate(
promoted,
kind,
promoted.Build,
promoted.MeshData,
promoted.LandblockId,
LandblockStreamTier.Near,
estimate);
return Advance(promoted, transaction, meter, ensureProgress);
}
public void PublishAsFar(
LandblockStreamResult acceptedResult,
LandblockBuild completedBuild,
LandblockMeshData meshData)
{
ArgumentNullException.ThrowIfNull(acceptedResult);
ArgumentNullException.ThrowIfNull(completedBuild);
ArgumentNullException.ThrowIfNull(meshData);
if (!_publications.TryGetValue(acceptedResult, out PublicationTransaction? transaction))
{
LoadedLandblock completed = completedBuild.Landblock;
var farLandblock = new LoadedLandblock(
completed.LandblockId,
completed.Heightmap,
Array.Empty(),
PhysicsDatBundle.Empty);
transaction = new PublicationTransaction
{
Kind = PublicationKind.Far,
Build = new LandblockBuild(
farLandblock,
Origin: completedBuild.Origin),
MeshData = meshData,
LandblockId = farLandblock.LandblockId,
Cost = LandblockStreamResultCost.Estimate(
new LandblockBuild(
farLandblock,
Origin: completedBuild.Origin),
meshData),
Tier = LandblockStreamTier.Far,
};
_publications.Add(acceptedResult, transaction);
}
else if (transaction.Kind != PublicationKind.Far)
{
throw new InvalidOperationException(
$"Landblock publication 0x{transaction.LandblockId:X8} changed kind while pending.");
}
Advance(
acceptedResult,
transaction,
meter: null,
ensureProgress: false);
}
internal LandblockPublicationAdvance PublishAsFar(
LandblockStreamResult acceptedResult,
LandblockBuild completedBuild,
LandblockMeshData meshData,
StreamingWorkMeter meter,
bool ensureProgress)
{
ArgumentNullException.ThrowIfNull(acceptedResult);
ArgumentNullException.ThrowIfNull(completedBuild);
ArgumentNullException.ThrowIfNull(meshData);
ArgumentNullException.ThrowIfNull(meter);
if (!_publications.TryGetValue(
acceptedResult,
out PublicationTransaction? transaction))
{
LoadedLandblock completed = completedBuild.Landblock;
var farLandblock = new LoadedLandblock(
completed.LandblockId,
completed.Heightmap,
Array.Empty(),
PhysicsDatBundle.Empty);
LandblockBuild farBuild = new(
farLandblock,
Origin: completedBuild.Origin);
transaction = new PublicationTransaction
{
Kind = PublicationKind.Far,
Build = farBuild,
MeshData = meshData,
LandblockId = farLandblock.LandblockId,
Cost = LandblockStreamResultCost.Estimate(farBuild, meshData),
Tier = LandblockStreamTier.Far,
};
_publications.Add(acceptedResult, transaction);
}
else if (transaction.Kind != PublicationKind.Far)
{
throw new InvalidOperationException(
$"Landblock publication 0x{transaction.LandblockId:X8} changed kind while pending.");
}
return Advance(acceptedResult, transaction, meter, ensureProgress);
}
private PublicationTransaction GetOrCreate(
LandblockStreamResult result,
PublicationKind kind,
LandblockBuild build,
LandblockMeshData meshData,
uint landblockId,
LandblockStreamTier tier,
LandblockStreamCostEstimate? estimate)
{
if (_publications.TryGetValue(result, out PublicationTransaction? existing))
{
if (existing.Kind != kind
&& !(existing.Kind is PublicationKind.PromoteExisting
or PublicationKind.PromoteSelfContained
&& kind is PublicationKind.PromoteExisting
or PublicationKind.PromoteSelfContained))
{
throw new InvalidOperationException(
$"Landblock publication 0x{existing.LandblockId:X8} changed kind while pending.");
}
return existing;
}
var created = new PublicationTransaction
{
Kind = kind,
Build = build,
MeshData = meshData,
LandblockId = landblockId,
Cost = estimate
?? LandblockStreamResultCost.Estimate(build, meshData),
Tier = tier,
};
_publications.Add(result, created);
return created;
}
private LandblockPublicationAdvance Advance(
LandblockStreamResult result,
PublicationTransaction transaction,
StreamingWorkMeter? meter,
bool ensureProgress)
{
bool progressed = false;
bool TryRun(
StreamingWorkCost cost,
string stage,
Action operation)
{
if (meter is null)
{
operation();
progressed = true;
return true;
}
StreamingWorkAdmission admission = meter.TryReserve(
cost,
stage,
ensureProgress && !progressed);
if (admission == StreamingWorkAdmission.Yielded)
return false;
try
{
operation();
meter.Complete();
progressed = true;
return true;
}
catch
{
meter.Fail();
throw;
}
}
if (!transaction.PresentationCommitted)
{
if (_renderPublisher is not null
&& _physicsPublisher is not null
&& _staticPublisher is not null)
{
while (transaction.RenderPublication is null)
{
if (!TryRun(
new StreamingWorkCost(EntityOperations: 1),
"publication-prepare-render",
() => transaction.RenderPublication =
_renderPublisher.PreparePublication(
transaction.Build,
transaction.MeshData)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (transaction.PhysicsPublication is null)
{
if (!TryRun(
default,
"publication-prepare-physics",
() => transaction.PhysicsPublication =
_physicsPublisher.CreatePublication(
transaction.RenderPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (!transaction.PhysicsPublication.PreparationCommitted)
{
int entityOperations =
transaction.PhysicsPublication.PreparationCursor
< transaction.Build.Landblock.Entities.Count
? 1
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),
"publication-index-physics",
() => _physicsPublisher.AdvancePreparationOne(
transaction.PhysicsPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (transaction.StaticPublication is null)
{
if (!TryRun(
default,
"publication-prepare-statics",
() => transaction.StaticPublication =
_staticPublisher.CreatePublication(
transaction.PhysicsPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (!transaction.StaticPublication.PreparationCommitted)
{
int entityOperations =
transaction.StaticPublication.PreparationCursor
< transaction.Build.Landblock.Entities.Count
? 1
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),
"publication-validate-statics",
() => _staticPublisher.AdvancePreparationOne(
transaction.StaticPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
// Prepare every immutable receipt before the first external
// mutation. Cross-owner identity/data errors therefore block
// the publication without leaving a committed prefix.
while (!transaction.RenderPublication.BeginCommitted)
{
StreamingWorkCost cost =
!transaction.RenderPublication.TerrainCommitted
? new StreamingWorkCost(
GpuUploadBytes:
transaction.Cost.TerrainPayloadBytes)
: !transaction.RenderPublication.VisibilityCommitted
? new StreamingWorkCost(
EntityOperations:
transaction.Cost.VisibilityCells)
: default;
if (!TryRun(
cost,
"publication-render-prefix",
() => _renderPublisher.AdvanceBeginOne(
transaction.RenderPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (!transaction.PhysicsPublication.BeginCommitted)
{
int entityOperations =
transaction.PhysicsPublication.TerrainSurface is not null
&& transaction.PhysicsPublication.Build.Landblock.PhysicsDats?
.Info is { } info
&& transaction.PhysicsPublication.CellCursor < info.NumCells
? 1
: transaction.PhysicsPublication.BuildingCursor
< transaction.PhysicsPublication.Buildings.Length
? 1
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),
"publication-physics-prefix",
() => _physicsPublisher.AdvanceBeginOne(
transaction.PhysicsPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
// Retail establishes the building/visible-cell suffix before
// each static object publishes lights, collision, and scripts.
while (!transaction.RenderPublication.CompletionCommitted)
{
int entityOperations =
transaction.RenderPublication.BuildingPublication is
{ PreparationCommitted: false } buildingPublication
&& buildingPublication.BuildingCursor
< buildingPublication.Buildings.Length
? 1
: transaction.RenderPublication.EnvCellPublication is
{ PreparationCommitted: false } envCellPublication
&& envCellPublication.ShellCursor
< envCellPublication.Build.Shells.Length
? 1
: !transaction.RenderPublication.EnvCellsCommitted
&& transaction.RenderPublication
.EnvCellPublication is null
? transaction.Cost.EnvCellShells
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),
"publication-render-suffix",
() => _renderPublisher.AdvanceCompleteOne(
transaction.RenderPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (!transaction.StaticPublication.BeginCommitted)
{
int entityOperations =
transaction.StaticPublication.PriorCleanupCursor
< transaction.StaticPublication
.OrderedPreviouslyActiveIds.Count
? 1
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),
"publication-static-cleanup",
() => _staticPublisher.AdvanceBeginOne(
transaction.StaticPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (!transaction.PhysicsPublication.CompletionCommitted)
{
int entityOperations =
transaction.PhysicsPublication.GfxCursor
< transaction.PhysicsPublication.GfxObjectIds.Length
? 1
: transaction.PhysicsPublication.PriorStaticCursor
< transaction.PhysicsPublication
.PriorStaticOwnerIds.Length
? 1
: transaction.PhysicsPublication.StaticCursor
< transaction.Build.Landblock.Entities.Count
? 1
: transaction.PhysicsPublication
.RefloodOwnerIds is not null
&& transaction.PhysicsPublication
.RefloodCursor
< transaction.PhysicsPublication
.RefloodOwnerIds.Length
? 1
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),
"publication-physics-statics",
() => _physicsPublisher.AdvanceCompleteOne(
transaction.PhysicsPublication,
entity =>
_staticPublisher
.PrepareEntityBeforeCollision(
transaction.StaticPublication,
entity))))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
while (!transaction.StaticPublication.CompletionCommitted)
{
int entityOperations =
transaction.StaticPublication.PluginCursor
< transaction.StaticPublication.OrderedSnapshots.Count
? 1
: 0;
if (!TryRun(
new StreamingWorkCost(
EntityOperations: entityOperations),
"publication-plugin-projection",
() => _staticPublisher.AdvanceCompleteOne(
transaction.StaticPublication)))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
}
else
{
StreamingWorkCost legacyCost = new(
EntityOperations: transaction.Cost.Entities,
GpuUploadBytes: transaction.Cost.TerrainPayloadBytes);
bool ran = TryRun(
legacyCost,
"publication-legacy-presentation",
() =>
{
try
{
(_publishBeforeSpatialCommit
?? throw new InvalidOperationException(
"The presentation pipeline has no publication owners."))(
transaction.Build,
transaction.MeshData);
}
catch (Exception error)
{
// Compatibility callbacks cannot report an exact retained
// receipt. Preserve the established fail-fast rule only
// for callbacks that explicitly report committed mutation.
if (error is StreamingMutationException
{
MutationCommitted: true,
})
{
_publications.Remove(result);
}
throw;
}
});
if (!ran)
{
return new LandblockPublicationAdvance(false, progressed);
}
}
transaction.PresentationCommitted = true;
}
if (!transaction.SpatialPresentationCommitted)
{
// Keep live-projection visibility publication at the shipped
// boundary: bucket mutation, pin/script reconciliation, then the
// outer spatial observer commit. If pinning throws, the receipt is
// retained and only that suffix retries.
if (!TryRun(
default,
"publication-spatial-commit",
() =>
{
using GpuWorldState.MutationBatch mutation =
_state.BeginMutationBatch();
if (!transaction.SpatialCommitted)
{
IEnumerable? renderIds =
transaction.Build.EnvCells?.Shells.Select(
static shell => shell.GeometryId);
transaction.SpatialPublication = transaction.Kind switch
{
PublicationKind.Loaded
or PublicationKind.PromoteSelfContained
or PublicationKind.Far =>
_state.CommitLandblockSpatial(
transaction.Build.Landblock,
renderIds,
transaction.Tier),
PublicationKind.PromoteExisting =>
_state.CommitEntitiesToExistingLandblockSpatial(
transaction.LandblockId,
transaction.Build.Landblock.Entities,
renderIds),
_ => throw new InvalidOperationException(
$"Unknown landblock publication kind {transaction.Kind}."),
};
transaction.SpatialCommitted = true;
}
_state.ActivateLandblockPresentation(
transaction.SpatialPublication
?? throw new InvalidOperationException(
"A committed spatial publication has no activation receipt."));
if (transaction.SpatialPublication.RequiresActivation)
{
_staticProjectionSink?.Reconcile(
transaction.Build,
transaction.SpatialPublication);
}
transaction.SpatialPresentationCommitted = true;
}))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
if (!transaction.EnvCellReplayCommitted)
{
if (!TryRun(
new StreamingWorkCost(
EntityOperations: transaction.Cost.EnvCellShells),
"publication-envcell-replay",
() =>
{
if (transaction.Kind != PublicationKind.Far
&& transaction.Build.EnvCells is
{
Shells.Length: > 0,
} envCells)
{
_ensureEnvCellMeshes?.Invoke(envCells);
}
transaction.EnvCellReplayCommitted = true;
}))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
if (!transaction.LiveRecoveryCommitted)
{
// Retained live projections can materialize only after the
// canonical bucket exists. Landblock load never reconstructs their
// identity or replays create-time resources.
if (!TryRun(
default,
"publication-live-recovery",
() =>
{
_onLandblockLoaded?.Invoke(transaction.LandblockId);
transaction.LiveRecoveryCommitted = true;
}))
{
return new LandblockPublicationAdvance(false, progressed);
}
}
_publications.Remove(result);
return new LandblockPublicationAdvance(true, progressed);
}
}