using AcDream.Runtime; using AcDream.Runtime.World; namespace AcDream.App.Streaming; /// /// Generation-scoped destination-work seam. The reveal coordinator is the /// graphical host for Runtime's canonical login/portal lifetime; the /// streaming scheduler only reserves capacity for the exact generation and /// destination it is given. /// internal interface IWorldRevealStreamingScheduler { void BeginDestinationReservation( long revealGeneration, uint destinationCell, int requiredRenderRadius); void EndDestinationReservation(long revealGeneration); } /// /// Generation-scoped renderer-resource profile used only while the normal /// world viewport is withheld for a login or portal destination. /// internal interface IWorldRevealRenderResourceScheduler { void BeginDestinationReveal(long revealGeneration); void EndDestinationReveal(long revealGeneration); } /// /// Graphical-host adapter for one canonical Runtime login/portal reveal /// lifetime. App owns render/collision readiness and destination resource /// reservations; Runtime owns generation, destination, materialization, /// simulation availability, completion, and cancellation. /// internal sealed class WorldRevealCoordinator { private sealed class HostProjection( RuntimeWorldHostProjectionToken token, int requiredRenderRadius, in WorldGenerationQuiescenceEdge quiescenceEdge) { public RuntimeWorldHostProjectionToken Token { get; } = token; public int RequiredRenderRadius { get; } = requiredRenderRadius; public WorldGenerationQuiescenceEdge QuiescenceEdge { get; } = quiescenceEdge; public bool QuiescenceCommitted { get; set; } public bool StreamingRegistered { get; set; } public bool RenderResourcesRegistered { get; set; } public bool SimulationReleaseProjected { get; set; } public bool StreamingReleased { get; set; } public bool RenderResourcesReleased { get; set; } } private readonly RuntimeWorldTransitState _transit; private readonly WorldRevealReadinessBarrier _readiness; private readonly WorldGenerationQuiescence? _quiescence; private readonly IWorldRevealStreamingScheduler? _streaming; private readonly IWorldRevealRenderResourceScheduler? _renderResources; private readonly List _hostProjections = []; private bool _hostRetryActive; private bool _hostRetryRequested; public WorldRevealCoordinator( RuntimeWorldTransitState transit, Func isRenderNeighborhoodReady, Func isSpawnCellReady, Func isTerrainNeighborhoodReady, Func areCompositeTexturesReady, Action prepareCompositeTextures, Action invalidateCompositeTextures, Func isSpawnClaimUnhydratable, WorldGenerationQuiescence? quiescence = null, IWorldRevealStreamingScheduler? streaming = null, IWorldRevealRenderResourceScheduler? renderResources = null) { _transit = transit ?? throw new ArgumentNullException(nameof(transit)); _readiness = new WorldRevealReadinessBarrier( isRenderNeighborhoodReady, isSpawnCellReady, isTerrainNeighborhoodReady, areCompositeTexturesReady, prepareCompositeTextures, invalidateCompositeTextures, isSpawnClaimUnhydratable); _quiescence = quiescence; _streaming = streaming; _renderResources = renderResources; } public RuntimePortalSnapshot Snapshot => _transit.Snapshot; public int PortalMaterializationCount => _transit.Snapshot.PortalMaterializationCount; public bool WaitCueShown => _transit.Snapshot.WaitCueShown; public RuntimeWorldTransitOwnershipSnapshot Ownership => _transit.Ownership; public long BeginLogin(uint destinationCell) { if (destinationCell == 0u) throw new ArgumentOutOfRangeException(nameof(destinationCell)); WithdrawHostForReplacement(); WorldGenerationQuiescenceEdge quiescenceEdge = _quiescence?.CaptureBegin() ?? default; _readiness.Begin(); long generation = _transit.BeginLoginReveal(destinationCell); BeginHostLifetime( generation, destinationCell, quiescenceEdge); return generation; } public bool TryBeginPortal( ushort teleportSequence, uint destinationCell, out long generation) { if (destinationCell == 0u) throw new ArgumentOutOfRangeException(nameof(destinationCell)); if (!_transit.CanBeginPortalReveal( teleportSequence, destinationCell)) { generation = 0; return false; } WithdrawHostForReplacement(); WorldGenerationQuiescenceEdge quiescenceEdge = _quiescence?.CaptureBegin() ?? default; if (!_transit.TryBeginPortalReveal( teleportSequence, destinationCell, out generation)) { return false; } _readiness.Begin(); BeginHostLifetime( generation, destinationCell, quiescenceEdge); return true; } private void BeginHostLifetime( long generation, uint destinationCell, in WorldGenerationQuiescenceEdge quiescenceEdge) { if (!_transit.TryRegisterHostProjection( generation, destinationCell, out RuntimeWorldHostProjectionToken token)) { throw new InvalidOperationException( "Runtime rejected the exact reveal host projection."); } _hostProjections.Add(new HostProjection( token, WorldRevealReadinessBarrier.RequiredRenderRadius( destinationCell), quiescenceEdge)); RetryPendingHostWork(); } /// /// Advances render-thread preparation and publishes the resulting /// canonical readiness snapshot to lifecycle diagnostics. /// public WorldRevealReadinessSnapshot PrepareAndEvaluate(uint destinationCell) { _readiness.Prepare(destinationCell); return Evaluate(destinationCell); } public WorldRevealReadinessSnapshot Evaluate(uint destinationCell) { RetryPendingHostWork(); WorldRevealReadinessSnapshot snapshot = _readiness.Evaluate(destinationCell); RuntimePortalSnapshot portal = _transit.Snapshot; if (portal.Generation != 0) { _transit.AcknowledgeDestinationReadiness( new RuntimeDestinationReadiness( portal.Generation, snapshot.DestinationCell, snapshot.IsIndoor, snapshot.IsUnhydratable, snapshot.RequiredRenderRadius, snapshot.IsRenderNeighborhoodReady, snapshot.AreCompositeTexturesReady, snapshot.IsCollisionReady)); } return snapshot; } /// /// Commits the accepted destination and resumes its hidden world /// simulation while the private portal viewport remains in front. /// /// /// Retail SmartBox::UseTime @ 0x00455410 resumes /// CObjectMaint/CPhysics before /// gmSmartBoxUI::EndTeleportAnimation @ 0x004D65A0 enters /// TAS_TUNNEL_CONTINUE. The normal world viewport is not restored /// until the later TUNNEL_FADE_OUT -> WORLD_FADE_IN edge. /// public bool CanPlacePortalDestination( long generation, ushort teleportSequence, uint destinationCell) => _transit.CanPlacePortalDestination( generation, teleportSequence, destinationCell); public bool ObserveMaterialized( long generation, ushort teleportSequence, uint destinationCell) { bool acknowledged = _transit.AcknowledgePortalMaterialized( generation, teleportSequence, destinationCell); RetryPendingHostWork(); return acknowledged; } public void ObserveWorldViewportVisible() { RetryPendingHostWork(); long generation = _transit.Snapshot.Generation; if (generation != 0) _transit.AcknowledgeWorldViewportVisible(generation); } public bool ObserveWait(TimeSpan elapsed) { long generation = _transit.Snapshot.Generation; return generation != 0 && _transit.ObserveWait(generation, elapsed); } /// /// Releases the destination streaming/resource reservation at retail's /// TunnelFadeOut -> WorldFadeIn viewport swap without completing the /// one-second WorldFadeIn/LoginComplete tail. World simulation already /// resumed when the accepted destination materialized. /// public void RevealWorldViewport() { HostProjection? host = FindCurrentHostProjection(); if (host is null) return; _transit.RequireDestinationReservationRelease(host.Token); RetryPendingHostWork(); } public void Complete() { long generation = _transit.Snapshot.Generation; if (generation == 0) return; _transit.Complete(generation); RetryPendingHostWork(); } public void Cancel() { long generation = _transit.Snapshot.Generation; if (generation == 0) return; _transit.Cancel(generation); RetryPendingHostWork(); } public void ResetSession() { ResetHostSession(); _transit.ResetSession(); } /// /// Retires only App streaming/presentation acknowledgements. The shared /// Runtime generation-reset transaction clears canonical transit state /// after this host boundary has converged. /// internal void ResetHostSession() { long generation = _transit.Snapshot.Generation; if (generation != 0) _transit.Cancel(generation); RetryPendingHostWork(); } /// /// Resumes the exact unfinished graphical-host suffix. Completed callback /// stages never replay; a thrown callback leaves its Runtime /// acknowledgement pending for the next call. /// internal void RetryPendingHostWork() { _hostRetryRequested = true; if (_hostRetryActive) return; _hostRetryActive = true; try { do { _hostRetryRequested = false; int index = 0; while (index < _hostProjections.Count) { HostProjection host = _hostProjections[index]; DrainHostProjection(host); if (index < _hostProjections.Count && ReferenceEquals( _hostProjections[index], host)) { index++; } } } while (_hostRetryRequested); } finally { _hostRetryActive = false; } } private void DrainHostProjection(HostProjection host) { if (!TryGetHostProjection(host, out var projection)) return; RuntimeWorldHostAcknowledgementStage pending = projection.PendingAcknowledgements; if ((pending & RuntimeWorldHostAcknowledgementStage .ProjectionRegistered) != 0) { CompleteHostRegistration(host); } if (!TryGetHostProjection(host, out projection)) return; pending = projection.PendingAcknowledgements; if ((pending & RuntimeWorldHostAcknowledgementStage .SimulationReleaseProjected) != 0) { CompleteSimulationRelease(host); } if (!TryGetHostProjection(host, out projection)) return; pending = projection.PendingAcknowledgements; if ((pending & RuntimeWorldHostAcknowledgementStage .DestinationReservationReleased) != 0) { CompleteDestinationReservationRelease(host); } if (!TryGetHostProjection(host, out projection)) return; if ((projection.PendingAcknowledgements & RuntimeWorldHostAcknowledgementStage.TerminalProjected) == 0) { return; } Acknowledge( host, RuntimeWorldHostAcknowledgementStage.TerminalProjected); RemoveHostProjection(host); } private void CompleteHostRegistration(HostProjection host) { if (!host.QuiescenceCommitted) { _quiescence?.CommitBegin(host.QuiescenceEdge); host.QuiescenceCommitted = true; } if (!IsStagePending( host, RuntimeWorldHostAcknowledgementStage .ProjectionRegistered)) { return; } if (!host.StreamingRegistered) { _streaming?.BeginDestinationReservation( host.Token.Generation, host.Token.DestinationCell, host.RequiredRenderRadius); host.StreamingRegistered = true; } if (!IsStagePending( host, RuntimeWorldHostAcknowledgementStage .ProjectionRegistered)) { return; } if (!host.RenderResourcesRegistered) { _renderResources?.BeginDestinationReveal( host.Token.Generation); host.RenderResourcesRegistered = true; } if (!IsStagePending( host, RuntimeWorldHostAcknowledgementStage .ProjectionRegistered)) { return; } Acknowledge( host, RuntimeWorldHostAcknowledgementStage.ProjectionRegistered); } private void CompleteSimulationRelease(HostProjection host) { if (!host.SimulationReleaseProjected) { _quiescence?.ObserveReleased(); host.SimulationReleaseProjected = true; } if (!IsStagePending( host, RuntimeWorldHostAcknowledgementStage .SimulationReleaseProjected)) { return; } Acknowledge( host, RuntimeWorldHostAcknowledgementStage .SimulationReleaseProjected); } private void CompleteDestinationReservationRelease(HostProjection host) { if (!host.StreamingReleased) { if (host.StreamingRegistered) { _streaming?.EndDestinationReservation( host.Token.Generation); } host.StreamingReleased = true; } if (!IsStagePending( host, RuntimeWorldHostAcknowledgementStage .DestinationReservationReleased)) { return; } if (!host.RenderResourcesReleased) { if (host.RenderResourcesRegistered) { _renderResources?.EndDestinationReveal( host.Token.Generation); } host.RenderResourcesReleased = true; } if (!IsStagePending( host, RuntimeWorldHostAcknowledgementStage .DestinationReservationReleased)) { return; } Acknowledge( host, RuntimeWorldHostAcknowledgementStage .DestinationReservationReleased); } private void WithdrawHostForReplacement() { HostProjection? host = FindCurrentHostProjection(); if (host is null) return; if (!_transit.BeginHostProjectionSupersession(host.Token)) { throw new InvalidOperationException( "Runtime rejected reveal-host supersession."); } RetryPendingHostWork(); } private HostProjection? FindCurrentHostProjection() { long generation = _transit.Snapshot.Generation; for (int i = 0; i < _hostProjections.Count; i++) { HostProjection candidate = _hostProjections[i]; if (candidate.Token.Generation == generation) return candidate; } return null; } private bool TryGetHostProjection( HostProjection host, out RuntimeWorldHostProjectionSnapshot projection) { if (_transit.TryGetHostProjection(host.Token, out projection)) return true; RemoveHostProjection(host); return false; } private bool IsStagePending( HostProjection host, RuntimeWorldHostAcknowledgementStage stage) => _transit.TryGetHostProjection( host.Token, out RuntimeWorldHostProjectionSnapshot projection) && (projection.PendingAcknowledgements & stage) != 0; private void RemoveHostProjection(HostProjection host) { for (int i = 0; i < _hostProjections.Count; i++) { if (!ReferenceEquals(_hostProjections[i], host)) continue; _hostProjections.RemoveAt(i); return; } } private void Acknowledge( HostProjection host, RuntimeWorldHostAcknowledgementStage stage) { if (!_transit.AcknowledgeHostProjection( new RuntimeWorldHostAcknowledgement( host.Token, stage))) { throw new InvalidOperationException( $"Runtime rejected reveal host acknowledgement {stage} " + $"for generation {host.Token.Generation}."); } } }