From d9c001a39f4562038fb608468169ea22cf074111 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 9 May 2021 12:06:18 -0400 Subject: fix version zero validation --- src/SMAPI/Framework/ModLoading/ModResolver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index c70820e4..7761a567 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -173,7 +173,7 @@ namespace StardewModdingAPI.Framework.ModLoading if (string.IsNullOrWhiteSpace(mod.Manifest.Name)) missingFields.Add(nameof(IManifest.Name)); - if (mod.Manifest.Version == null || mod.Manifest.Version.ToString() == "0.0") + if (mod.Manifest.Version == null || mod.Manifest.Version.ToString() == "0.0.0") missingFields.Add(nameof(IManifest.Version)); if (string.IsNullOrWhiteSpace(mod.Manifest.UniqueID)) missingFields.Add(nameof(IManifest.UniqueID)); -- cgit From 4b391d631ccde301969ae0c04f407a01ba4055b1 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 9 May 2021 12:12:03 -0400 Subject: normalize manifest array fields --- src/SMAPI/Framework/ModLoading/ModMetadata.cs | 19 +++++++++++-------- src/SMAPI/Framework/ModLoading/ModResolver.cs | 2 +- src/SMAPI/Framework/SCore.cs | 19 ++++++++----------- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index 17e6d59a..b703f74c 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -195,7 +195,10 @@ namespace StardewModdingAPI.Framework.ModLoading /// public IEnumerable GetUpdateKeys(bool validOnly = false) { - foreach (string rawKey in this.Manifest?.UpdateKeys ?? new string[0]) + if (this.Manifest == null) + yield break; + + foreach (string rawKey in this.Manifest.UpdateKeys) { UpdateKey updateKey = UpdateKey.Parse(rawKey); if (updateKey.LooksValid || !validOnly) @@ -251,16 +254,16 @@ namespace StardewModdingAPI.Framework.ModLoading { var ids = new Dictionary(StringComparer.OrdinalIgnoreCase); - // yield dependencies - if (this.Manifest?.Dependencies != null) + if (this.Manifest != null) { - foreach (var entry in this.Manifest?.Dependencies) + // yield dependencies + foreach (IManifestDependency entry in this.Manifest.Dependencies) ids[entry.UniqueID] = entry.IsRequired; - } - // yield content pack parent - if (this.Manifest?.ContentPackFor?.UniqueID != null) - ids[this.Manifest.ContentPackFor.UniqueID] = true; + // yield content pack parent + if (this.Manifest.ContentPackFor?.UniqueID != null) + ids[this.Manifest.ContentPackFor.UniqueID] = true; + } return ids; } diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 7761a567..3bf2b0a1 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -82,7 +82,7 @@ namespace StardewModdingAPI.Framework.ModLoading // get update URLs List updateUrls = new List(); - foreach (string key in mod.Manifest.UpdateKeys ?? new string[0]) + foreach (string key in mod.Manifest.UpdateKeys) { string url = getUpdateUrl(key); if (url != null) diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 5862b112..8b2c7544 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -1593,19 +1593,16 @@ namespace StardewModdingAPI.Framework // validate dependencies // Although dependencies are validated before mods are loaded, a dependency may have failed to load. - if (mod.Manifest.Dependencies?.Any() == true) + foreach (IManifestDependency dependency in mod.Manifest.Dependencies.Where(p => p.IsRequired)) { - foreach (IManifestDependency dependency in mod.Manifest.Dependencies.Where(p => p.IsRequired)) + if (this.ModRegistry.Get(dependency.UniqueID) == null) { - if (this.ModRegistry.Get(dependency.UniqueID) == null) - { - string dependencyName = mods - .FirstOrDefault(otherMod => otherMod.HasID(dependency.UniqueID)) - ?.DisplayName ?? dependency.UniqueID; - errorReasonPhrase = $"it needs the '{dependencyName}' mod, which couldn't be loaded."; - failReason = ModFailReason.MissingDependencies; - return false; - } + string dependencyName = mods + .FirstOrDefault(otherMod => otherMod.HasID(dependency.UniqueID)) + ?.DisplayName ?? dependency.UniqueID; + errorReasonPhrase = $"it needs the '{dependencyName}' mod, which couldn't be loaded."; + failReason = ModFailReason.MissingDependencies; + return false; } } -- cgit From 7c76c5cad2c6edac75d6387dbaa5999295be46d8 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 9 May 2021 12:13:39 -0400 Subject: add validation for the manifest 'Dependencies' field --- src/SMAPI/Framework/ModLoading/ModResolver.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 3bf2b0a1..2f506571 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -188,6 +188,28 @@ namespace StardewModdingAPI.Framework.ModLoading // validate ID format if (!PathUtilities.IsSlug(mod.Manifest.UniqueID)) mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, "its manifest specifies an invalid ID (IDs must only contain letters, numbers, underscores, periods, or hyphens)."); + + // validate dependencies + foreach (var dependency in mod.Manifest.Dependencies) + { + // null dependency + if (dependency == null) + { + mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has a null entry under {nameof(IManifest.Dependencies)}."); + continue; + } + + // missing ID + if (string.IsNullOrWhiteSpace(dependency.UniqueID)) + { + mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has a {nameof(IManifest.Dependencies)} entry with no {nameof(IManifestDependency.UniqueID)} field."); + continue; + } + + // invalid ID + if (!PathUtilities.IsSlug(dependency.UniqueID)) + mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its manifest has a {nameof(IManifest.Dependencies)} entry with an invalid {nameof(IManifestDependency.UniqueID)} field (IDs must only contain letters, numbers, underscores, periods, or hyphens)."); + } } // validate IDs are unique -- cgit From 4ac04ee3aca00f7c1e105ddabd1ed16995b17086 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 9 May 2021 12:19:30 -0400 Subject: fix error if a mod has a 'Dependencies' entry with no ID --- src/SMAPI/Framework/ModLoading/ModMetadata.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index b703f74c..0ace084f 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -195,7 +195,7 @@ namespace StardewModdingAPI.Framework.ModLoading /// public IEnumerable GetUpdateKeys(bool validOnly = false) { - if (this.Manifest == null) + if (!this.HasManifest()) yield break; foreach (string rawKey in this.Manifest.UpdateKeys) @@ -254,14 +254,17 @@ namespace StardewModdingAPI.Framework.ModLoading { var ids = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (this.Manifest != null) + if (this.HasManifest()) { // yield dependencies foreach (IManifestDependency entry in this.Manifest.Dependencies) - ids[entry.UniqueID] = entry.IsRequired; + { + if (!string.IsNullOrWhiteSpace(entry.UniqueID)) + ids[entry.UniqueID] = entry.IsRequired; + } // yield content pack parent - if (this.Manifest.ContentPackFor?.UniqueID != null) + if (!string.IsNullOrWhiteSpace(this.Manifest.ContentPackFor?.UniqueID)) ids[this.Manifest.ContentPackFor.UniqueID] = true; } -- cgit From c310875f902f82ef3b9486e2ff3705106b2ad40c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 19 May 2021 23:38:10 -0400 Subject: fix 'loaded with custom settings' message shown with default settings --- src/SMAPI/Framework/Models/SConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/Models/SConfig.cs b/src/SMAPI/Framework/Models/SConfig.cs index a71bafd9..10bf9f94 100644 --- a/src/SMAPI/Framework/Models/SConfig.cs +++ b/src/SMAPI/Framework/Models/SConfig.cs @@ -22,7 +22,7 @@ namespace StardewModdingAPI.Framework.Models [nameof(VerboseLogging)] = false, [nameof(LogNetworkTraffic)] = false, [nameof(RewriteMods)] = true, - [nameof(AggressiveMemoryOptimizations)] = true + [nameof(AggressiveMemoryOptimizations)] = false }; /// The default values for , to log changes if different. -- cgit From b149e11338ba2dbaf030a70783d28b6be21ccf1e Mon Sep 17 00:00:00 2001 From: DiscipleOfEris Date: Wed, 26 May 2021 11:50:49 -0700 Subject: Add `World.FurnitureListChanged` event Create a new event available to SMAPI mods to track furniture changes. To facilitate the event, a `FurnitureListChangedEventArgs` class is added as well. Fixes #778 --- src/SMAPI/Events/FurnitureListChangedEventArgs.cs | 44 ++++++++++++++++++++++ src/SMAPI/Events/IWorldEvents.cs | 3 ++ src/SMAPI/Framework/Events/EventManager.cs | 4 ++ src/SMAPI/Framework/Events/ModWorldEvents.cs | 7 ++++ src/SMAPI/Framework/SCore.cs | 4 ++ .../Framework/StateTracking/LocationTracker.cs | 7 +++- .../StateTracking/Snapshots/LocationSnapshot.cs | 4 ++ 7 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/SMAPI/Events/FurnitureListChangedEventArgs.cs (limited to 'src/SMAPI') diff --git a/src/SMAPI/Events/FurnitureListChangedEventArgs.cs b/src/SMAPI/Events/FurnitureListChangedEventArgs.cs new file mode 100644 index 00000000..04fcf9ff --- /dev/null +++ b/src/SMAPI/Events/FurnitureListChangedEventArgs.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using StardewValley; +using StardewValley.Objects; + +namespace StardewModdingAPI.Events +{ + /// Event arguments for a event. + public class FurnitureListChangedEventArgs : EventArgs + { + /********* + ** Accessors + *********/ + /// The location which changed. + public GameLocation Location { get; } + + /// The furniture added to the location. + public IEnumerable Added { get; } + + /// The furniture removed from the location. + public IEnumerable Removed { get; } + + /// Whether this is the location containing the local player. + public bool IsCurrentLocation => object.ReferenceEquals(this.Location, Game1.player?.currentLocation); + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The location which changed. + /// The furniture added to the location. + /// The furniture removed from the location. + internal FurnitureListChangedEventArgs(GameLocation location, IEnumerable added, IEnumerable removed) + { + this.Location = location; + this.Added = added.ToArray(); + this.Removed = removed.ToArray(); + } + } +} diff --git a/src/SMAPI/Events/IWorldEvents.cs b/src/SMAPI/Events/IWorldEvents.cs index 9569a57b..c023e1f0 100644 --- a/src/SMAPI/Events/IWorldEvents.cs +++ b/src/SMAPI/Events/IWorldEvents.cs @@ -28,5 +28,8 @@ namespace StardewModdingAPI.Events /// Raised after terrain features (like floors and trees) are added or removed in a location. event EventHandler TerrainFeatureListChanged; + + /// Raised after furniture are added or removed in a location. + event EventHandler FurnitureListChanged; } } diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs index f4abfffe..dfc289ed 100644 --- a/src/SMAPI/Framework/Events/EventManager.cs +++ b/src/SMAPI/Framework/Events/EventManager.cs @@ -162,6 +162,9 @@ namespace StardewModdingAPI.Framework.Events /// Raised after terrain features (like floors and trees) are added or removed in a location. public readonly ManagedEvent TerrainFeatureListChanged; + /// Raised after furniture are added or removed in a location. + public readonly ManagedEvent FurnitureListChanged; + /**** ** Specialized ****/ @@ -238,6 +241,7 @@ namespace StardewModdingAPI.Framework.Events this.ObjectListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.ObjectListChanged)); this.ChestInventoryChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.ChestInventoryChanged)); this.TerrainFeatureListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.TerrainFeatureListChanged)); + this.FurnitureListChanged = ManageEventOf(nameof(IModEvents.World), nameof(IWorldEvents.FurnitureListChanged)); this.LoadStageChanged = ManageEventOf(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.LoadStageChanged)); this.UnvalidatedUpdateTicking = ManageEventOf(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.UnvalidatedUpdateTicking), isPerformanceCritical: true); diff --git a/src/SMAPI/Framework/Events/ModWorldEvents.cs b/src/SMAPI/Framework/Events/ModWorldEvents.cs index 21b1b664..f4c40abc 100644 --- a/src/SMAPI/Framework/Events/ModWorldEvents.cs +++ b/src/SMAPI/Framework/Events/ModWorldEvents.cs @@ -65,6 +65,13 @@ namespace StardewModdingAPI.Framework.Events remove => this.EventManager.TerrainFeatureListChanged.Remove(value); } + /// Raised after furniture are added or removed in a location. + public event EventHandler FurnitureListChanged + { + add => this.EventManager.FurnitureListChanged.Add(value, this.Mod); + remove => this.EventManager.FurnitureListChanged.Remove(value); + } + /********* ** Public methods diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 8b2c7544..bf88798b 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -914,6 +914,10 @@ namespace StardewModdingAPI.Framework // terrain features changed if (locState.TerrainFeatures.IsChanged) events.TerrainFeatureListChanged.Raise(new TerrainFeatureListChangedEventArgs(location, locState.TerrainFeatures.Added, locState.TerrainFeatures.Removed)); + + // furniture changed + if (locState.Furniture.IsChanged) + events.FurnitureListChanged.Raise(new FurnitureListChangedEventArgs(location, locState.Furniture.Added, locState.Furniture.Removed)); } } diff --git a/src/SMAPI/Framework/StateTracking/LocationTracker.cs b/src/SMAPI/Framework/StateTracking/LocationTracker.cs index 519fe8f4..6d3a62bb 100644 --- a/src/SMAPI/Framework/StateTracking/LocationTracker.cs +++ b/src/SMAPI/Framework/StateTracking/LocationTracker.cs @@ -48,6 +48,9 @@ namespace StardewModdingAPI.Framework.StateTracking /// Tracks added or removed terrain features. public IDictionaryWatcher TerrainFeaturesWatcher { get; } + /// Tracks added or removed furniture. + public ICollectionWatcher FurnitureWatcher { get; } + /// Tracks items added or removed to chests. public IDictionary ChestWatchers { get; } = new Dictionary(); @@ -68,6 +71,7 @@ namespace StardewModdingAPI.Framework.StateTracking this.NpcsWatcher = WatcherFactory.ForNetCollection(location.characters); this.ObjectsWatcher = WatcherFactory.ForNetDictionary(location.netObjects); this.TerrainFeaturesWatcher = WatcherFactory.ForNetDictionary(location.terrainFeatures); + this.FurnitureWatcher = WatcherFactory.ForNetCollection(location.furniture); this.Watchers.AddRange(new IWatcher[] { @@ -76,7 +80,8 @@ namespace StardewModdingAPI.Framework.StateTracking this.LargeTerrainFeaturesWatcher, this.NpcsWatcher, this.ObjectsWatcher, - this.TerrainFeaturesWatcher + this.TerrainFeaturesWatcher, + this.FurnitureWatcher }); this.UpdateChestWatcherList(added: location.Objects.Pairs, removed: new KeyValuePair[0]); diff --git a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs index 6ae52fd0..6c9cc4f5 100644 --- a/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs +++ b/src/SMAPI/Framework/StateTracking/Snapshots/LocationSnapshot.cs @@ -34,6 +34,9 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots /// Tracks added or removed terrain features. public SnapshotListDiff> TerrainFeatures { get; } = new SnapshotListDiff>(); + /// Tracks added or removed furniture. + public SnapshotListDiff Furniture { get; } = new SnapshotListDiff(); + /// Tracks changed chest inventories. public IDictionary ChestItems { get; } = new Dictionary(); @@ -59,6 +62,7 @@ namespace StardewModdingAPI.Framework.StateTracking.Snapshots this.Npcs.Update(watcher.NpcsWatcher); this.Objects.Update(watcher.ObjectsWatcher); this.TerrainFeatures.Update(watcher.TerrainFeaturesWatcher); + this.Furniture.Update(watcher.FurnitureWatcher); // chest inventories this.ChestItems.Clear(); -- cgit From 9d7b31afc45b89278f5a74697da51a47215d6592 Mon Sep 17 00:00:00 2001 From: DiscipleOfEris Date: Wed, 26 May 2021 11:57:52 -0700 Subject: Update FurnitureListChangedEventArgs.cs Prune unnecessary `using` statements --- src/SMAPI/Events/FurnitureListChangedEventArgs.cs | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Events/FurnitureListChangedEventArgs.cs b/src/SMAPI/Events/FurnitureListChangedEventArgs.cs index 04fcf9ff..683f4620 100644 --- a/src/SMAPI/Events/FurnitureListChangedEventArgs.cs +++ b/src/SMAPI/Events/FurnitureListChangedEventArgs.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using StardewValley; using StardewValley.Objects; -- cgit From 03f8777afd4f0707dc8e61eae6e5eae73a88d738 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 27 May 2021 22:59:06 -0400 Subject: add asset propagation for paint masks --- src/SMAPI/Metadata/CoreAssetPropagator.cs | 50 ++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 8 deletions(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Metadata/CoreAssetPropagator.cs b/src/SMAPI/Metadata/CoreAssetPropagator.cs index 623c65d5..5641f90f 100644 --- a/src/SMAPI/Metadata/CoreAssetPropagator.cs +++ b/src/SMAPI/Metadata/CoreAssetPropagator.cs @@ -233,6 +233,16 @@ namespace StardewModdingAPI.Metadata return true; } + case "buildings\\houses_paintmask": // Farm + { + bool removedFromCache = this.RemoveFromPaintMaskCache(key); + + Farm farm = Game1.getFarm(); + farm?.ApplyHousePaint(); + + return removedFromCache || farm != null; + } + /**** ** Content\Characters\Farmer ****/ @@ -613,7 +623,7 @@ namespace StardewModdingAPI.Metadata return this.ReloadFarmAnimalSprites(content, key); if (this.IsInFolder(key, "Buildings")) - return this.ReloadBuildings(content, key); + return this.ReloadBuildings(key); if (this.KeyStartsWith(key, "LooseSprites\\Fence")) return this.ReloadFenceTextures(key); @@ -717,28 +727,39 @@ namespace StardewModdingAPI.Metadata } /// Reload building textures. - /// The content manager through which to reload the asset. /// The asset key to reload. /// Returns whether any textures were reloaded. - private bool ReloadBuildings(LocalizedContentManager content, string key) + private bool ReloadBuildings(string key) { - // get buildings + // get paint mask info + const string paintMaskSuffix = "_PaintMask"; + bool isPaintMask = key.EndsWith(paintMaskSuffix, StringComparison.OrdinalIgnoreCase); + + // get building type string type = Path.GetFileName(key); + if (isPaintMask) + type = type.Substring(0, type.Length - paintMaskSuffix.Length); + + // get buildings Building[] buildings = this.GetLocations(buildingInteriors: false) .OfType() .SelectMany(p => p.buildings) .Where(p => p.buildingType.Value == type) .ToArray(); - // reload buildings + // remove from paint mask cache + bool removedFromCache = this.RemoveFromPaintMaskCache(key); + + // reload textures if (buildings.Any()) { - Lazy texture = new Lazy(() => content.Load(key)); foreach (Building building in buildings) - building.texture = texture; + building.resetTexture(); + return true; } - return false; + + return removedFromCache; } /// Reload map seat textures. @@ -1295,5 +1316,18 @@ namespace StardewModdingAPI.Metadata // else just (re)load it from the main content manager return this.MainContentManager.Load(key); } + + /// Remove a case-insensitive key from the paint mask cache. + /// The paint mask asset key. + private bool RemoveFromPaintMaskCache(string key) + { + // make cache case-insensitive + // This is needed for cache invalidation since mods may specify keys with a different capitalization + if (!object.ReferenceEquals(BuildingPainter.paintMaskLookup.Comparer, StringComparer.OrdinalIgnoreCase)) + BuildingPainter.paintMaskLookup = new Dictionary>>(BuildingPainter.paintMaskLookup, StringComparer.OrdinalIgnoreCase); + + // remove key from cache + return BuildingPainter.paintMaskLookup.Remove(key); + } } } -- cgit From 4df8f4a656ce68b5bb7dc76eefd6da9c27620928 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 11 Jun 2021 19:14:59 -0400 Subject: fix edge case where save constants aren't set correctly --- src/SMAPI/Constants.cs | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 2e476208..38e8c500 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -321,32 +321,43 @@ namespace StardewModdingAPI *********/ /// Get the name of the save folder, if any. private static string GetSaveFolderName() + { + return Constants.GetSaveFolder()?.Name; + } + + /// Get the path to the current save folder, if any. + private static string GetSaveFolderPathIfExists() + { + DirectoryInfo saveFolder = Constants.GetSaveFolder(); + return saveFolder?.Exists == true + ? saveFolder.FullName + : null; + } + + /// Get the current save folder, if any. + private static DirectoryInfo GetSaveFolder() { // save not available if (Context.LoadStage == LoadStage.None) return null; // get basic info - string saveName = Game1.GetSaveGameName(set_value: false); + string rawSaveName = Game1.GetSaveGameName(set_value: false); ulong saveID = Context.LoadStage == LoadStage.SaveParsed ? SaveGame.loaded.uniqueIDForThisGame : Game1.uniqueIDForThisGame; - // build folder name - return $"{new string(saveName.Where(char.IsLetterOrDigit).ToArray())}_{saveID}"; - } - - /// Get the path to the current save folder, if any. - private static string GetSaveFolderPathIfExists() - { - string folderName = Constants.GetSaveFolderName(); - if (folderName == null) - return null; + // get best match (accounting for rare case where folder name isn't sanitized) + DirectoryInfo folder = null; + foreach (string saveName in new[] { rawSaveName, new string(rawSaveName.Where(char.IsLetterOrDigit).ToArray()) }) + { + folder = new DirectoryInfo(Path.Combine(Constants.SavesPath, $"{saveName}_{saveID}")); + if (folder.Exists) + return folder; + } - string path = Path.Combine(Constants.SavesPath, folderName); - return Directory.Exists(path) - ? path - : null; + // if save doesn't exist yet, return the default one we expect to be created + return folder; } } } -- cgit From 66f8920c29567de615dbcb0a06e78f774d128f6b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 24 Jun 2021 20:17:34 -0400 Subject: log trace message if conflicting software is detected --- src/SMAPI/Framework/SCore.cs | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index bf88798b..2de2b21a 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -11,6 +11,9 @@ using System.Security; using System.Text; using System.Threading; using System.Threading.Tasks; +#if SMAPI_FOR_WINDOWS +using Microsoft.Win32; +#endif using Microsoft.Xna.Framework; #if SMAPI_FOR_XNA using System.Windows.Forms; @@ -376,6 +379,9 @@ namespace StardewModdingAPI.Framework mods = resolver.ProcessDependencies(mods, modDatabase).ToArray(); this.LoadMods(mods, this.Toolkit.JsonHelper, this.ContentCore, modDatabase); + // check for software likely to cause issues + this.CheckForSoftwareConflicts(); + // check for updates this.CheckForUpdatesAsync(mods); } @@ -1251,6 +1257,55 @@ namespace StardewModdingAPI.Framework this.LogManager.SetConsoleTitle(consoleTitle); } + /// Log a warning if software known to cause issues is installed. + private void CheckForSoftwareConflicts() + { +#if SMAPI_FOR_WINDOWS + this.Monitor.Log("Checking for known software conflicts..."); + + try + { + string[] registryKeys = { @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" }; + + string[] installedNames = registryKeys + .SelectMany(registryKey => + { + using RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey); + if (key == null) + return new string[0]; + + return key + .GetSubKeyNames() + .Select(subkeyName => + { + using RegistryKey subkey = key.OpenSubKey(subkeyName); + string displayName = (string)subkey?.GetValue("DisplayName"); + string displayVersion = (string)subkey?.GetValue("DisplayVersion"); + + if (displayName != null && displayVersion != null && displayName.EndsWith($" {displayVersion}")) + displayName = displayName.Substring(0, displayName.Length - displayVersion.Length - 1); + + return displayName; + }) + .ToArray(); + }) + .Where(name => name != null && (name.Contains("MSI Afterburner") || name.Contains("RivaTuner"))) + .Distinct() + .OrderBy(name => name) + .ToArray(); + + if (installedNames.Any()) + this.Monitor.Log($" Found {string.Join(" and ", installedNames)} installed, which can conflict with SMAPI. If you experience errors or crashes, try disabling that software or adding an exception for SMAPI / Stardew Valley."); + else + this.Monitor.Log(" None found!"); + } + catch (Exception ex) + { + this.Monitor.Log($"Failed when checking for conflicting software. Technical details:\n{ex}"); + } +#endif + } + /// Asynchronously check for a new version of SMAPI and any installed mods, and print alerts to the console if an update is available. /// The mods to include in the update check (if eligible). private void CheckForUpdatesAsync(IModMetadata[] mods) -- cgit From 5e3a1abbd421196ee63442c82cb6606b6630a6ca Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 26 Jun 2021 11:16:12 -0400 Subject: improve error if SMAPI fails to dispose on exit --- src/SMAPI/Framework/SCore.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index 2de2b21a..c3285979 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -297,7 +297,14 @@ namespace StardewModdingAPI.Framework } finally { - this.Dispose(); + try + { + this.Dispose(); + } + catch (Exception ex) + { + this.Monitor.Log($"The game ended, but SMAPI wasn't able to dispose correctly. Technical details: {ex}", LogLevel.Error); + } } } -- cgit From 60af28760a6edc509b16e7c62aa8b00ba9798793 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 9 Jul 2021 22:29:43 -0400 Subject: prepare for release --- src/SMAPI/Constants.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI') diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 38e8c500..9e93551c 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -61,7 +61,7 @@ namespace StardewModdingAPI internal static int? LogScreenId { get; set; } /// SMAPI's current raw semantic version. - internal static string RawApiVersion = "3.10.1"; + internal static string RawApiVersion = "3.11.0"; } /// Contains SMAPI's constants and assumptions. -- cgit