From bcaf5b21c1e64ddca29b27d2b96652a3d925d8ff Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Apr 2017 18:57:43 -0400 Subject: remove Initialize/LoadContent overrides & deprecate related events (#265) --- src/StardewModdingAPI/Events/GameEvents.cs | 48 ++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 9 deletions(-) (limited to 'src/StardewModdingAPI/Events/GameEvents.cs') diff --git a/src/StardewModdingAPI/Events/GameEvents.cs b/src/StardewModdingAPI/Events/GameEvents.cs index 715083b9..47c1275b 100644 --- a/src/StardewModdingAPI/Events/GameEvents.cs +++ b/src/StardewModdingAPI/Events/GameEvents.cs @@ -6,18 +6,27 @@ namespace StardewModdingAPI.Events /// Events raised when the game changes state. public static class GameEvents { + /********* + ** Properties + *********/ + /// Manages deprecation warnings. + private static DeprecationManager DeprecationManager; + + /********* ** Events *********/ /// Raised during launch after configuring XNA or MonoGame. The game window hasn't been opened by this point. Called during . + [Obsolete("The " + nameof(Mod) + "." + nameof(Mod.Entry) + " method is now called after the " + nameof(Initialize) + " event, so any contained logic can be done directly in " + nameof(Mod.Entry) + ".")] public static event EventHandler Initialize; - /// Raised during launch after configuring Stardew Valley, loading it into memory, and opening the game window. The window is still blank by this point. - public static event EventHandler GameLoaded; - /// Raised before XNA loads or reloads graphics resources. Called during . + [Obsolete("The " + nameof(Mod) + "." + nameof(Mod.Entry) + " method is now called after the " + nameof(LoadContent) + " event, so any contained logic can be done directly in " + nameof(Mod.Entry) + ".")] public static event EventHandler LoadContent; + /// Raised during launch after configuring Stardew Valley, loading it into memory, and opening the game window. The window is still blank by this point. + public static event EventHandler GameLoaded; + /// Raised during the first game update tick. public static event EventHandler FirstUpdateTick; @@ -46,25 +55,46 @@ namespace StardewModdingAPI.Events /********* ** Internal methods *********/ - /// Raise a event. - /// Encapsulates monitoring and logging. - internal static void InvokeGameLoaded(IMonitor monitor) + /// Injects types required for backwards compatibility. + /// Manages deprecation warnings. + internal static void Shim(DeprecationManager deprecationManager) { - monitor.SafelyRaisePlainEvent($"{nameof(GameEvents)}.{nameof(GameEvents.GameLoaded)}", GameEvents.GameLoaded?.GetInvocationList()); + GameEvents.DeprecationManager = deprecationManager; } /// Raise an event. /// Encapsulates logging and monitoring. internal static void InvokeInitialize(IMonitor monitor) { - monitor.SafelyRaisePlainEvent($"{nameof(GameEvents)}.{nameof(GameEvents.Initialize)}", GameEvents.Initialize?.GetInvocationList()); + if (GameEvents.Initialize == null) + return; + + string name = $"{nameof(GameEvents)}.{nameof(GameEvents.Initialize)}"; + Delegate[] handlers = GameEvents.Initialize.GetInvocationList(); + + GameEvents.DeprecationManager.WarnForEvent(handlers, name, "1.10", DeprecationLevel.Info); + monitor.SafelyRaisePlainEvent(name, handlers); } /// Raise a event. /// Encapsulates logging and monitoring. internal static void InvokeLoadContent(IMonitor monitor) { - monitor.SafelyRaisePlainEvent($"{nameof(GameEvents)}.{nameof(GameEvents.LoadContent)}", GameEvents.LoadContent?.GetInvocationList()); + if (GameEvents.LoadContent == null) + return; + + string name = $"{nameof(GameEvents)}.{nameof(GameEvents.LoadContent)}"; + Delegate[] handlers = GameEvents.LoadContent.GetInvocationList(); + + GameEvents.DeprecationManager.WarnForEvent(handlers, name, "1.10", DeprecationLevel.Info); + monitor.SafelyRaisePlainEvent(name, handlers); + } + + /// Raise a event. + /// Encapsulates monitoring and logging. + internal static void InvokeGameLoaded(IMonitor monitor) + { + monitor.SafelyRaisePlainEvent($"{nameof(GameEvents)}.{nameof(GameEvents.GameLoaded)}", GameEvents.GameLoaded?.GetInvocationList()); } /// Raise an event. -- cgit From 86ef70feece302f21041204b3baa31d757730acc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Apr 2017 21:51:49 -0400 Subject: revamp startup process (#265) This revamps SMAPI's startup process to simplify mod development by ensuring that core components are ready by the time mods are loaded (which is also needed for the upcoming content API), and eliminate or reduce SEHExceptions some players experience. --- release-notes.md | 4 +- src/StardewModdingAPI/Events/GameEvents.cs | 12 +- src/StardewModdingAPI/Framework/SGame.cs | 2 + src/StardewModdingAPI/Program.cs | 356 ++++++++++++++--------------- 4 files changed, 189 insertions(+), 185 deletions(-) (limited to 'src/StardewModdingAPI/Events/GameEvents.cs') diff --git a/release-notes.md b/release-notes.md index 2f1dffd2..e6ddb3db 100644 --- a/release-notes.md +++ b/release-notes.md @@ -15,12 +15,14 @@ See [log](https://github.com/Pathoschild/SMAPI/compare/1.9...1.10). For players: * Added support for Stardew Valley 1.2 beta. -* SMAPI now rewrites many mods for compatibility with game updates, but some mods may still need an update. +* Added logic to rewrite many mods for compatibility with game updates, though some mods may still need an update. +* Fixed some players getting `SEHException` errors. For mod developers: * Fixed mouse-changed event never updating prior mouse position. * Fixed `monitor.ExitGameImmediately` not working correctly. * Mods can now specify a minimum game version in their `manifest.json`. +* Mods are now initialised after the `Initialize`/`LoadContent` phase, which means the `GameEvents.Initialize` and `GameEvents.LoadContent` events are deprecated. You can move any logic in those methods to your mod's `Entry` method. ## 1.9 See [log](https://github.com/Pathoschild/SMAPI/compare/1.8...1.9). diff --git a/src/StardewModdingAPI/Events/GameEvents.cs b/src/StardewModdingAPI/Events/GameEvents.cs index 47c1275b..babf7f31 100644 --- a/src/StardewModdingAPI/Events/GameEvents.cs +++ b/src/StardewModdingAPI/Events/GameEvents.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using StardewModdingAPI.Framework; namespace StardewModdingAPI.Events @@ -16,7 +17,10 @@ namespace StardewModdingAPI.Events /********* ** Events *********/ - /// Raised during launch after configuring XNA or MonoGame. The game window hasn't been opened by this point. Called during . + /// Raised during launch after configuring XNA or MonoGame. The game window hasn't been opened by this point. Called after . + internal static event EventHandler InitializeInternal; + + /// Raised during launch after configuring XNA or MonoGame. The game window hasn't been opened by this point. Called after . [Obsolete("The " + nameof(Mod) + "." + nameof(Mod.Entry) + " method is now called after the " + nameof(Initialize) + " event, so any contained logic can be done directly in " + nameof(Mod.Entry) + ".")] public static event EventHandler Initialize; @@ -66,12 +70,14 @@ namespace StardewModdingAPI.Events /// Encapsulates logging and monitoring. internal static void InvokeInitialize(IMonitor monitor) { + // notify SMAPI + monitor.SafelyRaisePlainEvent($"{nameof(GameEvents)}.{nameof(GameEvents.InitializeInternal)}", GameEvents.InitializeInternal?.GetInvocationList()); + + // notify mods if (GameEvents.Initialize == null) return; - string name = $"{nameof(GameEvents)}.{nameof(GameEvents.Initialize)}"; Delegate[] handlers = GameEvents.Initialize.GetInvocationList(); - GameEvents.DeprecationManager.WarnForEvent(handlers, name, "1.10", DeprecationLevel.Info); monitor.SafelyRaisePlainEvent(name, handlers); } diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs index 99b9f991..39cc5cde 100644 --- a/src/StardewModdingAPI/Framework/SGame.cs +++ b/src/StardewModdingAPI/Framework/SGame.cs @@ -196,6 +196,8 @@ namespace StardewModdingAPI.Framework this.Monitor = monitor; this.FirstUpdate = true; SGame.Instance = this; + + Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; // required by Stardew Valley } /**** diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 678fa90d..3bd91a7c 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -10,7 +9,6 @@ using System.Threading; using System.Management; using System.Windows.Forms; #endif -using Microsoft.Xna.Framework.Graphics; using Newtonsoft.Json; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Events; @@ -38,26 +36,30 @@ namespace StardewModdingAPI /// The core logger for SMAPI. private readonly Monitor Monitor; - /// The SMAPI configuration settings. - private readonly SConfig Settings; - /// Tracks whether the game should exit immediately and any pending initialisation should be cancelled. private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); - /// Whether the game is currently running. - private bool IsGameRunning; - /// The underlying game instance. private SGame GameInstance; + /// The SMAPI configuration settings. + /// This is initialised after the game starts. + private SConfig Settings; + /// Tracks the installed mods. - private readonly ModRegistry ModRegistry; + /// This is initialised after the game starts. + private ModRegistry ModRegistry; /// Manages deprecation warnings. - private readonly DeprecationManager DeprecationManager; + /// This is initialised after the game starts. + private DeprecationManager DeprecationManager; /// Manages console commands. - private readonly CommandManager CommandManager = new CommandManager(); + /// This is initialised after the game starts. + private CommandManager CommandManager; + + /// Whether the game is currently running. + private bool IsGameRunning; /********* @@ -65,7 +67,7 @@ namespace StardewModdingAPI *********/ /// The main entry point which hooks into and launches the game. /// The command-line arguments. - private static void Main(string[] args) + public static void Main(string[] args) { // get flags from arguments bool writeToConsole = !args.Contains("--no-terminal"); @@ -92,65 +94,28 @@ namespace StardewModdingAPI /// Construct an instance. /// Whether to output log messages to the console. /// The full file path to which to write log messages. - internal Program(bool writeToConsole, string logPath) + public Program(bool writeToConsole, string logPath) { - // load settings - this.Settings = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiConfigPath)); - - // initialise this.LogFile = new LogFileManager(logPath); this.Monitor = new Monitor("SMAPI", this.ConsoleManager, this.LogFile, this.ExitGameImmediately) { WriteToConsole = writeToConsole }; - this.ModRegistry = new ModRegistry(this.Settings.ModCompatibility); - this.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry); } /// Launch SMAPI. - internal void LaunchInteractively() + public void LaunchInteractively() { - // initialise logging - Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); // for consistent log formatting - this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} on {this.GetFriendlyPlatformName()}", LogLevel.Info); - Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)}"; - - // 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, this.GetSecondaryMonitor("legacy mod"), this.ModRegistry); - Mod.Shim(this.DeprecationManager); - ContentEvents.Shim(this.ModRegistry, this.Monitor); - GameEvents.Shim(this.DeprecationManager); - PlayerEvents.Shim(this.DeprecationManager); - TimeEvents.Shim(this.DeprecationManager); -#pragma warning restore 618 - - // redirect direct console output - { - Monitor monitor = this.GetSecondaryMonitor("Console.Out"); - monitor.WriteToFile = false; // not useful for troubleshooting mods per discussion - if (monitor.WriteToConsole) - this.ConsoleManager.OnLineIntercepted += line => monitor.Log(line, LogLevel.Trace); - } - - // add warning headers - if (this.Settings.DeveloperMode) + // initialise SMAPI + try { - 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 {Constants.ApiConfigPath}.", 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 reinstalling SMAPI or editing {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); + // init logging + this.Monitor.Log($"SMAPI {Constants.ApiVersion} with Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} on {this.GetFriendlyPlatformName()}", LogLevel.Info); + this.Monitor.Log($"Mods go here: {Constants.ModPath}"); + this.Monitor.Log("Preparing SMAPI..."); - // print file paths - this.Monitor.Log($"Mods go here: {Constants.ModPath}"); + // validate paths + this.VerifyPath(Constants.ModPath); + this.VerifyPath(Constants.LogDir); - // hook into & launch the game - try - { - // verify version + // validate game version if (Constants.GameVersion.IsOlderThan(Constants.MinimumGameVersion)) { this.Monitor.Log($"Oops! You're running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)}, but the oldest supported version is {Constants.GetGameDisplayVersion(Constants.MinimumGameVersion)}. Please update your game before using SMAPI. If you have the beta version on Steam, you may need to opt out to get the latest non-beta updates.", LogLevel.Error); @@ -164,29 +129,63 @@ namespace StardewModdingAPI return; } - // initialise folders - this.Monitor.Log("Loading SMAPI..."); - this.VerifyPath(Constants.ModPath); - this.VerifyPath(Constants.LogDir); + // add error handlers +#if SMAPI_FOR_WINDOWS + 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) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error); + + // override game & hook events + this.GameInstance = new SGame(this.Monitor); +#if SDV_1_2 + StardewValley.Program.gamePtr = this.GameInstance; +#else + { + Type type = typeof(Game1).Assembly.GetType("StardewValley.Program", true); + type.GetField("gamePtr").SetValue(null, this.GameInstance); + } +#endif - // check for update when game loads - if (this.Settings.CheckForUpdates) - GameEvents.GameLoaded += (sender, e) => this.CheckForUpdateAsync(); + // hook into game events + this.GameInstance.Exiting += (sender, e) => this.IsGameRunning = false; + this.GameInstance.Window.ClientSizeChanged += (sender, e) => GraphicsEvents.InvokeResize(this.Monitor, sender, e); + GameEvents.InitializeInternal += (sender, e) => this.InitialiseAfterGameStart(); + GameEvents.GameLoaded += (sender, e) => this.CheckForUpdateAsync(); - // launch game - this.StartGame(); + // set window titles + this.GameInstance.Window.Title = $"Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} - running SMAPI {Constants.ApiVersion}"; + Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)}"; } catch (Exception ex) { - this.Monitor.Log($"Critical error: {ex.GetLogSummary()}", LogLevel.Error); + this.Monitor.Log($"SMAPI failed to initialise: {ex.GetLogSummary()}", LogLevel.Error); + this.PressAnyKeyToExit(); + return; + } + + // start game + this.Monitor.Log("Starting game..."); + try + { + this.IsGameRunning = true; + this.GameInstance.Run(); + } + catch (Exception ex) + { + this.Monitor.Log($"The game failed unexpectedly: {ex.GetLogSummary()}", LogLevel.Error); + this.PressAnyKeyToExit(); + } + finally + { + this.IsGameRunning = false; } - 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 void ExitGameImmediately(string module, string reason) + public void ExitGameImmediately(string module, string reason) { this.Monitor.LogFatal($"{module} requested an immediate game shutdown: {reason}"); this.CancellationTokenSource.Cancel(); @@ -209,9 +208,106 @@ namespace StardewModdingAPI /********* ** Private methods *********/ + /// Initialise SMAPI and mods after the game starts. + private void InitialiseAfterGameStart() + { + // load settings + this.Settings = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiConfigPath)); + + // load core components + this.ModRegistry = new ModRegistry(this.Settings.ModCompatibility); + this.DeprecationManager = new DeprecationManager(this.Monitor, this.ModRegistry); + this.CommandManager = new CommandManager(); + + // 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, this.GetSecondaryMonitor("legacy mod"), this.ModRegistry); + Mod.Shim(this.DeprecationManager); + ContentEvents.Shim(this.ModRegistry, this.Monitor); + GameEvents.Shim(this.DeprecationManager); + PlayerEvents.Shim(this.DeprecationManager); + TimeEvents.Shim(this.DeprecationManager); +#pragma warning restore 618 + + // redirect direct console output + { + Monitor monitor = this.GetSecondaryMonitor("Console.Out"); + monitor.WriteToFile = false; // not useful for troubleshooting mods per discussion + if (monitor.WriteToConsole) + this.ConsoleManager.OnLineIntercepted += line => monitor.Log(line, LogLevel.Trace); + } + + // add warning headers + if (this.Settings.DeveloperMode) + { + 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 {Constants.ApiConfigPath}.", 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 reinstalling SMAPI or editing {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); + + // load mods + int modsLoaded = this.LoadMods(); + if (this.CancellationTokenSource.IsCancellationRequested) + { + this.Monitor.Log("Shutdown requested; interrupting initialisation.", LogLevel.Error); + return; + } + + // update window titles + this.GameInstance.Window.Title = $"Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} - running SMAPI {Constants.ApiVersion} with {modsLoaded} mods"; + Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} with {modsLoaded} mods"; + + // start SMAPI console + new Thread(this.RunConsoleLoop).Start(); + } + + /// Run a loop handling console input. + [SuppressMessage("ReSharper", "FunctionNeverReturns", Justification = "The thread is aborted when the game exits.")] + private void RunConsoleLoop() + { + // prepare help command + this.Monitor.Log("Starting console..."); + this.Monitor.Log("Type 'help' for help, or 'help ' for a command's usage", LogLevel.Info); + this.CommandManager.Add("SMAPI", "help", "Lists all commands | 'help ' returns command description", this.HandleHelpCommand); + + // start handling command line input + Thread inputThread = new Thread(() => + { + while (true) + { + string input = Console.ReadLine(); + try + { + if (!string.IsNullOrWhiteSpace(input) && !this.CommandManager.Trigger(input)) + this.Monitor.Log("Unknown command; type 'help' for a list of available commands.", LogLevel.Error); + } + catch (Exception ex) + { + this.Monitor.Log($"The handler registered for that command failed:\n{ex.GetLogSummary()}", LogLevel.Error); + } + } + }); + inputThread.Start(); + + // keep console thread alive while the game is running + while (this.IsGameRunning) + Thread.Sleep(1000 / 10); + if (inputThread.ThreadState == ThreadState.Running) + inputThread.Abort(); + } + /// Asynchronously check for a new version of SMAPI, and print a message to the console if an update is available. private void CheckForUpdateAsync() { + if (!this.Settings.CheckForUpdates) + return; + new Thread(() => { try @@ -228,91 +324,6 @@ namespace StardewModdingAPI }).Start(); } - /// Hook into Stardew Valley and launch the game. - private void StartGame() - { - try - { - this.Monitor.Log("Loading game..."); - - // add error handlers -#if SMAPI_FOR_WINDOWS - 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) => this.Monitor.Log($"Critical app domain exception: {e.ExceptionObject}", LogLevel.Error); - - // override Game1 instance - 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 {Constants.GameVersion} with SMAPI {Constants.ApiVersion}"; -#if SDV_1_2 - StardewValley.Program.gamePtr = this.GameInstance; -#else - { - Type type = typeof(Game1).Assembly.GetType("StardewValley.Program", true); - type.GetField("gamePtr").SetValue(null, this.GameInstance); - } -#endif - - // configure - Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; - - // load mods - this.LoadMods(); - if (this.CancellationTokenSource.IsCancellationRequested) - { - this.Monitor.Log("Shutdown requested; interrupting initialisation.", LogLevel.Error); - return; - } - - // initialise console after game launches - new Thread(() => - { - // wait for the game to load up - while (!this.IsGameRunning) - Thread.Sleep(1000); - - // register help command - this.CommandManager.Add("SMAPI", "help", "Lists all commands | 'help ' returns command description", this.HandleHelpCommand); - - // listen for command line input - 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 (this.IsGameRunning) - Thread.Sleep(1000 / 10); // Check if the game is still running 10 times a second - - // abort the console thread, we're closing - if (consoleInputThread.ThreadState == ThreadState.Running) - consoleInputThread.Abort(); - }).Start(); - - // start game loop - this.Monitor.Log("Starting game..."); - if (this.CancellationTokenSource.IsCancellationRequested) - { - this.Monitor.Log("Shutdown requested; interrupting initialisation.", LogLevel.Error); - return; - } - try - { - this.IsGameRunning = true; - this.GameInstance.Run(); - } - finally - { - this.IsGameRunning = false; - } - } - catch (Exception ex) - { - 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 void VerifyPath(string path) @@ -329,7 +340,8 @@ namespace StardewModdingAPI } /// Load and hook up all mods in the mod directory. - private void LoadMods() + /// Returns the number of mods loaded. + private int LoadMods() { this.Monitor.Log("Loading mods..."); @@ -354,7 +366,7 @@ namespace StardewModdingAPI if (this.CancellationTokenSource.IsCancellationRequested) { this.Monitor.Log("Shutdown requested; interrupting mod loading.", LogLevel.Error); - return; + return modsLoaded; } // get manifest path @@ -553,12 +565,12 @@ namespace StardewModdingAPI } // initialise mods - foreach (Mod mod in this.ModRegistry.GetMods()) + foreach (IMod mod in this.ModRegistry.GetMods()) { try { // call entry methods - mod.Entry(); // deprecated since 1.0 + (mod as Mod)?.Entry(); // deprecated since 1.0 mod.Entry(mod.Helper); // raise deprecation warning for old Entry() methods @@ -575,26 +587,8 @@ namespace StardewModdingAPI this.Monitor.Log($"Loaded {modsLoaded} mods."); foreach (Action warning in deprecationWarnings) warning(); - Console.Title = $"SMAPI {Constants.ApiVersion} - running Stardew Valley {Constants.GetGameDisplayVersion(Constants.GameVersion)} with {modsLoaded} mods"; - } - /// Run a loop handling console input. - [SuppressMessage("ReSharper", "FunctionNeverReturns", Justification = "The thread is aborted when the game exits.")] - private void ConsoleInputLoop() - { - while (true) - { - string input = Console.ReadLine(); - try - { - if (!string.IsNullOrWhiteSpace(input) && !this.CommandManager.Trigger(input)) - this.Monitor.Log("Unknown command; type 'help' for a list of available commands.", LogLevel.Error); - } - catch (Exception ex) - { - this.Monitor.Log($"The handler registered for that command failed:\n{ex.GetLogSummary()}", LogLevel.Error); - } - } + return modsLoaded; } /// The method called when the user submits the help command in the console. -- cgit From a9c220c0fe0fd6f52bee73a8f5da91fd1d007d0f Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 23 Apr 2017 23:00:51 -0400 Subject: minor cleanup --- src/StardewModdingAPI.Installer/InteractiveInstaller.cs | 3 +-- src/StardewModdingAPI/Events/GameEvents.cs | 5 ++--- src/StardewModdingAPI/Framework/Monitor.cs | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/StardewModdingAPI/Events/GameEvents.cs') diff --git a/src/StardewModdingAPI.Installer/InteractiveInstaller.cs b/src/StardewModdingAPI.Installer/InteractiveInstaller.cs index c456fdc0..38c19d2b 100644 --- a/src/StardewModdingAPI.Installer/InteractiveInstaller.cs +++ b/src/StardewModdingAPI.Installer/InteractiveInstaller.cs @@ -626,9 +626,8 @@ namespace StardewModdingApi.Installer private void Move(FileSystemInfo entry, string newPath) { // file - if (entry is FileInfo) + if (entry is FileInfo file) { - FileInfo file = (FileInfo)entry; file.CopyTo(newPath); file.Delete(); } diff --git a/src/StardewModdingAPI/Events/GameEvents.cs b/src/StardewModdingAPI/Events/GameEvents.cs index babf7f31..029ec1f9 100644 --- a/src/StardewModdingAPI/Events/GameEvents.cs +++ b/src/StardewModdingAPI/Events/GameEvents.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using StardewModdingAPI.Framework; namespace StardewModdingAPI.Events @@ -21,11 +20,11 @@ namespace StardewModdingAPI.Events internal static event EventHandler InitializeInternal; /// Raised during launch after configuring XNA or MonoGame. The game window hasn't been opened by this point. Called after . - [Obsolete("The " + nameof(Mod) + "." + nameof(Mod.Entry) + " method is now called after the " + nameof(Initialize) + " event, so any contained logic can be done directly in " + nameof(Mod.Entry) + ".")] + [Obsolete("The " + nameof(Mod) + "." + nameof(Mod.Entry) + " method is now called after the " + nameof(GameEvents.Initialize) + " event, so any contained logic can be done directly in " + nameof(Mod.Entry) + ".")] public static event EventHandler Initialize; /// Raised before XNA loads or reloads graphics resources. Called during . - [Obsolete("The " + nameof(Mod) + "." + nameof(Mod.Entry) + " method is now called after the " + nameof(LoadContent) + " event, so any contained logic can be done directly in " + nameof(Mod.Entry) + ".")] + [Obsolete("The " + nameof(Mod) + "." + nameof(Mod.Entry) + " method is now called after the " + nameof(GameEvents.LoadContent) + " event, so any contained logic can be done directly in " + nameof(Mod.Entry) + ".")] public static event EventHandler LoadContent; /// Raised during launch after configuring Stardew Valley, loading it into memory, and opening the game window. The window is still blank by this point. diff --git a/src/StardewModdingAPI/Framework/Monitor.cs b/src/StardewModdingAPI/Framework/Monitor.cs index 76793bbf..51feff78 100644 --- a/src/StardewModdingAPI/Framework/Monitor.cs +++ b/src/StardewModdingAPI/Framework/Monitor.cs @@ -35,7 +35,7 @@ namespace StardewModdingAPI.Framework }; /// A delegate which requests that SMAPI immediately exit the game. This should only be invoked when an irrecoverable fatal error happens that risks save corruption or game-breaking bugs. - private RequestExitDelegate RequestExit; + private readonly RequestExitDelegate RequestExit; /********* -- cgit