refactor save game patcher to minimize repeated iterations

This commit is contained in:
Jesse Plamondon-Willard 2021-07-30 01:40:43 -04:00
parent 10b7758bd2
commit 940bf92241
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
1 changed files with 45 additions and 54 deletions

View File

@ -57,25 +57,40 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
/// <returns>Returns whether to execute the original method.</returns>
private static bool Before_LoadDataToLocations(List<GameLocation> gamelocations)
{
bool removedAny =
SaveGamePatcher.RemoveBrokenBuildings(gamelocations)
| SaveGamePatcher.RemoveInvalidNpcs(gamelocations);
IDictionary<string, string> npcs = Game1.content.Load<Dictionary<string, string>>("Data\\NPCDispositions");
if (removedAny)
if (SaveGamePatcher.RemoveBrokenContent(gamelocations, npcs))
SaveGamePatcher.OnContentRemoved();
return true;
}
/// <summary>Remove buildings which don't exist in the game data.</summary>
/// <summary>Remove content which no longer exists in the game data.</summary>
/// <param name="locations">The current game locations.</param>
private static bool RemoveBrokenBuildings(IEnumerable<GameLocation> locations)
/// <param name="npcs">The NPC data.</param>
private static bool RemoveBrokenContent(IEnumerable<GameLocation> locations, IDictionary<string, string> npcs)
{
bool removedAny = false;
foreach (BuildableGameLocation location in locations.OfType<BuildableGameLocation>())
foreach (GameLocation location in locations)
removedAny |= SaveGamePatcher.RemoveBrokenContent(location, npcs);
return removedAny;
}
/// <summary>Remove content which no longer exists in the game data.</summary>
/// <param name="location">The current game location.</param>
/// <param name="npcs">The NPC data.</param>
private static bool RemoveBrokenContent(GameLocation location, IDictionary<string, string> npcs)
{
bool removedAny = false;
if (location == null)
return false;
// check buildings
if (location is BuildableGameLocation buildableLocation)
{
foreach (Building building in location.buildings.ToArray())
foreach (Building building in buildableLocation.buildings.ToArray())
{
try
{
@ -84,7 +99,28 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
catch (ContentLoadException)
{
SaveGamePatcher.Monitor.Log($"Removed invalid building type '{building.buildingType.Value}' in {location.Name} ({building.tileX}, {building.tileY}) to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom building mod?)", LogLevel.Warn);
location.buildings.Remove(building);
buildableLocation.buildings.Remove(building);
removedAny = true;
continue;
}
SaveGamePatcher.RemoveBrokenContent(building.indoors.Value, npcs);
}
}
// check NPCs
foreach (NPC npc in location.characters.ToArray())
{
if (npc.isVillager() && !npcs.ContainsKey(npc.Name))
{
try
{
npc.reloadSprite(); // this won't crash for special villagers like Bouncer
}
catch
{
SaveGamePatcher.Monitor.Log($"Removed invalid villager '{npc.Name}' in {location.Name} ({npc.getTileLocation()}) to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom NPC mod?)", LogLevel.Warn);
location.characters.Remove(npc);
removedAny = true;
}
}
@ -92,50 +128,5 @@ namespace StardewModdingAPI.Mods.ErrorHandler.Patches
return removedAny;
}
/// <summary>Remove NPCs which don't exist in the game data.</summary>
/// <param name="locations">The current game locations.</param>
private static bool RemoveInvalidNpcs(IEnumerable<GameLocation> locations)
{
bool removedAny = false;
IDictionary<string, string> data = Game1.content.Load<Dictionary<string, string>>("Data\\NPCDispositions");
foreach (GameLocation location in SaveGamePatcher.GetAllLocations(locations))
{
foreach (NPC npc in location.characters.ToArray())
{
if (npc.isVillager() && !data.ContainsKey(npc.Name))
{
try
{
npc.reloadSprite(); // this won't crash for special villagers like Bouncer
}
catch
{
SaveGamePatcher.Monitor.Log($"Removed invalid villager '{npc.Name}' in {location.Name} ({npc.getTileLocation()}) to avoid a crash when loading save '{Constants.SaveFolderName}'. (Did you remove a custom NPC mod?)", LogLevel.Warn);
location.characters.Remove(npc);
removedAny = true;
}
}
}
}
return removedAny;
}
/// <summary>Get all locations, including building interiors.</summary>
/// <param name="locations">The main game locations.</param>
private static IEnumerable<GameLocation> GetAllLocations(IEnumerable<GameLocation> locations)
{
foreach (GameLocation location in locations)
{
yield return location;
if (location is BuildableGameLocation buildableLocation)
{
foreach (GameLocation interior in buildableLocation.buildings.Select(p => p.indoors.Value).Where(p => p != null))
yield return interior;
}
}
}
}
}