diff options
8 files changed, 526 insertions, 327 deletions
diff --git a/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs b/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs index 4dbd1438..2d65eee9 100644 --- a/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs +++ b/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs @@ -141,7 +141,11 @@ namespace StardewModdingAPI.Tests.Core { // arrange Mock<IModMetadata> mock = this.GetMetadata("Mod A", new string[0], allowStatusChange: true); - this.SetupMetadataForValidation(mock, new ModDataRecord { Status = ModStatus.AssumeBroken, UpperVersion = new SemanticVersion("1.0"), UpdateUrls = new[] { "http://example.org" } }); + this.SetupMetadataForValidation(mock, new ModDataRecord + { + Compatibility = new[] { new ModCompatibility("~1.0", ModStatus.AssumeBroken, null) }, + UpdateUrls = new[] { "http://example.org" } + }); // act new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0")); diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs index 3674faec..31ba5bc5 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs @@ -61,14 +61,7 @@ namespace StardewModdingAPI.Framework.ModLoading string key = !string.IsNullOrWhiteSpace(manifest.UniqueID) ? manifest.UniqueID : manifest.EntryDll; // get data record - dataRecord = ( - from mod in dataRecords - where - mod.ID.Matches(key, manifest) - && (mod.LowerVersion == null || !manifest.Version.IsOlderThan(mod.LowerVersion)) - && (mod.UpperVersion == null || !manifest.Version.IsNewerThan(mod.UpperVersion)) - select mod - ).FirstOrDefault(); + dataRecord = dataRecords.FirstOrDefault(record => record.ID.Matches(key, manifest)); } // build metadata @@ -98,28 +91,26 @@ namespace StardewModdingAPI.Framework.ModLoading continue; // validate compatibility + ModCompatibility compatibility = mod.DataRecord?.GetCompatibility(mod.Manifest.Version); + switch (compatibility?.Status) { - ModDataRecord dataRecord = mod.DataRecord; - switch (dataRecord?.Status) - { - case ModStatus.Obsolete: - mod.SetStatus(ModMetadataStatus.Failed, $"it's obsolete: {dataRecord.ReasonPhrase}"); - continue; - - case ModStatus.AssumeBroken: - { - string reasonPhrase = dataRecord.ReasonPhrase ?? "it's no longer compatible"; - string error = $"{reasonPhrase}. Please check for a "; - if (mod.Manifest.Version.Equals(dataRecord.UpperVersion) && dataRecord.UpperVersionLabel == null) - error += "newer version"; - else - error += $"version newer than {dataRecord.UpperVersionLabel ?? dataRecord.UpperVersion.ToString()}"; - error += " at " + string.Join(" or ", dataRecord.UpdateUrls); - - mod.SetStatus(ModMetadataStatus.Failed, error); - continue; - } - } + case ModStatus.Obsolete: + mod.SetStatus(ModMetadataStatus.Failed, $"it's obsolete: {compatibility.ReasonPhrase}"); + continue; + + case ModStatus.AssumeBroken: + { + string reasonPhrase = compatibility.ReasonPhrase ?? "it's no longer compatible"; + string error = $"{reasonPhrase}. Please check for a "; + if (mod.Manifest.Version.Equals(compatibility.UpperVersion)) + error += "newer version"; + else + error += $"version newer than {compatibility.UpperVersion}"; + error += " at " + string.Join(" or ", mod.DataRecord.UpdateUrls); + + mod.SetStatus(ModMetadataStatus.Failed, error); + } + continue; } // validate SMAPI version diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs new file mode 100644 index 00000000..54737e6c --- /dev/null +++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs @@ -0,0 +1,55 @@ +using System; + +namespace StardewModdingAPI.Framework.Models +{ + /// <summary>Specifies the compatibility of a given mod version range.</summary> + internal class ModCompatibility + { + /********* + ** Accessors + *********/ + /// <summary>The lowest version in the range, or <c>null</c> for all past versions.</summary> + public ISemanticVersion LowerVersion { get; } + + /// <summary>The highest version in the range, or <c>null</c> for all future versions.</summary> + public ISemanticVersion UpperVersion { get; } + + /// <summary>The mod compatibility.</summary> + public ModStatus Status { get; } + + /// <summary>The reason phrase to show in log output, or <c>null</c> to use the default value.</summary> + /// <example>For example, "this version is incompatible with the latest version of the game".</example> + public string ReasonPhrase { get; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="versionRange">A version range, which consists of two version strings separated by a '~' character. Either side can be left blank for an unbounded range.</param> + /// <param name="status">The mod compatibility.</param> + /// <param name="reasonPhrase">The reason phrase to show in log output, or <c>null</c> to use the default value.</param> + public ModCompatibility(string versionRange, ModStatus status, string reasonPhrase) + { + // extract version strings + string[] versions = versionRange.Split('~'); + if (versions.Length != 2) + throw new FormatException($"Could not parse '{versionRange}' as a version range. It must have two version strings separated by a '~' character (either side can be left blank for an unbounded range)."); + + // initialise + this.LowerVersion = !string.IsNullOrWhiteSpace(versions[0]) ? new SemanticVersion(versions[0]) : null; + this.UpperVersion = !string.IsNullOrWhiteSpace(versions[1]) ? new SemanticVersion(versions[1]) : null; + this.Status = status; + this.ReasonPhrase = reasonPhrase; + } + + /// <summary>Get whether a given version is contained within this compatibility range.</summary> + /// <param name="version">The version to check.</param> + public bool MatchesVersion(ISemanticVersion version) + { + return + (this.LowerVersion == null || !version.IsOlderThan(this.LowerVersion)) + && (this.UpperVersion == null || !version.IsNewerThan(this.UpperVersion)); + } + } +} diff --git a/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs b/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs index 8126022d..de219076 100644 --- a/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs +++ b/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs @@ -1,3 +1,4 @@ +using System.Linq; using Newtonsoft.Json; using StardewModdingAPI.Framework.Serialisation; @@ -16,25 +17,22 @@ namespace StardewModdingAPI.Framework.Models /// <summary>The mod name.</summary> public string Name { get; set; } - /// <summary>The oldest incompatible mod version, or <c>null</c> for all past versions.</summary> - [JsonConverter(typeof(SFieldConverter))] - public ISemanticVersion LowerVersion { get; set; } - - /// <summary>The most recent incompatible mod version.</summary> - [JsonConverter(typeof(SFieldConverter))] - public ISemanticVersion UpperVersion { get; set; } - - /// <summary>A label to show to the user instead of <see cref="UpperVersion"/>, when the manifest version differs from the user-facing version.</summary> - public string UpperVersionLabel { get; set; } - /// <summary>The URLs the user can check for a newer version.</summary> public string[] UpdateUrls { get; set; } - /// <summary>The reason phrase to show in the warning, or <c>null</c> to use the default value.</summary> - /// <example>"this version is incompatible with the latest version of the game"</example> - public string ReasonPhrase { get; set; } + /// <summary>The compatibility of given mod versions (if any).</summary> + [JsonConverter(typeof(SFieldConverter))] + public ModCompatibility[] Compatibility { get; set; } = new ModCompatibility[0]; + - /// <summary>Indicates how SMAPI should treat the mod.</summary> - public ModStatus Status { get; set; } = ModStatus.AssumeBroken; + /********* + ** Public methods + *********/ + /// <summary>Get the compatibility record for a given version, if any.</summary> + /// <param name="version">The mod version to check.</param> + public ModCompatibility GetCompatibility(ISemanticVersion version) + { + return this.Compatibility.FirstOrDefault(p => p.MatchesVersion(version)); + } } } diff --git a/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs index d71e138b..ffece081 100644 --- a/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs +++ b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs @@ -27,7 +27,8 @@ namespace StardewModdingAPI.Framework.Serialisation return objectType == typeof(ISemanticVersion) || objectType == typeof(IManifestDependency[]) - || objectType == typeof(ModDataID); + || objectType == typeof(ModDataID) + || objectType == typeof(ModCompatibility[]); } /// <summary>Reads the JSON representation of the object.</summary> @@ -68,7 +69,7 @@ namespace StardewModdingAPI.Framework.Serialisation } } - // manifest dependency + // manifest dependencies if (objectType == typeof(IManifestDependency[])) { List<IManifestDependency> result = new List<IManifestDependency>(); @@ -82,13 +83,28 @@ namespace StardewModdingAPI.Framework.Serialisation return result.ToArray(); } - // mod compatibility ID + // mod data ID if (objectType == typeof(ModDataID)) { JToken token = JToken.Load(reader); return new ModDataID(token.Value<string>()); } + // mod compatibility records + if (objectType == typeof(ModCompatibility[])) + { + List<ModCompatibility> result = new List<ModCompatibility>(); + foreach (JProperty property in JObject.Load(reader).Properties()) + { + string range = property.Name; + ModStatus status = property.Value.Value<ModStatus>(nameof(ModCompatibility.Status)); + string reasonPhrase = property.Value.Value<string>(nameof(ModCompatibility.ReasonPhrase)); + + result.Add(new ModCompatibility(range, status, reasonPhrase)); + } + return result.ToArray(); + } + // unknown throw new NotSupportedException($"Unknown type '{objectType?.FullName}'."); } diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 63176dd1..4860968c 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -640,7 +640,7 @@ namespace StardewModdingAPI Assembly modAssembly; try { - modAssembly = modAssemblyLoader.Load(metadata, assemblyPath, assumeCompatible: metadata.DataRecord?.Status == ModStatus.AssumeCompatible); + modAssembly = modAssemblyLoader.Load(metadata, assemblyPath, assumeCompatible: metadata.DataRecord.GetCompatibility(metadata.Manifest.Version)?.Status == ModStatus.AssumeCompatible); } catch (IncompatibleInstructionException ex) { diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index c93ba130..3844b8b0 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -48,958 +48,1092 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha { "Name": "AccessChestAnywhere", "ID": "AccessChestAnywhere", - "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/257", "http://www.nexusmods.com/stardewvalley/mods/518" ], - "Notes": "Broke in SDV 1.1." + "Compatibility": { + "~1.1": { "Status": "AssumeBroken" } // broke in SDV 1.1 + } }, { "Name": "AdjustArtisanPrices", "ID": "1e36d4ca-c7ef-4dfb-9927-d27a6c3c8bdc", - "UpperVersion": "0.0", - "UpperVersionLabel": "0.01", "UpdateUrls": [ "http://community.playstarbound.com/resources/3532", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 1.9." + "Compatibility": { + "~0.01": { "Status": "AssumeBroken" } // broke in SMAPI 1.9 + } }, { "Name": "Advanced Location Loader", "ID": "Entoarox.AdvancedLocationLoader", - "UpperVersion": "1.2.10", "UpdateUrls": [ "http://community.playstarbound.com/resources/3619" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.2.10": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "AgingMod", "ID": "skn.AgingMod", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1129", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Almighty Tool", "ID": "AlmightyTool.dll", - "UpperVersion": "1.1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/439" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.1.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Animal Mood Fix", "ID": "GPeters-AnimalMoodFix", - "Status": "Obsolete", - "ReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." + "Compatibility": { + "~": { "Status": "Obsolete", "ReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." } + } }, { "Name": "Animal Sitter", "ID": "AnimalSitter.dll", - "UpperVersion": "1.0.8", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/581", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.8": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "A Tapper's Dream", "ID": "ddde5195-8f85-4061-90cc-0d4fd5459358", - "UpperVersion": "1.4", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/260", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Better Sprinklers", "ID": "SPDSprinklersMod | Speeder.BetterSprinklers", // changed in 2.3 - "UpperVersion": "2.3.1-pathoschild-update", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/41", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~2.3.1-pathoschild-update": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Birthday Mail", "ID": "005e02dc-d900-425c-9c68-1ff55c5a295d", - "UpperVersion": "1.2.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/276", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.2.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Build Endurance", "ID": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'BuildEndurance'}", // disambiguate from other Alpha_Omegasis mods - "UpperVersion": "1.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/445", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Build Health", "ID": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'BuildHealth'}", // disambiguate from other Alpha_Omegasis mods - "UpperVersion": "1.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/446", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Buy Cooking Recipes", "ID": "Denifia.BuyRecipes", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1126", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Buy Back Collectables", "ID": "BuyBackCollectables", - "UpperVersion": "1.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/507", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Chest Label System", "ID": "SPDChestLabel", - "UpperVersion": "1.6", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/242", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.1." + "Compatibility": { + "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.1 + } }, { "Name": "Chest Pooling", "ID": "ChestPooling.dll", - "UpperVersion": "1.2", "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Chests Anywhere", "ID": "ChestsAnywhere | Pathoschild.ChestsAnywhere", // changed in 1.9 - "UpperVersion": "1.9-beta", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/518" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.9-beta": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Choose Baby Gender", "ID": "ChooseBabyGender.dll", - "UpperVersion": "1.0.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/590", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "CJB Automation", "ID": "CJBAutomation", - "UpperVersion": "1.4", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/211", "http://www.nexusmods.com/stardewvalley/mods/1063" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.4": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "CJB Cheats Menu", "ID": "CJBCheatsMenu", - "UpperVersion": "1.12", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/4" ], - "Notes": "Broke in SDV 1.1." + "Compatibility": { + "~1.12": { "Status": "AssumeBroken" } // broke in SDV 1.1 + } }, { "Name": "CJB Item Spawner", "ID": "CJBItemSpawner", - "UpperVersion": "1.5", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/93" ], - "Notes": "Broke in SDV 1.1." + "Compatibility": { + "~1.5": { "Status": "AssumeBroken" } // broke in SDV 1.1 + } }, { "Name": "CJB Show Item Sell Price", "ID": "CJBShowItemSellPrice", - "UpperVersion": "1.6", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/93" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Climates of Ferngill", "ID": "KoihimeNakamura.ClimatesOfFerngill", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/604", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Cold Weather Haley", "ID": "LordXamon.ColdWeatherHaleyPRO", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1169", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Colored Chests", "ID": "4befde5c-731c-4853-8e4b-c5cdf946805f", - "Status": "Obsolete", - "ReasonPhrase": "colored chests were added in Stardew Valley 1.1." + "Compatibility": { + "~": { "Status": "Obsolete", "ReasonPhrase": "colored chests were added in Stardew Valley 1.1." } + } }, { "Name": "Combat with Farm Implements", "ID": "SPDFarmingImplementsInCombat", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/313", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Configurable Shipping Dates", "ID": "ConfigurableShippingDates", - "UpperVersion": "1.1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/675", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Cooking Skill", "ID": "CookingSkill | spacechase0.CookingSkill", // changed in 1.0.4–6 - "UpperVersion": "1.0.6", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/522" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.6": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "CrabNet", "ID": "CrabNet.dll", - "UpperVersion": "1.0.4", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/584", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Customize Exterior", "ID": "CustomizeExterior", - "UpperVersion": "1.0.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1099" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Customizable Traveling Cart Days", "ID": "TravelingCartYyeahdude", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/567", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Daily News", "ID": "bashNinja.DailyNews", - "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1141", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Dynamic Checklist", "ID": "gunnargolf.DynamicChecklist", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1145", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Dynamic Machines", "ID": "DynamicMachines", - "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/374", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Empty Hands", "ID": "QuicksilverFox.EmptyHands", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1176", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Enemy Health Bars", "ID": "SPDHealthBar", - "UpperVersion": "1.7", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/193", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.7": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Entoarox Framework", "ID": "eacdb74b-4080-4452-b16b-93773cda5cf9 | Entoarox.EntoaroxFramework", // changed in ??? - "UpperVersion": "1.7.9", "UpdateUrls": [ "http://community.playstarbound.com/resources/4228" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.7.9": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Extended Fridge", "ID": "Mystra007ExtendedFridge", - "UpperVersion": "1.0", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/485", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Extended Greenhouse", "ID": "ExtendedGreenhouse", - "UpperVersion": "1.0.2", "UpdateUrls": [ "http://community.playstarbound.com/resources/4303", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.0.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Fall 28 Snow Day", "ID": "{ID:'7ad4f6f7-c3de-4729-a40f-7a11d2b2a358', Name:'Fall28 Snow Day'}", // disambiguate from other mods by Alpha_Omegasis - "UpperVersion": "1.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/486", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Farm Automation: Barn Door Automation", "ID": "FarmAutomation.BarnDoorAutomation.dll", - "UpperVersion": "1.0", "UpdateUrls": [ "http://community.playstarbound.com/threads/111931", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Farm Automation: Item Collector", "ID": "FarmAutomation.ItemCollector.dll", - "UpperVersion": "1.0", "UpdateUrls": [ "http://community.playstarbound.com/threads/111931", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Farm Automation Unofficial: Item Collector", "ID": "Maddy99.FarmAutomation.ItemCollector", - "UpperVersion": "0.5", "UpdateUrls": [ "http://community.playstarbound.com/threads/125172", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~0.5": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Farm Expansion", "ID": "3888bdfd-73f6-4776-8bb7-8ad45aea1915 | AdvizeFarmExpansionMod-2-0 | AdvizeFarmExpansionMod-2-0-5", // changed in 2.0 and 2.0.5 - "UpperVersion": "2.0.5", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/130", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~2.0.5": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Farm Resource Generator", "ID": "FarmResourceGenerator.dll", - "UpperVersion": "1.0.4", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/647", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Faster Run", "ID": "FasterRun.dll", - "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/733", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "FlorenceMod", "ID": "FlorenceMod.dll", - "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/591", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Forage at the Farm", "ID": "ForageAtTheFarm", - "UpperVersion": "1.5.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/673", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.5.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Instant Geode", "ID": "InstantGeode", - "UpperVersion": "1.12", "UpdateUrls": [ "http://community.playstarbound.com/threads/109038", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.12": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Gate Opener", "ID": "GateOpener.dll | mralbobo.GateOpener", // changed in 1.1 - "UpperVersion": "1.0.1", "UpdateUrls": [ "http://community.playstarbound.com/threads/111988" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~1.0.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "GenericShopExtender", "ID": "GenericShopExtender", - "UpperVersion": "0.1.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/814", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~0.1.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Get Dressed", "ID": "GetDressed.dll | Advize.GetDressed", // changed in 3.3 - "UpperVersion": "3.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/331" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~3.3": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Gift Taste Helper", "ID": "8008db57-fa67-4730-978e-34b37ef191d6", - "UpperVersion": "2.3.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/229" ], - "Notes": "Broke in SDV 1.2." + "Compatibility": { + "~2.3.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 + } }, { "Name": "Happy Animals", "ID": "HappyAnimals", - "UpperVersion": "1.0.3", "UpdateUrls": [ "https://community.playstarbound.com/threads/126655", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Happy Birthday", "ID": "{ID:'HappyBirthday', Author:'Alpha_Omegasis'}", // disambiguate from Oxyligen's fork - "UpperVersion": "1.3", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/520", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Harvest With Scythe", "ID": "965169fd-e1ed-47d0-9f12-b104535fb4bc", - "UpperVersion": "1.0.6", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/236", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.0.6": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Hunger for Food", "ID": "HungerForFoodByTigerle", - "UpperVersion": "0.1.2", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/810", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~0.1.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Improved Quality of Life", "ID": "Demiacle.ImprovedQualityOfLife", - "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/1025", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Less Strict Over-Exertion (AntiExhaustion)", "ID": "BALANCEMOD_AntiExhaustion", - "UpperVersion": "1.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/637", "http://stardewvalleywiki.com/Modding:SMAPI_2.0" ], - "Notes": "Broke in SMAPI 2.0." + "Compatibility": { + "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 + } }, { "Name": "Lookup Anything", "ID": "LookupAnything | Pathoschild.LookupAnything", // changed in 1.10.1 - "UpperVersion": "1.10.1", "UpdateUrls": [ "http://www.nexusmods.com/stardewvalley/mods/541" ], - |
