From cf7bba5453f87e666759c70a892f76f7dae44dc2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Apr 2020 20:05:15 -0400 Subject: [PATCH] fix asset propagation for maps loaded through a temporary content manager --- docs/release-notes.md | 1 + src/SMAPI/Framework/ContentCoordinator.cs | 27 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 1eac1d62..c708133a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -20,6 +20,7 @@ * Added support for using patch helpers (e.g. for image/map patching) with arbitrary data (via `helper.Content.GetPatchHelper`). * Added `SDate` fields/methods: `SeasonIndex`, `FromDaysSinceStart`, `FromWorldDate`, `ToWorldDate`, and `ToLocaleString` (thanks to kdau!). * Added `SDate` translations taken from the Lookup Anything mod.ยน + * Fixed asset propagation for certain maps loaded through temporarily content managers (notably the farmhouse and town). * Fixed asset propagation on Linux/Mac for monster sprites, NPC dialogue, and NPC schedules. * Fixed asset propagation for NPC dialogue sometimes causing a spouse to skip marriage dialogue or not allow kisses. diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 0b1ccc3c..47ef30d4 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -14,6 +14,7 @@ using StardewModdingAPI.Metadata; using StardewModdingAPI.Toolkit.Serialization; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; +using xTile; namespace StardewModdingAPI.Framework { @@ -228,16 +229,32 @@ namespace StardewModdingAPI.Framework public IEnumerable InvalidateCache(Func predicate, bool dispose = false) { // invalidate cache & track removed assets - IDictionary> removedAssets = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); + IDictionary removedAssets = new Dictionary(StringComparer.InvariantCultureIgnoreCase); this.ContentManagerLock.InReadLock(() => { + // cached assets foreach (IContentManager contentManager in this.ContentManagers) { foreach (var entry in contentManager.InvalidateCache(predicate, dispose)) { - if (!removedAssets.TryGetValue(entry.Key, out ISet assets)) - removedAssets[entry.Key] = assets = new HashSet(new ObjectReferenceComparer()); - assets.Add(entry.Value); + if (!removedAssets.TryGetValue(entry.Key, out Type type)) + removedAssets[entry.Key] = entry.Value.GetType(); + } + } + + // special case: maps may be loaded through a temporary content manager that's removed while the map is still in use. + // This notably affects the town and farmhouse maps. + if (Game1.locations != null) + { + foreach (GameLocation location in Game1.locations) + { + if (location.map == null || string.IsNullOrWhiteSpace(location.mapPath.Value)) + continue; + + // get map path + string mapPath = this.MainContentManager.AssertAndNormalizeAssetName(location.mapPath.Value); + if (!removedAssets.ContainsKey(mapPath) && predicate(mapPath, typeof(Map))) + removedAssets[mapPath] = typeof(Map); } } }); @@ -245,7 +262,7 @@ namespace StardewModdingAPI.Framework // reload core game assets if (removedAssets.Any()) { - IDictionary propagated = this.CoreAssets.Propagate(this.MainContentManager, removedAssets.ToDictionary(p => p.Key, p => p.Value.First().GetType())); // use an intercepted content manager + IDictionary propagated = this.CoreAssets.Propagate(this.MainContentManager, removedAssets.ToDictionary(p => p.Key, p => p.Value)); // use an intercepted content manager this.Monitor.Log($"Invalidated {removedAssets.Count} asset names ({string.Join(", ", removedAssets.Keys.OrderBy(p => p, StringComparer.InvariantCultureIgnoreCase))}); propagated {propagated.Count(p => p.Value)} core assets.", LogLevel.Trace); } else