using AcDream.Core.Lighting; using AcDream.Core.Rendering; using AcDream.Core.World; namespace AcDream.App.Streaming; /// /// Composes the presentation-owner half of one detach-first landblock /// retirement. remains the exact /// retry ledger; this class only maps each stage to its concrete owner. /// /// /// Retail separates CLandBlock::release_all (0x0052FCF0), /// CLandBlock::destroy_static_objects (0x0052FA50), and /// CLandBlock::Destroy (0x0052FAA0). Acdream's asynchronous equivalent /// detaches spatial reachability first, then advances these concrete owners /// from the retained ticket without replaying successful stages or entities. /// 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); } }