fix errors during overridden update loop immediately crashing the game with no log entry
This commit is contained in:
parent
b977a8e48f
commit
8439594b10
|
@ -15,6 +15,7 @@ See [log](https://github.com/Pathoschild/SMAPI/compare/1.12...1.13).
|
|||
|
||||
For players:
|
||||
* SMAPI now recovers better from mod draw errors and detects when the error is irrecoverable.
|
||||
* SMAPI now recovers automatically from update errors if possible.
|
||||
* SMAPI now remembers if your game crashed and offers help next time you launch it.
|
||||
* Fixed installer finding redundant game paths on Linux.
|
||||
* Fixed save events not being raised after the first day on Linux/Mac.
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
namespace StardewModdingAPI.Framework
|
||||
{
|
||||
/// <summary>Counts down from a baseline value.</summary>
|
||||
internal class Countdown
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The initial value from which to count down.</summary>
|
||||
public int Initial { get; }
|
||||
|
||||
/// <summary>The current value.</summary>
|
||||
public int Current { get; private set; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="initial">The initial value from which to count down.</param>
|
||||
public Countdown(int initial)
|
||||
{
|
||||
this.Initial = initial;
|
||||
this.Current = initial;
|
||||
}
|
||||
|
||||
/// <summary>Reduce the current value by one.</summary>
|
||||
/// <returns>Returns whether the value was decremented (i.e. wasn't already zero).</returns>
|
||||
public bool Decrement()
|
||||
{
|
||||
if (this.Current <= 0)
|
||||
return false;
|
||||
|
||||
this.Current--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Restart the countdown.</summary>
|
||||
public void Reset()
|
||||
{
|
||||
this.Current = this.Initial;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,15 +30,15 @@ namespace StardewModdingAPI.Framework
|
|||
** SMAPI state
|
||||
****/
|
||||
/// <summary>The maximum number of consecutive attempts SMAPI should make to recover from a draw error.</summary>
|
||||
private readonly int MaxFailedDraws = 60; // roughly one second
|
||||
private readonly Countdown DrawCrashTimer = new Countdown(60); // 60 ticks = roughly one second
|
||||
|
||||
/// <summary>The maximum number of consecutive attempts SMAPI should make to recover from an update error.</summary>
|
||||
private readonly Countdown UpdateCrashTimer = new Countdown(60); // 60 ticks = roughly one second
|
||||
|
||||
/// <summary>The number of ticks until SMAPI should notify mods that the game has loaded.</summary>
|
||||
/// <remarks>Skipping a few frames ensures the game finishes initialising the world before mods try to change it.</remarks>
|
||||
private int AfterLoadTimer = 5;
|
||||
|
||||
/// <summary>The number of consecutive failed draws.</summary>
|
||||
private int FailedDraws;
|
||||
|
||||
/// <summary>Whether the game is returning to the menu.</summary>
|
||||
private bool IsExiting;
|
||||
|
||||
|
@ -233,6 +233,8 @@ namespace StardewModdingAPI.Framework
|
|||
/// <summary>The method called when the game is updating its state. This happens roughly 60 times per second.</summary>
|
||||
/// <param name="gameTime">A snapshot of the game timing state.</param>
|
||||
protected override void Update(GameTime gameTime)
|
||||
{
|
||||
try
|
||||
{
|
||||
/*********
|
||||
** Skip conditions
|
||||
|
@ -316,7 +318,7 @@ namespace StardewModdingAPI.Framework
|
|||
/*********
|
||||
** After load events
|
||||
*********/
|
||||
if (Context.IsSaveLoaded && !SaveGame.IsProcessing/*still loading save*/ && this.AfterLoadTimer >= 0)
|
||||
if (Context.IsSaveLoaded && !SaveGame.IsProcessing /*still loading save*/ && this.AfterLoadTimer >= 0)
|
||||
{
|
||||
if (this.AfterLoadTimer == 0)
|
||||
{
|
||||
|
@ -567,6 +569,18 @@ namespace StardewModdingAPI.Framework
|
|||
this.KStatePrior = this.KStateNow;
|
||||
for (PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
|
||||
this.PreviouslyPressedButtons[(int)i] = this.GetButtonsDown(i);
|
||||
|
||||
this.UpdateCrashTimer.Reset();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// log error
|
||||
this.Monitor.Log($"An error occured in the overridden update loop: {ex.GetLogSummary()}", LogLevel.Error);
|
||||
|
||||
// exit if irrecoverable
|
||||
if (!this.UpdateCrashTimer.Decrement())
|
||||
this.Monitor.ExitGameImmediately("the game crashed when updating, and SMAPI was unable to recover the game.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The method called to draw everything to the screen.</summary>
|
||||
|
@ -577,7 +591,7 @@ namespace StardewModdingAPI.Framework
|
|||
try
|
||||
{
|
||||
this.DrawImpl(gameTime);
|
||||
this.FailedDraws = 0;
|
||||
this.DrawCrashTimer.Reset();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -585,12 +599,11 @@ namespace StardewModdingAPI.Framework
|
|||
this.Monitor.Log($"An error occured in the overridden draw loop: {ex.GetLogSummary()}", LogLevel.Error);
|
||||
|
||||
// exit if irrecoverable
|
||||
if (this.FailedDraws >= this.MaxFailedDraws)
|
||||
if (!this.DrawCrashTimer.Decrement())
|
||||
{
|
||||
this.Monitor.ExitGameImmediately("the game crashed when drawing, and SMAPI was unable to recover the game.");
|
||||
return;
|
||||
}
|
||||
this.FailedDraws++;
|
||||
|
||||
// abort in known unrecoverable cases
|
||||
if (Game1.toolSpriteSheet?.IsDisposed == true)
|
||||
|
|
|
@ -121,6 +121,7 @@
|
|||
<Compile Include="Events\EventArgsStringChanged.cs" />
|
||||
<Compile Include="Events\GameEvents.cs" />
|
||||
<Compile Include="Events\GraphicsEvents.cs" />
|
||||
<Compile Include="Framework\Countdown.cs" />
|
||||
<Compile Include="Framework\ModLoading\IModMetadata.cs" />
|
||||
<Compile Include="Framework\ModLoading\InvalidModStateException.cs" />
|
||||
<Compile Include="Framework\ModLoading\ModDependencyStatus.cs" />
|
||||
|
|
Loading…
Reference in New Issue