diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/Framework/SGame.cs | 19 | ||||
-rw-r--r-- | src/SMAPI/Patches/LoadErrorPatch.cs | 28 | ||||
-rw-r--r-- | src/SMAPI/i18n/default.json | 2 |
4 files changed, 48 insertions, 3 deletions
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index bd131762..bfdf1c51 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -245,7 +245,7 @@ namespace StardewModdingAPI.Framework new DialogueErrorPatch(this.MonitorForGame, this.Reflection), new ObjectErrorPatch(), new LoadContextPatch(this.Reflection, this.GameInstance.OnLoadStageChanged), - new LoadErrorPatch(this.Monitor) + new LoadErrorPatch(this.Monitor, this.GameInstance.OnSaveContentRemoved) ); // add exit handler diff --git a/src/SMAPI/Framework/SGame.cs b/src/SMAPI/Framework/SGame.cs index 89705352..13858fc5 100644 --- a/src/SMAPI/Framework/SGame.cs +++ b/src/SMAPI/Framework/SGame.cs @@ -65,6 +65,9 @@ namespace StardewModdingAPI.Framework /// <remarks>Skipping a few frames ensures the game finishes initializing the world before mods try to change it.</remarks> private readonly Countdown AfterLoadTimer = new Countdown(5); + /// <summary>Whether custom content was removed from the save data to avoid a crash.</summary> + private bool IsSaveContentRemoved; + /// <summary>Whether the game is saving and SMAPI has already raised <see cref="IGameLoopEvents.Saving"/>.</summary> private bool IsBetweenSaveEvents; @@ -216,6 +219,12 @@ namespace StardewModdingAPI.Framework this.Events.ModMessageReceived.RaiseForMods(new ModMessageReceivedEventArgs(message), mod => mod != null && modIDs.Contains(mod.Manifest.UniqueID)); } + /// <summary>A callback invoked when custom content is removed from the save data to avoid a crash.</summary> + internal void OnSaveContentRemoved() + { + this.IsSaveContentRemoved = true; + } + /// <summary>A callback invoked when the game's low-level load stage changes.</summary> /// <param name="newStage">The new load stage.</param> internal void OnLoadStageChanged(LoadStage newStage) @@ -458,6 +467,16 @@ namespace StardewModdingAPI.Framework WatcherSnapshot state = this.WatcherSnapshot; /********* + ** Display in-game warnings + *********/ + // save content removed + if (this.IsSaveContentRemoved && Context.IsWorldReady) + { + this.IsSaveContentRemoved = false; + Game1.addHUDMessage(new HUDMessage(this.Translator.Get("warn.invalid-content-removed"), HUDMessage.error_type)); + } + + /********* ** Pre-update events *********/ { diff --git a/src/SMAPI/Patches/LoadErrorPatch.cs b/src/SMAPI/Patches/LoadErrorPatch.cs index 87e8ee14..eedb4164 100644 --- a/src/SMAPI/Patches/LoadErrorPatch.cs +++ b/src/SMAPI/Patches/LoadErrorPatch.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -20,6 +21,9 @@ namespace StardewModdingAPI.Patches /// <summary>Writes messages to the console and log file.</summary> private static IMonitor Monitor; + /// <summary>A callback invoked when custom content is removed from the save data to avoid a crash.</summary> + private static Action OnContentRemoved; + /********* ** Accessors @@ -33,9 +37,11 @@ namespace StardewModdingAPI.Patches *********/ /// <summary>Construct an instance.</summary> /// <param name="monitor">Writes messages to the console and log file.</param> - public LoadErrorPatch(IMonitor monitor) + /// <param name="onContentRemoved">A callback invoked when custom content is removed from the save data to avoid a crash.</param> + public LoadErrorPatch(IMonitor monitor, Action onContentRemoved) { LoadErrorPatch.Monitor = monitor; + LoadErrorPatch.OnContentRemoved = onContentRemoved; } @@ -58,6 +64,22 @@ namespace StardewModdingAPI.Patches /// <returns>Returns whether to execute the original method.</returns> private static bool Before_SaveGame_LoadDataToLocations(List<GameLocation> gamelocations) { + bool removedAny = false; + + // remove invalid locations + foreach (GameLocation location in gamelocations.ToArray()) + { + if (location is Cellar) + continue; // missing cellars will be added by the game code + + if (Game1.getLocationFromName(location.name) == null) + { + LoadErrorPatch.Monitor.Log($"Removed invalid location '{location.Name}' to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom location mod?)", LogLevel.Warn); + gamelocations.Remove(location); + removedAny = true; + } + } + // get building interiors var interiors = ( @@ -83,11 +105,15 @@ namespace StardewModdingAPI.Patches { LoadErrorPatch.Monitor.Log($"Removed invalid villager '{npc.Name}' to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom NPC mod?)", LogLevel.Warn); location.characters.Remove(npc); + removedAny = true; } } } } + if (removedAny) + LoadErrorPatch.OnContentRemoved(); + return true; } } diff --git a/src/SMAPI/i18n/default.json b/src/SMAPI/i18n/default.json index 0db3279e..5a3e4a6e 100644 --- a/src/SMAPI/i18n/default.json +++ b/src/SMAPI/i18n/default.json @@ -1,3 +1,3 @@ { - + "warn.invalid-content-removed": "Invalid content was removed to prevent a crash (see the SMAPI console for info)." } |