diff --git a/docs/release-notes.md b/docs/release-notes.md index 12fd5a3d..05fccd74 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -18,7 +18,8 @@ These changes have not been released yet. * Added `Context.IsGameLaunched` field. * Mods are now loaded much earlier in the game launch. This lets mods intercept any content asset, but the game is not fully initialised when `Entry` is called (use the `GameLaunched` event if you need to run code when the game is initialised). * When a mod is incompatible, the trace logs now list all detected issues instead of the first one. - * Dropped support for all deprecated APIs. + * Removed all deprecated APIs. + * Removed the `Monitor.ExitGameImmediately` method. * Updated to Json.NET 12.0.1. ## 2.11.3 diff --git a/src/SMAPI/Framework/Monitor.cs b/src/SMAPI/Framework/Monitor.cs index 617bfd85..3771f114 100644 --- a/src/SMAPI/Framework/Monitor.cs +++ b/src/SMAPI/Framework/Monitor.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Threading; using StardewModdingAPI.Framework.Logging; using StardewModdingAPI.Internal.ConsoleWriting; @@ -27,16 +26,10 @@ namespace StardewModdingAPI.Framework /// The maximum length of the values. private static readonly int MaxLevelLength = (from level in Enum.GetValues(typeof(LogLevel)).Cast() select level.ToString().Length).Max(); - /// Propagates notification that SMAPI should exit. - private readonly CancellationTokenSource ExitTokenSource; - /********* ** Accessors *********/ - /// Whether SMAPI is aborting. Mods don't need to worry about this unless they have background tasks. - public bool IsExiting => this.ExitTokenSource.IsCancellationRequested; - /// Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed. public bool IsVerbose { get; } @@ -57,10 +50,9 @@ namespace StardewModdingAPI.Framework /// The name of the module which logs messages using this instance. /// Intercepts access to the console output. /// The log file to which to write messages. - /// Propagates notification that SMAPI should exit. /// The console color scheme to use. /// Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed. - public Monitor(string source, ConsoleInterceptionManager consoleInterceptor, LogFileManager logFile, CancellationTokenSource exitTokenSource, MonitorColorScheme colorScheme, bool isVerbose) + public Monitor(string source, ConsoleInterceptionManager consoleInterceptor, LogFileManager logFile, MonitorColorScheme colorScheme, bool isVerbose) { // validate if (string.IsNullOrWhiteSpace(source)) @@ -71,7 +63,6 @@ namespace StardewModdingAPI.Framework this.LogFile = logFile ?? throw new ArgumentNullException(nameof(logFile), "The log file manager cannot be null."); this.ConsoleWriter = new ColorfulConsoleWriter(Constants.Platform, colorScheme); this.ConsoleInterceptor = consoleInterceptor; - this.ExitTokenSource = exitTokenSource; this.IsVerbose = isVerbose; } @@ -91,14 +82,6 @@ namespace StardewModdingAPI.Framework this.Log(message, LogLevel.Trace); } - /// 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 reason for the shutdown. - public void ExitGameImmediately(string reason) - { - this.LogFatal($"{this.Source} requested an immediate game shutdown: {reason}"); - this.ExitTokenSource.Cancel(); - } - /// Write a newline to the console and log file. internal void Newline() { @@ -107,6 +90,13 @@ namespace StardewModdingAPI.Framework this.LogFile.WriteLine(""); } + /// Log a fatal error message. + /// The message to log. + internal void LogFatal(string message) + { + this.LogImpl(this.Source, message, ConsoleLogLevel.Critical); + } + /// Log console input from the user. /// The user input to log. internal void LogUserInput(string input) @@ -120,13 +110,6 @@ namespace StardewModdingAPI.Framework /********* ** Private methods *********/ - /// Log a fatal error message. - /// The message to log. - private void LogFatal(string message) - { - this.LogImpl(this.Source, message, ConsoleLogLevel.Critical); - } - /// Write a message line to the log. /// The name of the mod logging the message. /// The message to log. diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index e8d5b672..03a27fa9 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -57,7 +57,7 @@ namespace StardewModdingAPI.Framework private readonly Monitor MonitorForGame; /// Tracks whether the game should exit immediately and any pending initialisation should be cancelled. - private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource CancellationToken = new CancellationTokenSource(); /// Simplifies access to private game code. private readonly Reflector Reflection = new Reflector(); @@ -144,7 +144,7 @@ namespace StardewModdingAPI.Framework // init basics this.Settings = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiConfigPath)); this.LogFile = new LogFileManager(logPath); - this.Monitor = new Monitor("SMAPI", this.ConsoleManager, this.LogFile, this.CancellationTokenSource, this.Settings.ColorScheme, this.Settings.VerboseLogging) + this.Monitor = new Monitor("SMAPI", this.ConsoleManager, this.LogFile, this.Settings.ColorScheme, this.Settings.VerboseLogging) { WriteToConsole = writeToConsole, ShowTraceInConsole = this.Settings.DeveloperMode, @@ -222,7 +222,8 @@ namespace StardewModdingAPI.Framework modRegistry: this.ModRegistry, deprecationManager: SCore.DeprecationManager, onGameInitialised: this.InitialiseAfterGameStart, - onGameExiting: this.Dispose + onGameExiting: this.Dispose, + cancellationToken: this.CancellationToken ); StardewValley.Program.gamePtr = this.GameInstance; @@ -237,7 +238,7 @@ namespace StardewModdingAPI.Framework // add exit handler new Thread(() => { - this.CancellationTokenSource.Token.WaitHandle.WaitOne(); + this.CancellationToken.Token.WaitHandle.WaitOne(); if (this.IsGameRunning) { try @@ -363,7 +364,7 @@ namespace StardewModdingAPI.Framework this.IsGameRunning = false; this.ConsoleManager?.Dispose(); this.ContentCore?.Dispose(); - this.CancellationTokenSource?.Dispose(); + this.CancellationToken?.Dispose(); this.GameInstance?.Dispose(); this.LogFile?.Dispose(); @@ -378,7 +379,7 @@ namespace StardewModdingAPI.Framework /// Initialise mods before the first game asset is loaded. At this point the core content managers are loaded (so mods can load their own assets), but the game is mostly uninitialised. private void InitialiseBeforeFirstAssetLoaded() { - if (this.Monitor.IsExiting) + if (this.CancellationToken.IsCancellationRequested) { this.Monitor.Log("SMAPI shutting down: aborting initialisation.", LogLevel.Warn); return; @@ -482,7 +483,7 @@ namespace StardewModdingAPI.Framework inputThread.Start(); // keep console thread alive while the game is running - while (this.IsGameRunning && !this.Monitor.IsExiting) + while (this.IsGameRunning && !this.CancellationToken.IsCancellationRequested) Thread.Sleep(1000 / 10); if (inputThread.ThreadState == ThreadState.Running) inputThread.Abort(); @@ -1336,7 +1337,7 @@ namespace StardewModdingAPI.Framework /// The name of the module which will log messages with this instance. private Monitor GetSecondaryMonitor(string name) { - return new Monitor(name, this.ConsoleManager, this.LogFile, this.CancellationTokenSource, this.Settings.ColorScheme, this.Settings.VerboseLogging) + return new Monitor(name, this.ConsoleManager, this.LogFile, this.Settings.ColorScheme, this.Settings.VerboseLogging) { WriteToConsole = this.Monitor.WriteToConsole, ShowTraceInConsole = this.Settings.DeveloperMode, diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index c06f62cf..2d43f8c4 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -43,7 +43,7 @@ namespace StardewModdingAPI.Framework ** SMAPI state ****/ /// Encapsulates monitoring and logging for SMAPI. - private readonly IMonitor Monitor; + private readonly Monitor Monitor; /// Encapsulates monitoring and logging on the game's behalf. private readonly IMonitor MonitorForGame; @@ -85,6 +85,9 @@ namespace StardewModdingAPI.Framework /// Simplifies access to private game code. private readonly Reflector Reflection; + /// Propagates notification that SMAPI should exit. + private readonly CancellationTokenSource CancellationToken; + /**** ** Game state ****/ @@ -137,7 +140,8 @@ namespace StardewModdingAPI.Framework /// Manages deprecation warnings. /// A callback to invoke after the game finishes initialising. /// A callback to invoke when the game exits. - internal SGame(IMonitor monitor, IMonitor monitorForGame, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, ModRegistry modRegistry, DeprecationManager deprecationManager, Action onGameInitialised, Action onGameExiting) + /// Propagates notification that SMAPI should exit. + internal SGame(Monitor monitor, IMonitor monitorForGame, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, ModRegistry modRegistry, DeprecationManager deprecationManager, Action onGameInitialised, Action onGameExiting, CancellationTokenSource cancellationToken) { this.OnLoadingFirstAsset = SGame.ConstructorHack.OnLoadingFirstAsset; SGame.ConstructorHack = null; @@ -161,6 +165,7 @@ namespace StardewModdingAPI.Framework Game1.input = new SInputState(); Game1.multiplayer = new SMultiplayer(monitor, eventManager, jsonHelper, modRegistry, reflection, this.OnModMessageReceived); Game1.hooks = new SModHooks(this.OnNewDayAfterFade); + this.CancellationToken = cancellationToken; // init observables Game1.locations = new ObservableCollection(); @@ -274,7 +279,7 @@ namespace StardewModdingAPI.Framework } // Abort if SMAPI is exiting. - if (this.Monitor.IsExiting) + if (this.CancellationToken.IsCancellationRequested) { this.Monitor.Log("SMAPI shutting down: aborting update.", LogLevel.Trace); return; @@ -805,7 +810,7 @@ namespace StardewModdingAPI.Framework // exit if irrecoverable if (!this.UpdateCrashTimer.Decrement()) - this.Monitor.ExitGameImmediately("the game crashed when updating, and SMAPI was unable to recover the game."); + this.ExitGameImmediately("The game crashed when updating, and SMAPI was unable to recover the game."); } } @@ -827,7 +832,7 @@ namespace StardewModdingAPI.Framework // exit if irrecoverable if (!this.DrawCrashTimer.Decrement()) { - this.Monitor.ExitGameImmediately("the game crashed when drawing, and SMAPI was unable to recover the game."); + this.ExitGameImmediately("The game crashed when drawing, and SMAPI was unable to recover the game."); return; } @@ -1481,5 +1486,13 @@ namespace StardewModdingAPI.Framework } } } + + /// 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 fatal log message. + private void ExitGameImmediately(string message) + { + this.Monitor.LogFatal(message); + this.CancellationToken.Cancel(); + } } } diff --git a/src/SMAPI/IMonitor.cs b/src/SMAPI/IMonitor.cs index 943c1c59..f2d110b8 100644 --- a/src/SMAPI/IMonitor.cs +++ b/src/SMAPI/IMonitor.cs @@ -6,9 +6,6 @@ namespace StardewModdingAPI /********* ** Accessors *********/ - /// Whether SMAPI is aborting. Mods don't need to worry about this unless they have background tasks. - bool IsExiting { get; } - /// Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed. bool IsVerbose { get; } @@ -24,9 +21,5 @@ namespace StardewModdingAPI /// Log a message that only appears when is enabled. /// The message to log. void VerboseLog(string message); - - /// 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 reason for the shutdown. - void ExitGameImmediately(string reason); } }