From bbf2c3b020192d91f8d24cd687c5c6272ac26e82 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 21 Dec 2020 15:14:18 -0500 Subject: [PATCH 1/9] link to 3.8 release highlights --- docs/release-notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index d550857c..428888d8 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,7 +8,7 @@ --> ## 3.8 -Released 21 December 2020 for Stardew Valley 1.5 or later. +Released 21 December 2020 for Stardew Valley 1.5 or later. See [release highlights](https://www.patreon.com/posts/45294737). * For players: * Updated for Stardew Valley 1.5, including split-screen support. From 63111621c9375ac2e9a68eefa73ffe1d817000dd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 23 Dec 2020 19:11:41 -0500 Subject: [PATCH 2/9] fix world events not raised for volcano levels --- docs/release-notes.md | 4 ++++ .../StateTracking/WorldLocationsTracker.cs | 15 ++++++++++++++- src/SMAPI/Framework/WatcherCore.cs | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index 428888d8..e54d6c71 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -7,6 +7,10 @@ * Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info). --> +## Upcoming release +* For modders: + * Fixed world events not raised for volcano levels. + ## 3.8 Released 21 December 2020 for Stardew Valley 1.5 or later. See [release highlights](https://www.patreon.com/posts/45294737). 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 /// Tracks changes to the list of active mine locations. private readonly ICollectionWatcher MineLocationListWatcher; + /// Tracks changes to the list of active volcano locations. + private readonly ICollectionWatcher VolcanoLocationListWatcher; + /// A lookup of the tracked locations. private IDictionary LocationDict { get; } = new Dictionary(new ObjectReferenceComparer()); @@ -53,10 +56,12 @@ namespace StardewModdingAPI.Framework.StateTracking /// Construct an instance. /// The game's list of locations. /// The game's list of active mine locations. - public WorldLocationsTracker(ObservableCollection locations, IList activeMineLocations) + /// The game's list of active volcano locations. + public WorldLocationsTracker(ObservableCollection locations, IList activeMineLocations, IList activeVolcanoLocations) { this.LocationListWatcher = WatcherFactory.ForObservableCollection(locations); this.MineLocationListWatcher = WatcherFactory.ForReferenceList(activeMineLocations); + this.VolcanoLocationListWatcher = WatcherFactory.ForReferenceList(activeVolcanoLocations); } /// Update the current value if needed. @@ -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(); } /// Set the current value as the baseline. @@ -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[] { From 9215f89825734f447b8637851569de9ffa08b661 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 23 Dec 2020 21:38:19 -0500 Subject: [PATCH 3/9] fix edge cases in SMAPI log parsing (#743) --- docs/release-notes.md | 3 +++ src/SMAPI.Web/Framework/LogParsing/LogParser.cs | 11 ++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index e54d6c71..e464154f 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -11,6 +11,9 @@ * For modders: * Fixed world events not raised for volcano levels. +For the web UI: + * Fixed edge cases in SMAPI log parsing. + ## 3.8 Released 21 December 2020 for Stardew Valley 1.5 or later. See [release highlights](https://www.patreon.com/posts/45294737). 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); /// A regex pattern matching an entry in SMAPI's mod update list. - private readonly Regex ModUpdateListEntryPattern = new Regex(@"^ (?.+?) (?[^\s]+): (?.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly Regex ModUpdateListEntryPattern = new Regex(@"^ (?.+) (?[^\s]+): (?.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// A regex pattern matching SMAPI's update line. private readonly Regex SmapiUpdatePattern = new Regex(@"^You can update SMAPI to (?[^\s]+): (?.+)$", 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)) From 2406380495cc5176d3cbd2309e6f17080389f9af Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Dec 2020 01:28:00 -0500 Subject: [PATCH 4/9] fix SMAPI using a cached translation when the game asks for an untranslated asset This mainly affects community center bundles in Stardew Valley 1.5, --- docs/release-notes.md | 5 ++++- src/SMAPI/Framework/ContentCoordinator.cs | 2 +- .../Framework/ContentManagers/BaseContentManager.cs | 8 +++++--- .../Framework/ContentManagers/GameContentManager.cs | 9 +++++---- src/SMAPI/Framework/ContentManagers/IContentManager.cs | 3 ++- src/SMAPI/Framework/ContentManagers/ModContentManager.cs | 3 ++- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index e464154f..2df7467a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -8,8 +8,11 @@ --> ## Upcoming release +* For players: + * Fixed community center bundle corruption for non-English players. + * For modders: - * Fixed world events not raised for volcano levels. + * World events are now raised for the volcano levels. For the web UI: * Fixed edge cases in SMAPI log parsing. 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 values = new List(); - 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(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 /// Get whether the content manager has already loaded and cached the given asset. /// The asset path relative to the loader root directory, not including the .xnb extension. - public bool IsLoaded(string assetName) + /// The language. + public bool IsLoaded(string assetName, LanguageCode language) { assetName = this.Cache.NormalizeKey(assetName); - return this.IsNormalizedKeyLoaded(assetName); + return this.IsNormalizedKeyLoaded(assetName, language); } /// Get the cached asset keys. @@ -315,7 +316,8 @@ namespace StardewModdingAPI.Framework.ContentManagers /// Get whether an asset has already been loaded. /// The normalized asset name. - protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName); + /// The language to check. + protected abstract bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language); /// Get the locale codes (like ja-JP) used in asset keys. private IDictionary 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(newAssetName, newLanguage, useCache); // get from cache - if (useCache && this.IsLoaded(assetName)) + if (useCache && this.IsLoaded(assetName, language)) return this.RawLoad(assetName, language, useCache: true); // get managed asset @@ -151,11 +151,12 @@ namespace StardewModdingAPI.Framework.ContentManagers *********/ /// Get whether an asset has already been loaded. /// The normalized asset name. - protected override bool IsNormalizedKeyLoaded(string normalizedAssetName) + /// The language to check. + 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(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(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 /// Get whether the content manager has already loaded and cached the given asset. /// The asset path relative to the loader root directory, not including the .xnb extension. - bool IsLoaded(string assetName); + /// The language. + bool IsLoaded(string assetName, LocalizedContentManager.LanguageCode language); /// Get the cached asset keys. IEnumerable 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 *********/ /// Get whether an asset has already been loaded. /// The normalized asset name. - protected override bool IsNormalizedKeyLoaded(string normalizedAssetName) + /// The language to check. + protected override bool IsNormalizedKeyLoaded(string normalizedAssetName, LanguageCode language) { return this.Cache.ContainsKey(normalizedAssetName); } From 5a8a684e22b2eec0b318c68adbc6780479d8efd4 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Dec 2020 01:30:27 -0500 Subject: [PATCH 5/9] add apply_save_fix command --- docs/release-notes.md | 1 + .../Commands/Other/ApplySaveFixCommand.cs | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/SMAPI.Mods.ConsoleCommands/Framework/Commands/Other/ApplySaveFixCommand.cs diff --git a/docs/release-notes.md b/docs/release-notes.md index 2df7467a..be51695e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,6 +13,7 @@ * For modders: * World events are now raised for the volcano levels. + * Added `apply_save_fix` command to reapply a save migration in exceptional cases. This should be used with extreme care. Type `help apply_save_fix` for details. For the web UI: * Fixed edge cases in SMAPI log parsing. 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 +{ + /// A command which runs one of the game's save migrations. + internal class ApplySaveFixCommand : TrainerCommand + { + /********* + ** Public methods + *********/ + /// Construct an instance. + 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 \nApply the named save fix.") { } + + /// Handle the command. + /// Writes messages to the console and log file. + /// The command name. + /// The command arguments. + 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 + *********/ + /// Get the valid save fix IDs. + private IEnumerable GetSaveIds() + { + foreach (SaveGame.SaveFixes id in Enum.GetValues(typeof(SaveGame.SaveFixes))) + { + if (id == SaveGame.SaveFixes.MAX) + continue; + + yield return id.ToString(); + } + } + } +} From 49c192fc4772417428d490dabf93b790f82c94c9 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Dec 2020 02:08:53 -0500 Subject: [PATCH 6/9] detect & fix broken community center bundles --- docs/release-notes.md | 2 +- src/SMAPI/Framework/SCore.cs | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index be51695e..fe614303 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -9,7 +9,7 @@ ## Upcoming release * For players: - * Fixed community center bundle corruption for non-English players. + * Fixed community center bundles broken for non-English saves created in SMAPI 3.8.0. Affected saves will be fixed automatically next time you load them. * For modders: * World events are now raised for the volcano levels. diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index a7f8fbed..6b8098cd 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,44 @@ namespace StardewModdingAPI.Framework this.EventManager.ReturnedToTitle.RaiseEmpty(); } + /// Apply fixes to the save after it's loaded. + private void ApplySaveFixes() + { + // get last SMAPI version used with this save + const string migrationKey = "Pathoschild.SMAPI/last-version"; + if (!Game1.CustomData.TryGetValue(migrationKey, out string rawVersion) || !SemanticVersion.TryParse(rawVersion, out ISemanticVersion lastVersionRun)) + lastVersionRun = 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 (lastVersionRun.IsOlderThan(new SemanticVersion(3, 8, 1))) + { + bool? hasInvalidBundleData = Game1.netWorldState?.Value + ?.BundleData + ?.Values + ?.Any(raw => raw != null && raw.Split('/').Length > 5); + + if (hasInvalidBundleData == true) + { + try + { + Game1.applySaveFix(SaveGame.SaveFixes.FixBotchedBundleData); + this.Monitor.Log("Found corrupted community center data due to a previous version of SMAPI, and fixed it automatically.", LogLevel.Info); + } + catch (Exception ex) + { + this.Monitor.Log("Found corrupted community center data due to a previous version of SMAPI, but was unable to fix it automatically.", LogLevel.Error); + this.Monitor.Log($"Technical details: {ex}"); + } + } + } + + // update last run + Game1.CustomData[migrationKey] = Constants.ApiVersion.ToString(); + } + /// Raised after custom content is removed from the save data to avoid a crash. internal void OnSaveContentRemoved() { From 8895021696fd2129af6de775e275f3bd83edf034 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Dec 2020 10:41:39 -0500 Subject: [PATCH 7/9] rewrite migration to avoid repeating game checks --- src/SMAPI/Framework/SCore.cs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 6b8098cd..e05213f0 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1061,33 +1061,29 @@ namespace StardewModdingAPI.Framework private void ApplySaveFixes() { // get last SMAPI version used with this save - const string migrationKey = "Pathoschild.SMAPI/last-version"; - if (!Game1.CustomData.TryGetValue(migrationKey, out string rawVersion) || !SemanticVersion.TryParse(rawVersion, out ISemanticVersion lastVersionRun)) - lastVersionRun = new SemanticVersion(3, 8, 0); + 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 (lastVersionRun.IsOlderThan(new SemanticVersion(3, 8, 1))) + if (lastVersion.IsOlderThan(new SemanticVersion(3, 8, 1)) && Game1.netWorldState?.Value?.BundleData != null) { - bool? hasInvalidBundleData = Game1.netWorldState?.Value - ?.BundleData - ?.Values - ?.Any(raw => raw != null && raw.Split('/').Length > 5); + var oldData = new Dictionary(Game1.netWorldState.Value.BundleData); - if (hasInvalidBundleData == true) + try { - try - { - Game1.applySaveFix(SaveGame.SaveFixes.FixBotchedBundleData); - this.Monitor.Log("Found corrupted community center data due to a previous version of SMAPI, and fixed it automatically.", LogLevel.Info); - } - catch (Exception ex) - { - this.Monitor.Log("Found corrupted community center data due to a previous version of SMAPI, but was unable to fix it automatically.", LogLevel.Error); - this.Monitor.Log($"Technical details: {ex}"); - } + 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}"); } } From 5cc069476e1cb63c8fef2dd245a540b6b5130e68 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Dec 2020 11:20:47 -0500 Subject: [PATCH 8/9] deprecate ConsoleCommands.Trigger method --- docs/release-notes.md | 1 + src/SMAPI/Framework/DeprecationManager.cs | 31 +++++++------------ .../Framework/ModHelpers/CommandHelper.cs | 8 +++++ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/docs/release-notes.md b/docs/release-notes.md index fe614303..0cc45116 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -14,6 +14,7 @@ * For modders: * World events are now raised for the volcano levels. * Added `apply_save_fix` command to reapply a save migration in exceptional cases. This should be used with extreme care. Type `help apply_save_fix` for details. + * **Deprecation notice:** the `Helper.ConsoleCommands.Trigger` method is now deprecated and should no longer be used. See [integration APIs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations) for better mod integration options. It will eventually be removed in SMAPI 4.0. For the web UI: * Fixed edge cases in SMAPI log parsing. 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; } - /// Log a deprecation warning for the old-style events. - public void WarnForOldEvents() + /// Get the source name for a mod from its unique ID. + public string GetSourceNameFromStack() { - this.Warn("legacy events", "2.9", DeprecationLevel.PendingRemoval); + return this.ModRegistry.GetFromStack()?.DisplayName; } - /// Log a deprecation warning. - /// A noun phrase describing what is deprecated. - /// The SMAPI version which deprecated it. - /// How deprecated the code is. - public void Warn(string nounPhrase, string version, DeprecationLevel severity) + /// Get the source name for a mod from its unique ID. + /// The mod's unique ID. + public string GetSourceName(string modId) { - this.Warn(this.ModRegistry.GetFromStack()?.DisplayName, nounPhrase, version, severity); + return this.ModRegistry.Get(modId)?.DisplayName; } /// Log a deprecation warning. @@ -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 ?? "", nounPhrase, version)) + if (!this.MarkWarned(source ?? this.GetSourceNameFromStack() ?? "", nounPhrase, version)) return; // queue warning @@ -111,21 +109,16 @@ namespace StardewModdingAPI.Framework this.QueuedWarnings.Clear(); } - /// Mark a deprecation warning as already logged. - /// A noun phrase describing what is deprecated (e.g. "the Extensions.AsInt32 method"). - /// The SMAPI version which deprecated it. - /// Returns whether the deprecation was successfully marked as warned. Returns false if it was already marked. - public bool MarkWarned(string nounPhrase, string version) - { - return this.MarkWarned(this.ModRegistry.GetFromStack()?.DisplayName, nounPhrase, version); - } + /********* + ** Private methods + *********/ /// Mark a deprecation warning as already logged. /// The friendly name of the assembly which used the deprecated code. /// A noun phrase describing what is deprecated (e.g. "the Extensions.AsInt32 method"). /// The SMAPI version which deprecated it. /// Returns whether the deprecation was successfully marked as warned. Returns false if it was already marked. - 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 } /// + [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); } } From 5ac46b7ab22ad5d6ce558a6b6d6d020971c178bd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Dec 2020 11:22:09 -0500 Subject: [PATCH 9/9] prepare for release --- build/common.targets | 2 +- docs/release-notes.md | 10 ++++++---- src/SMAPI.Mods.ConsoleCommands/manifest.json | 4 ++-- src/SMAPI.Mods.SaveBackup/manifest.json | 4 ++-- src/SMAPI/Constants.cs | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/build/common.targets b/build/common.targets index ee4f918f..6c79d290 100644 --- a/build/common.targets +++ b/build/common.targets @@ -4,7 +4,7 @@ - 3.8.0 + 3.8.1 SMAPI latest diff --git a/docs/release-notes.md b/docs/release-notes.md index 0cc45116..90557c54 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -7,13 +7,15 @@ * Migrated to Harmony 2.0 (see [_migrate to Harmony 2.0_](https://stardewvalleywiki.com/Modding:Migrate_to_Harmony_2.0) for more info). --> -## Upcoming release +## 3.8.1 +Released 26 December 2020 for Stardew Valley 1.5.1 or later. + * For players: - * Fixed community center bundles broken for non-English saves created in SMAPI 3.8.0. Affected saves will be fixed automatically next time you load them. + * Fixed broken community center bundles for non-English saves created in Stardew Valley 1.5. Affected saves will be fixed automatically on load. * For modders: - * World events are now raised for the volcano levels. - * Added `apply_save_fix` command to reapply a save migration in exceptional cases. This should be used with extreme care. Type `help apply_save_fix` for details. + * World events are now raised for volcano dungeon levels. + * Added `apply_save_fix` command to reapply a save migration in exceptional cases. This should be used very carefully. Type `help apply_save_fix` for details. * **Deprecation notice:** the `Helper.ConsoleCommands.Trigger` method is now deprecated and should no longer be used. See [integration APIs](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Integrations) for better mod integration options. It will eventually be removed in SMAPI 4.0. For the web UI: 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/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 ****/ /// SMAPI's current semantic version. - public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.8.0"); + public static ISemanticVersion ApiVersion { get; } = new Toolkit.SemanticVersion("3.8.1"); /// The minimum supported version of Stardew Valley. - public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.0"); + public static ISemanticVersion MinimumGameVersion { get; } = new GameVersion("1.5.1"); /// The maximum supported version of Stardew Valley. public static ISemanticVersion MaximumGameVersion { get; } = null;