summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2017-05-11 00:34:01 -0400
committerJesse Plamondon-Willard <github@jplamondonw.com>2017-05-11 00:34:01 -0400
commit48c5c9e36794ed3dae0cf6114194b0dc80dd4725 (patch)
tree47301f09115a81292fa57ad8ebcbae78cfb7ba4b /src
parent86c60c971ae533321b6550f140af4f01351cb2e3 (diff)
downloadSMAPI-48c5c9e36794ed3dae0cf6114194b0dc80dd4725.tar.gz
SMAPI-48c5c9e36794ed3dae0cf6114194b0dc80dd4725.tar.bz2
SMAPI-48c5c9e36794ed3dae0cf6114194b0dc80dd4725.zip
overhaul save handling to fix save events not triggering on Linux/Mac (#284)
Diffstat (limited to 'src')
-rw-r--r--src/StardewModdingAPI/Context.cs2
-rw-r--r--src/StardewModdingAPI/Framework/SGame.cs89
2 files changed, 61 insertions, 30 deletions
diff --git a/src/StardewModdingAPI/Context.cs b/src/StardewModdingAPI/Context.cs
index 2da14eed..45f6a05f 100644
--- a/src/StardewModdingAPI/Context.cs
+++ b/src/StardewModdingAPI/Context.cs
@@ -13,7 +13,7 @@ namespace StardewModdingAPI
public static bool IsSaveLoaded => Game1.hasLoadedGame && !string.IsNullOrEmpty(Game1.player.name);
/// <summary>Whether the game is currently writing to the save file.</summary>
- public static bool IsSaving => SaveGame.IsProcessing && (Game1.activeClickableMenu is SaveGameMenu || Game1.activeClickableMenu is ShippingMenu); // IsProcessing is never set to false on Linux/Mac
+ public static bool IsSaving => Game1.activeClickableMenu is SaveGameMenu || Game1.activeClickableMenu is ShippingMenu; // saving is performed by SaveGameMenu, but it's wrapped by ShippingMenu on days when the player shipping something
/// <summary>Whether the game is currently running the draw loop.</summary>
public static bool IsInDrawLoop { get; set; }
diff --git a/src/StardewModdingAPI/Framework/SGame.cs b/src/StardewModdingAPI/Framework/SGame.cs
index ed1ff647..f2098dbd 100644
--- a/src/StardewModdingAPI/Framework/SGame.cs
+++ b/src/StardewModdingAPI/Framework/SGame.cs
@@ -45,6 +45,9 @@ namespace StardewModdingAPI.Framework
/// <summary>Whether the game is returning to the menu.</summary>
private bool IsExiting;
+ /// <summary>Whether the game is saving and SMAPI has already raised <see cref="SaveEvents.BeforeSave"/>.</summary>
+ private bool IsBetweenSaveEvents;
+
/// <summary>Whether the game's zoom level is at 100% (i.e. nothing should be scaled).</summary>
public bool ZoomLevelIsOne => Game1.options.zoomLevel.Equals(1.0f);
@@ -216,6 +219,9 @@ namespace StardewModdingAPI.Framework
/// <param name="gameTime">A snapshot of the game timing state.</param>
protected override void Update(GameTime gameTime)
{
+ /*********
+ ** Skip conditions
+ *********/
// SMAPI exiting, stop processing game updates
if (this.Monitor.IsExiting)
{
@@ -242,14 +248,34 @@ namespace StardewModdingAPI.Framework
// While the game is writing to the save file in the background, mods can unexpectedly
// fail since they don't have exclusive access to resources (e.g. collection changed
// during enumeration errors). To avoid problems, events are not invoked while a save
- // is in progress.
+ // is in progress. It's safe to raise SaveEvents.BeforeSave as soon as the menu is
+ // opened (since the save hasn't started yet), but all other events should be suppressed.
if (Context.IsSaving)
{
+ // raise before-save
+ if (!this.IsBetweenSaveEvents)
+ {
+ this.IsBetweenSaveEvents = true;
+ this.Monitor.Log("Context: before save.", LogLevel.Trace);
+ SaveEvents.InvokeBeforeSave(this.Monitor);
+ }
+
+ // suppress non-save events
base.Update(gameTime);
return;
}
+ if(this.IsBetweenSaveEvents)
+ {
+ // raise after-save
+ this.IsBetweenSaveEvents = false;
+ this.Monitor.Log($"Context: after save, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
+ SaveEvents.InvokeAfterSave(this.Monitor);
+ TimeEvents.InvokeAfterDayStarted(this.Monitor);
+ }
- // raise game loaded
+ /*********
+ ** Game loaded events
+ *********/
if (this.FirstUpdate)
{
GameEvents.InvokeInitialize(this.Monitor);
@@ -257,7 +283,9 @@ namespace StardewModdingAPI.Framework
GameEvents.InvokeGameLoaded(this.Monitor);
}
- // content locale changed event
+ /*********
+ ** Locale changed events
+ *********/
if (this.PreviousLocale != LocalizedContentManager.CurrentLanguageCode)
{
var oldValue = this.PreviousLocale;
@@ -270,7 +298,9 @@ namespace StardewModdingAPI.Framework
this.PreviousLocale = newValue;
}
- // save loaded event
+ /*********
+ ** After load events
+ *********/
if (Context.IsSaveLoaded && !SaveGame.IsProcessing/*still loading save*/ && this.AfterLoadTimer >= 0)
{
if (this.AfterLoadTimer == 0)
@@ -284,6 +314,9 @@ namespace StardewModdingAPI.Framework
this.AfterLoadTimer--;
}
+ /*********
+ ** Exit to title events
+ *********/
// before exit to title
if (Game1.exitToTitle)
this.IsExiting = true;
@@ -297,7 +330,9 @@ namespace StardewModdingAPI.Framework
this.IsExiting = false;
}
- // input events
+ /*********
+ ** Input events
+ *********/
{
// get latest state
this.KStateNow = Keyboard.GetState();
@@ -350,7 +385,9 @@ namespace StardewModdingAPI.Framework
}
}
- // menu events
+ /*********
+ ** Menu events
+ *********/
if (Game1.activeClickableMenu != this.PreviousActiveMenu)
{
IClickableMenu previousMenu = this.PreviousActiveMenu;
@@ -367,20 +404,6 @@ namespace StardewModdingAPI.Framework
this.Monitor.Log($"Context: changed menu from {previousMenu.GetType().FullName} to {newMenu.GetType().FullName}.", LogLevel.Trace);
}
- // raise save events
- // (saving is performed by SaveGameMenu; on days when the player shipping something, ShippingMenu wraps SaveGameMenu)
- if (newMenu is SaveGameMenu || newMenu is ShippingMenu)
- {
- this.Monitor.Log("Context: before save.", LogLevel.Trace);
- SaveEvents.InvokeBeforeSave(this.Monitor);
- }
- else if (previousMenu is SaveGameMenu || previousMenu is ShippingMenu)
- {
- this.Monitor.Log($"Context: after save, starting {Game1.currentSeason} {Game1.dayOfMonth} Y{Game1.year}.", LogLevel.Trace);
- SaveEvents.InvokeAfterSave(this.Monitor);
- TimeEvents.InvokeAfterDayStarted(this.Monitor);
- }
-
// raise menu events
if (newMenu != null)
MenuEvents.InvokeMenuChanged(this.Monitor, previousMenu, newMenu);
@@ -392,7 +415,9 @@ namespace StardewModdingAPI.Framework
this.PreviousActiveMenu = newMenu;
}
- // world & player events
+ /*********
+ ** World & player events
+ *********/
if (this.IsWorldReady)
{
// raise location list changed
@@ -498,14 +523,18 @@ namespace StardewModdingAPI.Framework
}
}
- // raise game day transition event (obsolete)
+ /*********
+ ** Game day transition event (obsolete)
+ *********/
if (Game1.newDay != this.PreviousIsNewDay)
{
TimeEvents.InvokeOnNewDay(this.Monitor, this.PreviousDay, Game1.dayOfMonth, Game1.newDay);
this.PreviousIsNewDay = Game1.newDay;
}
- // let game update
+ /*********
+ ** Game update
+ *********/
try
{
base.Update(gameTime);
@@ -514,8 +543,10 @@ namespace StardewModdingAPI.Framework
{
this.Monitor.Log($"An error occured in the base update loop: {ex.GetLogSummary()}", LogLevel.Error);
}
-
- // raise update events
+
+ /*********
+ ** Update events
+ *********/
GameEvents.InvokeUpdateTick(this.Monitor);
if (this.FirstUpdate)
{
@@ -538,11 +569,11 @@ namespace StardewModdingAPI.Framework
if (this.CurrentUpdateTick >= 60)
this.CurrentUpdateTick = 0;
- // track keyboard state
+ /*********
+ ** Update input state
+ *********/
this.KStatePrior = this.KStateNow;
-
- // track controller button state
- for (var i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
+ for (PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
this.PreviouslyPressedButtons[(int)i] = this.GetButtonsDown(i);
}