using System.Globalization; namespace AcDream.Runtime.World; /// /// Canonical presentation-independent owner of one login/portal reveal /// generation. The graphical or headless host supplies typed destination /// readiness acknowledgements; Runtime owns which generation and destination /// those acknowledgements are allowed to advance. /// /// /// Retail ordering follows SmartBox::TeleportPlayer @ 0x00453910, /// SmartBox::UseTime @ 0x00455410, and /// gmSmartBoxUI::EndTeleportAnimation @ 0x004D65A0. Destination /// materialization resumes simulation before the later normal-world viewport /// edge. /// public sealed class RuntimeWorldTransitState : IRuntimePortalView { public static readonly TimeSpan RetailWaitCueDelay = TimeSpan.FromSeconds(5); private readonly Action _log; private RuntimePortalSnapshot _snapshot = RuntimePortalSnapshot.Idle; private long _nextGeneration; public RuntimeWorldTransitState(Action? log = null) { _log = log ?? (_ => { }); } public RuntimePortalSnapshot Snapshot => _snapshot; public bool IsWorldSimulationAvailable => _snapshot.WorldSimulationAvailable; public int DiagnosticFailureCount { get; private set; } public Exception? LastDiagnosticFailure { get; private set; } public long BeginReveal( RuntimePortalKind kind, uint destinationCell) { if (kind is RuntimePortalKind.None) throw new ArgumentOutOfRangeException(nameof(kind)); if (destinationCell == 0u) throw new ArgumentOutOfRangeException(nameof(destinationCell)); if (_snapshot.Generation != 0 && !_snapshot.Cancelled && !_snapshot.WorldViewportObserved) { Log("superseded", _snapshot); } long generation = checked(++_nextGeneration); int failures = _snapshot.InvariantFailureCount; int materializations = _snapshot.PortalMaterializationCount; _snapshot = new RuntimePortalSnapshot( generation, kind, new RuntimeDestinationReadiness( generation, destinationCell, IsIndoor: IsIndoor(destinationCell), IsUnhydratable: false, RequiredRenderRadius: 0, IsRenderNeighborhoodReady: false, AreCompositeTexturesReady: false, IsCollisionReady: false), Materialized: false, Completed: false, Cancelled: false, WorldViewportObserved: false, WorldSimulationAvailable: false, InvariantFailureCount: failures, WaitCueShown: false, PortalMaterializationCount: materializations); Log("begin", _snapshot); return generation; } public bool AcknowledgeDestinationReadiness( in RuntimeDestinationReadiness acknowledgement) { if (!ValidateActive( acknowledgement.Generation, acknowledgement.DestinationCell, "readiness")) { return false; } if (_snapshot.Readiness.IsReady) { if (_snapshot.Readiness == acknowledgement) return false; FailInvariant( "readiness-changed-after-ready", $"accepted={DescribeReadiness(_snapshot.Readiness)} " + $"actual={DescribeReadiness(acknowledgement)}"); return false; } bool isIndoor = IsIndoor(acknowledgement.DestinationCell); int requiredRenderRadius = isIndoor ? 0 : 1; if (acknowledgement.IsIndoor != isIndoor || acknowledgement.RequiredRenderRadius != requiredRenderRadius) { FailInvariant( "invalid-readiness-shape", $"indoor={acknowledgement.IsIndoor} " + $"expectedIndoor={isIndoor} " + $"radius={acknowledgement.RequiredRenderRadius} " + $"expectedRadius={requiredRenderRadius}"); return false; } if (_snapshot.Readiness == acknowledgement) return false; _snapshot = _snapshot with { Readiness = acknowledgement }; Log("readiness", _snapshot); return true; } public bool AcknowledgeMaterialized( long generation, uint destinationCell) { if (!ValidateActive(generation, destinationCell, "materialized")) return false; if (_snapshot.Materialized) return false; if (!_snapshot.Readiness.IsReady) { FailInvariant("materialized-before-ready", null); return false; } int count = _snapshot.PortalMaterializationCount; if (_snapshot.Kind == RuntimePortalKind.Portal) count = checked(count + 1); _snapshot = _snapshot with { Materialized = true, WorldSimulationAvailable = true, PortalMaterializationCount = count, }; Log("materialized", _snapshot); return true; } public bool AcknowledgeWorldViewportVisible(long generation) { if (!ValidateGeneration( generation, "world-visible", allowCompleted: true)) { return false; } if (_snapshot.WorldViewportObserved) return false; if (!_snapshot.Readiness.IsReady) { FailInvariant("viewport-before-ready", null); return false; } _snapshot = _snapshot with { WorldViewportObserved = true }; Log("world-visible", _snapshot); return true; } public bool ObserveWait(long generation, TimeSpan elapsed) { if (generation == 0 || generation != _snapshot.Generation || _snapshot.Cancelled || _snapshot.Completed || elapsed < RetailWaitCueDelay) { return false; } if (!_snapshot.WaitCueShown) { _snapshot = _snapshot with { WaitCueShown = true }; SafeLog( $"[world-reveal] event=wait-cue " + $"elapsedMs={elapsed.TotalMilliseconds:F0} " + Describe(_snapshot)); } return true; } public bool Complete(long generation) { if (!ValidateGeneration( generation, "complete", allowCompleted: true)) { return false; } if (_snapshot.Completed) return false; if (!_snapshot.Readiness.IsReady) { FailInvariant("complete-before-ready", null); return false; } if (_snapshot.Kind == RuntimePortalKind.Portal && !_snapshot.Materialized) { FailInvariant("portal-complete-before-materialized", null); return false; } _snapshot = _snapshot with { Completed = true, WorldSimulationAvailable = true, }; Log("complete", _snapshot); return true; } public bool Cancel(long generation) { if (generation == 0 || generation != _snapshot.Generation || _snapshot.Cancelled || _snapshot.Completed) { return false; } _snapshot = _snapshot with { Cancelled = true, WorldSimulationAvailable = true, }; Log("cancel", _snapshot); return true; } /// /// Clears session-visible state while retaining the monotonically /// increasing generation counter so a delayed prior-session host /// acknowledgement cannot match a future generation. /// public void ResetSession() { _snapshot = RuntimePortalSnapshot.Idle; } private bool ValidateActive( long generation, uint destinationCell, string eventName) { if (!ValidateGeneration( generation, eventName, allowCompleted: false)) { return false; } if (destinationCell == 0u || destinationCell != _snapshot.DestinationCell) { FailInvariant( $"{eventName}-destination-mismatch", $"expected=0x{_snapshot.DestinationCell:X8} " + $"actual=0x{destinationCell:X8}"); return false; } return true; } private bool ValidateGeneration( long generation, string eventName, bool allowCompleted) { if (generation == 0 || generation != _snapshot.Generation) { LogRejected( $"{eventName}-generation-mismatch", $"expected={_snapshot.Generation} actual={generation}"); return false; } if (_snapshot.Cancelled || (!allowCompleted && _snapshot.Completed)) { LogRejected( $"{eventName}-after-terminal", $"completed={_snapshot.Completed} " + $"cancelled={_snapshot.Cancelled}"); return false; } return true; } private void FailInvariant(string reason, string? detail) { _snapshot = _snapshot with { InvariantFailureCount = checked(_snapshot.InvariantFailureCount + 1), }; string suffix = string.IsNullOrWhiteSpace(detail) ? string.Empty : $" {detail}"; SafeLog( $"[world-reveal] event=invariant-failure " + $"reason={reason}{suffix} {Describe(_snapshot)}"); } private void LogRejected(string reason, string? detail) { string suffix = string.IsNullOrWhiteSpace(detail) ? string.Empty : $" {detail}"; SafeLog( $"[world-reveal] event=rejected " + $"reason={reason}{suffix} {Describe(_snapshot)}"); } private void Log(string eventName, RuntimePortalSnapshot snapshot) => SafeLog($"[world-reveal] event={eventName} {Describe(snapshot)}"); private void SafeLog(string message) { try { _log(message); } catch (Exception error) { DiagnosticFailureCount = checked(DiagnosticFailureCount + 1); LastDiagnosticFailure = error; } } private static string Describe(RuntimePortalSnapshot snapshot) { RuntimeDestinationReadiness ready = snapshot.Readiness; return string.Create( CultureInfo.InvariantCulture, $"generation={snapshot.Generation} kind={snapshot.Kind} " + $"cell=0x{ready.DestinationCell:X8} " + $"indoor={ready.IsIndoor} " + $"radius={ready.RequiredRenderRadius} " + $"unhydratable={ready.IsUnhydratable} " + $"render={ready.IsRenderNeighborhoodReady} " + $"composites={ready.AreCompositeTexturesReady} " + $"collision={ready.IsCollisionReady} ready={ready.IsReady} " + $"materialized={snapshot.Materialized} " + $"completed={snapshot.Completed} " + $"cancelled={snapshot.Cancelled} " + $"visible={snapshot.WorldViewportObserved} " + $"simulation={snapshot.WorldSimulationAvailable} " + $"failures={snapshot.InvariantFailureCount}"); } private static string DescribeReadiness( in RuntimeDestinationReadiness readiness) => string.Create( CultureInfo.InvariantCulture, $"indoor={readiness.IsIndoor}," + $"unhydratable={readiness.IsUnhydratable}," + $"radius={readiness.RequiredRenderRadius}," + $"render={readiness.IsRenderNeighborhoodReady}," + $"composites={readiness.AreCompositeTexturesReady}," + $"collision={readiness.IsCollisionReady}"); private static bool IsIndoor(uint cellId) => (cellId & 0xFFFFu) >= 0x0100u; }