diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs | 77 | ||||
-rw-r--r-- | src/SMAPI.Mods.ConsoleCommands/manifest.json | 4 | ||||
-rw-r--r-- | src/SMAPI.Mods.SaveBackup/manifest.json | 4 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/LogParsing/LogParser.cs | 11 | ||||
-rw-r--r-- | src/SMAPI/Constants.cs | 4 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentCoordinator.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/BaseContentManager.cs | 8 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/GameContentManager.cs | 9 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/IContentManager.cs | 3 | ||||
-rw-r--r-- | src/SMAPI/Framework/ContentManagers/ModContentManager.cs | 3 | ||||
-rw-r--r-- | src/SMAPI/Framework/DeprecationManager.cs | 31 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModHelpers/CommandHelper.cs | 8 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 37 | ||||
-rw-r--r-- | src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs | 15 | ||||
-rw-r--r-- | src/SMAPI/Framework/WatcherCore.cs | 2 |
15 files changed, 174 insertions, 44 deletions
diff --git a/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs new file mode 100644 index 00000000..8f59342e --- /dev/null +++ b/src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using StardewValley; + +namespace StardewModdingAPI.Mods.ConsoleCommands.Framework.Commands.Other +{ + /// <summary>A command which runs one of the game's save migrations.</summary> + internal class ApplySaveFixCommand : TrainerCommand + { + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + public ApplySaveFixCommand() + : base("apply_save_fix", "Apply one of the game's save migrations to the currently loaded save. WARNING: This may corrupt or make permanent changes to your save. DO NOT USE THIS unless you're absolutely sure.\n\nUsage: apply_save_fix list\nList all valid save IDs.\n\nUsage: apply_save_fix <fix ID>\nApply the named save fix.") { } + + /// <summary>Handle the command.</summary> + /// <param name="monitor">Writes messages to the console and log file.</param> + /// <param name="command">The command name.</param> + /// <param name="args">The command arguments.</param> + public override void Handle(IMonitor monitor, string command, ArgumentParser args) + { + // get fix ID + if (!args.TryGet(0, "fix_id", out string rawFixId, required: false)) + { + monitor.Log("Invalid usage. Type 'help apply_save_fix' for details.", LogLevel.Error); + return; + } + rawFixId = rawFixId.Trim(); + + + // list mode + if (rawFixId == "list") + { + monitor.Log("Valid save fix IDs:\n - " + string.Join("\n - ", this.GetSaveIds()), LogLevel.Info); + return; + } + + // validate fix ID + if (!Enum.TryParse(rawFixId, ignoreCase: true, out SaveGame.SaveFixes fixId)) + { + monitor.Log($"Invalid save ID '{rawFixId}'. Type 'help apply_save_fix' for details.", LogLevel.Error); + return; + } + + // apply + monitor.Log("THIS MAY CAUSE PERMANENT CHANGES TO YOUR SAVE FILE. If you're not sure, exit your game without saving to avoid issues.", LogLevel.Warn); + monitor.Log($"Trying to apply save fix ID: '{fixId}'.", LogLevel.Warn); + try + { + Game1.applySaveFix(fixId); + monitor.Log("Save fix applied.", LogLevel.Info); + } + catch (Exception ex) + { + monitor.Log("Applying save fix failed. The save may be in an invalid state; you should exit your game now without saving to avoid issues.", LogLevel.Error); + monitor.Log($"Technical details: {ex}", LogLevel.Debug); + } + } + + + /********* + ** Private methods + *********/ + /// <summary>Get the valid save fix IDs.</summary> + private IEnumerable<string> GetSaveIds() + { + foreach (SaveGame.SaveFixes id in Enum.GetValues(typeof(SaveGame.SaveFixes))) + { + if (id == SaveGame.SaveFixes.MAX) + continue; + + yield return id.ToString(); + } + } + } +} diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index a1a137a1..61c610d0 100644 --- a/src/SMAPI.Mods.ConsoleCommands/manifest.json +++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json @@ -1,9 +1,9 @@ { "Name": "Console Commands", "Author": "SMAPI", - "Version": "3.8.0", + "Version": "3.8.1", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "3.8.0" + "MinimumApiVersion": "3.8.1" } diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index 96822f4a..7cf63e66 100644 --- a/src/SMAPI.Mods.SaveBackup/manifest.json +++ b/src/SMAPI.Mods.SaveBackup/manifest.json @@ -1,9 +1,9 @@ { "Name": "Save Backup", "Author": "SMAPI", - "Version": "3.8.0", + "Version": "3.8.1", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueID": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "3.8.0" + "MinimumApiVersion": "3.8.1" } diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs index f69d4b6f..84013ccc 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs @@ -42,7 +42,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing private readonly Regex ModUpdateListStartPattern = new Regex(@"^You can update \d+ mods?:$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// <summary>A regex pattern matching an entry in SMAPI's mod update list.</summary> - private readonly Regex ModUpdateListEntryPattern = new Regex(@"^ (?<name>.+?) (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly Regex ModUpdateListEntryPattern = new Regex(@"^ (?<name>.+) (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// <summary>A regex pattern matching SMAPI's update line.</summary> private readonly Regex SmapiUpdatePattern = new Regex(@"^You can update SMAPI to (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -109,12 +109,9 @@ namespace StardewModdingAPI.Web.Framework.LogParsing if (message.Mod == "SMAPI") { // update flags - if (inModList && !this.ModListEntryPattern.IsMatch(message.Text)) - inModList = false; - if (inContentPackList && !this.ContentPackListEntryPattern.IsMatch(message.Text)) - inContentPackList = false; - if (inModUpdateList && !this.ModUpdateListEntryPattern.IsMatch(message.Text)) - inModUpdateList = false; + inModList = inModList && message.Level == LogLevel.Info && this.ModListEntryPattern.IsMatch(message.Text); + inContentPackList = inContentPackList && message.Level == LogLevel.Info && this.ContentPackListEntryPattern.IsMatch(message.Text); + inModUpdateList = inModUpdateList && message.Level == LogLevel.Alert && this.ModUpdateListEntryPattern.IsMatch(message.Text); // mod list if (!inModList && message.Level == LogLevel.Info && this.ModListStartPattern.IsMatch(message.Text)) diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 98d0277b..9d5501a3 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -54,10 +54,10 @@ namespace StardewModdingAPI ** Public ****/ /// <summary>SMAPI's current semantic version.</summary> - public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.8.0"); + public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.8.1"); /// <summary>The minimum supported version of Stardew Valley.</summary> - public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.0"); + public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.1"); /// <summary>The maximum supported version of Stardew Valley.</summary> public static ISemanticVersion MaximumGameVersion { get; } = null; diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 93371415..f9027972 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -278,7 +278,7 @@ namespace StardewModdingAPI.Framework return this.ContentManagerLock.InReadLock(() => { List<object> values = new List<object>(); - foreach (IContentManager content in this.ContentManagers.Where(p => !p.IsNamespaced && p.IsLoaded(assetName))) + foreach (IContentManager content in this.ContentManagers.Where(p => !p.IsNamespaced && p.IsLoaded(assetName, p.Language))) { object value = content.Load<object>(assetName, this.Language, useCache: true); values.Add(value); diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs index 6bc3a505..92264f8c 100644 --- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs @@ -169,10 +169,11 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <summary>Get whether the content manager has already loaded and cached the given asset.</summary> /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param> - public bool IsLoaded(string assetName) + /// <param name="language">The language.</param> + public bool IsLoaded(string assetName, LanguageCode language) { assetName = this.Cache.NormalizeKey(assetName); - return this.IsNormalizedKeyLoaded(assetName); + return this.IsNormalizedKeyLoaded(assetName, language); } /// <summary>Get the cached asset keys.</summary> @@ -315,7 +316,8 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <summary>Get whether an asset has already been loaded.</summary> /// <param name="normalizedAssetName">The normalized asset name.</param> - protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName); + /// <param name="language">The language to check.</param> + protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language); /// <summary>Get the locale codes (like <c>ja-JP</c>) used in asset keys.</summary> private IDictionary<LanguageCode, string> GetKeyLocales() diff --git a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs index 83a63986..ad8f2ef1 100644 --- a/src/SMAPI/Framework/ContentManagers/GameContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/GameContentManager.cs @@ -78,7 +78,7 @@ namespace StardewModdingAPI.Framework.ContentManagers return this.Load<T>(newAssetName, newLanguage, useCache); // get from cache - if (useCache && this.IsLoaded(assetName)) + if (useCache && this.IsLoaded(assetName, language)) return this.RawLoad<T>(assetName, language, useCache: true); // get managed asset @@ -151,11 +151,12 @@ namespace StardewModdingAPI.Framework.ContentManagers *********/ /// <summary>Get whether an asset has already been loaded.</summary> /// <param name="normalizedAssetName">The normalized asset name.</param> - protected override bool IsNormalizedKeyLoaded(string normalizedAssetName) + /// <param name="language">The language to check.</param> + protected override bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language) { string cachedKey = null; bool localized = - this.Language != LocalizedContentManager.LanguageCode.en + language != LocalizedContentManager.LanguageCode.en && !this.Coordinator.IsManagedAssetKey(normalizedAssetName) && this.LocalizedAssetNames.TryGetValue(normalizedAssetName, out cachedKey); @@ -214,7 +215,7 @@ namespace StardewModdingAPI.Framework.ContentManagers private T RawLoad<T>(string assetName, LanguageCode language, bool useCache) { // use cached key - if (this.LocalizedAssetNames.TryGetValue(assetName, out string cachedKey)) + if (language == this.Language && this.LocalizedAssetNames.TryGetValue(assetName, out string cachedKey)) return base.RawLoad<T>(cachedKey, useCache); // try translated key diff --git a/src/SMAPI/Framework/ContentManagers/IContentManager.cs b/src/SMAPI/Framework/ContentManagers/IContentManager.cs index 8da9a777..0e7edd8f 100644 --- a/src/SMAPI/Framework/ContentManagers/IContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/IContentManager.cs @@ -58,7 +58,8 @@ namespace StardewModdingAPI.Framework.ContentManagers /// <summary>Get whether the content manager has already loaded and cached the given asset.</summary> /// <param name="assetName">The asset path relative to the loader root directory, not including the <c>.xnb</c> extension.</param> - bool IsLoaded(string assetName); + /// <param name="language">The language.</param> + bool IsLoaded(string assetName, LocalizedContentManager.LanguageCode language); /// <summary>Get the cached asset keys.</summary> IEnumerable<string> GetAssetKeys(); diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs index 127705ea..753ec188 100644 --- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs @@ -211,7 +211,8 @@ namespace StardewModdingAPI.Framework.ContentManagers *********/ /// <summary>Get whether an asset has already been loaded.</summary> /// <param name="normalizedAssetName">The normalized asset name.</param> - protected override bool IsNormalizedKeyLoaded(string normalizedAssetName) + /// <param name="language">The language to check.</param> + protected override bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language) { return this.Cache.ContainsKey(normalizedAssetName); } diff --git a/src/SMAPI/Framework/DeprecationManager.cs b/src/SMAPI/Framework/DeprecationManager.cs index 94a2da85..c22b5718 100644 --- a/src/SMAPI/Framework/DeprecationManager.cs +++ b/src/SMAPI/Framework/DeprecationManager.cs @@ -35,19 +35,17 @@ namespace StardewModdingAPI.Framework this.ModRegistry = modRegistry; } - /// <summary>Log a deprecation warning for the old-style events.</summary> - public void WarnForOldEvents() + /// <summary>Get the source name for a mod from its unique ID.</summary> + public string GetSourceNameFromStack() { - this.Warn("legacy events", "2.9", DeprecationLevel.PendingRemoval); + return this.ModRegistry.GetFromStack()?.DisplayName; } - /// <summary>Log a deprecation warning.</summary> - /// <param name="nounPhrase">A noun phrase describing what is deprecated.</param> - /// <param name="version">The SMAPI version which deprecated it.</param> - /// <param name="severity">How deprecated the code is.</param> - public void Warn(string nounPhrase, string version, DeprecationLevel severity) + /// <summary>Get the source name for a mod from its unique ID.</summary> + /// <param name="modId">The mod's unique ID.</param> + public string GetSourceName(string modId) { - this.Warn(this.ModRegistry.GetFromStack()?.DisplayName, nounPhrase, version, severity); + return this.ModRegistry.Get(modId)?.DisplayName; } /// <summary>Log a deprecation warning.</summary> @@ -58,7 +56,7 @@ namespace StardewModdingAPI.Framework public void Warn(string source, string nounPhrase, string version, DeprecationLevel severity) { // ignore if already warned - if (!this.MarkWarned(source ?? "<unknown>", nounPhrase, version)) + if (!this.MarkWarned(source ?? this.GetSourceNameFromStack() ?? "<unknown>", nounPhrase, version)) return; // queue warning @@ -111,21 +109,16 @@ namespace StardewModdingAPI.Framework this.QueuedWarnings.Clear(); } - /// <summary>Mark a deprecation warning as already logged.</summary> - /// <param name="nounPhrase">A noun phrase describing what is deprecated (e.g. "the Extensions.AsInt32 method").</param> - /// <param name="version">The SMAPI version which deprecated it.</param> - /// <returns>Returns whether the deprecation was successfully marked as warned. Returns <c>false</c> if it was already marked.</returns> - public bool MarkWarned(string nounPhrase, string version) - { - return this.MarkWarned(this.ModRegistry.GetFromStack()?.DisplayName, nounPhrase, version); - } + /********* + ** Private methods + *********/ /// <summary>Mark a deprecation warning as already logged.</summary> /// <param name="source">The friendly name of the assembly which used the deprecated code.</param> /// <param name="nounPhrase">A noun phrase describing what is deprecated (e.g. "the Extensions.AsInt32 method").</param> /// <param name="version">The SMAPI version which deprecated it.</param> /// <returns>Returns whether the deprecation was successfully marked as warned. Returns <c>false</c> if it was already marked.</returns> - public bool MarkWarned(string source, string nounPhrase, string version) + private bool MarkWarned(string source, string nounPhrase, string version) { if (string.IsNullOrWhiteSpace(source)) throw new InvalidOperationException("The deprecation source cannot be empty."); diff --git a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs index 600f867f..69382009 100644 --- a/src/SMAPI/Framework/ModHelpers/CommandHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/CommandHelper.cs @@ -36,8 +36,16 @@ namespace StardewModdingAPI.Framework.ModHelpers } /// <inheritdoc /> + [Obsolete] public bool Trigger(string name, string[] arguments) { + SCore.DeprecationManager.Warn( + source: SCore.DeprecationManager.GetSourceName(this.ModID), + nounPhrase: $"{nameof(IModHelper)}.{nameof(IModHelper.ConsoleCommands)}.{nameof(ICommandHelper.Trigger)}", + version: "3.8.1", + severity: DeprecationLevel.Notice + ); + return this.CommandManager.Trigger(name, arguments); } } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index a7f8fbed..e05213f0 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -765,6 +765,9 @@ namespace StardewModdingAPI.Framework this.Monitor.Log(context); + // apply save fixes + this.ApplySaveFixes(); + // raise events this.OnLoadStageChanged(LoadStage.Ready); events.SaveLoaded.RaiseEmpty(); @@ -1054,6 +1057,40 @@ namespace StardewModdingAPI.Framework this.EventManager.ReturnedToTitle.RaiseEmpty(); } + /// <summary>Apply fixes to the save after it's loaded.</summary> + private void ApplySaveFixes() + { + // get last SMAPI version used with this save + const string migrationKey = "Pathoschild.SMAPI/api-version"; + if (!Game1.CustomData.TryGetValue(migrationKey, out string rawVersion) || !SemanticVersion.TryParse(rawVersion, out ISemanticVersion lastVersion)) + lastVersion = new SemanticVersion(3, 8, 0); + + // fix bundle corruption in SMAPI 3.8.0 + // For non-English players who created a new save in SMAPI 3.8.0, bundle data was + // incorrectly translated which caused the code to crash whenever the game tried to + // read it. + if (lastVersion.IsOlderThan(new SemanticVersion(3, 8, 1)) && Game1.netWorldState?.Value?.BundleData != null) + { + var oldData = new Dictionary<string, string>(Game1.netWorldState.Value.BundleData); + + try + { + Game1.applySaveFix(SaveGame.SaveFixes.FixBotchedBundleData); + bool changed = Game1.netWorldState.Value.BundleData.Any(p => oldData.TryGetValue(p.Key, out string oldValue) && oldValue != p.Value); + if (changed) + this.Monitor.Log("Found broken community center bundles and fixed them automatically.", LogLevel.Info); + } + catch (Exception ex) + { + this.Monitor.Log("Failed to verify community center data.", LogLevel.Error); // should never happen + this.Monitor.Log($"Technical details: {ex}"); + } + } + + // update last run + Game1.CustomData[migrationKey] = Constants.ApiVersion.ToString(); + } + /// <summary>Raised after custom content is removed from the save data to avoid a crash.</summary> internal void OnSaveContentRemoved() { diff --git a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs index 303a4f3a..e968d79c 100644 --- a/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs +++ b/src/SMAPI/Framework/StateTracking/WorldLocationsTracker.cs @@ -21,6 +21,9 @@ namespace StardewModdingAPI.Framework.StateTracking /// <summary>Tracks changes to the list of active mine locations.</summary> private readonly ICollectionWatcher<MineShaft> MineLocationListWatcher; + /// <summary>Tracks changes to the list of active volcano locations.</summary> + private readonly ICollectionWatcher<GameLocation> VolcanoLocationListWatcher; + /// <summary>A lookup of the tracked locations.</summary> private IDictionary<GameLocation, LocationTracker> LocationDict { get; } = new Dictionary<GameLocation, LocationTracker>(new ObjectReferenceComparer<GameLocation>()); @@ -53,10 +56,12 @@ namespace StardewModdingAPI.Framework.StateTracking /// <summary>Construct an instance.</summary> /// <param name="locations">The game's list of locations.</param> /// <param name="activeMineLocations">The game's list of active mine locations.</param> - public WorldLocationsTracker(ObservableCollection<GameLocation> locations, IList<MineShaft> activeMineLocations) + /// <param name="activeVolcanoLocations">The game's list of active volcano locations.</param> + public WorldLocationsTracker(ObservableCollection<GameLocation> locations, IList<MineShaft> activeMineLocations, IList<VolcanoDungeon> activeVolcanoLocations) { this.LocationListWatcher = WatcherFactory.ForObservableCollection(locations); this.MineLocationListWatcher = WatcherFactory.ForReferenceList(activeMineLocations); + this.VolcanoLocationListWatcher = WatcherFactory.ForReferenceList(activeVolcanoLocations); } /// <summary>Update the current value if needed.</summary> @@ -65,6 +70,7 @@ namespace StardewModdingAPI.Framework.StateTracking // update watchers this.LocationListWatcher.Update(); this.MineLocationListWatcher.Update(); + this.VolcanoLocationListWatcher.Update(); foreach (LocationTracker watcher in this.Locations) watcher.Update(); @@ -79,6 +85,11 @@ namespace StardewModdingAPI.Framework.StateTracking this.Remove(this.MineLocationListWatcher.Removed); this.Add(this.MineLocationListWatcher.Added); } + if (this.VolcanoLocationListWatcher.IsChanged) + { + this.Remove(this.VolcanoLocationListWatcher.Removed); + this.Add(this.VolcanoLocationListWatcher.Added); + } // detect building changed foreach (LocationTracker watcher in this.Locations.Where(p => p.BuildingsWatcher.IsChanged).ToArray()) @@ -107,6 +118,7 @@ namespace StardewModdingAPI.Framework.StateTracking this.Added.Clear(); this.LocationListWatcher.Reset(); this.MineLocationListWatcher.Reset(); + this.VolcanoLocationListWatcher.Reset(); } /// <summary>Set the current value as the baseline.</summary> @@ -243,6 +255,7 @@ namespace StardewModdingAPI.Framework.StateTracking { yield return this.LocationListWatcher; yield return this.MineLocationListWatcher; + yield return this.VolcanoLocationListWatcher; foreach (LocationTracker watcher in this.Locations) yield return watcher; } diff --git a/src/SMAPI/Framework/WatcherCore.cs b/src/SMAPI/Framework/WatcherCore.cs index 393f6a37..62a0c3b8 100644 --- a/src/SMAPI/Framework/WatcherCore.cs +++ b/src/SMAPI/Framework/WatcherCore.cs @@ -66,7 +66,7 @@ namespace StardewModdingAPI.Framework this.WindowSizeWatcher = WatcherFactory.ForEquatable(() => new Point(Game1.viewport.Width, Game1.viewport.Height)); this.TimeWatcher = WatcherFactory.ForEquatable(() => Game1.timeOfDay); this.ActiveMenuWatcher = WatcherFactory.ForReference(() => Game1.activeClickableMenu); - this.LocationsWatcher = new WorldLocationsTracker(gameLocations, MineShaft.activeMines); + this.LocationsWatcher = new WorldLocationsTracker(gameLocations, MineShaft.activeMines, VolcanoDungeon.activeLevels); this.LocaleWatcher = WatcherFactory.ForGenericEquality(() => LocalizedContentManager.CurrentLanguageCode); this.Watchers.AddRange(new IWatcher[] { |