From 84758d0ddc4418ff520d9f18a7b64781d1acc187 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 10 Jun 2025 13:49:18 -0500 Subject: [PATCH] Exclude time spent with emulator paused from play time With this change, the play time reported internally by a game's gameplay timer should match (or be much closer to matching) what Ryujinx displays in the application library. Aside from this being closer to the natural expectation of what "hours played" would take into account (as by definition time spent paused is time spent not playing), this also brings us closer to the behavior of other emulators and game libraries. --- src/Ryujinx/Systems/AppHost.cs | 7 ++++++- src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs | 7 +++++-- src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx/Systems/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs index 74005400c..94c2f2900 100644 --- a/src/Ryujinx/Systems/AppHost.cs +++ b/src/Ryujinx/Systems/AppHost.cs @@ -75,6 +75,7 @@ namespace Ryujinx.Ava.Systems private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; + private readonly Stopwatch _pauseTimer; private long _ticks; private readonly AccountManager _accountManager; @@ -175,6 +176,7 @@ namespace Ryujinx.Ava.Systems _chrono = new Stopwatch(); _ticksPerFrame = Stopwatch.Frequency / TargetFps; + _pauseTimer = new Stopwatch(); if (ApplicationPath.StartsWith("@SystemContent")) { @@ -616,7 +618,7 @@ namespace Ryujinx.Ava.Systems private void Dispose() { if (Device.Processes != null) - MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); + MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText, _pauseTimer.Elapsed); ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; @@ -635,6 +637,7 @@ namespace Ryujinx.Ava.Systems _gpuCancellationTokenSource.Dispose(); _chrono.Stop(); + _pauseTimer.Stop(); } public void DisposeGpu() @@ -877,6 +880,7 @@ namespace Ryujinx.Ava.Systems Device?.System.TogglePauseEmulation(false); _viewModel.IsPaused = false; + _pauseTimer.Stop(); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI); Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); } @@ -886,6 +890,7 @@ namespace Ryujinx.Ava.Systems Device?.System.TogglePauseEmulation(true); _viewModel.IsPaused = true; + _pauseTimer.Start(); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]); Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); } diff --git a/src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs b/src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs index 9d8488aeb..e80fae009 100644 --- a/src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs +++ b/src/Ryujinx/Systems/AppLibrary/ApplicationMetadata.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Text.Json.Serialization; namespace Ryujinx.Ava.Systems.AppLibrary @@ -33,7 +34,8 @@ namespace Ryujinx.Ava.Systems.AppLibrary /// /// Updates and . Call this after a game ends. /// - public void UpdatePostGame() + /// The amount of time emulation was paused while playing. + public void UpdatePostGame(TimeSpan pauseTime) { DateTime? prevLastPlayed = LastPlayed; UpdatePreGame(); @@ -44,7 +46,8 @@ namespace Ryujinx.Ava.Systems.AppLibrary } TimeSpan diff = DateTime.UtcNow - prevLastPlayed.Value; - double newTotalSeconds = TimePlayed.Add(diff).TotalSeconds; + Debug.Assert(pauseTime <= diff); + double newTotalSeconds = TimePlayed.Add(diff - pauseTime).TotalSeconds; TimePlayed = TimeSpan.FromSeconds(Math.Round(newTotalSeconds, MidpointRounding.AwayFromZero)); } } diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 8b9b04511..1ea9d009d 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -1688,8 +1688,8 @@ namespace Ryujinx.Ava.UI.ViewModels RendererHostControl.Focus(); }); - public static void UpdateGameMetadata(string titleId) - => ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame()); + public static void UpdateGameMetadata(string titleId, TimeSpan pauseTime) + => ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(pauseTime)); public void RefreshFirmwareStatus() {