/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { Timing.Accumulator += gameTime.ElapsedGameTime.TotalSeconds; int updateIterations = (int)Math.Floor(Timing.Accumulator / Timing.Step); if (Timing.Accumulator > Timing.Step * 6.0) { //if the game's running too slowly then we have no choice //but to skip a bunch of steps //otherwise it snowballs and becomes unplayable Timing.Accumulator = Timing.Step; } CrossThread.ProcessTasks(); PlayerInput.UpdateVariable(); if (SoundManager != null) { if (WindowActive || !Config.MuteOnFocusLost) { SoundManager.ListenerGain = SoundManager.CompressionDynamicRangeGain; } else { SoundManager.ListenerGain = 0.0f; } } while (Timing.Accumulator >= Timing.Step) { Timing.TotalTime += Timing.Step; Stopwatch sw = new Stopwatch(); sw.Start(); fixedTime.IsRunningSlowly = gameTime.IsRunningSlowly; TimeSpan addTime = new TimeSpan(0, 0, 0, 0, 16); fixedTime.ElapsedGameTime = addTime; fixedTime.TotalGameTime.Add(addTime); base.Update(fixedTime); PlayerInput.Update(Timing.Step); if (loadingScreenOpen) { //reset accumulator if loading // -> less choppy loading screens because the screen is rendered after each update // -> no pause caused by leftover time in the accumulator when starting a new shift GameMain.ResetFrameTime(); if (!TitleScreen.PlayingSplashScreen) { SoundPlayer.Update((float)Timing.Step); } if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (!waitForKeyHit || ((PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0 || PlayerInput.PrimaryMouseButtonClicked()) && WindowActive))) { loadingScreenOpen = false; } #if DEBUG if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && Config.AutomaticQuickStartEnabled && FirstLoad) { loadingScreenOpen = false; FirstLoad = false; MainMenuScreen.QuickStart(); } #endif if (!hasLoaded && !CoroutineManager.IsCoroutineRunning(loadingCoroutine)) { throw new LoadingException(loadingCoroutine.Exception); } } else if (hasLoaded) { if (ConnectLobby != 0) { if (Client != null) { Client.Disconnect(); Client = null; GameMain.MainMenuScreen.Select(); } Steam.SteamManager.JoinLobby(ConnectLobby, true); ConnectLobby = 0; ConnectEndpoint = null; ConnectName = null; } else if (!string.IsNullOrWhiteSpace(ConnectEndpoint)) { if (Client != null) { Client.Disconnect(); Client = null; GameMain.MainMenuScreen.Select(); } UInt64 serverSteamId = SteamManager.SteamIDStringToUInt64(ConnectEndpoint); Client = new GameClient(Config.PlayerName, serverSteamId != 0 ? null : ConnectEndpoint, serverSteamId, string.IsNullOrWhiteSpace(ConnectName) ? ConnectEndpoint : ConnectName); ConnectLobby = 0; ConnectEndpoint = null; ConnectName = null; } SoundPlayer.Update((float)Timing.Step); if (PlayerInput.KeyHit(Keys.Escape) && WindowActive) { // Check if a text input is selected. if (GUI.KeyboardDispatcher.Subscriber != null) { if (GUI.KeyboardDispatcher.Subscriber is GUITextBox textBox) { textBox.Deselect(); } GUI.KeyboardDispatcher.Subscriber = null; } //if a verification prompt (are you sure you want to x) is open, close it else if (GUIMessageBox.VisibleBox as GUIMessageBox != null && GUIMessageBox.VisibleBox.UserData as string == "verificationprompt") { ((GUIMessageBox)GUIMessageBox.VisibleBox).Close(); } else if (Tutorial.Initialized && Tutorial.ContentRunning) { (GameSession.GameMode as TutorialMode).Tutorial.CloseActiveContentGUI(); } else if (GameSession.IsTabMenuOpen) { gameSession.ToggleTabMenu(); } else if (GUI.PauseMenuOpen) { GUI.TogglePauseMenu(); } //open the pause menu if not controlling a character OR if the character has no UIs active that can be closed with ESC else if ((Character.Controlled == null || !itemHudActive()) //TODO: do we need to check Inventory.SelectedSlot? && Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null && !CrewManager.IsCommandInterfaceOpen && !(Screen.Selected is SubEditorScreen editor && !editor.WiringMode && Character.Controlled?.SelectedConstruction != null)) { // Otherwise toggle pausing, unless another window/interface is open. GUI.TogglePauseMenu(); } bool itemHudActive() { if (Character.Controlled?.SelectedConstruction == null) { return(false); } return (Character.Controlled.SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null) || ((Character.Controlled.ViewTarget as Item)?.Prefab?.FocusOnSelected ?? false)); } } #if DEBUG if (GameMain.NetworkMember == null) { if (PlayerInput.KeyHit(Keys.P) && !(GUI.KeyboardDispatcher.Subscriber is GUITextBox)) { DebugConsole.Paused = !DebugConsole.Paused; } } #endif GUI.ClearUpdateList(); Paused = (DebugConsole.IsOpen || GUI.PauseMenuOpen || GUI.SettingsMenuOpen || Tutorial.ContentRunning || DebugConsole.Paused) && (NetworkMember == null || !NetworkMember.GameStarted); #if !DEBUG if (NetworkMember == null && !WindowActive && !Paused && true && Screen.Selected != MainMenuScreen && Config.PauseOnFocusLost) { GUI.TogglePauseMenu(); Paused = true; } #endif Screen.Selected.AddToGUIUpdateList(); if (Client != null) { Client.AddToGUIUpdateList(); } FileSelection.AddToGUIUpdateList(); DebugConsole.AddToGUIUpdateList(); DebugConsole.Update((float)Timing.Step); Paused = Paused || (DebugConsole.IsOpen && (NetworkMember == null || !NetworkMember.GameStarted)); if (!Paused) { Screen.Selected.Update(Timing.Step); } else if (Tutorial.Initialized && Tutorial.ContentRunning) { (GameSession.GameMode as TutorialMode).Update((float)Timing.Step); } else if (DebugConsole.Paused) { if (Screen.Selected.Cam == null) { DebugConsole.Paused = false; } else { Screen.Selected.Cam.MoveCamera((float)Timing.Step); } } if (NetworkMember != null) { NetworkMember.Update((float)Timing.Step); } GUI.Update((float)Timing.Step); } CoroutineManager.Update((float)Timing.Step, Paused ? 0.0f : (float)Timing.Step); SteamManager.Update((float)Timing.Step); TaskPool.Update(); SoundManager?.Update(); Timing.Accumulator -= Timing.Step; sw.Stop(); PerformanceCounter.AddElapsedTicks("Update total", sw.ElapsedTicks); PerformanceCounter.UpdateTimeGraph.Update(sw.ElapsedTicks * 1000.0f / (float)Stopwatch.Frequency); PerformanceCounter.UpdateIterationsGraph.Update(updateIterations); } if (!Paused) { Timing.Alpha = Timing.Accumulator / Timing.Step; } }