refactor(streaming): own presentation retirement in pipeline
Move full and near-tier presentation retirement out of GameWindow and into a concrete pipeline-owned stage mapper. Harden the retry ledger against reentrancy and committed visibility observer failures, enforce a single state/resource ownership graph, and split concrete versus legacy controller construction so callbacks cannot be silently ignored. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
dc39772bf9
commit
ea7ffbc186
10 changed files with 860 additions and 140 deletions
|
|
@ -0,0 +1,119 @@
|
|||
using AcDream.Core.Lighting;
|
||||
using AcDream.Core.Rendering;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Streaming;
|
||||
|
||||
/// <summary>
|
||||
/// Composes the presentation-owner half of one detach-first landblock
|
||||
/// retirement. <see cref="LandblockRetirementTicket"/> remains the exact
|
||||
/// retry ledger; this class only maps each stage to its concrete owner.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Retail separates <c>CLandBlock::release_all</c> (0x0052FCF0),
|
||||
/// <c>CLandBlock::destroy_static_objects</c> (0x0052FA50), and
|
||||
/// <c>CLandBlock::Destroy</c> (0x0052FAA0). Acdream's asynchronous equivalent
|
||||
/// detaches spatial reachability first, then advances these concrete owners
|
||||
/// from the retained ticket without replaying successful stages or entities.
|
||||
/// </remarks>
|
||||
public sealed class LandblockPresentationRetirementOwner
|
||||
{
|
||||
private readonly LandblockRenderPublisher _render;
|
||||
private readonly LandblockPhysicsPublisher _physics;
|
||||
private readonly LandblockStaticPresentationPublisher _staticPresentation;
|
||||
private readonly LightingHookSink _lighting;
|
||||
private readonly TranslucencyFadeManager _translucency;
|
||||
|
||||
public LandblockPresentationRetirementOwner(
|
||||
LandblockRenderPublisher render,
|
||||
LandblockPhysicsPublisher physics,
|
||||
LandblockStaticPresentationPublisher staticPresentation,
|
||||
LightingHookSink lighting,
|
||||
TranslucencyFadeManager translucency)
|
||||
{
|
||||
_render = render ?? throw new ArgumentNullException(nameof(render));
|
||||
_physics = physics ?? throw new ArgumentNullException(nameof(physics));
|
||||
_staticPresentation = staticPresentation
|
||||
?? throw new ArgumentNullException(nameof(staticPresentation));
|
||||
_lighting = lighting ?? throw new ArgumentNullException(nameof(lighting));
|
||||
_translucency = translucency
|
||||
?? throw new ArgumentNullException(nameof(translucency));
|
||||
if (!_staticPresentation.MatchesResources(_lighting, _translucency))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The retirement resources must be those owned by the static " +
|
||||
"presentation publisher.",
|
||||
nameof(staticPresentation));
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Matches(
|
||||
LandblockRenderPublisher render,
|
||||
LandblockPhysicsPublisher physics,
|
||||
LandblockStaticPresentationPublisher staticPresentation) =>
|
||||
ReferenceEquals(_render, render)
|
||||
&& ReferenceEquals(_physics, physics)
|
||||
&& ReferenceEquals(_staticPresentation, staticPresentation)
|
||||
&& _staticPresentation.MatchesResources(_lighting, _translucency);
|
||||
|
||||
public void Advance(LandblockRetirementTicket ticket)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(ticket);
|
||||
bool staticOnly = ticket.Kind == LandblockRetirementKind.NearLayer;
|
||||
|
||||
ticket.RunForEachEntity(
|
||||
LandblockRetirementStage.EntityLighting,
|
||||
entity => !staticOnly || entity.ServerGuid == 0,
|
||||
RemoveLighting);
|
||||
ticket.RunForEachEntity(
|
||||
LandblockRetirementStage.EntityTranslucency,
|
||||
entity => !staticOnly || entity.ServerGuid == 0,
|
||||
RemoveTranslucency);
|
||||
ticket.RunForEachEntity(
|
||||
LandblockRetirementStage.PluginProjection,
|
||||
static entity => entity.ServerGuid == 0,
|
||||
_staticPresentation.RemovePluginProjection);
|
||||
|
||||
if (ticket.Kind == LandblockRetirementKind.Full)
|
||||
{
|
||||
ticket.RunOnce(
|
||||
LandblockRetirementStage.Terrain,
|
||||
() => _render.RemoveTerrain(ticket.LandblockId));
|
||||
}
|
||||
|
||||
ticket.RunOnce(
|
||||
LandblockRetirementStage.Physics,
|
||||
() =>
|
||||
{
|
||||
if (ticket.Kind == LandblockRetirementKind.Full)
|
||||
_physics.RemoveLandblock(ticket.LandblockId);
|
||||
else
|
||||
_physics.DemoteToTerrain(ticket.LandblockId);
|
||||
});
|
||||
ticket.RunOnce(
|
||||
LandblockRetirementStage.CellVisibility,
|
||||
() => _render.RemoveCellVisibility(ticket.LandblockId));
|
||||
ticket.RunOnce(
|
||||
LandblockRetirementStage.BuildingRegistry,
|
||||
() => _render.RemoveBuildingRegistry(ticket.LandblockId));
|
||||
ticket.RunOnce(
|
||||
LandblockRetirementStage.EnvironmentCells,
|
||||
() => _render.RemoveEnvironmentCells(ticket.LandblockId));
|
||||
}
|
||||
|
||||
private void RemoveLighting(WorldEntity entity)
|
||||
{
|
||||
if (entity.ServerGuid == 0)
|
||||
_staticPresentation.RemoveLighting(entity);
|
||||
else
|
||||
_lighting.UnregisterOwner(entity.Id);
|
||||
}
|
||||
|
||||
private void RemoveTranslucency(WorldEntity entity)
|
||||
{
|
||||
if (entity.ServerGuid == 0)
|
||||
_staticPresentation.RemoveTranslucency(entity);
|
||||
else
|
||||
_translucency.ClearEntity(entity.Id);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue