From 395d4fa5d919a53f2059ccdcfdc08b0b8d769cba Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Mon, 19 Apr 2021 22:32:58 +1200 Subject: [PATCH] First working version after refactor Have fixed the mistake I made with the Parallel.Invoke actions where I accidentally kept adding the SteamLibrary 3 times! This now works well, and as a bonus the game libraries are now thread safe! --- DisplayMagician/GameLibraries/OriginGame.cs | 1 + .../GameLibraries/OriginLibrary.cs | 6 +- DisplayMagician/GameLibraries/SteamGame.cs | 3 +- DisplayMagician/GameLibraries/SteamLibrary.cs | 42 +++--- DisplayMagician/GameLibraries/UplayGame.cs | 1 + DisplayMagician/GameLibraries/UplayLibrary.cs | 10 +- DisplayMagician/Program.cs | 120 +++++++++++++----- 7 files changed, 127 insertions(+), 56 deletions(-) diff --git a/DisplayMagician/GameLibraries/OriginGame.cs b/DisplayMagician/GameLibraries/OriginGame.cs index 03a7147..95fdb6c 100644 --- a/DisplayMagician/GameLibraries/OriginGame.cs +++ b/DisplayMagician/GameLibraries/OriginGame.cs @@ -18,6 +18,7 @@ namespace DisplayMagician.GameLibraries private string _originGameProcessName; private string _originGameIconPath; //private string _originURI; + private static readonly OriginLibrary _originGameLibrary = OriginLibrary.GetLibrary(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); static OriginGame() diff --git a/DisplayMagician/GameLibraries/OriginLibrary.cs b/DisplayMagician/GameLibraries/OriginLibrary.cs index 5b6e619..2523377 100644 --- a/DisplayMagician/GameLibraries/OriginLibrary.cs +++ b/DisplayMagician/GameLibraries/OriginLibrary.cs @@ -39,9 +39,11 @@ namespace DisplayMagician.GameLibraries // Other constants that are useful - #endregion - + #endregion + #region Class Constructors + static OriginLibrary() { } + private OriginLibrary() { try diff --git a/DisplayMagician/GameLibraries/SteamGame.cs b/DisplayMagician/GameLibraries/SteamGame.cs index 87cb869..1e1b4e7 100644 --- a/DisplayMagician/GameLibraries/SteamGame.cs +++ b/DisplayMagician/GameLibraries/SteamGame.cs @@ -20,6 +20,7 @@ namespace DisplayMagician.GameLibraries private string _steamGameExe; private string _steamGameProcessName; private string _steamGameIconPath; + private static readonly SteamLibrary _steamGameLibrary = SteamLibrary.GetLibrary(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); static SteamGame() @@ -32,7 +33,7 @@ namespace DisplayMagician.GameLibraries public SteamGame(string steamGameId, string steamGameName, string steamGameExePath, string steamGameIconPath) { - _gameRegistryKey = $@"{SteamLibrary.GetLibrary().SteamAppsRegistryKey}\\{steamGameId}"; + _gameRegistryKey = $@"{_steamGameLibrary.SteamAppsRegistryKey}\\{steamGameId}"; _steamGameId = steamGameId; _steamGameName = steamGameName; _steamGameExePath = steamGameExePath; diff --git a/DisplayMagician/GameLibraries/SteamLibrary.cs b/DisplayMagician/GameLibraries/SteamLibrary.cs index 270c3c2..3a97824 100644 --- a/DisplayMagician/GameLibraries/SteamLibrary.cs +++ b/DisplayMagician/GameLibraries/SteamLibrary.cs @@ -27,7 +27,7 @@ namespace DisplayMagician.GameLibraries // Static members are 'eagerly initialized', that is, // immediately when class is loaded for the first time. // .NET guarantees thread safety for static initialization - private static readonly SteamLibrary _instance = new SteamLibrary(); + private static SteamLibrary _instance = new SteamLibrary(); // Common items to the class private List _allSteamGames = new List(); @@ -44,7 +44,9 @@ namespace DisplayMagician.GameLibraries #endregion #region Class Constructors - SteamLibrary() + static SteamLibrary() { } + + private SteamLibrary() { try { @@ -573,7 +575,7 @@ namespace DisplayMagician.GameLibraries if (steamAppType.Equals("Game",StringComparison.OrdinalIgnoreCase)) { steamAppInfo.Add(detectedAppID, steamGameAppInfo); - logger.Trace($"SteamLibrary/LoadInstalledGames: Adding Game with ID {detectedAppID} to the list of games"); + logger.Trace($"SteamLibrary/LoadInstalledGames: Adding Game with ID {detectedAppID} '{steamGameAppInfo.GameName}' to the list of games"); } } @@ -647,30 +649,30 @@ namespace DisplayMagician.GameLibraries // This game is an installed game! so we start to populate it with data! string steamGameExe = ""; - string steamGameName = steamAppInfo[steamGameId].GameName; + string steamGameName = steamAppInfo[steamGameId].GameName; - // Construct the full path to the game dir from the appInfo and libraryAppManifest data - string steamGameInstallDir = Path.Combine(steamLibraryPath, @"steamapps", @"common", steamAppInfo[steamGameId].GameInstallDir); + // Construct the full path to the game dir from the appInfo and libraryAppManifest data + string steamGameInstallDir = Path.Combine(steamLibraryPath, @"steamapps", @"common", steamAppInfo[steamGameId].GameInstallDir); - logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game ID {steamGameId} at {steamGameInstallDir }"); + logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game ID {steamGameId} at {steamGameInstallDir }"); - // And finally we try to populate the 'where', to see what gets run - // And so we can extract the process name - if (steamAppInfo[steamGameId].GameExes.Count > 0) - { - foreach (string gameExe in steamAppInfo[steamGameId].GameExes) + // And finally we try to populate the 'where', to see what gets run + // And so we can extract the process name + if (steamAppInfo[steamGameId].GameExes.Count > 0) { - steamGameExe = Path.Combine(steamGameInstallDir, gameExe); - logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }"); - // If the game executable exists, then we can proceed - if (File.Exists(steamGameExe)) + foreach (string gameExe in steamAppInfo[steamGameId].GameExes) { - logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }"); - break; + steamGameExe = Path.Combine(steamGameInstallDir, gameExe); + logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }"); + // If the game executable exists, then we can proceed + if (File.Exists(steamGameExe)) + { + logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }"); + break; + } } - } - } + } // Next, we need to get the Icons we want to use, and make sure it's the latest one. string steamGameIconPath = ""; diff --git a/DisplayMagician/GameLibraries/UplayGame.cs b/DisplayMagician/GameLibraries/UplayGame.cs index 4751ed2..672ef60 100644 --- a/DisplayMagician/GameLibraries/UplayGame.cs +++ b/DisplayMagician/GameLibraries/UplayGame.cs @@ -18,6 +18,7 @@ namespace DisplayMagician.GameLibraries private string _uplayGameExe; private string _uplayGameProcessName; private string _uplayGameIconPath; + private static readonly UplayLibrary _uplayGameLibrary = UplayLibrary.GetLibrary(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); static UplayGame() diff --git a/DisplayMagician/GameLibraries/UplayLibrary.cs b/DisplayMagician/GameLibraries/UplayLibrary.cs index 8173b5e..dd2d93a 100644 --- a/DisplayMagician/GameLibraries/UplayLibrary.cs +++ b/DisplayMagician/GameLibraries/UplayLibrary.cs @@ -11,12 +11,14 @@ namespace DisplayMagician.GameLibraries { public class UplayLibrary : GameLibrary { + + #region Class Variables // Static members are 'eagerly initialized', that is, // immediately when class is loaded for the first time. // .NET guarantees thread safety for static initialization private static readonly UplayLibrary _instance = new UplayLibrary(); - + // Common items to the class private List _allGames = new List(); private string uplayAppIdRegex = @"/^[0-9A-F]{1,10}$"; @@ -34,9 +36,11 @@ namespace DisplayMagician.GameLibraries // Other constants that are useful #endregion - + #region Class Constructors - UplayLibrary() + static UplayLibrary() { } + + private UplayLibrary() { try { diff --git a/DisplayMagician/Program.cs b/DisplayMagician/Program.cs index 1cebfe6..0dabe6f 100644 --- a/DisplayMagician/Program.cs +++ b/DisplayMagician/Program.cs @@ -16,6 +16,7 @@ using System.Drawing; using DesktopNotifications; using System.Runtime.Serialization; using NLog.Config; +using System.Collections.Generic; namespace DisplayMagician { @@ -778,47 +779,106 @@ namespace DisplayMagician { }); + Action loadSteamGamesAction = new Action(() => + { + // Check if Steam is installed + GameLibrary steamLibrary = SteamLibrary.GetLibrary(); + if (steamLibrary.IsGameLibraryInstalled) + { + // Load Steam library games + logger.Info($"Program/LoadGamesInBackground: Loading Installed Steam Games"); + Console.Write("Loading Installed Steam Games..."); + if (!steamLibrary.LoadInstalledGames()) + { + logger.Info($"Program/LoadGamesInBackground: Cannot load installed Steam Games!"); + } + Console.WriteLine("Done."); + logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Steam Games (found {steamLibrary.InstalledGameCount})"); + } + else + { + logger.Info($"Program/LoadGamesInBackground: Steam not installed."); + Console.WriteLine("Steam not installed."); + } + }); - // Store all the tasks in an array so we can wait on them later - Task[] loadGamesTasks = new Task[3]; - loadGamesTasks[0] = loadSteamGamesTask; - loadGamesTasks[1] = loadUplayGamesTask; - loadGamesTasks[2] = loadOriginGamesTask; + // Now lets prepare loading all the Uplay games we have installed + Action loadUplayGamesAction = new Action(() => + { + // Check if Uplay is installed + GameLibrary uplayLibrary = UplayLibrary.GetLibrary(); + if (uplayLibrary.IsGameLibraryInstalled) + { + // Load Uplay library games + logger.Info($"Program/LoadGamesInBackground: Loading Installed Uplay Games"); + Console.Write("Loading Installed Uplay Games..."); + if (!uplayLibrary.LoadInstalledGames()) + { + logger.Info($"Program/LoadGamesInBackground: Cannot load installed Uplay Games!"); + } + Console.WriteLine("Done."); + logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Uplay Games (found {uplayLibrary.InstalledGameCount})"); + } + else + { + logger.Info($"Program/LoadGamesInBackground: Uplay not installed."); + Console.WriteLine("Uplay not installed."); + } - logger.Debug($"Program/LoadGamesInBackground: Running game loading tasks."); - // Go through and start all the tasks - foreach (Task loadGameTask in loadGamesTasks) - loadGameTask.Start(); + }); + + // Now lets prepare loading all the Origin games we have installed + Action loadOriginGamesAction = new Action(() => + { + // Check if Origin is installed + GameLibrary originLibrary = OriginLibrary.GetLibrary(); + if (originLibrary.IsGameLibraryInstalled) + { + // Load Origin library games + logger.Info($"Program/LoadGamesInBackground: Loading Installed Origin Games"); + Console.Write("Loading Installed Origin Games..."); + if (!originLibrary.LoadInstalledGames()) + { + logger.Info($"Program/LoadGamesInBackground: Cannot load installed Origin Games!"); + } + Console.WriteLine("Done."); + logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Origin Games (found {originLibrary.InstalledGameCount})"); + } + else + { + logger.Info($"Program/LoadGamesInBackground: Origin not installed."); + Console.WriteLine("Origin not installed."); + } + + }); + + + // Store all the actions in a array so we can wait on them later + List loadGamesActions = new List(); + loadGamesActions.Add(loadSteamGamesAction); + loadGamesActions.Add(loadUplayGamesAction); + loadGamesActions.Add(loadOriginGamesAction); try { - logger.Debug($"Program/LoadGamesInBackground: Waiting for all game loading tasks to finish"); - Task.WaitAll(loadGamesTasks); + logger.Debug($"Program/LoadGamesInBackground: Running game loading actions."); + // Go through and start all the actions, making sure we only have one threat per action to avoid thread issues + int threads = loadGamesActions.Count; + ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = threads }; + Parallel.Invoke(options, loadGamesActions.ToArray()); logger.Debug($"Program/LoadGamesInBackground: All game loading tasks finished"); } catch (AggregateException ae) { - logger.Error(ae, $"Program/LoadGamesInBackground exception during loadGamesTasks"); - Console.WriteLine("Program/LoadGamesInBackground : Task exception!"); - foreach (var e in ae.InnerExceptions) - { - // Handle the custom exception. - if (e is LoadingInstalledGamesException) - { - Console.WriteLine(e.Message); - } - // Rethrow any other exception. - else - { - throw; - } - } + logger.Error(ae, $"Program/LoadGamesInBackground exception during loadGamesActions"); } - bool failedTask = false; - foreach (var loadGameTask in loadGamesTasks) + + // TODO replicate this failed Task handling in Actions + /*bool failedAction = false; + foreach (var loadGameAction in loadGamesActions) { - if (loadGameTask.Exception != null) + if (loadGameAction. .Exception != null) { failedTask = true; foreach (var ex in loadGameTask.Exception.InnerExceptions) @@ -827,7 +887,7 @@ namespace DisplayMagician { } if (failedTask) - return false; + return false;*/ return true;