From 95a93a05b39d2b27b538ecdb0e6a18f28096c5c2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 7 Feb 2017 20:50:41 -0500 Subject: remove oldest deprecated code (#231) Since Stardew Valley 1.2 breaks most mods anyway, this commits removes the oldest deprecations and fixes the issues that are easiest for mods to update. See documentation for details. --- src/StardewModdingAPI/Program.cs | 46 +++++++++++++++------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 45bf1238..c0a05e2d 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -14,14 +14,13 @@ using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Models; -using StardewModdingAPI.Inheritance; using StardewValley; using Monitor = StardewModdingAPI.Framework.Monitor; namespace StardewModdingAPI { /// The main entry point for SMAPI, responsible for hooking into and launching the game. - public class Program + internal class Program { /********* ** Properties @@ -52,31 +51,27 @@ namespace StardewModdingAPI /// Tracks whether the game should exit immediately and any pending initialisation should be cancelled. private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); - - /********* - ** Accessors - *********/ - /// The number of mods currently loaded by SMAPI. - public static int ModsLoaded; - - /// The underlying game instance. - public static SGame gamePtr; - /// Whether the game is currently running. - public static bool ready; + private static bool ready; /// The underlying game assembly. - public static Assembly StardewAssembly; + private static Assembly StardewAssembly; /// The underlying type. - public static Type StardewProgramType; + private static Type StardewProgramType; /// The field containing game's main instance. - public static FieldInfo StardewGameInfo; + private static FieldInfo StardewGameInfo; - // ReSharper disable once PossibleNullReferenceException - /// The game's build type (i.e. GOG vs Steam). - public static int BuildType => (int)Program.StardewProgramType.GetField("buildType", BindingFlags.Public | BindingFlags.Static).GetValue(null); + + /********* + ** Accessors + *********/ + /// The underlying game instance. + internal static SGame gamePtr; + + /// The number of mods currently loaded by SMAPI. + internal static int ModsLoaded; /// Tracks the installed mods. internal static readonly ModRegistry ModRegistry = new ModRegistry(); @@ -358,7 +353,7 @@ namespace StardewModdingAPI string errorPrefix = $"Couldn't load mod for manifest '{manifestPath}'"; // read manifest - ManifestImpl manifest; + Manifest manifest; try { // read manifest text @@ -370,7 +365,7 @@ namespace StardewModdingAPI } // deserialise manifest - manifest = helper.ReadJsonFile("manifest.json"); + manifest = helper.ReadJsonFile("manifest.json"); if (manifest == null) { Program.Monitor.Log($"{errorPrefix}: the manifest file does not exist.", LogLevel.Error); @@ -381,10 +376,6 @@ namespace StardewModdingAPI Program.Monitor.Log($"{errorPrefix}: manifest doesn't specify an entry DLL.", LogLevel.Error); continue; } - - // log deprecated fields - if (manifest.UsedAuthourField) - deprecationWarnings.Add(() => Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.Authour)}", "1.0", DeprecationLevel.Notice)); } catch (Exception ex) { @@ -424,7 +415,7 @@ namespace StardewModdingAPI } catch (FormatException ex) when (ex.Message.Contains("not a valid semantic version")) { - Program.Monitor.Log($"{errorPrefix}: the mod specified an invalid minimum SMAPI version '{manifest.MinimumApiVersion}'. This should be a semantic version number like {Constants.Version}.", LogLevel.Error); + Program.Monitor.Log($"{errorPrefix}: the mod specified an invalid minimum SMAPI version '{manifest.MinimumApiVersion}'. This should be a semantic version number like {Constants.ApiVersion}.", LogLevel.Error); continue; } } @@ -528,14 +519,11 @@ namespace StardewModdingAPI { // call entry methods mod.Entry(); // deprecated since 1.0 - mod.Entry((ModHelper)mod.Helper); // deprecated since 1.1 mod.Entry(mod.Helper); // raise deprecation warning for old Entry() methods if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(object[]) })) Program.DeprecationManager.Warn(mod.ModManifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}(object[]) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.0", DeprecationLevel.Notice); - if (Program.DeprecationManager.IsVirtualMethodImplemented(mod.GetType(), typeof(Mod), nameof(Mod.Entry), new[] { typeof(ModHelper) })) - Program.DeprecationManager.Warn(mod.ModManifest.Name, $"{nameof(Mod)}.{nameof(Mod.Entry)}({nameof(ModHelper)}) instead of {nameof(Mod)}.{nameof(Mod.Entry)}({nameof(IModHelper)})", "1.1", DeprecationLevel.PendingRemoval); } catch (Exception ex) { -- cgit From 3e91af6b06deec6aa2dca80945c82af528094c52 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 10 Feb 2017 22:52:16 -0500 Subject: mark several mods incompatible with Stardew Valley 1.2+ (#231) --- src/StardewModdingAPI/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index c0a05e2d..75be23f2 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -385,7 +385,7 @@ namespace StardewModdingAPI // validate known incompatible mods IncompatibleMod compatibility; - if (incompatibleMods.TryGetValue(manifest.UniqueID ?? $"{manifest.Name}|{manifest.Author}|{manifest.EntryDll}", out compatibility)) + if (incompatibleMods.TryGetValue(!string.IsNullOrWhiteSpace(manifest.UniqueID) ? manifest.UniqueID : manifest.EntryDll, out compatibility)) { if (!compatibility.IsCompatible(manifest.Version)) { -- cgit From 46b7d7a4001e209d7201ed5cad38cad2f1ad2a7f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 11 Feb 2017 01:15:56 -0500 Subject: redirect the game's debug messages into trace logs (#233) The game writes debug messages directly to the console, which shows up for SMAPI users. This commit redirects direct console messages to a monitor. --- src/StardewModdingAPI/Program.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 75be23f2..a6302540 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -13,6 +13,7 @@ using Newtonsoft.Json; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; +using StardewModdingAPI.Framework.Logging; using StardewModdingAPI.Framework.Models; using StardewValley; using Monitor = StardewModdingAPI.Framework.Monitor; @@ -42,8 +43,11 @@ namespace StardewModdingAPI /// The log file to which to write messages. private static readonly LogFileManager LogFile = new LogFileManager(Constants.LogPath); + /// Manages console output interception. + private static readonly ConsoleInterceptionManager ConsoleManager = new ConsoleInterceptionManager(); + /// The core logger for SMAPI. - private static readonly Monitor Monitor = new Monitor("SMAPI", Program.LogFile); + private static readonly Monitor Monitor = new Monitor("SMAPI", Program.ConsoleManager, Program.LogFile); /// The user settings for SMAPI. private static UserSettings Settings; @@ -87,14 +91,12 @@ namespace StardewModdingAPI /// The command-line arguments. private static void Main(string[] args) { - // set log options + // initialise logging Program.Monitor.WriteToConsole = !args.Contains("--no-terminal"); Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); // for consistent log formatting - - // add info header Program.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version} on {Environment.OSVersion}", LogLevel.Info); - // initialise user settings + // read config { string settingsPath = Constants.ApiConfigPath; if (File.Exists(settingsPath)) @@ -108,6 +110,12 @@ namespace StardewModdingAPI File.WriteAllText(settingsPath, JsonConvert.SerializeObject(Program.Settings, Formatting.Indented)); } + // redirect direct console output + { + IMonitor monitor = Program.GetSecondaryMonitor("Console.Out"); + Program.ConsoleManager.OnLineIntercepted += line => monitor.Log(line, LogLevel.Trace); + } + // add warning headers if (Program.Settings.DeveloperMode) { @@ -574,7 +582,7 @@ namespace StardewModdingAPI /// The name of the module which will log messages with this instance. private static Monitor GetSecondaryMonitor(string name) { - return new Monitor(name, Program.LogFile) { WriteToConsole = Program.Monitor.WriteToConsole, ShowTraceInConsole = Program.Settings.DeveloperMode }; + return new Monitor(name, Program.ConsoleManager, Program.LogFile) { WriteToConsole = Program.Monitor.WriteToConsole, ShowTraceInConsole = Program.Settings.DeveloperMode }; } } } -- cgit From 824ca7174a2b6a46d8a4ea50cac41c78e56dc48f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 11 Feb 2017 02:04:01 -0500 Subject: delve into mod folders that only contain another folder (#208) This fixes a common issue when users unpack mods into a nested folder (e.g. "SomeMod-1.0.0\SomeMod\manifest.json"), which previously wouldn't be recognised as a mod. SMAPI will not do this if the folder contains files or more than one folder, to prevent backup folders and the like from being loaded. --- src/StardewModdingAPI/Program.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index a6302540..91a39042 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -337,9 +337,12 @@ namespace StardewModdingAPI // load mod assemblies List deprecationWarnings = new List(); // queue up deprecation warnings to show after mod list - foreach (string directory in Directory.GetDirectories(Program.ModPath)) + foreach (string directoryPath in Directory.GetDirectories(Program.ModPath)) { - string directoryName = new DirectoryInfo(directory).Name; + // passthrough empty directories + DirectoryInfo directory = new DirectoryInfo(directoryPath); + while (!directory.GetFiles().Any() && directory.GetDirectories().Length == 1) + directory = directory.GetDirectories().First(); // check for cancellation if (Program.CancellationTokenSource.IsCancellationRequested) @@ -349,13 +352,13 @@ namespace StardewModdingAPI } // get helper - IModHelper helper = new ModHelper(directory, Program.ModRegistry); + IModHelper helper = new ModHelper(directory.FullName, Program.ModRegistry); // get manifest path - string manifestPath = Path.Combine(directory, "manifest.json"); + string manifestPath = Path.Combine(directory.FullName, "manifest.json"); if (!File.Exists(manifestPath)) { - Program.Monitor.Log($"Ignored folder \"{directoryName}\" which doesn't have a manifest.json.", LogLevel.Warn); + Program.Monitor.Log($"Ignored folder \"{directory.Name}\" which doesn't have a manifest.json.", LogLevel.Warn); continue; } string errorPrefix = $"Couldn't load mod for manifest '{manifestPath}'"; @@ -434,7 +437,7 @@ namespace StardewModdingAPI deprecationWarnings.Add(() => Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Info)); try { - string psDir = Path.Combine(directory, "psconfigs"); + string psDir = Path.Combine(directory.FullName, "psconfigs"); Directory.CreateDirectory(psDir); if (!Directory.Exists(psDir)) { @@ -450,7 +453,7 @@ namespace StardewModdingAPI } // validate mod path to simplify errors - string assemblyPath = Path.Combine(directory, manifest.EntryDll); + string assemblyPath = Path.Combine(directory.FullName, manifest.EntryDll); if (!File.Exists(assemblyPath)) { Program.Monitor.Log($"{errorPrefix}: the entry DLL '{manifest.EntryDll}' does not exist.", LogLevel.Error); @@ -501,7 +504,7 @@ namespace StardewModdingAPI mod.ModManifest = manifest; mod.Helper = helper; mod.Monitor = Program.GetSecondaryMonitor(manifest.Name); - mod.PathOnDisk = directory; + mod.PathOnDisk = directory.FullName; // track mod Program.ModRegistry.Add(mod); -- cgit From 693f16f99ec3492e3bfcd0071af1d9d5e519dcfa Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 11 Feb 2017 02:08:21 -0500 Subject: don't write direct console output to log file (#233) Per discussion with mod developers. --- src/StardewModdingAPI/Program.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 91a39042..f4518e21 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -112,8 +112,10 @@ namespace StardewModdingAPI // redirect direct console output { - IMonitor monitor = Program.GetSecondaryMonitor("Console.Out"); - Program.ConsoleManager.OnLineIntercepted += line => monitor.Log(line, LogLevel.Trace); + Monitor monitor = Program.GetSecondaryMonitor("Console.Out"); + monitor.WriteToFile = false; // not useful for troubleshooting mods per discussion + if (monitor.WriteToConsole) + Program.ConsoleManager.OnLineIntercepted += line => monitor.Log(line, LogLevel.Trace); } // add warning headers -- cgit From d1080a8b2b54c777a446f08d9ecd5b76b4b2561a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 13 Feb 2017 00:13:29 -0500 Subject: move core JSON logic out of mod helper (#199) This lets SMAPI parse manifest.json files without a mod helper, so we can pass the mod name into the helper. --- src/StardewModdingAPI/Program.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index f4518e21..b86e186f 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -15,6 +15,7 @@ using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Logging; using StardewModdingAPI.Framework.Models; +using StardewModdingAPI.Framework.Serialisation; using StardewValley; using Monitor = StardewModdingAPI.Framework.Monitor; @@ -319,6 +320,9 @@ namespace StardewModdingAPI { Program.Monitor.Log("Loading mods..."); + // get JSON helper + JsonHelper jsonHelper = new JsonHelper(); + // get assembly loader AssemblyLoader modAssemblyLoader = new AssemblyLoader(Program.TargetPlatform, Program.Monitor); AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => modAssemblyLoader.ResolveAssembly(e.Name); @@ -353,9 +357,6 @@ namespace StardewModdingAPI return; } - // get helper - IModHelper helper = new ModHelper(directory.FullName, Program.ModRegistry); - // get manifest path string manifestPath = Path.Combine(directory.FullName, "manifest.json"); if (!File.Exists(manifestPath)) @@ -378,7 +379,7 @@ namespace StardewModdingAPI } // deserialise manifest - manifest = helper.ReadJsonFile("manifest.json"); + manifest = jsonHelper.ReadJsonFile(Path.Combine(directory.FullName, "manifest.json"), null); if (manifest == null) { Program.Monitor.Log($"{errorPrefix}: the manifest file does not exist.", LogLevel.Error); @@ -503,8 +504,9 @@ namespace StardewModdingAPI } // inject data + // get helper mod.ModManifest = manifest; - mod.Helper = helper; + mod.Helper = new ModHelper(directory.FullName, jsonHelper, Program.ModRegistry); mod.Monitor = Program.GetSecondaryMonitor(manifest.Name); mod.PathOnDisk = directory.FullName; -- cgit From 0441d0843c65775bc72377e32ed4b3b5ee0b8f75 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 13 Feb 2017 00:40:33 -0500 Subject: add new console command API with backward compatibility (#199) --- src/StardewModdingAPI/Program.cs | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index b86e186f..ff0dff29 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -84,6 +84,9 @@ namespace StardewModdingAPI /// Manages deprecation warnings. internal static readonly DeprecationManager DeprecationManager = new DeprecationManager(Program.Monitor, Program.ModRegistry); + /// Manages console commands. + internal static readonly CommandManager CommandManager = new CommandManager(); + /********* ** Public methods @@ -262,7 +265,7 @@ namespace StardewModdingAPI while (!Program.ready) Thread.Sleep(1000); // register help command - Command.RegisterCommand("help", "Lists all commands | 'help ' returns command description").CommandFired += Program.help_CommandFired; + Program.CommandManager.Add("SMAPI", "help", "Lists all commands | 'help ' returns command description", Program.HandleHelpCommand); // listen for command line input Program.Monitor.Log("Starting console..."); @@ -506,7 +509,7 @@ namespace StardewModdingAPI // inject data // get helper mod.ModManifest = manifest; - mod.Helper = new ModHelper(directory.FullName, jsonHelper, Program.ModRegistry); + mod.Helper = new ModHelper(manifest.Name, directory.FullName, jsonHelper, Program.ModRegistry, Program.CommandManager); mod.Monitor = Program.GetSecondaryMonitor(manifest.Name); mod.PathOnDisk = directory.FullName; @@ -556,24 +559,29 @@ namespace StardewModdingAPI private static void ConsoleInputLoop() { while (true) - Command.CallCommand(Console.ReadLine(), Program.Monitor); + { + string input = Console.ReadLine(); + if (!Program.CommandManager.Trigger(input)) + Program.Monitor.Log("Unknown command; type 'help' for a list of available commands.", LogLevel.Error); + } } /// The method called when the user submits the help command in the console. - /// The event sender. - /// The event data. - private static void help_CommandFired(object sender, EventArgsCommand e) + /// The command name. + /// The command arguments. + private static void HandleHelpCommand(string name, string[] arguments) { - if (e.Command.CalledArgs.Length > 0) + if (arguments.Any()) { - var command = Command.FindCommand(e.Command.CalledArgs[0]); - if (command == null) - Program.Monitor.Log("The specified command could't be found", LogLevel.Error); + + Framework.Command result = Program.CommandManager.Get(arguments[0]); + if (result == null) + Program.Monitor.Log("There's no command with that name.", LogLevel.Error); else - Program.Monitor.Log(command.CommandArgs.Length > 0 ? $"{command.CommandName}: {command.CommandDesc} - {string.Join(", ", command.CommandArgs)}" : $"{command.CommandName}: {command.CommandDesc}", LogLevel.Info); + Program.Monitor.Log($"{result.Name}: {result.Documentation} [added by {result.ModName}]", LogLevel.Info); } else - Program.Monitor.Log("Commands: " + string.Join(", ", Command.RegisteredCommands.Select(x => x.CommandName)), LogLevel.Info); + Program.Monitor.Log("Commands: " + string.Join(", ", Program.CommandManager.GetAll().Select(p => p.Name)), LogLevel.Info); } /// Show a 'press any key to exit' message, and exit when they press a key. -- cgit From 845fbaab12a040464e403aa693a0eaaa9c55c9ae Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 13 Feb 2017 01:19:02 -0500 Subject: migrate TrainerMod to new API (#199) --- src/StardewModdingAPI/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index ff0dff29..58b86e8f 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -578,7 +578,7 @@ namespace StardewModdingAPI if (result == null) Program.Monitor.Log("There's no command with that name.", LogLevel.Error); else - Program.Monitor.Log($"{result.Name}: {result.Documentation} [added by {result.ModName}]", LogLevel.Info); + Program.Monitor.Log($"{result.Name}: {result.Documentation}\n(Added by {result.ModName}.)", LogLevel.Info); } else Program.Monitor.Log("Commands: " + string.Join(", ", Program.CommandManager.GetAll().Select(p => p.Name)), LogLevel.Info); -- cgit From f140e844ed1e52d1f5955e07a397f44ef9ab3233 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 14 Feb 2017 22:06:06 -0500 Subject: streamline startup a bit --- src/StardewModdingAPI/Program.cs | 56 ++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 31 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 58b86e8f..c3f8f8d8 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -59,21 +59,12 @@ namespace StardewModdingAPI /// Whether the game is currently running. private static bool ready; - /// The underlying game assembly. - private static Assembly StardewAssembly; - - /// The underlying type. - private static Type StardewProgramType; - - /// The field containing game's main instance. - private static FieldInfo StardewGameInfo; - /********* ** Accessors *********/ /// The underlying game instance. - internal static SGame gamePtr; + internal static SGame GameInstance; /// The number of mods currently loaded by SMAPI. internal static int ModsLoaded; @@ -185,8 +176,8 @@ namespace StardewModdingAPI Program.CancellationTokenSource.Cancel(); if (Program.ready) { - Program.gamePtr.Exiting += (sender, e) => Program.PressAnyKeyToExit(); - Program.gamePtr.Exit(); + Program.GameInstance.Exiting += (sender, e) => Program.PressAnyKeyToExit(); + Program.GameInstance.Exit(); } } @@ -226,29 +217,31 @@ namespace StardewModdingAPI { try { - // load the game assembly - Program.Monitor.Log("Loading game..."); - Program.StardewAssembly = Assembly.UnsafeLoadFrom(Program.GameExecutablePath); - Program.StardewProgramType = Program.StardewAssembly.GetType("StardewValley.Program", true); - Program.StardewGameInfo = Program.StardewProgramType.GetField("gamePtr"); - Game1.version += $" | SMAPI {Constants.ApiVersion}"; - - // add error interceptors + // add error handlers #if SMAPI_FOR_WINDOWS Application.ThreadException += (sender, e) => Program.Monitor.Log($"Critical thread exception: {e.Exception.GetLogSummary()}", LogLevel.Error); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); #endif AppDomain.CurrentDomain.UnhandledException += (sender, e) => Program.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error); - // initialise game instance - Program.gamePtr = new SGame(Program.Monitor) { IsMouseVisible = false }; - Program.gamePtr.Exiting += (sender, e) => Program.ready = false; - Program.gamePtr.Window.ClientSizeChanged += (sender, e) => GraphicsEvents.InvokeResize(Program.Monitor, sender, e); - Program.gamePtr.Window.Title = $"Stardew Valley - Version {Game1.version}"; - Program.StardewGameInfo.SetValue(Program.StardewProgramType, Program.gamePtr); - - // patch graphics - Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; + // initialise game + { + // load assembly + Program.Monitor.Log("Loading game..."); + Assembly gameAssembly = Assembly.UnsafeLoadFrom(Program.GameExecutablePath); + Type gameProgramType = gameAssembly.GetType("StardewValley.Program", true); + + // set Game1 instance + Program.GameInstance = new SGame(Program.Monitor); + Program.GameInstance.Exiting += (sender, e) => Program.ready = false; + Program.GameInstance.Window.ClientSizeChanged += (sender, e) => GraphicsEvents.InvokeResize(Program.Monitor, sender, e); + Program.GameInstance.Window.Title = $"Stardew Valley - Version {Game1.version}"; + gameProgramType.GetField("gamePtr").SetValue(gameProgramType, Program.GameInstance); + + // configure + Game1.version += $" | SMAPI {Constants.ApiVersion}"; + Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; + } // load mods Program.LoadMods(); @@ -262,7 +255,8 @@ namespace StardewModdingAPI new Thread(() => { // wait for the game to load up - while (!Program.ready) Thread.Sleep(1000); + while (!Program.ready) + Thread.Sleep(1000); // register help command Program.CommandManager.Add("SMAPI", "help", "Lists all commands | 'help ' returns command description", Program.HandleHelpCommand); @@ -290,7 +284,7 @@ namespace StardewModdingAPI try { Program.ready = true; - Program.gamePtr.Run(); + Program.GameInstance.Run(); } finally { -- cgit From 176eddbf7b70934c2665aa3a0ac8b46bef04012a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 16 Feb 2017 00:54:41 -0500 Subject: make SMAPI core non-static, eliminate direct access between core components --- src/StardewModdingAPI/Program.cs | 287 +++++++++++++++++++++------------------ 1 file changed, 156 insertions(+), 131 deletions(-) (limited to 'src/StardewModdingAPI/Program.cs') diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index c3f8f8d8..0857d41b 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI ** Properties *********/ /// The target game platform. - private static readonly Platform TargetPlatform = + private readonly Platform TargetPlatform = #if SMAPI_FOR_WINDOWS Platform.Windows; #else @@ -36,47 +36,47 @@ namespace StardewModdingAPI #endif /// The full path to the Stardew Valley executable. - private static readonly string GameExecutablePath = Path.Combine(Constants.ExecutionPath, Program.TargetPlatform == Platform.Windows ? "Stardew Valley.exe" : "StardewValley.exe"); + private readonly string GameExecutablePath; /// The full path to the folder containing mods. - private static readonly string ModPath = Path.Combine(Constants.ExecutionPath, "Mods"); + private readonly string ModPath = Path.Combine(Constants.ExecutionPath, "Mods"); /// The log file to which to write messages. - private static readonly LogFileManager LogFile = new LogFileManager(Constants.LogPath); + private readonly LogFileManager LogFile = new LogFileManager(Constants.LogPath); /// Manages console output interception. - private static readonly ConsoleInterceptionManager ConsoleManager = new ConsoleInterceptionManager(); + private readonly ConsoleInterceptionManager ConsoleManager = new ConsoleInterceptionManager(); /// The core logger for SMAPI. - private static readonly Monitor Monitor = new Monitor("SMAPI", Program.ConsoleManager, Program.LogFile); + private readonly Monitor Monitor; /// The user settings for SMAPI. - private static UserSettings Settings; + private UserSettings Settings; /// Tracks whether the game should exit immediately and any pending initialisation should be cancelled. - private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); /// Whether the game is currently running. - private static bool ready; + private bool IsGameRunning; /********* ** Accessors *********/ /// The underlying game instance. - internal static SGame GameInstance; + internal SGame GameInstance; /// The number of mods currently loaded by SMAPI. - internal static int ModsLoaded; + internal int ModsLoaded; /// Tracks the installed mods. - internal static readonly ModRegistry ModRegistry = new ModRegistry(); + internal readonly ModRegistry ModRegistry = new ModRegistry(); /// Manages deprecation warnings. - internal static readonly DeprecationManager DeprecationManager = new DeprecationManager(Program.Monitor, Program.ModRegistry); + internal readonly DeprecationManager DeprecationManager; /// Manages console commands. - internal static readonly CommandManager CommandManager = new CommandManager(); + internal readonly CommandManager CommandManager = new CommandManager(); /********* @@ -85,11 +85,36 @@ namespace StardewModdingAPI /// The main entry point which hooks into and launches the game. /// The command-line arguments. private static void Main(string[] args) + { + new Program(writeToConsole: !args.Contains("--no-terminal")) + .LaunchInteractively(); + } + + /// Construct an instance. + internal Program(bool writeToConsole) + { + this.GameExecutablePath = Path.Combine(Constants.ExecutionPath, this.TargetPlatform == Platform.Windows ? "Stardew Valley.exe" : "StardewValley.exe"); + this.Monitor = new Monitor("SMAPI", this.ConsoleManager, this.LogFile, this.ExitGameImmediately) { WriteToConsole = writeToConsole }; + this.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry); + } + + /// Launch SMAPI. + internal void LaunchInteractively() { // initialise logging - Program.Monitor.WriteToConsole = !args.Contains("--no-terminal"); Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); // for consistent log formatting - Program.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version} on {Environment.OSVersion}", LogLevel.Info); + this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Game1.version} on {Environment.OSVersion}", LogLevel.Info); + + // inject compatibility shims +#pragma warning disable 618 + Command.Shim(this.CommandManager, this.DeprecationManager, this.ModRegistry); + Config.Shim(this.DeprecationManager); + InternalExtensions.Shim(this.ModRegistry); + Log.Shim(this.DeprecationManager); + Mod.Shim(this.DeprecationManager); + PlayerEvents.Shim(this.DeprecationManager); + TimeEvents.Shim(this.DeprecationManager); +#pragma warning restore 618 // read config { @@ -97,39 +122,39 @@ namespace StardewModdingAPI if (File.Exists(settingsPath)) { string json = File.ReadAllText(settingsPath); - Program.Settings = JsonConvert.DeserializeObject(json); + this.Settings = JsonConvert.DeserializeObject(json); } else - Program.Settings = new UserSettings(); + this.Settings = new UserSettings(); - File.WriteAllText(settingsPath, JsonConvert.SerializeObject(Program.Settings, Formatting.Indented)); + File.WriteAllText(settingsPath, JsonConvert.SerializeObject(this.Settings, Formatting.Indented)); } // redirect direct console output { - Monitor monitor = Program.GetSecondaryMonitor("Console.Out"); + Monitor monitor = this.GetSecondaryMonitor("Console.Out"); monitor.WriteToFile = false; // not useful for troubleshooting mods per discussion if (monitor.WriteToConsole) - Program.ConsoleManager.OnLineIntercepted += line => monitor.Log(line, LogLevel.Trace); + this.ConsoleManager.OnLineIntercepted += line => monitor.Log(line, LogLevel.Trace); } // add warning headers - if (Program.Settings.DeveloperMode) + if (this.Settings.DeveloperMode) { - Program.Monitor.ShowTraceInConsole = true; - Program.Monitor.Log($"You configured SMAPI to run in developer mode. The console may be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI, or by editing or deleting {Constants.ApiConfigPath}.", LogLevel.Warn); + this.Monitor.ShowTraceInConsole = true; + this.Monitor.Log($"You configured SMAPI to run in developer mode. The console may be much more verbose. You can disable developer mode by installing the non-developer version of SMAPI, or by editing or deleting {Constants.ApiConfigPath}.", LogLevel.Warn); } - if (!Program.Settings.CheckForUpdates) - Program.Monitor.Log($"You configured SMAPI to not check for updates. Running an old version of SMAPI is not recommended. You can enable update checks by editing or deleting {Constants.ApiConfigPath}.", LogLevel.Warn); - if (!Program.Monitor.WriteToConsole) - Program.Monitor.Log("Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn); + if (!this.Settings.CheckForUpdates) + this.Monitor.Log($"You configured SMAPI to not check for updates. Running an old version of SMAPI is not recommended. You can enable update checks by editing or deleting {Constants.ApiConfigPath}.", LogLevel.Warn); + if (!this.Monitor.WriteToConsole) + this.Monitor.Log("Writing to the terminal is disabled because the --no-terminal argument was received. This usually means launching the terminal failed.", LogLevel.Warn); // print file paths - Program.Monitor.Log($"Mods go here: {Program.ModPath}"); + this.Monitor.Log($"Mods go here: {this.ModPath}"); // initialise legacy log - Log.Monitor = Program.GetSecondaryMonitor("legacy mod"); - Log.ModRegistry = Program.ModRegistry; + Log.Monitor = this.GetSecondaryMonitor("legacy mod"); + Log.ModRegistry = this.ModRegistry; // hook into & launch the game try @@ -137,56 +162,56 @@ namespace StardewModdingAPI // verify version if (String.Compare(Game1.version, Constants.MinimumGameVersion, StringComparison.InvariantCultureIgnoreCase) < 0) { - Program.Monitor.Log($"Oops! You're running Stardew Valley {Game1.version}, but the oldest supported version is {Constants.MinimumGameVersion}. Please update your game before using SMAPI. If you're on the Steam beta channel, note that the beta channel may not receive the latest updates.", LogLevel.Error); + this.Monitor.Log($"Oops! You're running Stardew Valley {Game1.version}, but the oldest supported version is {Constants.MinimumGameVersion}. Please update your game before using SMAPI. If you're on the Steam beta channel, note that the beta channel may not receive the latest updates.", LogLevel.Error); return; } // initialise - Program.Monitor.Log("Loading SMAPI..."); - Console.Title = Constants.ConsoleTitle; - Program.VerifyPath(Program.ModPath); - Program.VerifyPath(Constants.LogDir); - if (!File.Exists(Program.GameExecutablePath)) + this.Monitor.Log("Loading SMAPI..."); + Console.Title = $"Stardew Modding API Console - Version {Constants.ApiVersion}"; + this.VerifyPath(this.ModPath); + this.VerifyPath(Constants.LogDir); + if (!File.Exists(this.GameExecutablePath)) { - Program.Monitor.Log($"Couldn't find executable: {Program.GameExecutablePath}", LogLevel.Error); - Program.PressAnyKeyToExit(); + this.Monitor.Log($"Couldn't find executable: {this.GameExecutablePath}", LogLevel.Error); + this.PressAnyKeyToExit(); return; } // check for update when game loads - if (Program.Settings.CheckForUpdates) - GameEvents.GameLoaded += (sender, e) => Program.CheckForUpdateAsync(); + if (this.Settings.CheckForUpdates) + GameEvents.GameLoaded += (sender, e) => this.CheckForUpdateAsync(); // launch game - Program.StartGame(); + this.StartGame(); } catch (Exception ex) { - Program.Monitor.Log($"Critical error: {ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"Critical error: {ex.GetLogSummary()}", LogLevel.Error); } - Program.PressAnyKeyToExit(); + this.PressAnyKeyToExit(); } /// Immediately exit the game without saving. This should only be invoked when an irrecoverable fatal error happens that risks save corruption or game-breaking bugs. /// The module which requested an immediate exit. /// The reason provided for the shutdown. - internal static void ExitGameImmediately(string module, string reason) + internal void ExitGameImmediately(string module, string reason) { - Program.Monitor.LogFatal($"{module} requested an immediate game shutdown: {reason}"); - Program.CancellationTokenSource.Cancel(); - if (Program.ready) + this.Monitor.LogFatal($"{module} requested an immediate game shutdown: {reason}"); + this.CancellationTokenSource.Cancel(); + if (this.IsGameRunning) { - Program.GameInstance.Exiting += (sender, e) => Program.PressAnyKeyToExit(); - Program.GameInstance.Exit(); + this.GameInstance.Exiting += (sender, e) => this.PressAnyKeyToExit(); + this.GameInstance.Exit(); } } /// Get a monitor for legacy code which doesn't have one passed in. [Obsolete("This method should only be used when needed for backwards compatibility.")] - internal static IMonitor GetLegacyMonitorForMod() + internal IMonitor GetLegacyMonitorForMod() { - string modName = Program.ModRegistry.GetModFromStack() ?? "unknown"; - return Program.GetSecondaryMonitor(modName); + string modName = this.ModRegistry.GetModFromStack() ?? "unknown"; + return this.GetSecondaryMonitor(modName); } @@ -194,7 +219,7 @@ namespace StardewModdingAPI ** Private methods *********/ /// Asynchronously check for a new version of SMAPI, and print a message to the console if an update is available. - private static void CheckForUpdateAsync() + private void CheckForUpdateAsync() { new Thread(() => { @@ -203,40 +228,40 @@ namespace StardewModdingAPI GitRelease release = UpdateHelper.GetLatestVersionAsync(Constants.GitHubRepository).Result; ISemanticVersion latestVersion = new SemanticVersion(release.Tag); if (latestVersion.IsNewerThan(Constants.ApiVersion)) - Program.Monitor.Log($"You can update SMAPI from version {Constants.ApiVersion} to {latestVersion}", LogLevel.Alert); + this.Monitor.Log($"You can update SMAPI from version {Constants.ApiVersion} to {latestVersion}", LogLevel.Alert); } catch (Exception ex) { - Program.Monitor.Log($"Couldn't check for a new version of SMAPI. This won't affect your game, but you may not be notified of new versions if this keeps happening.\n{ex.GetLogSummary()}"); + this.Monitor.Log($"Couldn't check for a new version of SMAPI. This won't affect your game, but you may not be notified of new versions if this keeps happening.\n{ex.GetLogSummary()}"); } }).Start(); } /// Hook into Stardew Valley and launch the game. - private static void StartGame() + private void StartGame() { try { // add error handlers #if SMAPI_FOR_WINDOWS - Application.ThreadException += (sender, e) => Program.Monitor.Log($"Critical thread exception: {e.Exception.GetLogSummary()}", LogLevel.Error); + Application.ThreadException += (sender, e) => this.Monitor.Log($"Critical thread exception: {e.Exception.GetLogSummary()}", LogLevel.Error); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); #endif - AppDomain.CurrentDomain.UnhandledException += (sender, e) => Program.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error); + AppDomain.CurrentDomain.UnhandledException += (sender, e) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error); // initialise game { // load assembly - Program.Monitor.Log("Loading game..."); - Assembly gameAssembly = Assembly.UnsafeLoadFrom(Program.GameExecutablePath); + this.Monitor.Log("Loading game..."); + Assembly gameAssembly = Assembly.UnsafeLoadFrom(this.GameExecutablePath); Type gameProgramType = gameAssembly.GetType("StardewValley.Program", true); // set Game1 instance - Program.GameInstance = new SGame(Program.Monitor); - Program.GameInstance.Exiting += (sender, e) => Program.ready = false; - Program.GameInstance.Window.ClientSizeChanged += (sender, e) => GraphicsEvents.InvokeResize(Program.Monitor, sender, e); - Program.GameInstance.Window.Title = $"Stardew Valley - Version {Game1.version}"; - gameProgramType.GetField("gamePtr").SetValue(gameProgramType, Program.GameInstance); + this.GameInstance = new SGame(this.Monitor); + this.GameInstance.Exiting += (sender, e) => this.IsGameRunning = false; + this.GameInstance.Window.ClientSizeChanged += (sender, e) => GraphicsEvents.InvokeResize(this.Monitor, sender, e); + this.GameInstance.Window.Title = $"Stardew Valley - Version {Game1.version}"; + gameProgramType.GetField("gamePtr").SetValue(gameProgramType, this.GameInstance); // configure Game1.version += $" | SMAPI {Constants.ApiVersion}"; @@ -244,10 +269,10 @@ namespace StardewModdingAPI } // load mods - Program.LoadMods(); - if (Program.CancellationTokenSource.IsCancellationRequested) + this.LoadMods(); + if (this.CancellationTokenSource.IsCancellationRequested) { - Program.Monitor.Log("Shutdown requested; interrupting initialisation.", LogLevel.Error); + this.Monitor.Log("Shutdown requested; interrupting initialisation.", LogLevel.Error); return; } @@ -255,18 +280,18 @@ namespace StardewModdingAPI new Thread(() => { // wait for the game to load up - while (!Program.ready) + while (!this.IsGameRunning) Thread.Sleep(1000); // register help command - Program.CommandManager.Add("SMAPI", "help", "Lists all commands | 'help ' returns command description", Program.HandleHelpCommand); + this.CommandManager.Add("SMAPI", "help", "Lists all commands | 'help ' returns command description", this.HandleHelpCommand); // listen for command line input - Program.Monitor.Log("Starting console..."); - Program.Monitor.Log("Type 'help' for help, or 'help ' for a command's usage", LogLevel.Info); - Thread consoleInputThread = new Thread(Program.ConsoleInputLoop); + this.Monitor.Log("Starting console..."); + this.Monitor.Log("Type 'help' for help, or 'help ' for a command's usage", LogLevel.Info); + Thread consoleInputThread = new Thread(this.ConsoleInputLoop); consoleInputThread.Start(); - while (Program.ready) + while (this.IsGameRunning) Thread.Sleep(1000 / 10); // Check if the game is still running 10 times a second // abort the console thread, we're closing @@ -275,31 +300,31 @@ namespace StardewModdingAPI }).Start(); // start game loop - Program.Monitor.Log("Starting game..."); - if (Program.CancellationTokenSource.IsCancellationRequested) + this.Monitor.Log("Starting game..."); + if (this.CancellationTokenSource.IsCancellationRequested) { - Program.Monitor.Log("Shutdown requested; interrupting initialisation.", LogLevel.Error); + this.Monitor.Log("Shutdown requested; interrupting initialisation.", LogLevel.Error); return; } try { - Program.ready = true; - Program.GameInstance.Run(); + this.IsGameRunning = true; + this.GameInstance.Run(); } finally { - Program.ready = false; + this.IsGameRunning = false; } } catch (Exception ex) { - Program.Monitor.Log($"The game encountered a fatal error:\n{ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"The game encountered a fatal error:\n{ex.GetLogSummary()}", LogLevel.Error); } } /// Create a directory path if it doesn't exist. /// The directory path. - private static void VerifyPath(string path) + private void VerifyPath(string path) { try { @@ -308,20 +333,20 @@ namespace StardewModdingAPI } catch (Exception ex) { - Program.Monitor.Log($"Couldn't create a path: {path}\n\n{ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"Couldn't create a path: {path}\n\n{ex.GetLogSummary()}", LogLevel.Error); } } /// Load and hook up all mods in the mod directory. - private static void LoadMods() + private void LoadMods() { - Program.Monitor.Log("Loading mods..."); + this.Monitor.Log("Loading mods..."); // get JSON helper JsonHelper jsonHelper = new JsonHelper(); // get assembly loader - AssemblyLoader modAssemblyLoader = new AssemblyLoader(Program.TargetPlatform, Program.Monitor); + AssemblyLoader modAssemblyLoader = new AssemblyLoader(this.TargetPlatform, this.Monitor); AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => modAssemblyLoader.ResolveAssembly(e.Name); // get known incompatible mods @@ -335,12 +360,12 @@ namespace StardewModdingAPI catch (Exception ex) { incompatibleMods = new Dictionary(); - Program.Monitor.Log($"Couldn't read metadata file at {Constants.ApiModMetadataPath}. SMAPI will still run, but some features may be disabled.\n{ex}", LogLevel.Warn); + this.Monitor.Log($"Couldn't read metadata file at {Constants.ApiModMetadataPath}. SMAPI will still run, but some features may be disabled.\n{ex}", LogLevel.Warn); } // load mod assemblies List deprecationWarnings = new List(); // queue up deprecation warnings to show after mod list - foreach (string directoryPath in Directory.GetDirectories(Program.ModPath)) + foreach (string directoryPath in Directory.GetDirectories(this.ModPath)) { // passthrough empty directories DirectoryInfo directory = new DirectoryInfo(directoryPath); @@ -348,9 +373,9 @@ namespace StardewModdingAPI directory = directory.GetDirectories().First(); // check for cancellation - if (Program.CancellationTokenSource.IsCancellationRequested) + if (this.CancellationTokenSource.IsCancellationRequested) { - Program.Monitor.Log("Shutdown requested; interrupting mod loading.", LogLevel.Error); + this.Monitor.Log("Shutdown requested; interrupting mod loading.", LogLevel.Error); return; } @@ -358,7 +383,7 @@ namespace StardewModdingAPI string manifestPath = Path.Combine(directory.FullName, "manifest.json"); if (!File.Exists(manifestPath)) { - Program.Monitor.Log($"Ignored folder \"{directory.Name}\" which doesn't have a manifest.json.", LogLevel.Warn); + this.Monitor.Log($"Ignored folder \"{directory.Name}\" which doesn't have a manifest.json.", LogLevel.Warn); continue; } string errorPrefix = $"Couldn't load mod for manifest '{manifestPath}'"; @@ -371,7 +396,7 @@ namespace StardewModdingAPI string json = File.ReadAllText(manifestPath); if (string.IsNullOrEmpty(json)) { - Program.Monitor.Log($"{errorPrefix}: manifest is empty.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: manifest is empty.", LogLevel.Error); continue; } @@ -379,18 +404,18 @@ namespace StardewModdingAPI manifest = jsonHelper.ReadJsonFile(Path.Combine(directory.FullName, "manifest.json"), null); if (manifest == null) { - Program.Monitor.Log($"{errorPrefix}: the manifest file does not exist.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: the manifest file does not exist.", LogLevel.Error); continue; } if (string.IsNullOrEmpty(manifest.EntryDll)) { - Program.Monitor.Log($"{errorPrefix}: manifest doesn't specify an entry DLL.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: manifest doesn't specify an entry DLL.", LogLevel.Error); continue; } } catch (Exception ex) { - Program.Monitor.Log($"{errorPrefix}: manifest parsing failed.\n{ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: manifest parsing failed.\n{ex.GetLogSummary()}", LogLevel.Error); continue; } @@ -407,7 +432,7 @@ namespace StardewModdingAPI if (!string.IsNullOrWhiteSpace(compatibility.UnofficialUpdateUrl)) warning += $"{Environment.NewLine}- unofficial update: {compatibility.UnofficialUpdateUrl}"; - Program.Monitor.Log(warning, LogLevel.Error); + this.Monitor.Log(warning, LogLevel.Error); continue; } } @@ -420,13 +445,13 @@ namespace StardewModdingAPI ISemanticVersion minVersion = new SemanticVersion(manifest.MinimumApiVersion); if (minVersion.IsNewerThan(Constants.ApiVersion)) { - Program.Monitor.Log($"{errorPrefix}: this mod requires SMAPI {minVersion} or later. Please update SMAPI to the latest version to use this mod.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: this mod requires SMAPI {minVersion} or later. Please update SMAPI to the latest version to use this mod.", LogLevel.Error); continue; } } catch (FormatException ex) when (ex.Message.Contains("not a valid semantic version")) { - Program.Monitor.Log($"{errorPrefix}: the mod specified an invalid minimum SMAPI version '{manifest.MinimumApiVersion}'. This should be a semantic version number like {Constants.ApiVersion}.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: the mod specified an invalid minimum SMAPI version '{manifest.MinimumApiVersion}'. This should be a semantic version number like {Constants.ApiVersion}.", LogLevel.Error); continue; } } @@ -434,20 +459,20 @@ namespace StardewModdingAPI // create per-save directory if (manifest.PerSaveConfigs) { - deprecationWarnings.Add(() => Program.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Info)); + deprecationWarnings.Add(() => this.DeprecationManager.Warn(manifest.Name, $"{nameof(Manifest)}.{nameof(Manifest.PerSaveConfigs)}", "1.0", DeprecationLevel.Info)); try { string psDir = Path.Combine(directory.FullName, "psconfigs"); Directory.CreateDirectory(psDir); if (!Directory.Exists(psDir)) { - Program.Monitor.Log($"{errorPrefix}: couldn't create the per-save configuration directory ('psconfigs') requested by this mod. The failure reason is unknown.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: couldn't create the per-save configuration directory ('psconfigs') requested by this mod. The failure reason is unknown.", LogLevel.Error); continue; } } catch (Exception ex) { - Program.Monitor.Log($"{errorPrefix}: couldn't create the per-save configuration directory ('psconfigs') requested by this mod.\n{ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: couldn't create the per-save configuration directory ('psconfigs') requested by this mod.\n{ex.GetLogSummary()}", LogLevel.Error); continue; } } @@ -456,7 +481,7 @@ namespace StardewModdingAPI string assemblyPath = Path.Combine(directory.FullName, manifest.EntryDll); if (!File.Exists(assemblyPath)) { - Program.Monitor.Log($"{errorPrefix}: the entry DLL '{manifest.EntryDll}' does not exist.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: the entry DLL '{manifest.EntryDll}' does not exist.", LogLevel.Error); continue; } @@ -468,7 +493,7 @@ namespace StardewModdingAPI } catch (Exception ex) { - Program.Monitor.Log($"{errorPrefix}: an error occurred while preprocessing '{manifest.EntryDll}'.\n{ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: an error occurred while preprocessing '{manifest.EntryDll}'.\n{ex.GetLogSummary()}", LogLevel.Error); continue; } @@ -477,13 +502,13 @@ namespace StardewModdingAPI { if (modAssembly.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) == 0) { - Program.Monitor.Log($"{errorPrefix}: the mod DLL does not contain an implementation of the 'Mod' class.", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: the mod DLL does not contain an implementation of the 'Mod' class.", LogLevel.Error); continue; } } catch (Exception ex) { - Program.Monitor.Log($"{errorPrefix}: an error occurred while reading the mod DLL.\n{ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"{errorPrefix}: an error occurred while reading the mod DLL.\n{ex.GetLogSummary()}", LogLevel.Error); continue; } @@ -496,25 +521,25 @@ namespace StardewModdingAPI mod = (Mod)modAssembly.CreateInstance(modEntryType.ToString()); if (mod == null) { - Program.Monitor.Log($"{errorPrefix}: the mod's entry class could not be instantiated."); + this.Monitor.Log($"{errorPrefix}: the mod's entry class could not be instantiated.");