using OWML.Utils; using QSB.Utility; using QSB.WorldSync; using System; using System.Linq; using UnityEngine; using UnityEngine.UI; namespace QSB.TimeSync { internal class TimeSyncUI : MonoBehaviour { public static TimeSyncUI Instance; public static float TargetTime; private Canvas _canvas; private Text _text; private float _startTime; private bool _isSetUp; private TimeSyncType _currentType; private Enum _currentReason; public void Awake() { Instance = this; enabled = false; QSBSceneManager.OnUniverseSceneLoaded += OnUniverseSceneLoad; } private void OnUniverseSceneLoad(OWScene oldScene, OWScene newScene) { _isSetUp = true; var obj = QSBWorldSync.GetUnityObjects().First(); _canvas = obj.GetValue("_canvas"); _text = obj.GetValue("_text"); _canvas.enabled = false; } public void OnDestroy() { QSBSceneManager.OnUniverseSceneLoaded -= OnUniverseSceneLoad; if (_canvas != null && _canvas.enabled) { Canvas.willRenderCanvases -= OnWillRenderCanvases; } } public static void Start(TimeSyncType type, Enum reason) => QSBCore.UnityEvents.RunWhen(() => Instance._isSetUp, () => Instance.StartTimeSync(type, reason)); public static void Stop() => QSBCore.UnityEvents.RunWhen(() => Instance._isSetUp, () => Instance.EndTimeSync()); private void StartTimeSync(TimeSyncType type, Enum reason) { if (!QSBSceneManager.IsInUniverse) { DebugLog.ToConsole("Error - Tried to start time sync UI when not in universe!", OWML.Common.MessageType.Error); return; } _currentType = type; _currentReason = reason; _startTime = Time.timeSinceLevelLoad; enabled = true; _canvas.enabled = true; Canvas.willRenderCanvases += OnWillRenderCanvases; } private void EndTimeSync() { _currentType = TimeSyncType.None; enabled = false; _canvas.enabled = false; Canvas.willRenderCanvases -= OnWillRenderCanvases; } private void OnWillRenderCanvases() { if (!_isSetUp) { return; } var totalSeconds = Mathf.Max(TargetTime - Time.timeSinceLevelLoad, 0f); var minutes = Mathf.FloorToInt(totalSeconds / 60f); var seconds = Mathf.FloorToInt(totalSeconds) % 60; var milliseconds = totalSeconds % 1 * 1000; var text = ""; switch (_currentType) { case TimeSyncType.Fastforwarding: switch ((FastForwardReason)_currentReason) { case FastForwardReason.TooFarBehind: text = $"{minutes:D2}:{seconds:D2}.{milliseconds:000}" + Environment.NewLine + "Fast-forwarding to match server time..."; break; } break; case TimeSyncType.Pausing: switch ((PauseReason)_currentReason) { case PauseReason.ServerNotStarted: text = "Waiting for server to start..."; break; case PauseReason.TooFarAhead: text = "Pausing to match server time..."; break; case PauseReason.WaitingForAllPlayersToBeReady: text = "Waiting for start of loop..."; break; case PauseReason.WaitingForAllPlayersToDie: text = "Waiting for end of loop..."; break; } break; } _text.text = text; } } }