drop monitor.ExitGameImmediately method

This is bad practice in most cases, and was only used by two mods which didn't legitimately need to exit immediately.
This commit is contained in:
Jesse Plamondon-Willard 2019-05-01 19:40:47 -04:00
parent e18ffc009d
commit a450b0ebef
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
5 changed files with 37 additions and 46 deletions

View File

@ -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

View File

@ -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
/// <summary>The maximum length of the <see cref="LogLevel"/> values.</summary>
private static readonly int MaxLevelLength = (from level in Enum.GetValues(typeof(LogLevel)).Cast<LogLevel>() select level.ToString().Length).Max();
/// <summary>Propagates notification that SMAPI should exit.</summary>
private readonly CancellationTokenSource ExitTokenSource;
/*********
** Accessors
*********/
/// <summary>Whether SMAPI is aborting. Mods don't need to worry about this unless they have background tasks.</summary>
public bool IsExiting => this.ExitTokenSource.IsCancellationRequested;
/// <summary>Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed.</summary>
public bool IsVerbose { get; }
@ -57,10 +50,9 @@ namespace StardewModdingAPI.Framework
/// <param name="source">The name of the module which logs messages using this instance.</param>
/// <param name="consoleInterceptor">Intercepts access to the console output.</param>
/// <param name="logFile">The log file to which to write messages.</param>
/// <param name="exitTokenSource">Propagates notification that SMAPI should exit.</param>
/// <param name="colorScheme">The console color scheme to use.</param>
/// <param name="isVerbose">Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed.</param>
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);
}
/// <summary>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.</summary>
/// <param name="reason">The reason for the shutdown.</param>
public void ExitGameImmediately(string reason)
{
this.LogFatal($"{this.Source} requested an immediate game shutdown: {reason}");
this.ExitTokenSource.Cancel();
}
/// <summary>Write a newline to the console and log file.</summary>
internal void Newline()
{
@ -107,6 +90,13 @@ namespace StardewModdingAPI.Framework
this.LogFile.WriteLine("");
}
/// <summary>Log a fatal error message.</summary>
/// <param name="message">The message to log.</param>
internal void LogFatal(string message)
{
this.LogImpl(this.Source, message, ConsoleLogLevel.Critical);
}
/// <summary>Log console input from the user.</summary>
/// <param name="input">The user input to log.</param>
internal void LogUserInput(string input)
@ -120,13 +110,6 @@ namespace StardewModdingAPI.Framework
/*********
** Private methods
*********/
/// <summary>Log a fatal error message.</summary>
/// <param name="message">The message to log.</param>
private void LogFatal(string message)
{
this.LogImpl(this.Source, message, ConsoleLogLevel.Critical);
}
/// <summary>Write a message line to the log.</summary>
/// <param name="source">The name of the mod logging the message.</param>
/// <param name="message">The message to log.</param>

View File

@ -57,7 +57,7 @@ namespace StardewModdingAPI.Framework
private readonly Monitor MonitorForGame;
/// <summary>Tracks whether the game should exit immediately and any pending initialisation should be cancelled.</summary>
private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
private readonly CancellationTokenSource CancellationToken = new CancellationTokenSource();
/// <summary>Simplifies access to private game code.</summary>
private readonly Reflector Reflection = new Reflector();
@ -144,7 +144,7 @@ namespace StardewModdingAPI.Framework
// init basics
this.Settings = JsonConvert.DeserializeObject<SConfig>(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
/// <summary>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.</summary>
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
/// <param name="name">The name of the module which will log messages with this instance.</param>
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,

View File

@ -43,7 +43,7 @@ namespace StardewModdingAPI.Framework
** SMAPI state
****/
/// <summary>Encapsulates monitoring and logging for SMAPI.</summary>
private readonly IMonitor Monitor;
private readonly Monitor Monitor;
/// <summary>Encapsulates monitoring and logging on the game's behalf.</summary>
private readonly IMonitor MonitorForGame;
@ -85,6 +85,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Simplifies access to private game code.</summary>
private readonly Reflector Reflection;
/// <summary>Propagates notification that SMAPI should exit.</summary>
private readonly CancellationTokenSource CancellationToken;
/****
** Game state
****/
@ -137,7 +140,8 @@ namespace StardewModdingAPI.Framework
/// <param name="deprecationManager">Manages deprecation warnings.</param>
/// <param name="onGameInitialised">A callback to invoke after the game finishes initialising.</param>
/// <param name="onGameExiting">A callback to invoke when the game exits.</param>
internal SGame(IMonitor monitor, IMonitor monitorForGame, Reflector reflection, EventManager eventManager, JsonHelper jsonHelper, ModRegistry modRegistry, DeprecationManager deprecationManager, Action onGameInitialised, Action onGameExiting)
/// <param name="cancellationToken">Propagates notification that SMAPI should exit.</param>
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<GameLocation>();
@ -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
}
}
}
/// <summary>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.</summary>
/// <param name="message">The fatal log message.</param>
private void ExitGameImmediately(string message)
{
this.Monitor.LogFatal(message);
this.CancellationToken.Cancel();
}
}
}

View File

@ -6,9 +6,6 @@ namespace StardewModdingAPI
/*********
** Accessors
*********/
/// <summary>Whether SMAPI is aborting. Mods don't need to worry about this unless they have background tasks.</summary>
bool IsExiting { get; }
/// <summary>Whether verbose logging is enabled. This enables more detailed diagnostic messages than are normally needed.</summary>
bool IsVerbose { get; }
@ -24,9 +21,5 @@ namespace StardewModdingAPI
/// <summary>Log a message that only appears when <see cref="IsVerbose"/> is enabled.</summary>
/// <param name="message">The message to log.</param>
void VerboseLog(string message);
/// <summary>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.</summary>
/// <param name="reason">The reason for the shutdown.</param>
void ExitGameImmediately(string reason);
}
}