set error code on exit (#868)
This commit is contained in:
parent
352fa4759e
commit
e376386d25
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
## Upcoming release
|
## Upcoming release
|
||||||
* For players:
|
* For players:
|
||||||
|
* SMAPI now sets a success/error code when the game exits.
|
||||||
|
_This is used by your OS (like Windows) to decide whether to keep the console window open when the game ends._
|
||||||
* Fixed SMAPI on Windows applying different DPI awareness settings than the game (thanks to spacechase0!).
|
* Fixed SMAPI on Windows applying different DPI awareness settings than the game (thanks to spacechase0!).
|
||||||
* Fixed Linux/macOS installer's color scheme question partly unreadable if the terminal background is dark.
|
* Fixed Linux/macOS installer's color scheme question partly unreadable if the terminal background is dark.
|
||||||
* Fixed error message when a mod loads an invalid PNG file (thanks to atravita!).
|
* Fixed error message when a mod loads an invalid PNG file (thanks to atravita!).
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace StardewModdingAPI.Framework
|
||||||
|
{
|
||||||
|
/// <summary>The SMAPI exit state.</summary>
|
||||||
|
internal enum ExitState
|
||||||
|
{
|
||||||
|
/// <summary>SMAPI didn't trigger an explicit exit.</summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>The game is exiting normally.</summary>
|
||||||
|
GameExit,
|
||||||
|
|
||||||
|
/// <summary>The game is exiting due to an error.</summary>
|
||||||
|
Crash
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -50,6 +49,7 @@ using StardewModdingAPI.Utilities;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
using StardewValley.Menus;
|
using StardewValley.Menus;
|
||||||
using StardewValley.Objects;
|
using StardewValley.Objects;
|
||||||
|
using StardewValley.SDKs;
|
||||||
using xTile.Display;
|
using xTile.Display;
|
||||||
using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode;
|
using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode;
|
||||||
using MiniMonoModHotfix = MonoMod.Utils.MiniMonoModHotfix;
|
using MiniMonoModHotfix = MonoMod.Utils.MiniMonoModHotfix;
|
||||||
|
@ -67,8 +67,11 @@ namespace StardewModdingAPI.Framework
|
||||||
/****
|
/****
|
||||||
** Low-level components
|
** Low-level components
|
||||||
****/
|
****/
|
||||||
|
/// <summary>A state which indicates whether SMAPI should exit immediately and any pending initialization should be cancelled.</summary>
|
||||||
|
private ExitState ExitState;
|
||||||
|
|
||||||
/// <summary>Whether the game should exit immediately and any pending initialization should be cancelled.</summary>
|
/// <summary>Whether the game should exit immediately and any pending initialization should be cancelled.</summary>
|
||||||
private bool IsExiting;
|
private bool IsExiting => this.ExitState != ExitState.None;
|
||||||
|
|
||||||
/// <summary>Manages the SMAPI console window and log file.</summary>
|
/// <summary>Manages the SMAPI console window and log file.</summary>
|
||||||
private readonly LogManager LogManager;
|
private readonly LogManager LogManager;
|
||||||
|
@ -297,22 +300,13 @@ namespace StardewModdingAPI.Framework
|
||||||
this.IsGameRunning = true;
|
this.IsGameRunning = true;
|
||||||
StardewValley.Program.releaseBuild = true; // game's debug logic interferes with SMAPI opening the game window
|
StardewValley.Program.releaseBuild = true; // game's debug logic interferes with SMAPI opening the game window
|
||||||
this.Game.Run();
|
this.Game.Run();
|
||||||
|
this.Dispose(isError: false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.LogManager.LogFatalLaunchError(ex);
|
this.LogManager.LogFatalLaunchError(ex);
|
||||||
this.LogManager.PressAnyKeyToExit();
|
this.LogManager.PressAnyKeyToExit();
|
||||||
}
|
this.Dispose(isError: true);
|
||||||
finally
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
this.Monitor.Log($"The game ended, but SMAPI wasn't able to dispose correctly. Technical details: {ex}", LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,6 +321,14 @@ namespace StardewModdingAPI.Framework
|
||||||
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||||
[SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "May be disposed before SMAPI is fully initialized.")]
|
[SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "May be disposed before SMAPI is fully initialized.")]
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.Dispose(isError: true); // if we got here, SMAPI didn't detect the exit before it happened
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||||
|
/// <param name="isError">Whether the process is exiting due to an error or crash.</param>
|
||||||
|
[SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract", Justification = "May be disposed before SMAPI is fully initialized.")]
|
||||||
|
public void Dispose(bool isError)
|
||||||
{
|
{
|
||||||
// skip if already disposed
|
// skip if already disposed
|
||||||
if (this.IsDisposed)
|
if (this.IsDisposed)
|
||||||
|
@ -349,13 +351,29 @@ namespace StardewModdingAPI.Framework
|
||||||
|
|
||||||
// dispose core components
|
// dispose core components
|
||||||
this.IsGameRunning = false;
|
this.IsGameRunning = false;
|
||||||
this.IsExiting = true;
|
if (this.ExitState == ExitState.None || isError)
|
||||||
|
this.ExitState = isError ? ExitState.Crash : ExitState.GameExit;
|
||||||
this.ContentCore?.Dispose();
|
this.ContentCore?.Dispose();
|
||||||
this.Game?.Dispose();
|
this.Game?.Dispose();
|
||||||
this.LogManager.Dispose(); // dispose last to allow for any last-second log messages
|
this.LogManager.Dispose(); // dispose last to allow for any last-second log messages
|
||||||
|
|
||||||
// end game (moved from Game1.OnExiting to let us clean up first)
|
// clean up SDK
|
||||||
Process.GetCurrentProcess().Kill();
|
// This avoids Steam connection errors when it exits unexpectedly. The game avoids this
|
||||||
|
// by killing the entire process, but we can't set the error code if we do that.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FieldInfo? field = typeof(StardewValley.Program).GetField("_sdk", BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
|
SDKHelper? sdk = field?.GetValue(null) as SDKHelper;
|
||||||
|
sdk?.Shutdown();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// well, at least we tried
|
||||||
|
}
|
||||||
|
|
||||||
|
// end game with error code
|
||||||
|
// This helps the OS decide whether to keep the window open (e.g. Windows may keep it open on error).
|
||||||
|
Environment.Exit(this.ExitState == ExitState.Crash ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1250,7 +1268,7 @@ namespace StardewModdingAPI.Framework
|
||||||
private void OnGameExiting()
|
private void OnGameExiting()
|
||||||
{
|
{
|
||||||
this.Multiplayer.Disconnect(StardewValley.Multiplayer.DisconnectType.ClosedGame);
|
this.Multiplayer.Disconnect(StardewValley.Multiplayer.DisconnectType.ClosedGame);
|
||||||
this.Dispose();
|
this.Dispose(isError: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Raised when a mod network message is received.</summary>
|
/// <summary>Raised when a mod network message is received.</summary>
|
||||||
|
@ -2239,7 +2257,7 @@ namespace StardewModdingAPI.Framework
|
||||||
this.Monitor.LogFatal(message);
|
this.Monitor.LogFatal(message);
|
||||||
this.LogManager.WriteCrashLog();
|
this.LogManager.WriteCrashLog();
|
||||||
|
|
||||||
this.IsExiting = true;
|
this.ExitState = ExitState.Crash;
|
||||||
this.Game.Exit();
|
this.Game.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue