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!
This commit is contained in:
Terry MacDonald 2021-04-19 22:32:58 +12:00
parent 4c54e72dc2
commit 395d4fa5d9
7 changed files with 127 additions and 56 deletions

View File

@ -18,6 +18,7 @@ namespace DisplayMagician.GameLibraries
private string _originGameProcessName; private string _originGameProcessName;
private string _originGameIconPath; private string _originGameIconPath;
//private string _originURI; //private string _originURI;
private static readonly OriginLibrary _originGameLibrary = OriginLibrary.GetLibrary();
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
static OriginGame() static OriginGame()

View File

@ -39,9 +39,11 @@ namespace DisplayMagician.GameLibraries
// Other constants that are useful // Other constants that are useful
#endregion #endregion
#region Class Constructors #region Class Constructors
static OriginLibrary() { }
private OriginLibrary() private OriginLibrary()
{ {
try try

View File

@ -20,6 +20,7 @@ namespace DisplayMagician.GameLibraries
private string _steamGameExe; private string _steamGameExe;
private string _steamGameProcessName; private string _steamGameProcessName;
private string _steamGameIconPath; private string _steamGameIconPath;
private static readonly SteamLibrary _steamGameLibrary = SteamLibrary.GetLibrary();
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
static SteamGame() static SteamGame()
@ -32,7 +33,7 @@ namespace DisplayMagician.GameLibraries
public SteamGame(string steamGameId, string steamGameName, string steamGameExePath, string steamGameIconPath) public SteamGame(string steamGameId, string steamGameName, string steamGameExePath, string steamGameIconPath)
{ {
_gameRegistryKey = $@"{SteamLibrary.GetLibrary().SteamAppsRegistryKey}\\{steamGameId}"; _gameRegistryKey = $@"{_steamGameLibrary.SteamAppsRegistryKey}\\{steamGameId}";
_steamGameId = steamGameId; _steamGameId = steamGameId;
_steamGameName = steamGameName; _steamGameName = steamGameName;
_steamGameExePath = steamGameExePath; _steamGameExePath = steamGameExePath;

View File

@ -27,7 +27,7 @@ namespace DisplayMagician.GameLibraries
// Static members are 'eagerly initialized', that is, // Static members are 'eagerly initialized', that is,
// immediately when class is loaded for the first time. // immediately when class is loaded for the first time.
// .NET guarantees thread safety for static initialization // .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 // Common items to the class
private List<Game> _allSteamGames = new List<Game>(); private List<Game> _allSteamGames = new List<Game>();
@ -44,7 +44,9 @@ namespace DisplayMagician.GameLibraries
#endregion #endregion
#region Class Constructors #region Class Constructors
SteamLibrary() static SteamLibrary() { }
private SteamLibrary()
{ {
try try
{ {
@ -573,7 +575,7 @@ namespace DisplayMagician.GameLibraries
if (steamAppType.Equals("Game",StringComparison.OrdinalIgnoreCase)) if (steamAppType.Equals("Game",StringComparison.OrdinalIgnoreCase))
{ {
steamAppInfo.Add(detectedAppID, steamGameAppInfo); 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! // This game is an installed game! so we start to populate it with data!
string steamGameExe = ""; 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 // Construct the full path to the game dir from the appInfo and libraryAppManifest data
string steamGameInstallDir = Path.Combine(steamLibraryPath, @"steamapps", @"common", steamAppInfo[steamGameId].GameInstallDir); 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 finally we try to populate the 'where', to see what gets run
// And so we can extract the process name // And so we can extract the process name
if (steamAppInfo[steamGameId].GameExes.Count > 0) if (steamAppInfo[steamGameId].GameExes.Count > 0)
{
foreach (string gameExe in steamAppInfo[steamGameId].GameExes)
{ {
steamGameExe = Path.Combine(steamGameInstallDir, gameExe); foreach (string gameExe in steamAppInfo[steamGameId].GameExes)
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 }"); steamGameExe = Path.Combine(steamGameInstallDir, gameExe);
break; 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. // Next, we need to get the Icons we want to use, and make sure it's the latest one.
string steamGameIconPath = ""; string steamGameIconPath = "";

View File

@ -18,6 +18,7 @@ namespace DisplayMagician.GameLibraries
private string _uplayGameExe; private string _uplayGameExe;
private string _uplayGameProcessName; private string _uplayGameProcessName;
private string _uplayGameIconPath; private string _uplayGameIconPath;
private static readonly UplayLibrary _uplayGameLibrary = UplayLibrary.GetLibrary();
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
static UplayGame() static UplayGame()

View File

@ -11,12 +11,14 @@ namespace DisplayMagician.GameLibraries
{ {
public class UplayLibrary : GameLibrary public class UplayLibrary : GameLibrary
{ {
#region Class Variables #region Class Variables
// Static members are 'eagerly initialized', that is, // Static members are 'eagerly initialized', that is,
// immediately when class is loaded for the first time. // immediately when class is loaded for the first time.
// .NET guarantees thread safety for static initialization // .NET guarantees thread safety for static initialization
private static readonly UplayLibrary _instance = new UplayLibrary(); private static readonly UplayLibrary _instance = new UplayLibrary();
// Common items to the class // Common items to the class
private List<Game> _allGames = new List<Game>(); private List<Game> _allGames = new List<Game>();
private string uplayAppIdRegex = @"/^[0-9A-F]{1,10}$"; private string uplayAppIdRegex = @"/^[0-9A-F]{1,10}$";
@ -34,9 +36,11 @@ namespace DisplayMagician.GameLibraries
// Other constants that are useful // Other constants that are useful
#endregion #endregion
#region Class Constructors #region Class Constructors
UplayLibrary() static UplayLibrary() { }
private UplayLibrary()
{ {
try try
{ {

View File

@ -16,6 +16,7 @@ using System.Drawing;
using DesktopNotifications; using DesktopNotifications;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using NLog.Config; using NLog.Config;
using System.Collections.Generic;
namespace DisplayMagician { 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 // Now lets prepare loading all the Uplay games we have installed
Task[] loadGamesTasks = new Task[3]; Action loadUplayGamesAction = new Action(() =>
loadGamesTasks[0] = loadSteamGamesTask; {
loadGamesTasks[1] = loadUplayGamesTask; // Check if Uplay is installed
loadGamesTasks[2] = loadOriginGamesTask; 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) // Now lets prepare loading all the Origin games we have installed
loadGameTask.Start(); 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<Action> loadGamesActions = new List<Action>();
loadGamesActions.Add(loadSteamGamesAction);
loadGamesActions.Add(loadUplayGamesAction);
loadGamesActions.Add(loadOriginGamesAction);
try try
{ {
logger.Debug($"Program/LoadGamesInBackground: Waiting for all game loading tasks to finish"); logger.Debug($"Program/LoadGamesInBackground: Running game loading actions.");
Task.WaitAll(loadGamesTasks); // 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"); logger.Debug($"Program/LoadGamesInBackground: All game loading tasks finished");
} }
catch (AggregateException ae) catch (AggregateException ae)
{ {
logger.Error(ae, $"Program/LoadGamesInBackground exception during loadGamesTasks"); logger.Error(ae, $"Program/LoadGamesInBackground exception during loadGamesActions");
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;
}
}
} }
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; failedTask = true;
foreach (var ex in loadGameTask.Exception.InnerExceptions) foreach (var ex in loadGameTask.Exception.InnerExceptions)
@ -827,7 +887,7 @@ namespace DisplayMagician {
} }
if (failedTask) if (failedTask)
return false; return false;*/
return true; return true;