From 083e68ad5bbbe966c904d6aec48ab6b92f69a531 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 9 Sep 2019 23:47:45 -0400 Subject: fix key errors during asset propagation in some cases --- src/SMAPI/Metadata/CoreAssetPropagator.cs | 45 ++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs index c0d57f4b..fceb840f 100644 --- a/src/SMAPI/Metadata/CoreAssetPropagator.cs +++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs @@ -24,8 +24,8 @@ namespace StardewModdingAPI.Metadata /********* ** Fields *********/ - /// Normalizes an asset key to match the cache key. - private readonly Func GetNormalizedPath; + /// Normalizes an asset key to match the cache key and assert that it's valid. + private readonly Func AssertAndNormalizeAssetName; /// Simplifies access to private game code. private readonly Reflector Reflection; @@ -51,12 +51,12 @@ namespace StardewModdingAPI.Metadata ** Public methods *********/ /// Initialize the core asset data. - /// Normalizes an asset key to match the cache key. + /// Normalizes an asset key to match the cache key and assert that it's valid. /// Simplifies access to private code. /// Encapsulates monitoring and logging. - public CoreAssetPropagator(Func getNormalizedPath, Reflector reflection, IMonitor monitor) + public CoreAssetPropagator(Func assertAndNormalizeAssetName, Reflector reflection, IMonitor monitor) { - this.GetNormalizedPath = getNormalizedPath; + this.AssertAndNormalizeAssetName = assertAndNormalizeAssetName; this.Reflection = reflection; this.Monitor = monitor; } @@ -112,7 +112,7 @@ namespace StardewModdingAPI.Metadata /// Returns whether an asset was loaded. The return value may be true or false, or a non-null value for true. private bool PropagateOther(LocalizedContentManager content, string key, Type type) { - key = this.GetNormalizedPath(key); + key = this.AssertAndNormalizeAssetName(key); /**** ** Special case: current map tilesheet @@ -123,7 +123,7 @@ namespace StardewModdingAPI.Metadata { foreach (TileSheet tilesheet in Game1.currentLocation.map.TileSheets) { - if (this.GetNormalizedPath(tilesheet.ImageSource) == key) + if (this.NormalizeAssetNameIgnoringEmpty(tilesheet.ImageSource) == key) Game1.mapDisplayDevice.LoadTileSheet(tilesheet); } } @@ -136,7 +136,7 @@ namespace StardewModdingAPI.Metadata bool anyChanged = false; foreach (GameLocation location in this.GetLocations()) { - if (!string.IsNullOrWhiteSpace(location.mapPath.Value) && this.GetNormalizedPath(location.mapPath.Value) == key) + if (!string.IsNullOrWhiteSpace(location.mapPath.Value) && this.NormalizeAssetNameIgnoringEmpty(location.mapPath.Value) == key) { // general updates location.reloadMap(); @@ -507,7 +507,7 @@ namespace StardewModdingAPI.Metadata // find matches TAnimal[] animals = this.GetCharacters() .OfType() - .Where(p => key == this.GetNormalizedPath(p.Sprite?.Texture?.Name)) + .Where(p => key == this.NormalizeAssetNameIgnoringEmpty(p.Sprite?.Texture?.Name)) .ToArray(); if (!animals.Any()) return false; @@ -588,7 +588,7 @@ namespace StardewModdingAPI.Metadata let locCritters = this.Reflection.GetField>(location, "critters").GetValue() where locCritters != null from Critter critter in locCritters - where this.GetNormalizedPath(critter.sprite?.Texture?.Name) == key + where this.NormalizeAssetNameIgnoringEmpty(critter.sprite?.Texture?.Name) == key select critter ) .ToArray(); @@ -653,11 +653,11 @@ namespace StardewModdingAPI.Metadata { IDictionary data = content.Load>(key); bool changed = false; - foreach (NPC character in this.GetCharacters()) + foreach (NPC npc in this.GetCharacters()) { - if (character.isVillager() && data.ContainsKey(character.Name)) + if (npc.isVillager() && data.ContainsKey(npc.Name)) { - character.reloadData(); + npc.reloadData(); changed = true; } } @@ -674,7 +674,7 @@ namespace StardewModdingAPI.Metadata // get NPCs HashSet lookup = new HashSet(keys, StringComparer.InvariantCultureIgnoreCase); NPC[] characters = this.GetCharacters() - .Where(npc => npc.Sprite != null && lookup.Contains(this.GetNormalizedPath(npc.Sprite?.Texture?.Name))) + .Where(npc => npc.Sprite != null && lookup.Contains(this.NormalizeAssetNameIgnoringEmpty(npc.Sprite?.Texture?.Name))) .ToArray(); if (!characters.Any()) return 0; @@ -700,7 +700,7 @@ namespace StardewModdingAPI.Metadata HashSet lookup = new HashSet(keys, StringComparer.InvariantCultureIgnoreCase); var villagers = this .GetCharacters() - .Where(npc => npc.isVillager() && lookup.Contains(this.GetNormalizedPath(npc.Portrait?.Name))) + .Where(npc => npc.isVillager() && lookup.Contains(this.NormalizeAssetNameIgnoringEmpty(npc.Portrait?.Name))) .ToArray(); if (!villagers.Any()) return 0; @@ -849,12 +849,25 @@ namespace StardewModdingAPI.Metadata } } + /// Normalize an asset key to match the cache key and assert that it's valid, but don't raise an error for null or empty values. + /// The asset key to normalize. + private string NormalizeAssetNameIgnoringEmpty(string path) + { + if (string.IsNullOrWhiteSpace(path)) + return null; + + return this.AssertAndNormalizeAssetName(path); + } + /// Get whether a key starts with a substring after the substring is normalized. /// The key to check. /// The substring to normalize and find. private bool KeyStartsWith(string key, string rawSubstring) { - return key.StartsWith(this.GetNormalizedPath(rawSubstring), StringComparison.InvariantCultureIgnoreCase); + if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(rawSubstring)) + return false; + + return key.StartsWith(this.NormalizeAssetNameIgnoringEmpty(rawSubstring), StringComparison.InvariantCultureIgnoreCase); } /// Get whether a normalized asset key is in the given folder. -- cgit