From d61ccd276735a85ccdd5bf64c78cc25129959b99 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Thu, 20 Jun 2024 02:34:59 +0100 Subject: [PATCH 01/23] Move Playtime logic into GameManagement --- .../BaseClass/GamePlaytimeBase.cs | 32 +++ .../GamePlaytime/Universal/Context.cs | 8 + .../GamePlaytime/Universal/GamePlaytime.cs | 109 +++++++++ .../Universal/RegistryClass/Playtime.cs | 215 ++++++++++++++++++ CollapseLauncher/Classes/GamePropertyVault.cs | 31 +-- .../Classes/Interfaces/IGamePlaytime.cs | 16 ++ .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 179 ++++----------- Hi3Helper.Core/Lang/en_US.json | 2 +- 8 files changed, 444 insertions(+), 148 deletions(-) create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs create mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs create mode 100644 CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs new file mode 100644 index 000000000..74dbcb120 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs @@ -0,0 +1,32 @@ +using CollapseLauncher.GamePlaytime.Universal; +using CollapseLauncher.Interfaces; +using Microsoft.Win32; +using System; +using System.IO; + +namespace CollapseLauncher.GamePlaytime.Base +{ + internal class GamePlaytimeBase + { +#nullable enable + #region Properties + internal static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); + + protected RegistryKey? RegistryRoot; + protected Playtime? _playtime; + #endregion + + public GamePlaytimeBase(IGameVersionCheck GameVersionManager) + { + _gameVersionManager = GameVersionManager; + } +#nullable disable + + protected IGameVersionCheck _gameVersionManager { get; set; } + + protected static string TimeSpanToString(TimeSpan timeSpan) + { + return $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; + } + } +} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs new file mode 100644 index 000000000..0a97f3097 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace CollapseLauncher.GamePlaytime.Universal +{ + [JsonSourceGenerationOptions(IncludeFields = false, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = true)] + [JsonSerializable(typeof(Playtime))] + internal sealed partial class UniversalPlaytimeJSONContext : JsonSerializerContext { } +} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs new file mode 100644 index 000000000..ede6755a1 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs @@ -0,0 +1,109 @@ +using CollapseLauncher.GamePlaytime.Base; +using CollapseLauncher.Interfaces; +using Hi3Helper; +using Microsoft.Win32; +using System; +using System.Diagnostics; +using System.IO; +using System.Timers; +using static Hi3Helper.Logger; +using static CollapseLauncher.Dialogs.SimpleDialogs; +using static CollapseLauncher.InnerLauncherConfig; + +namespace CollapseLauncher.GamePlaytime.Universal +{ + internal class GamePlaytime : GamePlaytimeBase, IGamePlaytime + { + #region Properties + public event EventHandler PlaytimeUpdated; + #endregion + + public GamePlaytime(IGameVersionCheck GameVersionManager) : base(GameVersionManager) + { + string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig); + RegistryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); + + RegistryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); + + _gameVersionManager = GameVersionManager; + + _playtime = Playtime.Load(RegistryRoot); + } + + public void ForceUpdate() + { + PlaytimeUpdated?.Invoke(this, _playtime); + } + + public void Update(TimeSpan timeSpan) + { + TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + + _playtime.Update(timeSpan); + PlaytimeUpdated?.Invoke(this, _playtime); + + LogWriteLine($"Playtime counter changed to {TimeSpanToString(timeSpan)}m. (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); + } + + public void Reset() + { + TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + + _playtime.Reset(); + PlaytimeUpdated?.Invoke(this, _playtime); + + LogWriteLine($"Playtime counter was reset! (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); + } + + public async void StartSession(Process proc) + { + DateTime begin = DateTime.Now; + TimeSpan initialTimeSpan = _playtime.CurrentPlaytime; + + _playtime.LastPlayed = begin; + _playtime.Save(); + +#if DEBUG + LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - Started session at {begin.ToLongTimeString()}."); +#endif + int elapsedSeconds = 0; + + using (var inGameTimer = new Timer()) + { + inGameTimer.Interval = 60000; + inGameTimer.Elapsed += (_, _) => + { + elapsedSeconds += 60; + DateTime now = DateTime.Now; + + _playtime.Add(TimeSpan.FromMinutes(1)); + PlaytimeUpdated?.Invoke(this, _playtime); + +#if DEBUG + LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - {elapsedSeconds}s elapsed. ({now.ToLongTimeString()})"); +#endif + }; + + inGameTimer.Start(); + await proc.WaitForExitAsync(); + inGameTimer.Stop(); + } + + DateTime end = DateTime.Now; + double totalElapsedSeconds = (end - begin).TotalSeconds; + if (totalElapsedSeconds < 0) + { + LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({elapsedSeconds}s)", LogType.Error); + Dialog_InvalidPlaytime(m_mainPage?.Content, elapsedSeconds); + totalElapsedSeconds = elapsedSeconds; + } + + TimeSpan totalTimeSpan = TimeSpan.FromSeconds(totalElapsedSeconds); + LogWriteLine($"Added {totalElapsedSeconds}s [{totalTimeSpan.Hours}h {totalTimeSpan.Minutes}m {totalTimeSpan.Seconds}s] " + + $"to {_gameVersionManager.GamePreset.ProfileName} playtime.", LogType.Default, true); + + _playtime.Update(initialTimeSpan.Add(totalTimeSpan), false); + PlaytimeUpdated?.Invoke(this, _playtime); + } + } +} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs new file mode 100644 index 000000000..efc91da79 --- /dev/null +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs @@ -0,0 +1,215 @@ +using CollapseLauncher.GamePlaytime.Base; +using Hi3Helper; +using Microsoft.Win32; +using System; +using System.Globalization; +using System.Text; +using static Hi3Helper.Logger; + +namespace CollapseLauncher.GamePlaytime.Universal +{ + internal class Playtime + { + #region Fields + + private const string _ValueName = "CollapseLauncher_Playtime"; + private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; + private RegistryKey _RegistryRoot; + + private static bool _IsDeserializing; + + #endregion + + #region Properties + + /// + /// Represents the total time a game was played.

+ /// Default: TimeSpan.MinValue + ///
+ public TimeSpan CurrentPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the daily playtime.
+ /// The ControlDate field is used to check if this value should be reset.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan DailyPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the weekly playtime.
+ /// The ControlDate field is used to check if this value should be reset.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan WeeklyPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the monthly playtime.
+ /// The ControlDate field is used to check if this value should be reset.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan MonthlyPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the last time the game was launched.

+ /// Default: null + ///
+ public DateTime? LastPlayed { get; set; } = null; + + /// + /// Represents a control date.
+ /// This date is used to check if a specific playtime statistic should be reset.

+ /// Default: DateTime.Today + ///
+ public DateTime ControlDate { get; set; } = DateTime.Today; + #endregion + + #region Methods +#nullable enable + /// + /// Reads from the Registry and deserializes the contents.
+ /// Converts RegistryKey values if they are of type DWORD (that is, if they were saved by the old implementation). + ///
+ public static Playtime Load(RegistryKey root) + { + try + { + _IsDeserializing = true; + if (root == null) throw new NullReferenceException($"Cannot load {_ValueName} RegistryKey is unexpectedly not initialized!"); + + object? value = root.GetValue(_ValueName, null); + + // The value being an int means this value was saved by the old implementaion and represents the total number of seconds played. + if (value is int oldPlaytime) + { + object? lastPlayed = root.GetValue(_OldLastPlayedValueName, null); + root.DeleteValue(_OldLastPlayedValueName, false); + + LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); + _IsDeserializing = false; + + Playtime playtime = new Playtime() + { + CurrentPlaytime = TimeSpan.FromSeconds(oldPlaytime), + LastPlayed = lastPlayed != null ? GamePlaytimeBase.BaseDate.AddSeconds((int)lastPlayed) : null, + _RegistryRoot = root + }; + playtime.Save(); + + return playtime; + } + + if (value != null) + { + ReadOnlySpan byteStr = (byte[])value; +#if DEBUG + LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); +#endif + Playtime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new Playtime(); + playtime._RegistryRoot = root; + + return playtime; + } + } + catch (Exception ex) + { + LogWriteLine($"Failed while reading {_ValueName}\r\n{ex}", LogType.Error, true); + } + finally + { + _IsDeserializing = false; + } + + return new Playtime(); + } + + /// + /// Serializes all fields and saves them to the Registry. + /// + public void Save() + { + try + { + if (_RegistryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); + + string data = this.Serialize(UniversalPlaytimeJSONContext.Default, true); + byte[] dataByte = Encoding.UTF8.GetBytes(data); +#if DEBUG + LogWriteLine($"Saved Playtime:\r\n{data}", LogType.Debug, true); +#endif + _RegistryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); + } + catch (Exception ex) + { + LogWriteLine($"Failed to save {_ValueName}!\r\n{ex}", LogType.Error, true); + } + } + + /// + /// Resets all fields and saves to the Registry. + /// + public void Reset() + { + CurrentPlaytime = TimeSpan.Zero; + DailyPlaytime = TimeSpan.Zero; + WeeklyPlaytime = TimeSpan.Zero; + MonthlyPlaytime = TimeSpan.Zero; + ControlDate = DateTime.Today; + + if (!_IsDeserializing) Save(); + } + + /// + /// Updates the current Playtime TimeSpan to the provided value and saves to the Registry.

+ ///
+ /// New playtime value + /// Reset all other fields + public void Update(TimeSpan timeSpan, bool reset = true) + { + if (reset) + { + DailyPlaytime = TimeSpan.Zero; + WeeklyPlaytime = TimeSpan.Zero; + MonthlyPlaytime = TimeSpan.Zero; + ControlDate = DateTime.Today; + } + + CurrentPlaytime = timeSpan; + + if (!_IsDeserializing) Save(); + } + + /// + /// Updates all fields, and checks if any should be reset.
+ /// After it saves to the Registry.

+ ///
+ /// New playtime value + public void Add(TimeSpan timeSpan) + { + CurrentPlaytime = CurrentPlaytime.Add(timeSpan); + + DateTime today = DateTime.Today; + if (today == DateTime.Today) + { + DailyPlaytime = DailyPlaytime.Add(timeSpan); + WeeklyPlaytime = WeeklyPlaytime.Add(timeSpan); + MonthlyPlaytime = MonthlyPlaytime.Add(timeSpan); + } + else + { + DailyPlaytime = timeSpan; + WeeklyPlaytime = IsDifferentWeek(ControlDate, today) ? timeSpan : WeeklyPlaytime.Add(timeSpan); + MonthlyPlaytime = IsDifferentMonth(ControlDate, today) ? timeSpan : MonthlyPlaytime.Add(timeSpan); + + ControlDate = today; + } + + if (!_IsDeserializing) Save(); + } + + private bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; + + private bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); + + #endregion + } +} diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 50a23db3d..8f49b61c9 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -30,18 +30,20 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes switch (GamePreset!.GameType) { case GameNameType.Honkai: - _GameVersion = new GameTypeHonkaiVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameVersion = new GameTypeHonkaiVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); _GameSettings = new HonkaiSettings(_GameVersion); - _GameCache = new HonkaiCache(UIElementParent, _GameVersion); - _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); - _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); + _GameCache = new HonkaiCache(UIElementParent, _GameVersion); + _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); + _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); + _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.StarRail: - _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); + _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); _GameSettings = new StarRailSettings(_GameVersion); - _GameCache = new StarRailCache(UIElementParent, _GameVersion); - _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); - _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); + _GameCache = new StarRailCache(UIElementParent, _GameVersion); + _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); + _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); + _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.Genshin: _GameVersion = new GameTypeGenshinVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); @@ -49,6 +51,7 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = null; _GameRepair = new GenshinRepair(UIElementParent, _GameVersion, _GameVersion.GameAPIProp!.data!.game!.latest!.decompressed_path); _GameInstall = new GenshinInstall(UIElementParent, _GameVersion); + _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; default: throw new NotSupportedException($"[GamePresetProperty.Ctor] Game type: {GamePreset.GameType} ({GamePreset.ProfileName} - {GamePreset.ZoneName}) is not supported!"); @@ -58,6 +61,7 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes internal RegionResourceProp _APIResouceProp { get; set; } internal PresetConfig _GamePreset { get => _GameVersion.GamePreset; } internal IGameSettings _GameSettings { get; set; } + internal IGamePlaytime _GamePlaytime { get; set; } internal IRepair _GameRepair { get; set; } internal ICache _GameCache { get; set; } internal IGameVersionCheck _GameVersion { get; set; } @@ -120,11 +124,12 @@ public void Dispose() _GameInstall?.Dispose(); _APIResouceProp = null; - _GameSettings = null; - _GameRepair = null; - _GameCache = null; - _GameVersion = null; - _GameInstall = null; + _GameSettings = null; + _GameRepair = null; + _GameCache = null; + _GameVersion = null; + _GameInstall = null; + _GamePlaytime = null; } } diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs new file mode 100644 index 000000000..f2dbf36c9 --- /dev/null +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -0,0 +1,16 @@ +using CollapseLauncher.GamePlaytime.Universal; +using System; +using System.Diagnostics; + +namespace CollapseLauncher.Interfaces +{ + internal interface IGamePlaytime + { + event EventHandler PlaytimeUpdated; + + void Reset(); + void ForceUpdate(); + void Update(TimeSpan timeSpan); + void StartSession(Process proc); + } +} diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 6fc99d132..00dd227a7 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -5,6 +5,7 @@ using CollapseLauncher.Dialogs; using CollapseLauncher.Extension; using CollapseLauncher.FileDialogCOM; +using CollapseLauncher.GamePlaytime.Universal; using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.Helper; using CollapseLauncher.Helper.Animation; @@ -53,7 +54,6 @@ using Brush = Microsoft.UI.Xaml.Media.Brush; using Image = Microsoft.UI.Xaml.Controls.Image; using Size = System.Drawing.Size; -using Timer = System.Timers.Timer; using UIElementExtensions = CollapseLauncher.Extension.UIElementExtensions; namespace CollapseLauncher.Pages @@ -183,8 +183,9 @@ private async void StartLoadedRoutine(object sender, RoutedEventArgs e) if (await CurrentGameProperty._GameInstall.TryShowFailedDeltaPatchState()) return; if (await CurrentGameProperty._GameInstall.TryShowFailedGameConversionState()) return; - UpdatePlaytime(); - UpdateLastPlayed(); + CurrentGameProperty._GamePlaytime.PlaytimeUpdated += UpdatePlaytime; + CurrentGameProperty._GamePlaytime.ForceUpdate(); + CheckRunningGameInstance(PageToken.Token); StartCarouselAutoScroll(CarouselToken.Token); @@ -234,6 +235,7 @@ private async void StartLoadedRoutine(object sender, RoutedEventArgs e) private void Page_Unloaded(object sender, RoutedEventArgs e) { IsPageUnload = true; + CurrentGameProperty._GamePlaytime.PlaytimeUpdated -= UpdatePlaytime; PageToken.Cancel(); CarouselToken.Cancel(); } @@ -1331,7 +1333,8 @@ private async void StartGame(object sender, RoutedEventArgs e) break; } - StartPlaytimeCounter(proc, _gamePreset); + CurrentGameProperty._GamePlaytime.StartSession(proc); + if (GetAppConfigValue("LowerCollapsePrioOnGameLaunch").ToBool()) CollapsePrioControl(proc); // Set game process priority to Above Normal when GameBoost is on @@ -1818,26 +1821,20 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) if (_cachedIsGameRunning) return; - UpdatePlaytime(); + CurrentGameProperty._GamePlaytime.ForceUpdate(); } private async void ChangePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (await Dialog_ChangePlaytime(this) != ContentDialogResult.Primary) return; - int playtimeMins = int.Parse("0" + MinutePlaytimeTextBox.Text); - int playtimeHours = int.Parse("0" + HourPlaytimeTextBox.Text); - int finalPlaytimeMinutes = playtimeMins % 60; - int finalPlaytimeHours = playtimeHours + playtimeMins / 60; - if (finalPlaytimeHours > 99999) { finalPlaytimeHours = 99999; finalPlaytimeMinutes = 59; } - MinutePlaytimeTextBox.Text = finalPlaytimeMinutes.ToString(); - HourPlaytimeTextBox.Text = finalPlaytimeHours.ToString(); - - int finalPlaytime = finalPlaytimeHours * 3600 + finalPlaytimeMinutes * 60; - - SavePlaytimeToRegistry(true, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation, finalPlaytime); - LogWriteLine($"Playtime counter changed to {HourPlaytimeTextBox.Text + "h " + MinutePlaytimeTextBox.Text + "m"}. (Previous value: {PlaytimeMainBtn.Text})"); - UpdatePlaytime(false, finalPlaytime); + int Mins = int.Parse("0" + MinutePlaytimeTextBox.Text); + int Hours = int.Parse("0" + HourPlaytimeTextBox.Text); + + TimeSpan time = TimeSpan.FromMinutes(Hours * 60 + Mins); + if (time.Hours > 99999) time = new TimeSpan(99999, 59, 0); + + CurrentGameProperty._GamePlaytime.Update(time); PlaytimeFlyout.Hide(); } @@ -1845,9 +1842,7 @@ private async void ResetPlaytimeButton_Click(object sender, RoutedEventArgs e) { if (await Dialog_ResetPlaytime(this) != ContentDialogResult.Primary) return; - SavePlaytimeToRegistry(true, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation, 0); - LogWriteLine($"Playtime counter changed to 0h 0m. (Previous value: {PlaytimeMainBtn.Text})"); - UpdatePlaytime(false, 0); + CurrentGameProperty._GamePlaytime.Reset(); PlaytimeFlyout.Hide(); } @@ -1856,129 +1851,45 @@ private void NumberValidationTextBox(TextBox sender, TextBoxBeforeTextChangingEv sender.MaxLength = sender == HourPlaytimeTextBox ? 5 : 3; args.Cancel = args.NewText.Any(c => !char.IsDigit(c)); } - #endregion - - #region Playtime Tracker Method - private void UpdatePlaytime(bool readRegistry = true, int value = 0) - { - if (readRegistry) - value = ReadPlaytimeFromRegistry(true, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation); - - HourPlaytimeTextBox.Text = (value / 3600).ToString(); - MinutePlaytimeTextBox.Text = (value % 3600 / 60).ToString(); - PlaytimeMainBtn.Text = string.Format(Lang._HomePage.GamePlaytime_Display, (value / 3600), (value % 3600 / 60)); - } - private DateTime Hoyoception => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - private void UpdateLastPlayed(bool readRegistry = true, int value = 0) + private void UpdatePlaytime(object sender, Playtime playtime) { - if (readRegistry) - value = ReadPlaytimeFromRegistry(false, CurrentGameProperty._GameVersion.GamePreset.ConfigRegistryLocation); - - DateTime last = Hoyoception.AddSeconds(value).ToLocalTime(); - - if (value == 0) + DispatcherQueue.TryEnqueue(() => { - PlaytimeLastOpen.Visibility = Visibility.Collapsed; - return; - } - - PlaytimeLastOpen.Visibility = Visibility.Visible; - string formattedText = string.Format(Lang._HomePage.GamePlaytime_ToolTipDisplay, last.Day, - last.Month, last.Year, last.Hour, last.Minute); - ToolTipService.SetToolTip(PlaytimeBtn, formattedText); - } - - private async void StartPlaytimeCounter(Process proc, PresetConfig gamePreset) - { - int currentPlaytime = ReadPlaytimeFromRegistry(true, gamePreset.ConfigRegistryLocation); - - DateTime begin = DateTime.Now; - int lastPlayed = (int)(begin.ToUniversalTime() - Hoyoception).TotalSeconds; - SavePlaytimeToRegistry(false, gamePreset.ConfigRegistryLocation, lastPlayed); - UpdateLastPlayed(false, lastPlayed); - int numOfLoops = 0; - -#if DEBUG - LogWriteLine($"{gamePreset.ProfileName} - Started session at {begin.ToLongTimeString()}."); -#endif + PlaytimeMainBtn.Text = FormatTimeStamp(playtime.CurrentPlaytime); + HourPlaytimeTextBox.Text = (playtime.CurrentPlaytime.Days * 24 + playtime.CurrentPlaytime.Hours).ToString(); + MinutePlaytimeTextBox.Text = playtime.CurrentPlaytime.Minutes.ToString(); - using (var inGameTimer = new Timer()) - { - inGameTimer.Interval = 60000; - inGameTimer.Elapsed += (_, _) => + if (playtime.LastPlayed == null) { - numOfLoops++; - - DateTime now = DateTime.Now; - int elapsedSeconds = (int)(now - begin).TotalSeconds; - if (elapsedSeconds < 0) - elapsedSeconds = numOfLoops * 60; - - if (GamePropertyVault.GetCurrentGameProperty()._GamePreset.ProfileName == gamePreset.ProfileName) - m_homePage?.DispatcherQueue?.TryEnqueue(() => - { - m_homePage.UpdatePlaytime(false, currentPlaytime + elapsedSeconds); - }); -#if DEBUG - LogWriteLine($"{gamePreset.ProfileName} - {elapsedSeconds}s elapsed. ({now.ToLongTimeString()})"); -#endif - SavePlaytimeToRegistry(true, gamePreset.ConfigRegistryLocation, currentPlaytime + elapsedSeconds); - }; - - inGameTimer.Start(); - await proc.WaitForExitAsync(); - inGameTimer.Stop(); - } + ToolTipService.SetToolTip(PlaytimeBtn, null); + return; + } - DateTime end = DateTime.Now; - int elapsedSeconds = (int)(end - begin).TotalSeconds; - if (elapsedSeconds < 0) - { - LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({elapsedSeconds}s)", LogType.Error); - elapsedSeconds = numOfLoops * 60; - Dialog_InvalidPlaytime(m_mainPage?.Content, elapsedSeconds); - } + DateTime? last = playtime.LastPlayed?.ToLocalTime(); + string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_ToolTipDisplay, last?.Day, + last?.Month, last?.Year, last?.Hour, last?.Minute); - SavePlaytimeToRegistry(true, gamePreset.ConfigRegistryLocation, currentPlaytime + elapsedSeconds); - LogWriteLine($"Added {elapsedSeconds}s [{elapsedSeconds / 3600}h {elapsedSeconds % 3600 / 60}m {elapsedSeconds % 3600 % 60}s] " + - $"to {gamePreset.ProfileName} playtime.", LogType.Default, true); - if (GamePropertyVault.GetCurrentGameProperty()._GamePreset.ProfileName == gamePreset.ProfileName) - m_homePage?.DispatcherQueue?.TryEnqueue(() => + StackPanel panel = new StackPanel() { - m_homePage.UpdatePlaytime(false, currentPlaytime + elapsedSeconds); - }); - } + Children = + { + new TextBlock() { Text = "Last opened" }, + new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = "Daily Playtime" }, + new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = "Weekly Playtime" }, + new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = "Monthly Playtime" }, + new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) } + } + }; - private const string _playtimeRegName = "CollapseLauncher_Playtime"; - private const string _playtimeLastPlayedRegName = "CollapseLauncher_LastPlayed"; - private static int ReadPlaytimeFromRegistry(bool isPlaytime, string regionRegistryKey) - { - try - { - return (int) - Registry.CurrentUser.OpenSubKey(regionRegistryKey, true)! - .GetValue(isPlaytime ? _playtimeRegName : _playtimeLastPlayedRegName, 0); - } - catch (Exception ex) - { - LogWriteLine($"Playtime - There was an error reading from the registry. \n {ex}"); - return 0; - } - } + ToolTipService.SetToolTip(PlaytimeBtn, panel); + }); + return; - private static void SavePlaytimeToRegistry(bool isPlaytime, string regionRegistryKey, int value) - { - try - { - Registry.CurrentUser.OpenSubKey(regionRegistryKey, true)! - .SetValue(isPlaytime ? _playtimeRegName : _playtimeLastPlayedRegName, value, - RegistryValueKind.DWord); - } - catch (Exception ex) - { - LogWriteLine($"Playtime - There was an error writing to registry. \n {ex}"); - } + static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); } #endregion diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 57a84ff20..db4f6fc49 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -159,7 +159,7 @@ "GamePlaytime_Running_Info1": "An instance of this game is currently running, therefore playtime can't be edited.", "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", - "GamePlaytime_ToolTipDisplay": "Last opened in {0:00}/{1:00}/{2:0000} at {3:00}:{4:00}", + "GamePlaytime_ToolTipDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From eba6e58e8bbdd43b13e4c6773d887ada28344564 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Thu, 20 Jun 2024 22:24:51 +0100 Subject: [PATCH 02/23] Playtime - Add Dispose and rename classes --- .../BaseClass/GamePlaytimeBase.cs | 32 --------------- .../GamePlaytime/{Universal => }/Context.cs | 4 +- .../GamePlaytime.cs => Playtime.cs} | 41 ++++++++++++++----- .../CollapsePlaytime.cs} | 41 ++++++++++--------- CollapseLauncher/Classes/GamePropertyVault.cs | 9 ++-- .../Classes/Interfaces/IGamePlaytime.cs | 6 +-- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 4 +- 7 files changed, 64 insertions(+), 73 deletions(-) delete mode 100644 CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs rename CollapseLauncher/Classes/GameManagement/GamePlaytime/{Universal => }/Context.cs (75%) rename CollapseLauncher/Classes/GameManagement/GamePlaytime/{Universal/GamePlaytime.cs => Playtime.cs} (72%) rename CollapseLauncher/Classes/GameManagement/GamePlaytime/{Universal/RegistryClass/Playtime.cs => RegistryClass/CollapsePlaytime.cs} (82%) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs deleted file mode 100644 index 74dbcb120..000000000 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/BaseClass/GamePlaytimeBase.cs +++ /dev/null @@ -1,32 +0,0 @@ -using CollapseLauncher.GamePlaytime.Universal; -using CollapseLauncher.Interfaces; -using Microsoft.Win32; -using System; -using System.IO; - -namespace CollapseLauncher.GamePlaytime.Base -{ - internal class GamePlaytimeBase - { -#nullable enable - #region Properties - internal static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - - protected RegistryKey? RegistryRoot; - protected Playtime? _playtime; - #endregion - - public GamePlaytimeBase(IGameVersionCheck GameVersionManager) - { - _gameVersionManager = GameVersionManager; - } -#nullable disable - - protected IGameVersionCheck _gameVersionManager { get; set; } - - protected static string TimeSpanToString(TimeSpan timeSpan) - { - return $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; - } - } -} diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Context.cs similarity index 75% rename from CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs rename to CollapseLauncher/Classes/GameManagement/GamePlaytime/Context.cs index 0a97f3097..843d46ae3 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/Context.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Context.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; -namespace CollapseLauncher.GamePlaytime.Universal +namespace CollapseLauncher.GamePlaytime { [JsonSourceGenerationOptions(IncludeFields = false, GenerationMode = JsonSourceGenerationMode.Metadata, IgnoreReadOnlyFields = true)] - [JsonSerializable(typeof(Playtime))] + [JsonSerializable(typeof(CollapsePlaytime))] internal sealed partial class UniversalPlaytimeJSONContext : JsonSerializerContext { } } diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs similarity index 72% rename from CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs rename to CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index ede6755a1..b2cb5c9a0 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/GamePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -1,4 +1,4 @@ -using CollapseLauncher.GamePlaytime.Base; +using CollapseLauncher.Extension; using CollapseLauncher.Interfaces; using Hi3Helper; using Microsoft.Win32; @@ -10,25 +10,32 @@ using static CollapseLauncher.Dialogs.SimpleDialogs; using static CollapseLauncher.InnerLauncherConfig; -namespace CollapseLauncher.GamePlaytime.Universal +namespace CollapseLauncher.GamePlaytime { - internal class GamePlaytime : GamePlaytimeBase, IGamePlaytime + internal class Playtime : IGamePlaytime { #region Properties - public event EventHandler PlaytimeUpdated; + public event EventHandler PlaytimeUpdated; +#nullable enable + private RegistryKey? _registryRoot; + private CollapsePlaytime? _playtime; + private IGameVersionCheck _gameVersionManager; + + private CancellationTokenSourceWrapper _token = new(); #endregion - public GamePlaytime(IGameVersionCheck GameVersionManager) : base(GameVersionManager) + public Playtime(IGameVersionCheck GameVersionManager) { string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig); - RegistryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); + _registryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); - RegistryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); + _registryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); _gameVersionManager = GameVersionManager; - _playtime = Playtime.Load(RegistryRoot); + _playtime = CollapsePlaytime.Load(_registryRoot); } +#nullable disable public void ForceUpdate() { @@ -85,7 +92,7 @@ public async void StartSession(Process proc) }; inGameTimer.Start(); - await proc.WaitForExitAsync(); + await proc.WaitForExitAsync(_token.Token); inGameTimer.Stop(); } @@ -93,7 +100,7 @@ public async void StartSession(Process proc) double totalElapsedSeconds = (end - begin).TotalSeconds; if (totalElapsedSeconds < 0) { - LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({elapsedSeconds}s)", LogType.Error); + LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({totalElapsedSeconds}s)", LogType.Error); Dialog_InvalidPlaytime(m_mainPage?.Content, elapsedSeconds); totalElapsedSeconds = elapsedSeconds; } @@ -105,5 +112,19 @@ public async void StartSession(Process proc) _playtime.Update(initialTimeSpan.Add(totalTimeSpan), false); PlaytimeUpdated?.Invoke(this, _playtime); } + + private static string TimeSpanToString(TimeSpan timeSpan) + { + return $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; + } + + public void Dispose() + { + _token.Cancel(); + _playtime?.Save(); + + _playtime = null; + _registryRoot = null; + } } } diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs similarity index 82% rename from CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs rename to CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index efc91da79..7be4c4e63 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Universal/RegistryClass/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -1,27 +1,26 @@ -using CollapseLauncher.GamePlaytime.Base; -using Hi3Helper; +using Hi3Helper; using Microsoft.Win32; using System; using System.Globalization; using System.Text; using static Hi3Helper.Logger; -namespace CollapseLauncher.GamePlaytime.Universal +namespace CollapseLauncher.GamePlaytime { - internal class Playtime + internal class CollapsePlaytime { #region Fields + private static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - private const string _ValueName = "CollapseLauncher_Playtime"; - private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; - private RegistryKey _RegistryRoot; + private const string _ValueName = "CollapseLauncher_Playtime"; + private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; - private static bool _IsDeserializing; + private static bool _IsDeserializing; + private RegistryKey _registryRoot; #endregion #region Properties - /// /// Represents the total time a game was played.

/// Default: TimeSpan.MinValue @@ -69,7 +68,7 @@ internal class Playtime /// Reads from the Registry and deserializes the contents.
/// Converts RegistryKey values if they are of type DWORD (that is, if they were saved by the old implementation). ///
- public static Playtime Load(RegistryKey root) + public static CollapsePlaytime Load(RegistryKey root) { try { @@ -87,11 +86,11 @@ public static Playtime Load(RegistryKey root) LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); _IsDeserializing = false; - Playtime playtime = new Playtime() + CollapsePlaytime playtime = new CollapsePlaytime() { CurrentPlaytime = TimeSpan.FromSeconds(oldPlaytime), - LastPlayed = lastPlayed != null ? GamePlaytimeBase.BaseDate.AddSeconds((int)lastPlayed) : null, - _RegistryRoot = root + LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null, + _registryRoot = root }; playtime.Save(); @@ -104,8 +103,8 @@ public static Playtime Load(RegistryKey root) #if DEBUG LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); #endif - Playtime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new Playtime(); - playtime._RegistryRoot = root; + CollapsePlaytime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); + playtime._registryRoot = root; return playtime; } @@ -119,7 +118,7 @@ public static Playtime Load(RegistryKey root) _IsDeserializing = false; } - return new Playtime(); + return new CollapsePlaytime(); } /// @@ -129,14 +128,14 @@ public void Save() { try { - if (_RegistryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); + if (_registryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); string data = this.Serialize(UniversalPlaytimeJSONContext.Default, true); byte[] dataByte = Encoding.UTF8.GetBytes(data); #if DEBUG LogWriteLine($"Saved Playtime:\r\n{data}", LogType.Debug, true); #endif - _RegistryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); + _registryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); } catch (Exception ex) { @@ -206,10 +205,12 @@ public void Add(TimeSpan timeSpan) if (!_IsDeserializing) Save(); } - private bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; + #endregion - private bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); + #region Utility Methods + private static bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; + private static bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); #endregion } } diff --git a/CollapseLauncher/Classes/GamePropertyVault.cs b/CollapseLauncher/Classes/GamePropertyVault.cs index 8f49b61c9..f93f1f145 100644 --- a/CollapseLauncher/Classes/GamePropertyVault.cs +++ b/CollapseLauncher/Classes/GamePropertyVault.cs @@ -1,4 +1,5 @@ -using CollapseLauncher.GameSettings.Genshin; +using CollapseLauncher.GamePlaytime; +using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.GameSettings.Honkai; using CollapseLauncher.GameSettings.StarRail; using CollapseLauncher.GameVersioning; @@ -35,7 +36,6 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = new HonkaiCache(UIElementParent, _GameVersion); _GameRepair = new HonkaiRepair(UIElementParent, _GameVersion, _GameCache, _GameSettings); _GameInstall = new HonkaiInstall(UIElementParent, _GameVersion, _GameCache, _GameSettings); - _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.StarRail: _GameVersion = new GameTypeStarRailVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); @@ -43,7 +43,6 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = new StarRailCache(UIElementParent, _GameVersion); _GameRepair = new StarRailRepair(UIElementParent, _GameVersion); _GameInstall = new StarRailInstall(UIElementParent, _GameVersion); - _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; case GameNameType.Genshin: _GameVersion = new GameTypeGenshinVersion(UIElementParent, _APIResouceProp, GameName, GameRegion); @@ -51,11 +50,12 @@ internal GamePresetProperty(UIElement UIElementParent, RegionResourceProp APIRes _GameCache = null; _GameRepair = new GenshinRepair(UIElementParent, _GameVersion, _GameVersion.GameAPIProp!.data!.game!.latest!.decompressed_path); _GameInstall = new GenshinInstall(UIElementParent, _GameVersion); - _GamePlaytime = new GamePlaytime.Universal.GamePlaytime(_GameVersion); break; default: throw new NotSupportedException($"[GamePresetProperty.Ctor] Game type: {GamePreset.GameType} ({GamePreset.ProfileName} - {GamePreset.ZoneName}) is not supported!"); } + + _GamePlaytime = new Playtime(_GameVersion); } internal RegionResourceProp _APIResouceProp { get; set; } @@ -122,6 +122,7 @@ public void Dispose() _GameRepair?.Dispose(); _GameCache?.Dispose(); _GameInstall?.Dispose(); + _GamePlaytime?.Dispose(); _APIResouceProp = null; _GameSettings = null; diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index f2dbf36c9..291b31003 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -1,12 +1,12 @@ -using CollapseLauncher.GamePlaytime.Universal; +using CollapseLauncher.GamePlaytime; using System; using System.Diagnostics; namespace CollapseLauncher.Interfaces { - internal interface IGamePlaytime + internal interface IGamePlaytime : IDisposable { - event EventHandler PlaytimeUpdated; + event EventHandler PlaytimeUpdated; void Reset(); void ForceUpdate(); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 00dd227a7..c3e30519a 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -5,7 +5,7 @@ using CollapseLauncher.Dialogs; using CollapseLauncher.Extension; using CollapseLauncher.FileDialogCOM; -using CollapseLauncher.GamePlaytime.Universal; +using CollapseLauncher.GamePlaytime; using CollapseLauncher.GameSettings.Genshin; using CollapseLauncher.Helper; using CollapseLauncher.Helper.Animation; @@ -1852,7 +1852,7 @@ private void NumberValidationTextBox(TextBox sender, TextBoxBeforeTextChangingEv args.Cancel = args.NewText.Any(c => !char.IsDigit(c)); } - private void UpdatePlaytime(object sender, Playtime playtime) + private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { DispatcherQueue.TryEnqueue(() => { From 9fcecc095ee7e930b1cb0f87afcd93d2461182f3 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Sat, 22 Jun 2024 12:12:23 +0100 Subject: [PATCH 03/23] Playtime - Add localization support --- .../GamePlaytime/RegistryClass/CollapsePlaytime.cs | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 12 ++++++------ Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 6 +++++- Hi3Helper.Core/Lang/en_US.json | 6 +++++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 7be4c4e63..0096aea62 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -207,7 +207,7 @@ public void Add(TimeSpan timeSpan) #endregion - #region Utility Methods + #region Utility private static bool IsDifferentMonth(DateTime date1, DateTime date2) => date1.Year != date2.Year || date1.Month != date2.Month; private static bool IsDifferentWeek(DateTime date1, DateTime date2) => date1.Year != date2.Year || ISOWeek.GetWeekOfYear(date1) != ISOWeek.GetWeekOfYear(date2); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index c3e30519a..d30974832 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -1815,7 +1815,7 @@ private async void MoveGameLocationButton_Click(object sender, RoutedEventArgs e } #endregion - #region Playtime Buttons + #region Playtime private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (_cachedIsGameRunning) @@ -1867,20 +1867,20 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) } DateTime? last = playtime.LastPlayed?.ToLocalTime(); - string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_ToolTipDisplay, last?.Day, + string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_DateDisplay, last?.Day, last?.Month, last?.Year, last?.Hour, last?.Minute); StackPanel panel = new StackPanel() { Children = { - new TextBlock() { Text = "Last opened" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = "Daily Playtime" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Daily }, new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = "Weekly Playtime" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Weekly }, new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = "Monthly Playtime" }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Monthly }, new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) } } }; diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index c53daa33a..22de83977 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -58,7 +58,11 @@ public sealed class LangHomePage public string GamePlaytime_Running_Info1 { get; set; } = LangFallback?._HomePage.GamePlaytime_Running_Info1; public string GamePlaytime_Running_Info2 { get; set; } = LangFallback?._HomePage.GamePlaytime_Running_Info2; public string GamePlaytime_Display { get; set; } = LangFallback?._HomePage.GamePlaytime_Display; - public string GamePlaytime_ToolTipDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_ToolTipDisplay; + public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; + public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; + public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; + public string GamePlaytime_Stats_Weekly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Weekly; + public string GamePlaytime_Stats_Monthly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Monthly; public string PostPanel_Events { get; set; } = LangFallback?._HomePage.PostPanel_Events; public string PostPanel_Notices { get; set; } = LangFallback?._HomePage.PostPanel_Notices; public string PostPanel_Info { get; set; } = LangFallback?._HomePage.PostPanel_Info; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index db4f6fc49..ae8722930 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -159,7 +159,11 @@ "GamePlaytime_Running_Info1": "An instance of this game is currently running, therefore playtime can't be edited.", "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", - "GamePlaytime_ToolTipDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", + "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", + "GamePlaytime_Stats_LastPlayed": "Last played at", + "GamePlaytime_Stats_Daily": "Daily Playtime", + "GamePlaytime_Stats_Weekly": "Weekly Playtime", + "GamePlaytime_Stats_Monthly": "Monthly Playtime", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From a9235116c73476c6df6159cce02d4e83908805d3 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:56:02 +0100 Subject: [PATCH 04/23] Playtime - Misc fixes + Last Session Played --- .../GameManagement/GamePlaytime/Playtime.cs | 28 ++++--- .../RegistryClass/CollapsePlaytime.cs | 74 +++++++++++-------- .../Classes/Interfaces/IGamePlaytime.cs | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 14 ++-- Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 1 + Hi3Helper.Core/Lang/en_US.json | 3 +- 6 files changed, 69 insertions(+), 53 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index b2cb5c9a0..6992b66ae 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -17,8 +17,10 @@ internal class Playtime : IGamePlaytime #region Properties public event EventHandler PlaytimeUpdated; #nullable enable + public CollapsePlaytime CollapsePlaytime => _playtime; + private RegistryKey? _registryRoot; - private CollapsePlaytime? _playtime; + private CollapsePlaytime _playtime; private IGameVersionCheck _gameVersionManager; private CancellationTokenSourceWrapper _token = new(); @@ -26,25 +28,20 @@ internal class Playtime : IGamePlaytime public Playtime(IGameVersionCheck GameVersionManager) { - string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig); + string registryPath = Path.Combine($"Software\\{GameVersionManager.VendorTypeProp.VendorType}", GameVersionManager.GamePreset.InternalGameNameInConfig!); _registryRoot = Registry.CurrentUser.OpenSubKey(registryPath, true); _registryRoot ??= Registry.CurrentUser.CreateSubKey(registryPath, true, RegistryOptions.None); _gameVersionManager = GameVersionManager; - _playtime = CollapsePlaytime.Load(_registryRoot); + _playtime = CollapsePlaytime.Load(_registryRoot, _gameVersionManager.GamePreset.HashID); } #nullable disable - public void ForceUpdate() - { - PlaytimeUpdated?.Invoke(this, _playtime); - } - public void Update(TimeSpan timeSpan) { - TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + TimeSpan oldTimeSpan = _playtime.TotalPlaytime; _playtime.Update(timeSpan); PlaytimeUpdated?.Invoke(this, _playtime); @@ -54,7 +51,7 @@ public void Update(TimeSpan timeSpan) public void Reset() { - TimeSpan oldTimeSpan = _playtime.CurrentPlaytime; + TimeSpan oldTimeSpan = _playtime.TotalPlaytime; _playtime.Reset(); PlaytimeUpdated?.Invoke(this, _playtime); @@ -65,10 +62,12 @@ public void Reset() public async void StartSession(Process proc) { DateTime begin = DateTime.Now; - TimeSpan initialTimeSpan = _playtime.CurrentPlaytime; + TimeSpan initialTimeSpan = _playtime.TotalPlaytime; - _playtime.LastPlayed = begin; + _playtime.LastPlayed = begin; + _playtime.LastSession = TimeSpan.Zero; _playtime.Save(); + PlaytimeUpdated?.Invoke(this, _playtime); #if DEBUG LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - Started session at {begin.ToLongTimeString()}."); @@ -83,9 +82,8 @@ public async void StartSession(Process proc) elapsedSeconds += 60; DateTime now = DateTime.Now; - _playtime.Add(TimeSpan.FromMinutes(1)); + _playtime.AddMinute(); PlaytimeUpdated?.Invoke(this, _playtime); - #if DEBUG LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - {elapsedSeconds}s elapsed. ({now.ToLongTimeString()})"); #endif @@ -121,7 +119,7 @@ private static string TimeSpanToString(TimeSpan timeSpan) public void Dispose() { _token.Cancel(); - _playtime?.Save(); + _playtime.Save(); _playtime = null; _registryRoot = null; diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 0096aea62..3cd28dd70 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -1,6 +1,7 @@ using Hi3Helper; using Microsoft.Win32; using System; +using System.Collections.Generic; using System.Globalization; using System.Text; using static Hi3Helper.Logger; @@ -15,17 +16,24 @@ internal class CollapsePlaytime private const string _ValueName = "CollapseLauncher_Playtime"; private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; - private static bool _IsDeserializing; - private RegistryKey _registryRoot; + private static Dictionary _IsDeserializing = []; + private RegistryKey _registryRoot; + private int _hashID; #endregion #region Properties /// /// Represents the total time a game was played.

- /// Default: TimeSpan.MinValue + /// Default: TimeSpan.Zero ///
- public TimeSpan CurrentPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan TotalPlaytime { get; set; } = TimeSpan.Zero; + + /// + /// Represents the total time the last/current session lasted.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan LastSession { get; set; } = TimeSpan.Zero; /// /// Represents the daily playtime.
@@ -68,11 +76,11 @@ internal class CollapsePlaytime /// Reads from the Registry and deserializes the contents.
/// Converts RegistryKey values if they are of type DWORD (that is, if they were saved by the old implementation). ///
- public static CollapsePlaytime Load(RegistryKey root) + public static CollapsePlaytime Load(RegistryKey root, int hashID) { try { - _IsDeserializing = true; + _IsDeserializing[hashID] = true; if (root == null) throw new NullReferenceException($"Cannot load {_ValueName} RegistryKey is unexpectedly not initialized!"); object? value = root.GetValue(_ValueName, null); @@ -84,13 +92,14 @@ public static CollapsePlaytime Load(RegistryKey root) root.DeleteValue(_OldLastPlayedValueName, false); LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); - _IsDeserializing = false; + _IsDeserializing[hashID] = false; CollapsePlaytime playtime = new CollapsePlaytime() { - CurrentPlaytime = TimeSpan.FromSeconds(oldPlaytime), + TotalPlaytime = TimeSpan.FromSeconds(oldPlaytime), LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null, - _registryRoot = root + _registryRoot = root, + _hashID = hashID }; playtime.Save(); @@ -105,6 +114,7 @@ public static CollapsePlaytime Load(RegistryKey root) #endif CollapsePlaytime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); playtime._registryRoot = root; + playtime._hashID = hashID; return playtime; } @@ -115,10 +125,10 @@ public static CollapsePlaytime Load(RegistryKey root) } finally { - _IsDeserializing = false; + _IsDeserializing[hashID] = false; } - return new CollapsePlaytime(); + return new CollapsePlaytime() { _hashID = hashID, _registryRoot = root }; } /// @@ -148,13 +158,14 @@ public void Save() /// public void Reset() { - CurrentPlaytime = TimeSpan.Zero; - DailyPlaytime = TimeSpan.Zero; - WeeklyPlaytime = TimeSpan.Zero; + TotalPlaytime = TimeSpan.Zero; + LastSession = TimeSpan.Zero; + DailyPlaytime = TimeSpan.Zero; + WeeklyPlaytime = TimeSpan.Zero; MonthlyPlaytime = TimeSpan.Zero; - ControlDate = DateTime.Today; + ControlDate = DateTime.Today; - if (!_IsDeserializing) Save(); + if (!_IsDeserializing[_hashID]) Save(); } /// @@ -166,43 +177,46 @@ public void Update(TimeSpan timeSpan, bool reset = true) { if (reset) { + LastSession = TimeSpan.Zero; DailyPlaytime = TimeSpan.Zero; WeeklyPlaytime = TimeSpan.Zero; MonthlyPlaytime = TimeSpan.Zero; ControlDate = DateTime.Today; } - CurrentPlaytime = timeSpan; + TotalPlaytime = timeSpan; - if (!_IsDeserializing) Save(); + if (!_IsDeserializing[_hashID]) Save(); } /// - /// Updates all fields, and checks if any should be reset.
+ /// Adds a minute to all fields, and checks if any should be reset.
/// After it saves to the Registry.

///
- /// New playtime value - public void Add(TimeSpan timeSpan) + public void AddMinute() { - CurrentPlaytime = CurrentPlaytime.Add(timeSpan); + TimeSpan minute = TimeSpan.FromMinutes(1); + DateTime today = DateTime.Today; - DateTime today = DateTime.Today; + TotalPlaytime = TotalPlaytime.Add(minute); + LastSession = LastSession.Add(minute); + if (today == DateTime.Today) { - DailyPlaytime = DailyPlaytime.Add(timeSpan); - WeeklyPlaytime = WeeklyPlaytime.Add(timeSpan); - MonthlyPlaytime = MonthlyPlaytime.Add(timeSpan); + DailyPlaytime = DailyPlaytime.Add(minute); + WeeklyPlaytime = WeeklyPlaytime.Add(minute); + MonthlyPlaytime = MonthlyPlaytime.Add(minute); } else { - DailyPlaytime = timeSpan; - WeeklyPlaytime = IsDifferentWeek(ControlDate, today) ? timeSpan : WeeklyPlaytime.Add(timeSpan); - MonthlyPlaytime = IsDifferentMonth(ControlDate, today) ? timeSpan : MonthlyPlaytime.Add(timeSpan); + DailyPlaytime = minute; + WeeklyPlaytime = IsDifferentWeek(ControlDate, today) ? minute : WeeklyPlaytime.Add(minute); + MonthlyPlaytime = IsDifferentMonth(ControlDate, today) ? minute : MonthlyPlaytime.Add(minute); ControlDate = today; } - if (!_IsDeserializing) Save(); + if (!_IsDeserializing[_hashID]) Save(); } #endregion diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index 291b31003..9b6a21529 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -7,9 +7,9 @@ namespace CollapseLauncher.Interfaces internal interface IGamePlaytime : IDisposable { event EventHandler PlaytimeUpdated; + CollapsePlaytime? CollapsePlaytime { get; } void Reset(); - void ForceUpdate(); void Update(TimeSpan timeSpan); void StartSession(Process proc); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index d30974832..dc5ebb64a 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -184,7 +184,7 @@ private async void StartLoadedRoutine(object sender, RoutedEventArgs e) if (await CurrentGameProperty._GameInstall.TryShowFailedGameConversionState()) return; CurrentGameProperty._GamePlaytime.PlaytimeUpdated += UpdatePlaytime; - CurrentGameProperty._GamePlaytime.ForceUpdate(); + UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); CheckRunningGameInstance(PageToken.Token); StartCarouselAutoScroll(CarouselToken.Token); @@ -1821,7 +1821,7 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) if (_cachedIsGameRunning) return; - CurrentGameProperty._GamePlaytime.ForceUpdate(); + UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); } private async void ChangePlaytimeButton_Click(object sender, RoutedEventArgs e) @@ -1856,9 +1856,9 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { DispatcherQueue.TryEnqueue(() => { - PlaytimeMainBtn.Text = FormatTimeStamp(playtime.CurrentPlaytime); - HourPlaytimeTextBox.Text = (playtime.CurrentPlaytime.Days * 24 + playtime.CurrentPlaytime.Hours).ToString(); - MinutePlaytimeTextBox.Text = playtime.CurrentPlaytime.Minutes.ToString(); + PlaytimeMainBtn.Text = FormatTimeStamp(playtime.TotalPlaytime); + HourPlaytimeTextBox.Text = (playtime.TotalPlaytime.Days * 24 + playtime.TotalPlaytime.Hours).ToString(); + MinutePlaytimeTextBox.Text = playtime.TotalPlaytime.Minutes.ToString(); if (playtime.LastPlayed == null) { @@ -1876,12 +1876,14 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastSession }, + new TextBlock() { Text = FormatTimeStamp(playtime.LastSession), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Daily }, new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Weekly }, new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Monthly }, - new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) } + new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold } } }; diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index 22de83977..056bf47e5 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -60,6 +60,7 @@ public sealed class LangHomePage public string GamePlaytime_Display { get; set; } = LangFallback?._HomePage.GamePlaytime_Display; public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; + public string GamePlaytime_Stats_LastSession { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession; public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; public string GamePlaytime_Stats_Weekly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Weekly; public string GamePlaytime_Stats_Monthly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Monthly; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index ae8722930..60531ac5b 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -160,7 +160,8 @@ "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", - "GamePlaytime_Stats_LastPlayed": "Last played at", + "GamePlaytime_Stats_LastPlayed": "Last Played At", + "GamePlaytime_Stats_LastSession": "Last Session", "GamePlaytime_Stats_Daily": "Daily Playtime", "GamePlaytime_Stats_Weekly": "Weekly Playtime", "GamePlaytime_Stats_Monthly": "Monthly Playtime", From aa5e5c773e4ea705d56afa63e3f073cab67526c3 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Wed, 26 Jun 2024 18:53:48 +0100 Subject: [PATCH 05/23] Playtime - Grid Stats Display --- .../Classes/Interfaces/IGamePlaytime.cs | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml | 7 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 243 +++++++++++------- Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 1 + Hi3Helper.Core/Lang/en_US.json | 7 +- 5 files changed, 155 insertions(+), 105 deletions(-) diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index 9b6a21529..037e99e55 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -7,7 +7,7 @@ namespace CollapseLauncher.Interfaces internal interface IGamePlaytime : IDisposable { event EventHandler PlaytimeUpdated; - CollapsePlaytime? CollapsePlaytime { get; } + CollapsePlaytime CollapsePlaytime { get; } void Reset(); void Update(TimeSpan timeSpan); diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 31ab10c52..5dee37ed8 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -920,8 +920,7 @@ + MaxWidth="224"> @@ -938,8 +937,7 @@ BeforeTextChanging="NumberValidationTextBox" Text="" /> - + ImageFileInfo.Load(copyIconFileStream)); - var width = (int)(iconImageInfo.Frames[0].Width * scaleFactor); - var height = (int)(iconImageInfo.Frames[0].Height * scaleFactor); + var width = (int)(iconImageInfo.Frames[0].Width * scaleFactor); + var height = (int)(iconImageInfo.Frames[0].Height * scaleFactor); copyIconFileStream.Position = 0; // Reset the original icon stream position await ImageLoaderHelper.ResizeImageStream(copyIconFileStream, cachedIconFileStream, @@ -344,7 +345,7 @@ public async void StartCarouselAutoScroll(int delaySeconds = 5) } } - private void CarouselPointerExited(object sender = null, PointerRoutedEventArgs e = null) => CarouselRestartScroll(5); + private void CarouselPointerExited(object sender = null, PointerRoutedEventArgs e = null) => CarouselRestartScroll(5); private async void CarouselPointerEntered(object sender = null, PointerRoutedEventArgs e = null) => await CarouselStopScroll(); public async void CarouselRestartScroll(int delaySeconds = 5) @@ -373,10 +374,10 @@ private async void HideImageCarousel(bool hide) HideImageEventImg(hide); - Storyboard storyboard = new Storyboard(); + Storyboard storyboard = new Storyboard(); DoubleAnimation OpacityAnimation = new DoubleAnimation(); - OpacityAnimation.From = hide ? 1 : 0; - OpacityAnimation.To = hide ? 0 : 1; + OpacityAnimation.From = hide ? 1 : 0; + OpacityAnimation.To = hide ? 0 : 1; OpacityAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.10)); Storyboard.SetTarget(OpacityAnimation, ImageCarouselAndPostPanel); @@ -397,8 +398,8 @@ private void FadeInSocMedButton(object sender, PointerRoutedEventArgs e) Button btn = (Button)sender; btn.Translation = Shadow16; - Grid iconGrid = btn.FindDescendant(); - Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; + Grid iconGrid = btn.FindDescendant(); + Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; Image iconSecond = iconGrid!.FindDescendant("IconHover") as Image; TimeSpan dur = TimeSpan.FromSeconds(0.25f); @@ -418,8 +419,8 @@ private void FadeOutSocMedButton(object sender, PointerRoutedEventArgs e) flyout!.Hide(); } - Grid iconGrid = btn.FindDescendant(); - Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; + Grid iconGrid = btn.FindDescendant(); + Image iconFirst = iconGrid!.FindDescendant("Icon") as Image; Image iconSecond = iconGrid!.FindDescendant("IconHover") as Image; TimeSpan dur = TimeSpan.FromSeconds(0.25f); @@ -858,12 +859,12 @@ private void RaiseBackgroundInstallationStatus(GameInstallStateEnum GameInstalla private async void CheckRunningGameInstance(CancellationToken Token) { - TextBlock StartGameBtnText = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); - FontIcon StartGameBtnIcon = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); - Grid StartGameBtnAnimatedIconGrid = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); + TextBlock StartGameBtnText = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); + FontIcon StartGameBtnIcon = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); + Grid StartGameBtnAnimatedIconGrid = (StartGameBtn.Content as Grid)!.Children.OfType().FirstOrDefault(); // AnimatedVisualPlayer StartGameBtnAnimatedIcon = StartGameBtnAnimatedIconGrid!.Children.OfType().FirstOrDefault(); - string StartGameBtnIconGlyph = StartGameBtnIcon!.Glyph; - string StartGameBtnRunningIconGlyph = ""; + string StartGameBtnIconGlyph = StartGameBtnIcon!.Glyph; + string StartGameBtnRunningIconGlyph = ""; StartGameBtnIcon.EnableSingleImplicitAnimation(VisualPropertyType.Opacity); StartGameBtnAnimatedIconGrid.EnableSingleImplicitAnimation(VisualPropertyType.Opacity); @@ -876,52 +877,52 @@ private async void CheckRunningGameInstance(CancellationToken Token) { _cachedIsGameRunning = true; - StartGameBtn.IsEnabled = false; - StartGameBtnText!.Text = Lang._HomePage.StartBtnRunning; - StartGameBtnIcon.Glyph = StartGameBtnRunningIconGlyph; - StartGameBtnAnimatedIconGrid.Opacity = 0; - StartGameBtnIcon.Opacity = 1; + StartGameBtn.IsEnabled = false; + StartGameBtnText!.Text = Lang._HomePage.StartBtnRunning; + StartGameBtnIcon.Glyph = StartGameBtnRunningIconGlyph; + StartGameBtnAnimatedIconGrid.Opacity = 0; + StartGameBtnIcon.Opacity = 1; //GameStartupSetting.IsEnabled = false; - RepairGameButton.IsEnabled = false; - UninstallGameButton.IsEnabled = false; - ConvertVersionButton.IsEnabled = false; - CustomArgsTextBox.IsEnabled = false; + RepairGameButton.IsEnabled = false; + UninstallGameButton.IsEnabled = false; + ConvertVersionButton.IsEnabled = false; + CustomArgsTextBox.IsEnabled = false; MoveGameLocationButton.IsEnabled = false; - StopGameButton.IsEnabled = true; + StopGameButton.IsEnabled = true; - PlaytimeIdleStack.Visibility = Visibility.Collapsed; + PlaytimeIdleStack.Visibility = Visibility.Collapsed; PlaytimeRunningStack.Visibility = Visibility.Visible; - #if !DISABLEDISCORD +#if !DISABLEDISCORD AppDiscordPresence?.SetActivity(ActivityType.Play); - #endif +#endif await Task.Delay(RefreshRate, Token); } _cachedIsGameRunning = false; - StartGameBtn.IsEnabled = true; - StartGameBtnText!.Text = Lang._HomePage.StartBtn; - StartGameBtnIcon.Glyph = StartGameBtnIconGlyph; - StartGameBtnAnimatedIconGrid.Opacity = 1; - StartGameBtnIcon.Opacity = 0; + StartGameBtn.IsEnabled = true; + StartGameBtnText!.Text = Lang._HomePage.StartBtn; + StartGameBtnIcon.Glyph = StartGameBtnIconGlyph; + StartGameBtnAnimatedIconGrid.Opacity = 1; + StartGameBtnIcon.Opacity = 0; - GameStartupSetting.IsEnabled = true; - RepairGameButton.IsEnabled = true; + GameStartupSetting.IsEnabled = true; + RepairGameButton.IsEnabled = true; MoveGameLocationButton.IsEnabled = true; - UninstallGameButton.IsEnabled = true; - ConvertVersionButton.IsEnabled = true; - CustomArgsTextBox.IsEnabled = true; - StopGameButton.IsEnabled = false; + UninstallGameButton.IsEnabled = true; + ConvertVersionButton.IsEnabled = true; + CustomArgsTextBox.IsEnabled = true; + StopGameButton.IsEnabled = false; - PlaytimeIdleStack.Visibility = Visibility.Visible; + PlaytimeIdleStack.Visibility = Visibility.Visible; PlaytimeRunningStack.Visibility = Visibility.Collapsed; - #if !DISABLEDISCORD +#if !DISABLEDISCORD AppDiscordPresence?.SetActivity(ActivityType.Idle); - #endif +#endif await Task.Delay(RefreshRate, Token); } @@ -931,7 +932,7 @@ private async void CheckRunningGameInstance(CancellationToken Token) // Ignore } catch (Exception e) - { + { LogWriteLine($"Error when checking if game is running!\r\n{e}", LogType.Error, true); } } @@ -1497,7 +1498,7 @@ internal async void StartResizableWindowPayload(string executableName, IGameSett #region Game Launch Argument Builder bool RequireWindowExclusivePayload; - + internal string GetLaunchArguments(IGameSettingsUniversal _Settings) { StringBuilder parameter = new StringBuilder(); @@ -1552,12 +1553,12 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) parameter.Append("-window-mode exclusive -screen-fullscreen 1 "); RequireWindowExclusivePayload = true; } - + // Enable mobile mode if (_Settings.SettingsCollapseMisc.LaunchMobileMode) { - const string regLoc = GameSettings.StarRail.Model._ValueName; - var regRoot = GameSettings.Base.SettingsBase.RegistryRoot; + const string regLoc = GameSettings.StarRail.Model._ValueName; + var regRoot = GameSettings.Base.SettingsBase.RegistryRoot; if (regRoot != null || !string.IsNullOrEmpty(regLoc)) { @@ -1603,7 +1604,7 @@ internal string GetLaunchArguments(IGameSettingsUniversal _Settings) RequireWindowExclusivePayload = true; LogWriteLine($"Exclusive mode is enabled in Genshin Impact, stability may suffer!\r\nTry not to Alt+Tab when game is on its loading screen :)", LogType.Warning, true); } - + // Enable mobile mode if (_Settings.SettingsCollapseMisc.LaunchMobileMode) parameter.Append("use_mobile_platform -is_cloud 1 -platform_type CLOUD_THIRD_PARTY_MOBILE "); @@ -1689,9 +1690,9 @@ public async void TryInstallMediaPack() { StartInfo = new ProcessStartInfo { - FileName = Path.Combine(AppFolder, "Misc", "InstallMediaPack.cmd"), + FileName = Path.Combine(AppFolder, "Misc", "InstallMediaPack.cmd"), UseShellExecute = true, - Verb = "runas" + Verb = "runas" } }; @@ -1738,23 +1739,23 @@ public async void ReadOutputLog() Directory.CreateDirectory(Path.GetDirectoryName(logPath)!); await using (FileStream fs = new FileStream(logPath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) - using (StreamReader reader = new StreamReader(fs)) + using (StreamReader reader = new StreamReader(fs)) + { + while (true) { - while (true) + while (!reader.EndOfStream) { - while (!reader.EndOfStream) + var line = await reader.ReadLineAsync(WatchOutputLog.Token); + if (RequireWindowExclusivePayload && line == "MoleMole.MonoGameEntry:Awake()") { - var line = await reader.ReadLineAsync(WatchOutputLog.Token); - if (RequireWindowExclusivePayload && line == "MoleMole.MonoGameEntry:Awake()") - { - StartExclusiveWindowPayload(); - RequireWindowExclusivePayload = false; - } - LogWriteLine(line!, LogType.Game, GetAppConfigValue("IncludeGameLogs").ToBool()); + StartExclusiveWindowPayload(); + RequireWindowExclusivePayload = false; } - await Task.Delay(100, WatchOutputLog.Token); + LogWriteLine(line!, LogType.Game, GetAppConfigValue("IncludeGameLogs").ToBool()); } + await Task.Delay(100, WatchOutputLog.Token); } + } } catch (OperationCanceledException) { } catch (Exception ex) @@ -1876,12 +1877,12 @@ private async void ChangePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (await Dialog_ChangePlaytime(this) != ContentDialogResult.Primary) return; - int Mins = int.Parse("0" + MinutePlaytimeTextBox.Text); - int Hours = int.Parse("0" + HourPlaytimeTextBox.Text); - - TimeSpan time = TimeSpan.FromMinutes(Hours * 60 + Mins); + int Mins = int.Parse("0" + MinutePlaytimeTextBox.Text); + int Hours = int.Parse("0" + HourPlaytimeTextBox.Text); + + TimeSpan time = TimeSpan.FromMinutes(Hours * 60 + Mins); if (time.Hours > 99999) time = new TimeSpan(99999, 59, 0); - + CurrentGameProperty._GamePlaytime.Update(time); PlaytimeFlyout.Hide(); } @@ -1922,16 +1923,44 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) { Children = { - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, - new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastSession }, - new TextBlock() { Text = FormatTimeStamp(playtime.LastSession), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Daily }, - new TextBlock() { Text = FormatTimeStamp(playtime.DailyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Weekly }, - new TextBlock() { Text = FormatTimeStamp(playtime.WeeklyPlaytime), FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 5) }, - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_Monthly }, - new TextBlock() { Text = FormatTimeStamp(playtime.MonthlyPlaytime), FontWeight = FontWeights.Bold } + new Grid() + { + ColumnDefinitions = + { + new ColumnDefinition(), + new ColumnDefinition() { Width = GridLength.Auto }, + new ColumnDefinition() + }, + ColumnSpacing = 20, + Children = + { + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime, HorizontalAlignment.Left, gridCol:0), + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, gridCol:1), + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, HorizontalAlignment.Right, gridCol:2) + } + }, + new Grid() + { + ColumnDefinitions = + { + new ColumnDefinition() { Width = GridLength.Auto }, + new ColumnDefinition() + }, + ColumnSpacing = 20, + Children = + { + new StackPanel() + { + HorizontalAlignment = HorizontalAlignment.Left, + Children = + { + new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, + new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold } + } + }, + TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, HorizontalAlignment.Right, isLast:true, gridCol:1) + } + } } }; @@ -1939,6 +1968,28 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) }); return; + static StackPanel TimeSpanPanel(string label, TimeSpan time, HorizontalAlignment alignment = HorizontalAlignment.Center, bool isLast = false, int gridCol = 0) + { + StackPanel panel = new StackPanel() + { + HorizontalAlignment = alignment, + Orientation = Orientation.Vertical, + Children = + { + new TextBlock() { Text = label }, + new TextBlock() + { + Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, + Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), + HorizontalAlignment = alignment + } + } + }; + Grid.SetColumn(panel, gridCol); + + return panel; + } + static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); } #endregion @@ -2091,7 +2142,7 @@ private async void CollapsePrioControl(Process proc) using (Process collapseProcess = Process.GetCurrentProcess()) { collapseProcess.PriorityBoostEnabled = false; - collapseProcess.PriorityClass = ProcessPriorityClass.BelowNormal; + collapseProcess.PriorityClass = ProcessPriorityClass.BelowNormal; LogWriteLine($"Collapse process [PID {collapseProcess.Id}] priority is set to Below Normal, " + $"PriorityBoost is off, carousel is temporarily stopped", LogType.Default, true); } @@ -2102,7 +2153,7 @@ private async void CollapsePrioControl(Process proc) using (Process collapseProcess = Process.GetCurrentProcess()) { collapseProcess.PriorityBoostEnabled = true; - collapseProcess.PriorityClass = ProcessPriorityClass.Normal; + collapseProcess.PriorityClass = ProcessPriorityClass.Normal; LogWriteLine($"Collapse process [PID {collapseProcess.Id}] priority is set to Normal, " + $"PriorityBoost is on, carousel is started", LogType.Default, true); } @@ -2133,7 +2184,7 @@ private void GenshinHDREnforcer() private async void GameBoost_Invoke(GamePresetProperty gameProp) { - #nullable enable +#nullable enable // Init new target process Process? toTargetProc = null; try @@ -2169,7 +2220,7 @@ private async void GameBoost_Invoke(GamePresetProperty gameProp) LogWriteLine($"[HomePage::GameBoost_Invoke] There has been error while boosting game priority to Above Normal!\r\n" + $"\tTarget Process : {toTargetProc?.ProcessName} [{toTargetProc?.Id}]\r\n{ex}", LogType.Error, true); } - #nullable restore +#nullable restore } #endregion @@ -2361,7 +2412,7 @@ private void ApplyShadowToImageElement(object sender, RoutedEventArgs e) private bool IsPointerInsideSidePanel; private bool IsSidePanelCurrentlyScaledOut; - + private async void SidePanelScaleOutHoveredPointerEntered(object sender, PointerRoutedEventArgs e) { IsPointerInsideSidePanel = true; @@ -2371,9 +2422,9 @@ private async void SidePanelScaleOutHoveredPointerEntered(object sender, Pointer if (IsSidePanelCurrentlyScaledOut) return; if (!IsPointerInsideSidePanel) return; - var toScale = WindowSize.WindowSize.CurrentWindowSize.PostEventPanelScaleFactor; + var toScale = WindowSize.WindowSize.CurrentWindowSize.PostEventPanelScaleFactor; var storyboard = new Storyboard(); - var transform = (CompositeTransform)elementPanel.RenderTransform; + var transform = (CompositeTransform)elementPanel.RenderTransform; transform.CenterY = elementPanel.ActualHeight + 8; var cubicEaseOut = new CubicEase() { @@ -2382,9 +2433,9 @@ private async void SidePanelScaleOutHoveredPointerEntered(object sender, Pointer var scaleXAnim = new DoubleAnimation { - From = transform.ScaleX, - To = toScale, - Duration = new Duration(TimeSpan.FromSeconds(0.2)), + From = transform.ScaleX, + To = toScale, + Duration = new Duration(TimeSpan.FromSeconds(0.2)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleXAnim, transform); @@ -2393,9 +2444,9 @@ private async void SidePanelScaleOutHoveredPointerEntered(object sender, Pointer var scaleYAnim = new DoubleAnimation { - From = transform.ScaleY, - To = toScale, - Duration = new Duration(TimeSpan.FromSeconds(0.2)), + From = transform.ScaleY, + To = toScale, + Duration = new Duration(TimeSpan.FromSeconds(0.2)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleYAnim, transform); @@ -2421,7 +2472,7 @@ private async void SidePanelScaleInHoveredPointerExited(object sender, PointerRo HideImageEventImg(false); var storyboard = new Storyboard(); - var transform = (CompositeTransform)elementPanel.RenderTransform; + var transform = (CompositeTransform)elementPanel.RenderTransform; transform.CenterY = elementPanel.ActualHeight + 8; var cubicEaseOut = new CubicEase() { @@ -2430,9 +2481,9 @@ private async void SidePanelScaleInHoveredPointerExited(object sender, PointerRo var scaleXAnim = new DoubleAnimation { - From = transform.ScaleX, - To = 1, - Duration = new Duration(TimeSpan.FromSeconds(0.25)), + From = transform.ScaleX, + To = 1, + Duration = new Duration(TimeSpan.FromSeconds(0.25)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleXAnim, transform); @@ -2441,9 +2492,9 @@ private async void SidePanelScaleInHoveredPointerExited(object sender, PointerRo var scaleYAnim = new DoubleAnimation { - From = transform.ScaleY, - To = 1, - Duration = new Duration(TimeSpan.FromSeconds(0.25)), + From = transform.ScaleY, + To = 1, + Duration = new Duration(TimeSpan.FromSeconds(0.25)), EasingFunction = cubicEaseOut }; Storyboard.SetTarget(scaleYAnim, transform); diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index 056bf47e5..b965a62dd 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -59,6 +59,7 @@ public sealed class LangHomePage public string GamePlaytime_Running_Info2 { get; set; } = LangFallback?._HomePage.GamePlaytime_Running_Info2; public string GamePlaytime_Display { get; set; } = LangFallback?._HomePage.GamePlaytime_Display; public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; + public string GamePlaytime_Stats_Title { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Title; public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; public string GamePlaytime_Stats_LastSession { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession; public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index b6c1e9134..4404a1236 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -160,11 +160,12 @@ "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", "GamePlaytime_Display": "{0}h {1}m", "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", + "GamePlaytime_Stats_Title": "Playtime Statistics", "GamePlaytime_Stats_LastPlayed": "Last Played At", "GamePlaytime_Stats_LastSession": "Last Session", - "GamePlaytime_Stats_Daily": "Daily Playtime", - "GamePlaytime_Stats_Weekly": "Weekly Playtime", - "GamePlaytime_Stats_Monthly": "Monthly Playtime", + "GamePlaytime_Stats_Daily": "Daily", + "GamePlaytime_Stats_Weekly": "Weekly", + "GamePlaytime_Stats_Monthly": "Monthly", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From 7741bf599cdfd8468340658c14b4ed8c94af5a5d Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:35:15 +0100 Subject: [PATCH 06/23] Playtime - Change stats ui --- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 106 +++++++++--------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 395ffbdf6..4bfb8db33 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -56,6 +56,7 @@ using Size = System.Drawing.Size; using UIElementExtensions = CollapseLauncher.Extension.UIElementExtensions; using CollapseLauncher.InstallManager.Base; +using Microsoft.UI.Xaml.Documents; using Orientation = Microsoft.UI.Xaml.Controls.Orientation; namespace CollapseLauncher.Pages @@ -1919,48 +1920,44 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) string lastPlayed = string.Format(Lang._HomePage.GamePlaytime_DateDisplay, last?.Day, last?.Month, last?.Year, last?.Hour, last?.Minute); + Grid grid = new Grid() + { + ColumnDefinitions = + { + new ColumnDefinition(), + new ColumnDefinition() + }, + RowDefinitions = + { + new RowDefinition(), + new RowDefinition(), + new RowDefinition(), + new RowDefinition() + } + }; + + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime); + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, 1); + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, 2); + TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, 3); + + TextBlock lastPlayedBlock = new TextBlock() + { + Margin = new Thickness(0, 0, 0, 5), + Inlines = + { + new Run() { Text = "(Started at " }, + new Run() { Text = lastPlayed, FontWeight = FontWeights.Bold }, + new Run() { Text = ")" } + } + }; + StackPanel panel = new StackPanel() { Children = { - new Grid() - { - ColumnDefinitions = - { - new ColumnDefinition(), - new ColumnDefinition() { Width = GridLength.Auto }, - new ColumnDefinition() - }, - ColumnSpacing = 20, - Children = - { - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime, HorizontalAlignment.Left, gridCol:0), - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, gridCol:1), - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, HorizontalAlignment.Right, gridCol:2) - } - }, - new Grid() - { - ColumnDefinitions = - { - new ColumnDefinition() { Width = GridLength.Auto }, - new ColumnDefinition() - }, - ColumnSpacing = 20, - Children = - { - new StackPanel() - { - HorizontalAlignment = HorizontalAlignment.Left, - Children = - { - new TextBlock() { Text = Lang._HomePage.GamePlaytime_Stats_LastPlayed }, - new TextBlock() { Text = lastPlayed, FontWeight = FontWeights.Bold } - } - }, - TimeSpanPanel(Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, HorizontalAlignment.Right, isLast:true, gridCol:1) - } - } + grid, + lastPlayedBlock } }; @@ -1968,26 +1965,23 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) }); return; - static StackPanel TimeSpanPanel(string label, TimeSpan time, HorizontalAlignment alignment = HorizontalAlignment.Center, bool isLast = false, int gridCol = 0) + static void TimeSpanPanel(Grid grid, string label, TimeSpan time, int row = 0, bool isLast = false) { - StackPanel panel = new StackPanel() - { - HorizontalAlignment = alignment, - Orientation = Orientation.Vertical, - Children = - { - new TextBlock() { Text = label }, - new TextBlock() - { - Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, - Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), - HorizontalAlignment = alignment - } - } - }; - Grid.SetColumn(panel, gridCol); + TextBlock labelBlock = new TextBlock() { Text = label }; + Grid.SetColumn(labelBlock, 0); + Grid.SetRow(labelBlock, row); + + TextBlock timeBlock = new TextBlock() + { + Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, + Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), + HorizontalAlignment = HorizontalAlignment.Right + }; + Grid.SetColumn(timeBlock, 2); + Grid.SetRow(timeBlock, row); - return panel; + grid.Children.Add(labelBlock); + grid.Children.Add(timeBlock); } static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); From e4d0a6a1e2a6a49ba39eddaa59c145d65e9acbbf Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Sun, 30 Jun 2024 16:40:53 +0100 Subject: [PATCH 07/23] Playtime - Align last played date to the right --- .../XAMLs/MainApp/Pages/HomePage.xaml | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 6ead46a1d..66d7edebb 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -922,7 +922,7 @@ VerticalAlignment="Center" FontWeight="Medium" Text="-" /> - Date: Mon, 8 Jul 2024 19:30:47 +0100 Subject: [PATCH 08/23] Playtime - Move Stats UI into Xaml --- .../RegistryClass/CollapsePlaytime.cs | 1 + .../XAMLs/MainApp/Pages/HomePage.xaml | 94 ++++++++++++++++++- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 70 ++------------ Hi3Helper.Core/Lang/en_US.json | 6 +- 4 files changed, 103 insertions(+), 68 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 3cd28dd70..7ca87e463 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -164,6 +164,7 @@ public void Reset() WeeklyPlaytime = TimeSpan.Zero; MonthlyPlaytime = TimeSpan.Zero; ControlDate = DateTime.Today; + LastPlayed = null; if (!_IsDeserializing[_hashID]) Save(); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 66d7edebb..a08a48c7f 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -928,7 +928,99 @@ FontFamily="{ThemeResource FontAwesomeSolid}" FontSize="18" Opacity="0.25" - Text="" /> + Text=""> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 82363df7f..282b7f286 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -1942,7 +1942,7 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) HourPlaytimeTextBox.Text = (playtime.TotalPlaytime.Days * 24 + playtime.TotalPlaytime.Hours).ToString(); MinutePlaytimeTextBox.Text = playtime.TotalPlaytime.Minutes.ToString(); - string lastPlayed = "-"; + string lastPlayed = "Never Played"; if (playtime.LastPlayed != null) { DateTime? last = playtime.LastPlayed?.ToLocalTime(); @@ -1950,72 +1950,14 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) last?.Month, last?.Year, last?.Hour, last?.Minute); } - Grid grid = new Grid() - { - ColumnDefinitions = - { - new ColumnDefinition(), - new ColumnDefinition() - }, - ColumnSpacing = 8, - RowDefinitions = - { - new RowDefinition(), - new RowDefinition(), - new RowDefinition(), - new RowDefinition() - } - }; - - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Daily, playtime.DailyPlaytime); - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Weekly, playtime.WeeklyPlaytime, 1); - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_Monthly, playtime.MonthlyPlaytime, 2); - TimeSpanPanel(grid, Lang._HomePage.GamePlaytime_Stats_LastSession, playtime.LastSession, 3); - - TextBlock lastPlayedBlock = new TextBlock() - { - Margin = new Thickness(0, 0, 0, 5), - Inlines = - { - new Run() { Text = "(Started at " }, - new Run() { Text = lastPlayed, FontWeight = FontWeights.Bold }, - new Run() { Text = ")" } - }, - HorizontalAlignment = HorizontalAlignment.Right - }; - - StackPanel panel = new StackPanel() - { - Children = - { - grid, - lastPlayedBlock - } - }; - - ToolTipService.SetToolTip(PlaytimeBtn, panel); + PlaytimeStatsDaily.Text = FormatTimeStamp(playtime.DailyPlaytime); + PlaytimeStatsWeekly.Text = FormatTimeStamp(playtime.WeeklyPlaytime); + PlaytimeStatsMonthly.Text = FormatTimeStamp(playtime.MonthlyPlaytime); + PlaytimeStatsLastSession.Text = FormatTimeStamp(playtime.LastSession); + PlaytimeStatsLastPlayed.Text = lastPlayed; }); return; - static void TimeSpanPanel(Grid grid, string label, TimeSpan time, int row = 0, bool isLast = false) - { - TextBlock labelBlock = new TextBlock() { Text = label }; - Grid.SetColumn(labelBlock, 0); - Grid.SetRow(labelBlock, row); - - TextBlock timeBlock = new TextBlock() - { - Text = FormatTimeStamp(time), FontWeight = FontWeights.Bold, - Margin = new Thickness(0, 0, 0, isLast ? 0 : 5), - HorizontalAlignment = HorizontalAlignment.Right - }; - Grid.SetColumn(timeBlock, 2); - Grid.SetRow(timeBlock, row); - - grid.Children.Add(labelBlock); - grid.Children.Add(timeBlock); - } - static string FormatTimeStamp(TimeSpan time) => string.Format(Lang._HomePage.GamePlaytime_Display, time.Days * 24 + time.Hours, time.Minutes); } #nullable restore diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 6a1ed7da8..e8200dd15 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -166,9 +166,9 @@ "GamePlaytime_Stats_Title": "Playtime Statistics", "GamePlaytime_Stats_LastPlayed": "Last Played At", "GamePlaytime_Stats_LastSession": "Last Session", - "GamePlaytime_Stats_Daily": "Daily", - "GamePlaytime_Stats_Weekly": "Weekly", - "GamePlaytime_Stats_Monthly": "Monthly", + "GamePlaytime_Stats_Daily": "Today", + "GamePlaytime_Stats_Weekly": "Week", + "GamePlaytime_Stats_Monthly": "Month", "PostPanel_Events": "Events", "PostPanel_Notices": "Notices", From c91a65e79241ebb148880760c4d057afed59ffa5 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Thu, 22 Aug 2024 18:42:31 +0100 Subject: [PATCH 09/23] Playtime - Ensure compatibility with other versions --- .../RegistryClass/CollapsePlaytime.cs | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 7ca87e463..ab3ebc975 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Runtime.Serialization; using System.Text; using static Hi3Helper.Logger; @@ -13,8 +14,9 @@ internal class CollapsePlaytime #region Fields private static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - private const string _ValueName = "CollapseLauncher_Playtime"; - private const string _OldLastPlayedValueName = "CollapseLauncher_LastPlayed"; + private const string _TotalTimeValueName = "CollapseLauncher_Playtime"; + private const string _LastPlayedValueName = "CollapseLauncher_LastPlayed"; + private const string _StatsValueName = "CollapseLauncher_PlaytimeStats"; private static Dictionary _IsDeserializing = []; private RegistryKey _registryRoot; @@ -27,12 +29,14 @@ internal class CollapsePlaytime /// Represents the total time a game was played.

/// Default: TimeSpan.Zero ///
+ [IgnoreDataMember] public TimeSpan TotalPlaytime { get; set; } = TimeSpan.Zero; /// /// Represents the total time the last/current session lasted.

/// Default: TimeSpan.Zero ///
+ [IgnoreDataMember] public TimeSpan LastSession { get; set; } = TimeSpan.Zero; /// @@ -81,47 +85,37 @@ public static CollapsePlaytime Load(RegistryKey root, int hashID) try { _IsDeserializing[hashID] = true; - if (root == null) throw new NullReferenceException($"Cannot load {_ValueName} RegistryKey is unexpectedly not initialized!"); + if (root == null) throw new NullReferenceException($"Cannot load playtime. RegistryKey is unexpectedly not initialized!"); - object? value = root.GetValue(_ValueName, null); + int? totalTime = (int?)root.GetValue(_TotalTimeValueName,null); + int? lastPlayed = (int?)root.GetValue(_LastPlayedValueName,null); + object? stats = root.GetValue(_StatsValueName, null); - // The value being an int means this value was saved by the old implementaion and represents the total number of seconds played. - if (value is int oldPlaytime) - { - object? lastPlayed = root.GetValue(_OldLastPlayedValueName, null); - root.DeleteValue(_OldLastPlayedValueName, false); - - LogWriteLine($"Found old Playtime RegistryKey! Converting to the new format... (Playtime: {oldPlaytime} | Last Played: {lastPlayed})", writeToLog: true); - _IsDeserializing[hashID] = false; - - CollapsePlaytime playtime = new CollapsePlaytime() - { - TotalPlaytime = TimeSpan.FromSeconds(oldPlaytime), - LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null, - _registryRoot = root, - _hashID = hashID - }; - playtime.Save(); - - return playtime; - } + CollapsePlaytime playtime; - if (value != null) + if (stats != null) { - ReadOnlySpan byteStr = (byte[])value; + ReadOnlySpan byteStr = (byte[])stats; #if DEBUG LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); #endif - CollapsePlaytime playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); - playtime._registryRoot = root; - playtime._hashID = hashID; - - return playtime; + playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); + } + else + { + playtime = new CollapsePlaytime(); } + + playtime._registryRoot = root; + playtime._hashID = hashID; + playtime.TotalPlaytime = TimeSpan.FromSeconds(totalTime ?? 0); + playtime.LastPlayed = lastPlayed != null ? BaseDate.AddSeconds((int)lastPlayed) : null; + + return playtime; } catch (Exception ex) { - LogWriteLine($"Failed while reading {_ValueName}\r\n{ex}", LogType.Error, true); + LogWriteLine($"Failed while reading playtime.\r\n{ex}", LogType.Error, true); } finally { @@ -138,18 +132,23 @@ public void Save() { try { - if (_registryRoot == null) throw new NullReferenceException($"Cannot save {_ValueName} since RegistryKey is unexpectedly not initialized!"); + if (_registryRoot == null) throw new NullReferenceException($"Cannot save playtime since RegistryKey is unexpectedly not initialized!"); string data = this.Serialize(UniversalPlaytimeJSONContext.Default, true); byte[] dataByte = Encoding.UTF8.GetBytes(data); #if DEBUG LogWriteLine($"Saved Playtime:\r\n{data}", LogType.Debug, true); #endif - _registryRoot.SetValue(_ValueName, dataByte, RegistryValueKind.Binary); + _registryRoot.SetValue(_StatsValueName, dataByte, RegistryValueKind.Binary); + _registryRoot.SetValue(_TotalTimeValueName, TotalPlaytime.TotalSeconds, RegistryValueKind.DWord); + + double? lastPlayed = (LastPlayed?.ToUniversalTime() - BaseDate)?.TotalSeconds; + if (lastPlayed != null) + _registryRoot.SetValue(_LastPlayedValueName, lastPlayed); } catch (Exception ex) { - LogWriteLine($"Failed to save {_ValueName}!\r\n{ex}", LogType.Error, true); + LogWriteLine($"Failed to save playtime!\r\n{ex}", LogType.Error, true); } } From 1d61cf9088dab44abcad0066fcc373526e3d02db Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Fri, 23 Aug 2024 00:34:38 +0100 Subject: [PATCH 10/23] Playtime - Reload playtime before session and when trying to edit --- .../GameManagement/GamePlaytime/Playtime.cs | 17 ++++++++++++++++- .../RegistryClass/CollapsePlaytime.cs | 6 +++--- .../Classes/Interfaces/IGamePlaytime.cs | 1 + .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index 6992b66ae..a965c3675 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -19,6 +19,7 @@ internal class Playtime : IGamePlaytime #nullable enable public CollapsePlaytime CollapsePlaytime => _playtime; + public bool IsSessionRunning { get; protected set; } private RegistryKey? _registryRoot; private CollapsePlaytime _playtime; private IGameVersionCheck _gameVersionManager; @@ -39,6 +40,13 @@ public Playtime(IGameVersionCheck GameVersionManager) } #nullable disable + public void Reload() + { + if (IsSessionRunning) return; + + _playtime = CollapsePlaytime.Load(_registryRoot!, _gameVersionManager.GamePreset.HashID); + } + public void Update(TimeSpan timeSpan) { TimeSpan oldTimeSpan = _playtime.TotalPlaytime; @@ -46,7 +54,7 @@ public void Update(TimeSpan timeSpan) _playtime.Update(timeSpan); PlaytimeUpdated?.Invoke(this, _playtime); - LogWriteLine($"Playtime counter changed to {TimeSpanToString(timeSpan)}m. (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); + LogWriteLine($"Playtime counter changed to {TimeSpanToString(timeSpan)}. (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); } public void Reset() @@ -61,6 +69,11 @@ public void Reset() public async void StartSession(Process proc) { + if (IsSessionRunning) return; + + Reload(); + IsSessionRunning = true; + DateTime begin = DateTime.Now; TimeSpan initialTimeSpan = _playtime.TotalPlaytime; @@ -109,6 +122,8 @@ public async void StartSession(Process proc) _playtime.Update(initialTimeSpan.Add(totalTimeSpan), false); PlaytimeUpdated?.Invoke(this, _playtime); + + IsSessionRunning = false; } private static string TimeSpanToString(TimeSpan timeSpan) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index ab3ebc975..eade37247 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -97,7 +97,7 @@ public static CollapsePlaytime Load(RegistryKey root, int hashID) { ReadOnlySpan byteStr = (byte[])stats; #if DEBUG - LogWriteLine($"Loaded Playtime:\r\n{Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); + LogWriteLine($"Loaded Playtime:\r\nTotal: {totalTime}s\r\nLastPlayed: {lastPlayed}\r\nStats: {Encoding.UTF8.GetString(byteStr.TrimEnd((byte)0))}", LogType.Debug, true); #endif playtime = byteStr.Deserialize(UniversalPlaytimeJSONContext.Default) ?? new CollapsePlaytime(); } @@ -144,7 +144,7 @@ public void Save() double? lastPlayed = (LastPlayed?.ToUniversalTime() - BaseDate)?.TotalSeconds; if (lastPlayed != null) - _registryRoot.SetValue(_LastPlayedValueName, lastPlayed); + _registryRoot.SetValue(_LastPlayedValueName, lastPlayed, RegistryValueKind.DWord); } catch (Exception ex) { @@ -201,7 +201,7 @@ public void AddMinute() TotalPlaytime = TotalPlaytime.Add(minute); LastSession = LastSession.Add(minute); - if (today == DateTime.Today) + if (ControlDate == today) { DailyPlaytime = DailyPlaytime.Add(minute); WeeklyPlaytime = WeeklyPlaytime.Add(minute); diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index 037e99e55..bddc993f3 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -10,6 +10,7 @@ internal interface IGamePlaytime : IDisposable CollapsePlaytime CollapsePlaytime { get; } void Reset(); + void Reload(); void Update(TimeSpan timeSpan); void StartSession(Process proc); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 4a13a117e..d32c87e21 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -2132,6 +2132,7 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (_cachedIsGameRunning) return; + CurrentGameProperty._GamePlaytime.Reload(); UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); } From 4a601cfcbdc763428c6d13a621917ce583f2ae46 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:14:00 +0100 Subject: [PATCH 11/23] Playtime - Ignore total playtime and last session serializer wise --- .../GameManagement/GamePlaytime/Playtime.cs | 9 ------ .../RegistryClass/CollapsePlaytime.cs | 30 +++++++++---------- .../Classes/Interfaces/IGamePlaytime.cs | 1 - .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 1 - 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index a965c3675..c0b939fc0 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -40,13 +40,6 @@ public Playtime(IGameVersionCheck GameVersionManager) } #nullable disable - public void Reload() - { - if (IsSessionRunning) return; - - _playtime = CollapsePlaytime.Load(_registryRoot!, _gameVersionManager.GamePreset.HashID); - } - public void Update(TimeSpan timeSpan) { TimeSpan oldTimeSpan = _playtime.TotalPlaytime; @@ -71,7 +64,6 @@ public async void StartSession(Process proc) { if (IsSessionRunning) return; - Reload(); IsSessionRunning = true; DateTime begin = DateTime.Now; @@ -136,7 +128,6 @@ public void Dispose() _token.Cancel(); _playtime.Save(); - _playtime = null; _registryRoot = null; } } diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index eade37247..45ecac1dd 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Runtime.Serialization; using System.Text; +using System.Text.Json.Serialization; using static Hi3Helper.Logger; namespace CollapseLauncher.GamePlaytime @@ -29,49 +29,49 @@ internal class CollapsePlaytime /// Represents the total time a game was played.

/// Default: TimeSpan.Zero ///
- [IgnoreDataMember] - public TimeSpan TotalPlaytime { get; set; } = TimeSpan.Zero; - - /// - /// Represents the total time the last/current session lasted.

- /// Default: TimeSpan.Zero - ///
- [IgnoreDataMember] - public TimeSpan LastSession { get; set; } = TimeSpan.Zero; + [JsonIgnore] + public TimeSpan TotalPlaytime { get; private set; } = TimeSpan.Zero; /// /// Represents the daily playtime.
/// The ControlDate field is used to check if this value should be reset.

/// Default: TimeSpan.Zero ///
- public TimeSpan DailyPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan DailyPlaytime { get; private set; } = TimeSpan.Zero; /// /// Represents the weekly playtime.
/// The ControlDate field is used to check if this value should be reset.

/// Default: TimeSpan.Zero ///
- public TimeSpan WeeklyPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan WeeklyPlaytime { get; private set; } = TimeSpan.Zero; /// /// Represents the monthly playtime.
/// The ControlDate field is used to check if this value should be reset.

/// Default: TimeSpan.Zero ///
- public TimeSpan MonthlyPlaytime { get; set; } = TimeSpan.Zero; + public TimeSpan MonthlyPlaytime { get; private set; } = TimeSpan.Zero; + + /// + /// Represents the total time the last/current session lasted.

+ /// Default: TimeSpan.Zero + ///
+ public TimeSpan LastSession { get; set; } = TimeSpan.Zero; /// /// Represents the last time the game was launched.

/// Default: null ///
- public DateTime? LastPlayed { get; set; } = null; + [JsonIgnore] + public DateTime? LastPlayed { get; set; } /// /// Represents a control date.
/// This date is used to check if a specific playtime statistic should be reset.

/// Default: DateTime.Today ///
- public DateTime ControlDate { get; set; } = DateTime.Today; + public DateTime ControlDate { get; private set; } = DateTime.Today; #endregion #region Methods diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index bddc993f3..037e99e55 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -10,7 +10,6 @@ internal interface IGamePlaytime : IDisposable CollapsePlaytime CollapsePlaytime { get; } void Reset(); - void Reload(); void Update(TimeSpan timeSpan); void StartSession(Process proc); } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index d32c87e21..4a13a117e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -2132,7 +2132,6 @@ private void ForceUpdatePlaytimeButton_Click(object sender, RoutedEventArgs e) { if (_cachedIsGameRunning) return; - CurrentGameProperty._GamePlaytime.Reload(); UpdatePlaytime(null, CurrentGameProperty._GamePlaytime.CollapsePlaytime); } From ee702159e9fb7255eeab660d068a6ef7585354b4 Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:15:32 +0100 Subject: [PATCH 12/23] Fix stop game keyboard shortcut --- CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs index 3fc9b1280..9e67d4b84 100644 --- a/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/MainPage.xaml.cs @@ -1967,9 +1967,9 @@ private void OpenGameCacheFolder_Invoked(KeyboardAccelerator sender, KeyboardAcc private void ForceCloseGame_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args) { - if (!CurrentGameProperty.IsGameRunning) return; + if (!GetCurrentGameProperty().IsGameRunning) return; - PresetConfig gamePreset = CurrentGameProperty._GameVersion.GamePreset; + PresetConfig gamePreset = GetCurrentGameProperty()._GameVersion.GamePreset; try { var gameProcess = Process.GetProcessesByName(gamePreset.GameExecutableName!.Split('.')[0]); From cc4adc88b37a287666ea55bf8494f11c3c5c685f Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:38:01 +0100 Subject: [PATCH 13/23] Sync branch with main --- .../GameManagement/GamePlaytime/Playtime.cs | 34 ++++++++++--------- .../RegistryClass/CollapsePlaytime.cs | 30 ++++++++-------- .../Classes/Interfaces/IGamePlaytime.cs | 2 +- .../XAMLs/MainApp/Pages/HomePage.xaml | 1 + .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 17 ++++------ 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs index c0b939fc0..d80930354 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/Playtime.cs @@ -3,6 +3,7 @@ using Hi3Helper; using Microsoft.Win32; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Timers; @@ -19,10 +20,10 @@ internal class Playtime : IGamePlaytime #nullable enable public CollapsePlaytime CollapsePlaytime => _playtime; - public bool IsSessionRunning { get; protected set; } - private RegistryKey? _registryRoot; - private CollapsePlaytime _playtime; - private IGameVersionCheck _gameVersionManager; + private static HashSet _activeSessions = []; + private RegistryKey? _registryRoot; + private CollapsePlaytime _playtime; + private IGameVersionCheck _gameVersionManager; private CancellationTokenSourceWrapper _token = new(); #endregion @@ -60,13 +61,18 @@ public void Reset() LogWriteLine($"Playtime counter was reset! (Previous value: {TimeSpanToString(oldTimeSpan)})", writeToLog: true); } - public async void StartSession(Process proc) + public async void StartSession(Process proc, DateTime? begin = null) { - if (IsSessionRunning) return; + int hashId = _gameVersionManager.GamePreset.HashID; - IsSessionRunning = true; + // If a playtime HashSet has already tracked, then return (do not track the playtime more than once) + if (_activeSessions.Contains(hashId)) return; + + // Otherwise, add it to track list + _activeSessions.Add(hashId); + + begin ??= DateTime.Now; - DateTime begin = DateTime.Now; TimeSpan initialTimeSpan = _playtime.TotalPlaytime; _playtime.LastPlayed = begin; @@ -75,7 +81,7 @@ public async void StartSession(Process proc) PlaytimeUpdated?.Invoke(this, _playtime); #if DEBUG - LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - Started session at {begin.ToLongTimeString()}."); + LogWriteLine($"{_gameVersionManager.GamePreset.ProfileName} - Started session at {begin?.ToLongTimeString()}."); #endif int elapsedSeconds = 0; @@ -100,7 +106,7 @@ public async void StartSession(Process proc) } DateTime end = DateTime.Now; - double totalElapsedSeconds = (end - begin).TotalSeconds; + double totalElapsedSeconds = (end - begin.Value).TotalSeconds; if (totalElapsedSeconds < 0) { LogWriteLine($"[HomePage::StartPlaytimeCounter] Date difference cannot be lower than 0. ({totalElapsedSeconds}s)", LogType.Error); @@ -115,19 +121,15 @@ public async void StartSession(Process proc) _playtime.Update(initialTimeSpan.Add(totalTimeSpan), false); PlaytimeUpdated?.Invoke(this, _playtime); - IsSessionRunning = false; + _activeSessions.Remove(hashId); } - private static string TimeSpanToString(TimeSpan timeSpan) - { - return $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; - } + private static string TimeSpanToString(TimeSpan timeSpan) => $"{timeSpan.Days * 24 + timeSpan.Hours}h {timeSpan.Minutes}m"; public void Dispose() { _token.Cancel(); _playtime.Save(); - _registryRoot = null; } } diff --git a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs index 45ecac1dd..aa79b0557 100644 --- a/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs +++ b/CollapseLauncher/Classes/GameManagement/GamePlaytime/RegistryClass/CollapsePlaytime.cs @@ -14,11 +14,11 @@ internal class CollapsePlaytime #region Fields private static DateTime BaseDate => new(2012, 2, 13, 0, 0, 0, DateTimeKind.Utc); - private const string _TotalTimeValueName = "CollapseLauncher_Playtime"; - private const string _LastPlayedValueName = "CollapseLauncher_LastPlayed"; - private const string _StatsValueName = "CollapseLauncher_PlaytimeStats"; + private const string TotalTimeValueName = "CollapseLauncher_Playtime"; + private const string LastPlayedValueName = "CollapseLauncher_LastPlayed"; + private const string StatsValueName = "CollapseLauncher_PlaytimeStats"; - private static Dictionary _IsDeserializing = []; + private static HashSet _isDeserializing = []; private RegistryKey _registryRoot; private int _hashID; @@ -84,12 +84,12 @@ public static CollapsePlaytime Load(RegistryKey root, int hashID) { try { - _IsDeserializing[hashID] = true; + _isDeserializing.Add(hashID); if (root == null) throw new NullReferenceException($"Cannot load playtime. RegistryKey is unexpectedly not initialized!"); - int? totalTime = (int?)root.GetValue(_TotalTimeValueName,null); - int? lastPlayed = (int?)root.GetValue(_LastPlayedValueName,null); - object? stats = root.GetValue(_StatsValueName, null); + int? totalTime = (int?)root.GetValue(TotalTimeValueName,null); + int? lastPlayed = (int?)root.GetValue(LastPlayedValueName,null); + object? stats = root.GetValue(StatsValueName, null); CollapsePlaytime playtime; @@ -119,7 +119,7 @@ public static CollapsePlaytime Load(RegistryKey root, int hashID) } finally { - _IsDeserializing[hashID] = false; + _isDeserializing.Remove(hashID); } return new CollapsePlaytime() { _hashID = hashID, _registryRoot = root }; @@ -139,12 +139,12 @@ public void Save() #if DEBUG LogWriteLine($"Saved Playtime:\r\n{data}", LogType.Debug, true); #endif - _registryRoot.SetValue(_StatsValueName, dataByte, RegistryValueKind.Binary); - _registryRoot.SetValue(_TotalTimeValueName, TotalPlaytime.TotalSeconds, RegistryValueKind.DWord); + _registryRoot.SetValue(StatsValueName, dataByte, RegistryValueKind.Binary); + _registryRoot.SetValue(TotalTimeValueName, TotalPlaytime.TotalSeconds, RegistryValueKind.DWord); double? lastPlayed = (LastPlayed?.ToUniversalTime() - BaseDate)?.TotalSeconds; if (lastPlayed != null) - _registryRoot.SetValue(_LastPlayedValueName, lastPlayed, RegistryValueKind.DWord); + _registryRoot.SetValue(LastPlayedValueName, lastPlayed, RegistryValueKind.DWord); } catch (Exception ex) { @@ -165,7 +165,7 @@ public void Reset() ControlDate = DateTime.Today; LastPlayed = null; - if (!_IsDeserializing[_hashID]) Save(); + if (!_isDeserializing.Contains(_hashID)) Save(); } /// @@ -186,7 +186,7 @@ public void Update(TimeSpan timeSpan, bool reset = true) TotalPlaytime = timeSpan; - if (!_IsDeserializing[_hashID]) Save(); + if (!_isDeserializing.Contains(_hashID)) Save(); } /// @@ -216,7 +216,7 @@ public void AddMinute() ControlDate = today; } - if (!_IsDeserializing[_hashID]) Save(); + if (!_isDeserializing.Contains(_hashID)) Save(); } #endregion diff --git a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs index 037e99e55..537fe81a0 100644 --- a/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs +++ b/CollapseLauncher/Classes/Interfaces/IGamePlaytime.cs @@ -11,6 +11,6 @@ internal interface IGamePlaytime : IDisposable void Reset(); void Update(TimeSpan timeSpan); - void StartSession(Process proc); + void StartSession(Process proc, DateTime? begin = null); } } diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 40d55fc47..09f68b11e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -1528,6 +1528,7 @@ Date: Tue, 17 Sep 2024 19:01:00 +0100 Subject: [PATCH 14/23] Localize "Never Played" --- CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs | 6 +++--- Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 1 + Hi3Helper.Core/Lang/en_US.json | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index 37798edda..d90c411d5 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -980,12 +980,12 @@ private async void CheckRunningGameInstance(CancellationToken Token) IGameSettingsUniversal gameSettings = CurrentGameProperty!._GameSettings!.AsIGameSettingsUniversal(); PresetConfig gamePreset = CurrentGameProperty._GamePreset; - CurrentGameProperty!._GamePlaytime!.StartSession(currentGameProcess, fromActivityOffset); + CurrentGameProperty!._GamePlaytime!.StartSession(currentGameProcess); int? height = gameSettings.SettingsScreen.height; int? width = gameSettings.SettingsScreen.width; - // Start the resizable window payload (also use the same token as PlaytimeToken) + // Start the resizable window payload StartResizableWindowPayload( gamePreset.GameExecutableName, gameSettings, @@ -2334,7 +2334,7 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) HourPlaytimeTextBox.Text = (playtime.TotalPlaytime.Days * 24 + playtime.TotalPlaytime.Hours).ToString(); MinutePlaytimeTextBox.Text = playtime.TotalPlaytime.Minutes.ToString(); - string lastPlayed = "Never Played"; + string lastPlayed = Lang._HomePage.GamePlaytime_Stats_NeverPlayed; if (playtime.LastPlayed != null) { DateTime? last = playtime.LastPlayed?.ToLocalTime(); diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index 5ca23bed1..a5ca41704 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -67,6 +67,7 @@ public sealed class LangHomePage public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; public string GamePlaytime_Stats_Title { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Title; public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; + public string GamePlaytime_Stats_NeverPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_NeverPlayed; public string GamePlaytime_Stats_LastSession { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession; public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; public string GamePlaytime_Stats_Weekly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Weekly; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 43fd69873..05f124200 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -166,11 +166,12 @@ "GamePlaytime_Idle_ResetBtn": "Reset", "GamePlaytime_Idle_ChangeBtn": "Change", "GamePlaytime_Running_Info1": "An instance of this game is currently running, therefore playtime can't be edited.", - "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point) & only sessions started using Collapse will be tracked.", + "GamePlaytime_Running_Info2": "Please be aware that fully closing Collapse will stop playtime tracking (saving what was played until that point).", "GamePlaytime_Display": "{0}h {1}m", "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", "GamePlaytime_Stats_Title": "Playtime Statistics", "GamePlaytime_Stats_LastPlayed": "Last Played At", + "GamePlaytime_Stats_NeverPlayed": "Never Played", "GamePlaytime_Stats_LastSession": "Last Session", "GamePlaytime_Stats_Daily": "Today", "GamePlaytime_Stats_Weekly": "Week", From 8d45f2ecc548ea61c9206bb6c3e864126241fcfc Mon Sep 17 00:00:00 2001 From: Gabriel Lima <44784408+gablm@users.noreply.github.com> Date: Tue, 17 Sep 2024 23:46:13 +0100 Subject: [PATCH 15/23] Update Flyout buttons style to match others --- .../XAMLs/MainApp/Pages/HomePage.xaml | 46 ++++++++----------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 09f68b11e..62e2d14c8 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -1444,7 +1444,6 @@ @@ -1480,54 +1479,47 @@ Date: Sat, 28 Sep 2024 14:33:34 +0100 Subject: [PATCH 16/23] Playtime - Recover prototype UI --- .../XAMLs/MainApp/Pages/HomePage.xaml | 93 ++++++++++++++++++- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 6 +- Hi3Helper.Core/Lang/Locale/LangHomePage.cs | 3 +- Hi3Helper.Core/Lang/en_US.json | 5 +- 4 files changed, 100 insertions(+), 7 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 62e2d14c8..17a24f4f6 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -1430,13 +1430,104 @@ FontWeight="SemiBold" Text="-" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs index d90c411d5..0e4aaec2f 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml.cs @@ -2342,11 +2342,11 @@ private void UpdatePlaytime(object sender, CollapsePlaytime playtime) last?.Month, last?.Year, last?.Hour, last?.Minute); } - /*PlaytimeStatsDaily.Text = FormatTimeStamp(playtime.DailyPlaytime); + PlaytimeStatsDaily.Text = FormatTimeStamp(playtime.DailyPlaytime); PlaytimeStatsWeekly.Text = FormatTimeStamp(playtime.WeeklyPlaytime); PlaytimeStatsMonthly.Text = FormatTimeStamp(playtime.MonthlyPlaytime); - PlaytimeStatsLastSession.Text = FormatTimeStamp(playtime.LastSession);*/ - ToolTipService.SetToolTip(PlaytimeBtn, lastPlayed); + PlaytimeStatsLastSession.Text = FormatTimeStamp(playtime.LastSession); + PlaytimeStatsLastPlayed.Text = lastPlayed; }); return; diff --git a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs index a5ca41704..02b47e841 100644 --- a/Hi3Helper.Core/Lang/Locale/LangHomePage.cs +++ b/Hi3Helper.Core/Lang/Locale/LangHomePage.cs @@ -66,9 +66,10 @@ public sealed class LangHomePage public string GamePlaytime_Display { get; set; } = LangFallback?._HomePage.GamePlaytime_Display; public string GamePlaytime_DateDisplay { get; set; } = LangFallback?._HomePage.GamePlaytime_DateDisplay; public string GamePlaytime_Stats_Title { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Title; - public string GamePlaytime_Stats_LastPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastPlayed; public string GamePlaytime_Stats_NeverPlayed { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_NeverPlayed; public string GamePlaytime_Stats_LastSession { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession; + public string GamePlaytime_Stats_LastSession_StartTime { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession_StartTime; + public string GamePlaytime_Stats_LastSession_Duration { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_LastSession_Duration; public string GamePlaytime_Stats_Daily { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Daily; public string GamePlaytime_Stats_Weekly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Weekly; public string GamePlaytime_Stats_Monthly { get; set; } = LangFallback?._HomePage.GamePlaytime_Stats_Monthly; diff --git a/Hi3Helper.Core/Lang/en_US.json b/Hi3Helper.Core/Lang/en_US.json index 841d3f4aa..8da3c30d5 100644 --- a/Hi3Helper.Core/Lang/en_US.json +++ b/Hi3Helper.Core/Lang/en_US.json @@ -170,9 +170,10 @@ "GamePlaytime_Display": "{0}h {1}m", "GamePlaytime_DateDisplay": "{0:00}/{1:00}/{2:0000} {3:00}:{4:00}", "GamePlaytime_Stats_Title": "Playtime Statistics", - "GamePlaytime_Stats_LastPlayed": "Last Played At", "GamePlaytime_Stats_NeverPlayed": "Never Played", - "GamePlaytime_Stats_LastSession": "Last Session", + "GamePlaytime_Stats_LastSession": "Most Recent Session", + "GamePlaytime_Stats_LastSession_StartTime": "Start Time", + "GamePlaytime_Stats_LastSession_Duration": "Duration", "GamePlaytime_Stats_Daily": "Today", "GamePlaytime_Stats_Weekly": "Week", "GamePlaytime_Stats_Monthly": "Month", From 8e3113f3f957bd2e71b4459de6c422871f93a687 Mon Sep 17 00:00:00 2001 From: Kemal Setya Adhi Date: Sun, 29 Sep 2024 15:11:35 +0700 Subject: [PATCH 17/23] Switch to Flyout for Playtime Stats --- .../XAMLs/MainApp/Pages/HomePage.xaml | 266 +++++++++--------- .../XAMLs/MainApp/Pages/HomePage.xaml.cs | 31 ++ 2 files changed, 168 insertions(+), 129 deletions(-) diff --git a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml index 17a24f4f6..bea2aba6e 100644 --- a/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml +++ b/CollapseLauncher/XAMLs/MainApp/Pages/HomePage.xaml @@ -654,7 +654,8 @@ HorizontalAlignment="Left" SelectedIndex="{x:Bind DefaultPostPanelIndex}"> -