auto-fix save data when a custom location mod is removed
This commit is contained in:
parent
845deb43d6
commit
65997c1243
|
@ -25,8 +25,8 @@ doesn't change any of your game files. It serves eight main purposes:
|
|||
possible to troubleshoot errors in the game itself that would otherwise show a generic 'program
|
||||
has stopped working' type of message._
|
||||
|
||||
_That also includes automatically fixing save data when a load would crash, e.g. due to a custom
|
||||
NPC mod the player removed._
|
||||
_SMAPI also automatically fixes save data in some cases when a load would crash, e.g. due to a
|
||||
custom location or NPC mod that was removed._
|
||||
|
||||
6. **Provide update checks.**
|
||||
_SMAPI automatically checks for new versions of your installed mods, and notifies you when any
|
||||
|
|
|
@ -13,7 +13,7 @@ For players:
|
|||
SMAPI should have less impact on game performance and startup time for some players.
|
||||
|
||||
* **Added more error recovery.**
|
||||
SMAPI now detects and prevents more crashes due to game or mod bugs, or due to removing some mods which add custom content.
|
||||
SMAPI now detects and prevents more crashes due to game/mod bugs, or due to removing mods which add custom locations or NPCs.
|
||||
|
||||
* **Improved mod scanning.**
|
||||
SMAPI now supports some non-standard mod structures automatically, improves compatibility with the Vortex mod manager, and improves various error/skip messages related to mod loading.
|
||||
|
@ -46,7 +46,7 @@ For modders:
|
|||
* Improved mod scanning:
|
||||
* Now ignores metadata files and folders (like `__MACOSX` and `__folder_managed_by_vortex`) and content files (like `.txt` or `.png`), which avoids missing-manifest errors in some common cases.
|
||||
* Now detects XNB mods more accurately, and consolidates multi-folder XNB mods in logged messages.
|
||||
* SMAPI now automatically fixes your save if you remove a custom NPC mod. (Invalid NPCs are now removed on load, with a warning in the console.)
|
||||
* SMAPI now automatically removes invalid content when loading a save to prevent crashes. A warning is shown in-game when this happens. This applies for locations and NPCs.
|
||||
* Added support for configuring console colors via `smapi-internal/config.json` (intended for players with unusual consoles).
|
||||
* Improved launch script compatibility on Linux (thanks to kurumushi and toastal!).
|
||||
* Save Backup now works in the background, to avoid affecting startup time for players with a large number of saves.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
@ -457,6 +466,16 @@ namespace StardewModdingAPI.Framework
|
|||
this.Watchers.Reset();
|
||||
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
|
||||
*********/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
|
||||
"warn.invalid-content-removed": "Invalid content was removed to prevent a crash (see the SMAPI console for info)."
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue