diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-02-16 18:50:46 -0500 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-02-16 18:50:46 -0500 |
commit | 024489c33827ce8e1463eac199daa996a8a99216 (patch) | |
tree | c0a314f1bf1476656153d929104682faba86577a /src/SMAPI | |
parent | cf383870837748e83b99bf63d36d7a8709743715 (diff) | |
download | SMAPI-024489c33827ce8e1463eac199daa996a8a99216.tar.gz SMAPI-024489c33827ce8e1463eac199daa996a8a99216.tar.bz2 SMAPI-024489c33827ce8e1463eac199daa996a8a99216.zip |
overhaul internal mod data format (#439)
The new format is much more concise, reduces the memory footprint by only parsing metadata for loaded mods, and adds support for versioning and defaulting most fields.
Diffstat (limited to 'src/SMAPI')
-rw-r--r-- | src/SMAPI/Framework/IModMetadata.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModLoading/ModMetadata.cs | 4 | ||||
-rw-r--r-- | src/SMAPI/Framework/ModLoading/ModResolver.cs | 24 | ||||
-rw-r--r-- | src/SMAPI/Framework/Models/ModCompatibility.cs | 55 | ||||
-rw-r--r-- | src/SMAPI/Framework/Models/ModDataField.cs | 82 | ||||
-rw-r--r-- | src/SMAPI/Framework/Models/ModDataFieldKey.cs | 18 | ||||
-rw-r--r-- | src/SMAPI/Framework/Models/ModDataID.cs | 85 | ||||
-rw-r--r-- | src/SMAPI/Framework/Models/ModDataRecord.cs | 220 | ||||
-rw-r--r-- | src/SMAPI/Framework/Models/ParsedModDataRecord.cs | 48 | ||||
-rw-r--r-- | src/SMAPI/Framework/Serialisation/SmapiConverters/ModCompatibilityArrayConverter.cs | 61 | ||||
-rw-r--r-- | src/SMAPI/Framework/Serialisation/SmapiConverters/ModDataIdConverter.cs | 19 | ||||
-rw-r--r-- | src/SMAPI/Program.cs | 2 | ||||
-rw-r--r-- | src/SMAPI/StardewModdingAPI.config.json | 1731 | ||||
-rw-r--r-- | src/SMAPI/StardewModdingAPI.csproj | 7 |
14 files changed, 1095 insertions, 1263 deletions
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs index a36994fd..41484567 100644 --- a/src/SMAPI/Framework/IModMetadata.cs +++ b/src/SMAPI/Framework/IModMetadata.cs @@ -19,7 +19,7 @@ namespace StardewModdingAPI.Framework IManifest Manifest { get; } /// <summary>>Metadata about the mod from SMAPI's internal data (if any).</summary> - ModDataRecord DataRecord { get; } + ParsedModDataRecord DataRecord { get; } /// <summary>The metadata resolution status.</summary> ModMetadataStatus Status { get; } diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index 30fe211b..1a71920e 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -18,7 +18,7 @@ namespace StardewModdingAPI.Framework.ModLoading public IManifest Manifest { get; } /// <summary>Metadata about the mod from SMAPI's internal data (if any).</summary> - public ModDataRecord DataRecord { get; } + public ParsedModDataRecord DataRecord { get; } /// <summary>The metadata resolution status.</summary> public ModMetadataStatus Status { get; private set; } @@ -41,7 +41,7 @@ namespace StardewModdingAPI.Framework.ModLoading /// <param name="directoryPath">The mod's full directory path.</param> /// <param name="manifest">The mod manifest.</param> /// <param name="dataRecord">Metadata about the mod from SMAPI's internal data (if any).</param> - public ModMetadata(string displayName, string directoryPath, IManifest manifest, ModDataRecord dataRecord) + public ModMetadata(string displayName, string directoryPath, IManifest manifest, ParsedModDataRecord dataRecord) { this.DisplayName = displayName; this.DirectoryPath = directoryPath; diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 9802d9e9..6671e880 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -53,18 +53,15 @@ namespace StardewModdingAPI.Framework.ModLoading error = $"parsing its manifest failed:\n{ex.GetLogSummary()}"; } - // get internal data record (if any) - ModDataRecord dataRecord = null; + // parse internal data record (if any) + ParsedModDataRecord dataRecord = null; if (manifest != null) { - string key = !string.IsNullOrWhiteSpace(manifest.UniqueID) ? manifest.UniqueID : manifest.EntryDll; - dataRecord = dataRecords.FirstOrDefault(record => record.ID.Matches(key, manifest)); + ModDataRecord rawDataRecord = dataRecords.FirstOrDefault(p => p.Matches(manifest)); + if (rawDataRecord != null) + dataRecord = rawDataRecord.ParseFieldsFor(manifest); } - // add default update keys - if (manifest != null && manifest.UpdateKeys == null && dataRecord?.UpdateKeys != null) - manifest.UpdateKeys = dataRecord.UpdateKeys; - // build metadata string displayName = !string.IsNullOrWhiteSpace(manifest?.Name) ? manifest.Name @@ -93,17 +90,16 @@ namespace StardewModdingAPI.Framework.ModLoading continue; // validate compatibility - ModCompatibility compatibility = mod.DataRecord?.GetCompatibility(mod.Manifest.Version); - switch (compatibility?.Status) + switch (mod.DataRecord?.Status) { case ModStatus.Obsolete: - mod.SetStatus(ModMetadataStatus.Failed, $"it's obsolete: {compatibility.ReasonPhrase}"); + mod.SetStatus(ModMetadataStatus.Failed, $"it's obsolete: {mod.DataRecord.StatusReasonPhrase}"); continue; case ModStatus.AssumeBroken: { // get reason - string reasonPhrase = compatibility.ReasonPhrase ?? "it's no longer compatible"; + string reasonPhrase = mod.DataRecord.StatusReasonPhrase ?? "it's no longer compatible"; // get update URLs List<string> updateUrls = new List<string>(); @@ -124,10 +120,10 @@ namespace StardewModdingAPI.Framework.ModLoading // build error string error = $"{reasonPhrase}. Please check for a "; - if (mod.Manifest.Version.Equals(compatibility.UpperVersion)) + if (mod.DataRecord.StatusUpperVersion == null || mod.Manifest.Version.Equals(mod.DataRecord.StatusUpperVersion)) error += "newer version"; else - error += $"version newer than {compatibility.UpperVersion}"; + error += $"version newer than {mod.DataRecord.StatusUpperVersion}"; error += " at " + string.Join(" or ", updateUrls); mod.SetStatus(ModMetadataStatus.Failed, error); diff --git a/src/SMAPI/Framework/Models/ModCompatibility.cs b/src/SMAPI/Framework/Models/ModCompatibility.cs deleted file mode 100644 index 54737e6c..00000000 --- a/src/SMAPI/Framework/Models/ModCompatibility.cs +++ /dev/null @@ -1,55 +0,0 @@ -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/SMAPI/Framework/Models/ModDataField.cs b/src/SMAPI/Framework/Models/ModDataField.cs new file mode 100644 index 00000000..0812b39b --- /dev/null +++ b/src/SMAPI/Framework/Models/ModDataField.cs @@ -0,0 +1,82 @@ +using System.Linq; + +namespace StardewModdingAPI.Framework.Models +{ + /// <summary>A versioned mod metadata field.</summary> + internal class ModDataField + { + /********* + ** Accessors + *********/ + /// <summary>The field key.</summary> + public ModDataFieldKey Key { get; } + + /// <summary>The field value.</summary> + public string Value { get; } + + /// <summary>Whether this field should only be applied if it's not already set.</summary> + public bool IsDefault { get; } + + /// <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; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="key">The field key.</param> + /// <param name="value">The field value.</param> + /// <param name="isDefault">Whether this field should only be applied if it's not already set.</param> + /// <param name="lowerVersion">The lowest version in the range, or <c>null</c> for all past versions.</param> + /// <param name="upperVersion">The highest version in the range, or <c>null</c> for all future versions.</param> + public ModDataField(ModDataFieldKey key, string value, bool isDefault, ISemanticVersion lowerVersion, ISemanticVersion upperVersion) + { + this.Key = key; + this.Value = value; + this.IsDefault = isDefault; + this.LowerVersion = lowerVersion; + this.UpperVersion = upperVersion; + } + + /// <summary>Get whether this data field applies for the given manifest.</summary> + /// <param name="manifest">The mod manifest.</param> + public bool IsMatch(IManifest manifest) + { + return + manifest?.Version != null // ignore invalid manifest + && (!this.IsDefault || !this.HasFieldValue(manifest, this.Key)) + && (this.LowerVersion == null || !manifest.Version.IsOlderThan(this.LowerVersion)) + && (this.UpperVersion == null || !manifest.Version.IsNewerThan(this.UpperVersion)); + } + + + /********* + ** Private methods + *********/ + /// <summary>Get whether a manifest field has a meaningful value for the purposes of enforcing <see cref="IsDefault"/>.</summary> + /// <param name="manifest">The mod manifest.</param> + /// <param name="key">The field key matching <see cref="ModDataFieldKey"/>.</param> + private bool HasFieldValue(IManifest manifest, ModDataFieldKey key) + { + switch (key) + { + // update key + case ModDataFieldKey.UpdateKey: + return manifest.UpdateKeys != null && manifest.UpdateKeys.Any(); + + // non-manifest fields + case ModDataFieldKey.AlternativeUrl: + case ModDataFieldKey.StatusReasonPhrase: + case ModDataFieldKey.Status: + return false; + + default: + return false; + } + } + } +} diff --git a/src/SMAPI/Framework/Models/ModDataFieldKey.cs b/src/SMAPI/Framework/Models/ModDataFieldKey.cs new file mode 100644 index 00000000..5767afc9 --- /dev/null +++ b/src/SMAPI/Framework/Models/ModDataFieldKey.cs @@ -0,0 +1,18 @@ +namespace StardewModdingAPI.Framework.Models +{ + /// <summary>The valid field keys.</summary> + public enum ModDataFieldKey + { + /// <summary>A manifest update key.</summary> + UpdateKey, + + /// <summary>An alternative URL the player can check for an updated version.</summary> + AlternativeUrl, + + /// <summary>The mod's predefined compatibility status.</summary> + Status, + + /// <summary>A reason phrase for the <see cref="Status"/>, or <c>null</c> to use the default reason.</summary> + StatusReasonPhrase + } +} diff --git a/src/SMAPI/Framework/Models/ModDataID.cs b/src/SMAPI/Framework/Models/ModDataID.cs deleted file mode 100644 index d19434fa..00000000 --- a/src/SMAPI/Framework/Models/ModDataID.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Linq; -using Newtonsoft.Json; - -namespace StardewModdingAPI.Framework.Models -{ - /// <summary>Uniquely identifies a mod in SMAPI's internal data.</summary> - /// <remarks> - /// This represents a custom format which uniquely identifies a mod across all versions, even - /// if its field values change or it doesn't specify a unique ID. This is mapped to a string - /// with the following format: - /// - /// 1. If the mod's identifier changed over time, multiple variants can be separated by the <c>|</c> - /// character. - /// 2. Each variant can take one of two forms: - /// - A simple string matching the mod's UniqueID value. - /// - A JSON structure containing any of three manifest fields (ID, Name, and Author) to match. - /// </remarks> - internal class ModDataID - { - /********* - ** Properties - *********/ - /// <summary>The unique sets of field values which identify this mod.</summary> - private readonly FieldSnapshot[] Snapshots; - - - /********* - ** Public methods - *********/ - /// <summary>Construct an instance.</summary> - public ModDataID() { } - - /// <summary>Construct an instance.</summary> - /// <param name="data">The mod identifier string (see remarks on <see cref="ModDataID"/>).</param> - public ModDataID(string data) - { - this.Snapshots = - ( - from string part in data.Split('|') - let str = part.Trim() - select str.StartsWith("{") - ? JsonConvert.DeserializeObject<FieldSnapshot>(str) - : new FieldSnapshot { ID = str } - ) - .ToArray(); - } - - /// <summary>Get whether this ID matches a given mod manifest.</summary> - /// <param name="id">The mod's unique ID, or a substitute ID if it isn't set in the manifest.</param> - /// <param name="manifest">The manifest to check.</param> - public bool Matches(string id, IManifest manifest) - { - return this.Snapshots.Any(snapshot => - snapshot.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase) - && ( - snapshot.Author == null - || snapshot.Author.Equals(manifest.Author, StringComparison.InvariantCultureIgnoreCase) - || (manifest.ExtraFields.ContainsKey("Authour") && snapshot.Author.Equals(manifest.ExtraFields["Authour"].ToString(), StringComparison.InvariantCultureIgnoreCase)) - ) - && (snapshot.Name == null || snapshot.Name.Equals(manifest.Name, StringComparison.InvariantCultureIgnoreCase)) - ); - } - - - /********* - ** Private models - *********/ - /// <summary>A unique set of fields which identifies the mod.</summary> - private class FieldSnapshot - { - /********* - ** Accessors - *********/ - /// <summary>The unique mod ID.</summary> - public string ID { get; set; } - - /// <summary>The mod name, or <c>null</c> to ignore the mod name.</summary> - public string Name { get; set; } - - /// <summary>The author name, or <c>null</c> to ignore the author.</summary> - public string Author { get; set; } - } - } -} diff --git a/src/SMAPI/Framework/Models/ModDataRecord.cs b/src/SMAPI/Framework/Models/ModDataRecord.cs index 580acb70..2c26741c 100644 --- a/src/SMAPI/Framework/Models/ModDataRecord.cs +++ b/src/SMAPI/Framework/Models/ModDataRecord.cs @@ -1,49 +1,188 @@ +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.Serialization; using Newtonsoft.Json; -using StardewModdingAPI.Framework.Serialisation.SmapiConverters; +using Newtonsoft.Json.Linq; namespace StardewModdingAPI.Framework.Models { - /// <summary>Metadata about a mod from SMAPI's internal data.</summary> + /// <summary>Raw mod metadata from SMAPI's internal mod list.</summary> internal class ModDataRecord { /********* - ** Accessors + ** Properties *********/ - /// <summary>The unique mod identifier.</summary> - [JsonConverter(typeof(ModDataIdConverter))] - public ModDataID ID { get; set; } + /// <summary>This field stores properties that aren't mapped to another field before they're parsed into <see cref="Fields"/>.</summary> + [JsonExtensionData] + private IDictionary<string, JToken> ExtensionData; - /// <summary>A value to inject into <see cref="IManifest.UpdateKeys"/> field if it's not already set.</summary> - public string[] UpdateKeys { get; set; } - /// <summary>The URL where the player can get an unofficial or alternative version of the mod if the official version isn't compatible.</summary> - public string AlternativeUrl { get; set; } + /********* + ** Accessors + *********/ + /// <summary>The mod's current unique ID.</summary> + public string ID { get; set; } - /// <summary>The compatibility of given mod versions (if any).</summary> - [JsonConverter(typeof(ModCompatibilityArrayConverter))] - public ModCompatibility[] Compatibility { get; set; } = new ModCompatibility[0]; + /// <summary>The former mod IDs (if any).</summary> + /// <remarks> + /// This uses a custom format which uniquely identifies a mod across multiple versions and + /// supports matching other fields if no ID was specified. This doesn't include the latest + /// ID, if any. Format rules: + /// 1. If the mod's ID changed over time, multiple variants can be separated by the + /// <c>|</c> character. + /// 2. Each variant can take one of two forms: + /// - A simple string matching the mod's UniqueID value. + /// - A JSON structure containing any of four manifest fields (ID, Name, Author, and + /// EntryDll) to match. + /// </remarks> + public string FormerIDs { get; set; } - /// <summary>Map local versions to a semantic version for update checks.</summary> + /// <summary>Maps local versions to a semantic version for update checks.</summary> public IDictionary<string, string> MapLocalVersions { get; set; } = new Dictionary<string, string>(); - /// <summary>Map remote versions to a semantic version for update checks.</summary> + /// <summary>Maps remote versions to a semantic version for update checks.</summary> public IDictionary<string, string> MapRemoteVersions { get; set; } = new Dictionary<string, string>(); + /// <summary>The versioned field data.</summary> + /// <remarks> + /// This maps field names to values. This should be accessed via <see cref="GetFields"/>. + /// Format notes: + /// - Each key consists of a field name prefixed with any combination of version range + /// and <c>Default</c>, separated by pipes (whitespace trimmed). For example, <c>Name</c> + /// will always override the name, <c>Default | Name</c> will only override a blank + /// name, and <c>~1.1 | Default | Name</c> will override blank names up to version 1.1. + /// - The version format is <c>min~max</c> (where either side can be blank for unbounded), or + /// a single version number. + /// - The field name itself corresponds to a <see cref="ModDataFieldKey"/> value. + /// </remarks> + public IDictionary<string, string> Fields { get; set; } = new Dictionary<string, string>(); + /********* ** 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) + /// <summary>Get whether the manifest matches the <see cref="FormerIDs"/> field.</summary> + /// <param name="manifest">The mod manifest to check.</param> + public bool Matches(IManifest manifest) + { + // try main ID + if (this.ID != null && this.ID.Equals(manifest.UniqueID, StringComparison.InvariantCultureIgnoreCase)) + return true; + + // try former IDs + if (this.FormerIDs != null) + { + foreach (string part in this.FormerIDs.Split('|')) + { + // packed field snapshot + if (part.StartsWith("{")) + { + FieldSnapshot snapshot = JsonConvert.DeserializeObject<FieldSnapshot>(part); + bool isMatch = + (snapshot.ID == null || snapshot.ID.Equals(manifest.UniqueID, StringComparison.InvariantCultureIgnoreCase)) + && (snapshot.EntryDll == null || snapshot.EntryDll.Equals(manifest.EntryDll, StringComparison.InvariantCultureIgnoreCase)) + && ( + snapshot.Author == null + || snapshot.Author.Equals(manifest.Author, StringComparison.InvariantCultureIgnoreCase) + || (manifest.ExtraFields.ContainsKey("Authour") && snapshot.Author.Equals(manifest.ExtraFields["Authour"].ToString(), StringComparison.InvariantCultureIgnoreCase)) + ) + && (snapshot.Name == null || snapshot.Name.Equals(manifest.Name, StringComparison.InvariantCultureIgnoreCase)); + + if (isMatch) + return true; + } + + // plain ID + else if (part.Equals(manifest.UniqueID, StringComparison.InvariantCultureIgnoreCase)) + return true; + } + } + + // no match + return false; + } + + /// <summary>Get a parsed representation of the <see cref="Fields"/>.</summary> + public IEnumerable<ModDataField> GetFields() { - return this.Compatibility.FirstOrDefault(p => p.MatchesVersion(version)); + foreach (KeyValuePair<string, string> pair in this.Fields) + { + // init fields + string packedKey = pair.Key; + string value = pair.Value; + bool isDefault = false; + ISemanticVersion lowerVersion = null; + ISemanticVersion upperVersion = null; + + // parse + string[] parts = packedKey.Split('|').Select(p => p.Trim()).ToArray(); + ModDataFieldKey fieldKey = (ModDataFieldKey)Enum.Parse(typeof(ModDataFieldKey), parts.Last(), ignoreCase: true); + foreach (string part in parts.Take(parts.Length - 1)) + { + // 'default' + if (part.Equals("Default", StringComparison.InvariantCultureIgnoreCase)) + { + isDefault = true; + continue; + } + + // version range + if (part.Contains("~")) + { + string[] versionParts = part.Split(new[] { '~' }, 2); + lowerVersion = versionParts[0] != "" ? new SemanticVersion(versionParts[0]) : null; + upperVersion = versionParts[1] != "" ? new SemanticVersion(versionParts[1]) : null; + continue; + } + + // single version + lowerVersion = new SemanticVersion(part); + upperVersion = new SemanticVersion(part); + } + + yield return new ModDataField(fieldKey, value, isDefault, lowerVersion, upperVersion); + } + } + + /// <summary>Get a parsed representation of the <see cref="Fields"/> which match a given manifest.</summary> + /// <param name="manifest">The manifest to match.</param> + public ParsedModDataRecord ParseFieldsFor(IManifest manifest) + { + ParsedModDataRecord parsed = new ParsedModDataRecord { DataRecord = this }; + foreach (ModDataField field in this.GetFields().Where(field => field.IsMatch(manifest))) + { + switch (field.Key) + { + // update key + case ModDataFieldKey.UpdateKey: + parsed.UpdateKey = field.Value; + break; + + // alternative URL + case ModDataFieldKey.AlternativeUrl: + parsed.AlternativeUrl = field.Value; + break; + + // status + case ModDataFieldKey.Status: + parsed.Status = (ModStatus)Enum.Parse(typeof(ModStatus), field.Value, ignoreCase: true); + parsed.StatusUpperVersion = field.UpperVersion; + break; + + // status reason phrase + case ModDataFieldKey.StatusReasonPhrase: + parsed.StatusReasonPhrase = field.Value; + break; + } + } + + return parsed; } /// <summary>Get a semantic local version for update checks.</summary> - /// <param name="version">The local version to normalise.</param> + /// <param name="version">The remote version to normalise.</param> public string GetLocalVersionForUpdateChecks(string version) { return this.MapLocalVersions != null && this.MapLocalVersions.TryGetValue(version, out string newVersion) @@ -59,5 +198,46 @@ namespace StardewModdingAPI.Framework.Models ? newVersion : version; } + + + /********* + ** Private methods + *********/ + /// <summary>The method invoked after JSON deserialisation.</summary> + /// <param name="context">The deserialisation context.</param> + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + if (this.ExtensionData != null) + { + this.Fields = this.ExtensionData.ToDictionary(p => p.Key, p => p.Value.ToString()); + this.ExtensionData = null; + } + } + + + /********* + ** Private models + *********/ + /// <summary>A unique set of fields which identifies the mod.</summary> + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local", Justification = "Used via JSON deserialisation.")] + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local", Justification = "Used via JSON deserialisation.")] + private class FieldSnapshot + { + /********* + ** Accessors + *********/ + /// <summary>The unique mod ID (or <c>null</c> to ignore it).</summary> + public string ID { get; set; } + + /// <summary>The entry DLL (or <c>null</c> to ignore it).</summary> + public string EntryDll { get; set; } + + /// <summary>The mod name (or <c>null</c> to ignore it).</summary> + public string Name { get; set; } + + /// <summary>The author name (or <c>null</c> to ignore it).</summary> + public string Author { get; set; } + } } } diff --git a/src/SMAPI/Framework/Models/ParsedModDataRecord.cs b/src/SMAPI/Framework/Models/ParsedModDataRecord.cs new file mode 100644 index 00000000..0abc7b89 --- /dev/null +++ b/src/SMAPI/Framework/Models/ParsedModDataRecord.cs @@ -0,0 +1,48 @@ +namespace StardewModdingAPI.Framework.Models +{ + /// <summary>A parsed representation of the fields from a <see cref="ModDataRecord"/> for a specific manifest.</summary> + internal class ParsedModDataRecord + { + /********* + ** Accessors + *********/ + /// <summary>The underlying data record.</summary> + public ModDataRecord DataRecord { get; set; } + + /// <summary>The update key to apply.</summary> + public string UpdateKey { get; set; } + + /// <summary>The mod version to apply.</summary> + public ISemanticVersion Version { get; set; } + + /// <summary>The alternative URL the player can check for an updated version.</summary> + public string AlternativeUrl { get; set; } + + /// <summary>The predefined compatibility status.</summary> + public ModStatus Status { get; set; } = ModStatus.None; + + /// <summary>A reason phrase for the <see cref="Status"/>, or <c>null</c> to use the default reason.</summary> + public string StatusReasonPhrase { get; set; } + + /// <summary>The upper version for which the <see cref="Status"/> applies (if any).</summary> + public ISemanticVersion StatusUpperVersion { get; set; } + + + /********* + ** Public methods + *********/ + /// <summary>Get a semantic local version for update checks.</summary> + /// <param name="version">The remote version to normalise.</param> + public string GetLocalVersionForUpdateChecks(string version) + { + return this.DataRecord.GetLocalVersionForUpdateChecks(version); + } + + /// <summary>Get a semantic remote version for update checks.</summary> + /// <param name="version">The remote version to normalise.</param> + public string GetRemoteVersionForUpdateChecks(string version) + { + return this.DataRecord.GetRemoteVersionForUpdateChecks(version); + } + } +} diff --git a/src/SMAPI/Framework/Serialisation/SmapiConverters/ModCompatibilityArrayConverter.cs b/src/SMAPI/Framework/Serialisation/SmapiConverters/ModCompatibilityArrayConverter.cs deleted file mode 100644 index 3232dde4..00000000 --- a/src/SMAPI/Framework/Serialisation/SmapiConverters/ModCompatibilityArrayConverter.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Models; - -namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters -{ - /// <summary>Handles deserialisation of <see cref="ModCompatibility"/> arrays.</summary> - internal class ModCompatibilityArrayConverter : JsonConverter - { - /********* - ** Accessors - *********/ - /// <summary>Whether this converter can write JSON.</summary> - public override bool CanWrite => false; - - - /********* - ** Public methods - *********/ - /// <summary>Get whether this instance can convert the specified object type.</summary> - /// <param name="objectType">The object type.</param> - public override bool CanConvert(Type objectType) - { - return objectType == typeof(ModCompatibility[]); - } - - - /********* - ** Protected methods - *********/ - /// <summary>Read the JSON representation of the object.</summary> - /// <param name="reader">The JSON reader.</param> - /// <param name="objectType">The object type.</param> - /// <param name="existingValue">The object being read.</param> - /// <param name="serializer">The calling serializer.</param> - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - List<ModCompatibility> result = new List<ModCompatibility>(); - foreach (JProperty property in JObject.Load(reader).Properties()) - { - string range = property.Name; - ModStatus status = (ModStatus)Enum.Parse(typeof(ModStatus), property.Value.Value<string>(nameof(ModCompatibility.Status))); - string reasonPhrase = property.Value.Value<string>(nameof(ModCompatibility.ReasonPhrase)); - - result.Add(new ModCompatibility(range, status, reasonPhrase)); - } - return result.ToArray(); - } - - /// <summary>Writes the JSON representation of the object.</summary> - /// <param name="writer">The JSON writer.</param> - /// <param name="value">The value.</param> - /// <param name="serializer">The calling serializer.</param> - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new InvalidOperationException("This converter does not write JSON."); - } - } -} diff --git a/src/SMAPI/Framework/Serialisation/SmapiConverters/ModDataIdConverter.cs b/src/SMAPI/Framework/Serialisation/SmapiConverters/ModDataIdConverter.cs deleted file mode 100644 index 8a10db47..00000000 --- a/src/SMAPI/Framework/Serialisation/SmapiConverters/ModDataIdConverter.cs +++ /dev/null @@ -1,19 +0,0 @@ -using StardewModdingAPI.Framework.Models; - -namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters -{ - /// <summary>Handles deserialisation of <see cref="ModDataID"/>.</summary> - internal class ModDataIdConverter : SimpleReadOnlyConverter<ModDataID> - { - /********* - ** Protected methods - *********/ - /// <summary>Read a JSON string.</summary> - /// <param name="str">The JSON string value.</param> - /// <param name="path">The path to the current JSON node.</param> - protected override ModDataID ReadString(string str, string path) - { - return new ModDataID(str); - } - } -} diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs index c7da581d..b5ce3033 100644 --- a/src/SMAPI/Program.cs +++ b/src/SMAPI/Program.cs @@ -679,7 +679,7 @@ namespace StardewModdingAPI Assembly modAssembly; try { - modAssembly = modAssemblyLoader.Load(metadata, assemblyPath, assumeCompatible: metadata.DataRecord?.GetCompatibility(metadata.Manifest.Version)?.Status == ModStatus.AssumeCompatible); + modAssembly = modAssemblyLoader.Load(metadata, assemblyPath, assumeCompatible: metadata.DataRecord?.Status == ModStatus.AssumeCompatible); } catch (IncompatibleInstructionException ex) { diff --git a/src/SMAPI/StardewModdingAPI.config.json b/src/SMAPI/StardewModdingAPI.config.json index 18a9f978..ca0c20b1 100644 --- a/src/SMAPI/StardewModdingAPI.config.json +++ b/src/SMAPI/StardewModdingAPI.config.json @@ -39,2041 +39,1770 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "VerboseLogging": false, /** - * Extra metadata about some SMAPI mods. All fields except 'ID' are optional. + * Metadata about some SMAPI mods used in compatibility, update, and dependency checks. This + * field shouldn't be edited by players in most cases. * - * - 'ID' uniquely identifies the mod across all versions, even if its manifest fields changed or - * the mod doesn't have a unique ID. The format is as follows: - * - If the mod's identifier changed over time, multiple variants are separated by |. - * - Each variant can take one of two forms: a simple string matching the mod's UniqueID, - * or a JSON structure containing any of three manifest fields (ID, Name, and Author) to - * match. + * Standard fields + * =============== + * The predefined fields are documented below (only 'ID' is required). * - * - 'UpdateKeys' specifies the value of the equivalent manifest field if it's not already set. - * This is used to enable update checks for older mods that haven't been updated to use it yet. + * - ID: the mod's latest unique ID (if any). * - * - 'AlternativeUrl' specifies a URL where the player can find an unofficial update or - * alternative if the mod is no longer compatible. + * - FormerIDs: uniquely identifies the mod across multiple versions, and supports matching + * other fields if no ID was specified. This doesn't include the latest ID, if any. + * Format rules: + * 1. If the mod's ID changed over time, multiple variants can be separated by the '|' + * character. + * 2. Each variant can take one of two forms: + * - A simple string matching the mod's UniqueID value. + * - A JSON structure containing any of four manifest fields (ID, Name, Author, and + * EntryDll) to match. * - * - 'Compatibility' overrides SMAPI's normal compatibility detection. The keys are version - * ranges in the form lower~upper, where either side can be blank for an unbounded range. (For - * example, "~1.0" means all versions up to 1.0 inclusively.) The values have two fields: - * - 'Status' specifies the compatibility. Valid values are Obsolete (SMAPI won't load it - * because the mod should no longer be used), AssumeBroken (SMAPI won't load it because - * the specified version isn't compatible), or AssumeCompatible (SMAPI will load it even - * if it detects incompatible code). - * - 'ReasonPhrase' (optional) specifies a message to show to the player explaining why the - * mod isn't loaded. This has no effect for AssumeCompatible. + * - MapLocalVersions and MapRemoteVersions crrect local manifest versions and remote versions + * during update checks. For example, if the API returns version '1.1-1078' where '1078' is + * intended to be a build number, MapRemoteVersions can map it to '1.1' when comparing to the + * mod's current version. This is only meant to support legacy mods with injected update keys. * - * - 'MapLocalVersions' and 'MapRemoteVersions' substitute versions for update checks. For - * example, if the API returns version '1.1-1078', MapRemoteVersions can map it to '1.1' when - * comparing to the mod's current version. This is only intended to support legacy mods with - * injected update keys. + * Versioned metadata + * ================== + * Each record can also specify extra metadata using the field keys below. + * + * Each key consists of a field name prefixed with any combination of version range and 'Default', + * separated by pipes (whitespace trimmed). For example, 'UpdateKey' will always override, + * 'Default | UpdateKey' will only override if the mod has no update keys, and + * '~1.1 | Default | Name' will do the same up to version 1.1. + * + * The version format is 'min~max' (where either side can be blank for unbounded), or a single + * version number. + * + * These are the valid field names: + * + * - UpdateKey: the update key to set in the mod's manifest. This is used to enable update + * checks for older mods that haven't been updated to use it yet. + * + * - Status: overrides compatibility checks. The possible values are Obsolete (SMAPI won't load + * it because the mod should no longer be used), AssumeBroken (SMAPI won't load it because + * the specified version isn't compatible), or AssumeCompatible (SMAPI will try to load it + * even if it detects incompatible code). + * + * - StatusReasonPhrase: a message to show to the player explaining why the mod can't be loaded + * (if applicable). If blank, will default to a generic not-compatible message. + * + * - AlternativeUrl: a URL where the player can find an unofficial update or alternative if the + * mod is no longer compatible. */ "ModData": [ { // AccessChestAnywhere "ID": "AccessChestAnywhere", - "UpdateKeys": [ "Nexus:257" ], - "AlternativeUrl": "https://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SDV 1.1 - }, - "MapLocalVersions": { - "1.1-1078": "1.1" - } + "MapLocalVersions": { "1.1-1078": "1.1" }, + "Default | UpdateKey": "Nexus:257", + "~1.1 | Status": "AssumeBroken", + "~1.1 | AlternativeUrl": "https://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // AdjustArtisanPrices "ID": "1e36d4ca-c7ef-4dfb-9927-d27a6c3c8bdc", - "UpdateKeys": [ "Chucklefish:3532" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~0.1": { "Status": "AssumeBroken" } // broke in SMAPI 1.9 - } + "Default | UpdateKey": "Chucklefish:3532", + "~0.1 | Status": "AssumeBroken", + "~0.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Adjust Monster "ID": "mmanlapat.AdjustMonster", - "UpdateKeys": [ "Nexus:1161" ] + "Default | UpdateKey": "Nexus:1161" }, { // Advanced Location Loader "ID": "Entoarox.AdvancedLocationLoader", - //"UpdateKeys": [ "Chucklefish:3619" ], // Entoarox opted out of mod update checks - "Compatibility": { - "~1.2.10": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + //"Default | UpdateKey": "Chucklefish:3619", // Entoarox opted out of mod update checks + "~1.2.10 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Adventure Shop Inventory "ID": "HammurabiAdventureShopInventory", - "UpdateKeys": [ "Chucklefish:4608" ] + "Default | UpdateKey": "Chucklefish:4608" }, { // AgingMod "ID": "skn.AgingMod", - "UpdateKeys": [ "Nexus:1129" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1129", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // All Crops All Seasons - "ID": "29ee8246-d67b-4242-a340-35a9ae0d5dd7 | community.AllCropsAllSeasons", // changed in 1.3 - "UpdateKeys": [ "Nexus:170" ] + "ID": "community.AllCropsAllSeasons", + "FormerIDs": "29ee8246-d67b-4242-a340-35a9ae0d5dd7", // changed in 1.3 + "Default | UpdateKey": "Nexus:170" }, { // All Professions - "ID": "8c37b1a7-4bfb-4916-9d8a-9533e6363ea3 | community.AllProfessions", // changed in 1.2 - "UpdateKeys": [ "Nexus:174" ] + "ID": "community.AllProfessions", + "FormerIDs": "8c37b1a7-4bfb-4916-9d8a-9533e6363ea3", // changed in 1.2 + "Default | UpdateKey": "Nexus:174" }, { // Almighty Tool - "ID": "AlmightyTool.dll | 439", // changed in 1.2.1 - "UpdateKeys": [ "Nexus:439" ], - "Compatibility": { - "~1.1.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - }, - "MapRemoteVersions": { - "1.21": "1.2.1" - } + "ID": "439", + "FormerIDs": "{EntryDLL: 'AlmightyTool.dll'}", // changed in 1.2.1 + "MapRemoteVersions": { "1.21": "1.2.1" }, + "Default | UpdateKey": "Nexus:439", + "~1.1.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Animal Mood Fix "ID": "GPeters-AnimalMoodFix", - "Compatibility": { - "~": { - "Status": "Obsolete", - "ReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." - } - } + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "the animal mood bugs were fixed in Stardew Valley 1.2." }, { // Animal Sitter - "ID": "AnimalSitter.dll | jwdred.AnimalSitter", // changed in 1.0.9 - "UpdateKeys": [ "Nexus:581" ], - "Compatibility": { - "~1.0.8": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "jwdred.AnimalSitter", + "FormerIDs": "{EntryDLL: 'AnimalSitter.dll'}", // changed in 1.0.9 + "Default | UpdateKey": "Nexus:581", + "~1.0.8 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // A Tapper's Dream "ID": "ddde5195-8f85-4061-90cc-0d4fd5459358", - "UpdateKeys": [ "Nexus:260" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:260", + "~1.4 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.4 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Auto Animal Doors "ID": "AaronTaggart.AutoAnimalDoors", - "UpdateKeys": [ "Nexus:1019" ], - "MapRemoteVersions": { - "1.1.1": "1.1" // manifest not updated - } + "MapRemoteVersions": { "1.1.1": "1.1" }, // manifest not updated + "Default | UpdateKey": "Nexus:1019" }, { // Auto-Eat - "ID": "BALANCEMOD_AutoEat | Permamiss.AutoEat", // changed in 1.1.1 - "UpdateKeys": [ "Nexus:643" ] + "ID": "Permamiss.AutoEat", + "FormerIDs": "BALANCEMOD_AutoEat", // changed in 1.1.1 + "Default | UpdateKey": "Nexus:643" }, { // AutoGate "ID": "AutoGate", - "UpdateKeys": [ "Nexus:820" ] + "Default | UpdateKey": "Nexus:820" }, { // Automate "ID": "Pathoschild.Automate", - "UpdateKeys": [ "Nexus:1063" ] + "Default | UpdateKey": "Nexus:1063" }, { // Automated Doors - "ID": "1abcfa07-2cf4-4dc3-a6e9-6068b642112b | azah.automated-doors", // changed in 1.4.1 - "UpdateKeys": [ "GitHub:azah/AutomatedDoors" ], - "MapLocalVersions": { - "1.4.1-1": "1.4.1" - } + "ID": "azah.automated-doors", + "FormerIDs": "1abcfa07-2cf4-4dc3-a6e9-6068b642112b", // changed in 1.4.1 + "MapLocalVersions": { "1.4.1-1": "1.4.1" }, + "Default | UpdateKey": "GitHub:azah/AutomatedDoors" }, { // AutoSpeed - "ID": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'AutoSpeed'} | Omegasis.AutoSpeed", // changed in 1.4; disambiguate from other Alpha_Omegasis mods - "UpdateKeys": [ "Nexus:443" ] // added in 1.4.1 + "ID": "Omegasis.AutoSpeed", + "FormerIDs": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'AutoSpeed'}", // changed in 1.4; disambiguate from other Alpha_Omegasis mods + "Default | UpdateKey": "Nexus:443" // added in 1.4.1 }, { // Basic Sprinkler Improved "ID": "lrsk_sdvm_bsi.0117171308", - "UpdateKeys": [ "Nexus:833" ], - "MapRemoteVersions": { - "1.0.2": "1.0.1-release" // manifest not updated - } + "MapRemoteVersions": { "1.0.2": "1.0.1-release" }, // manifest not updated + "Default | UpdateKey": "Nexus:833" }, { // Better Hay "ID": "cat.betterhay", - "UpdateKeys": [ "Nexus:1430" ] + "Default | UpdateKey": "Nexus:1430" }, { // Better Quality More Seasons "ID": "SB_BQMS", - "UpdateKeys": [ "Nexus:935" ] + "Default | UpdateKey": "Nexus:935" }, { // Better Quarry "ID": "BetterQuarry", - "UpdateKeys": [ "Nexus:771" ] + "Default | UpdateKey": "Nexus:771" }, { // Better Ranching "ID": "BetterRanching", - "UpdateKeys": [ "Nexus:859" ] + "Default | UpdateKey": "Nexus:859" }, { // Better Shipping Box "ID": "Kithio:BetterShippingBox", - "UpdateKeys": [ "Chucklefish:4302" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "1.0.1": "1.0.2" - } + "MapLocalVersions": { "1.0.1": "1.0.2" }, + "Default | UpdateKey": "Chucklefish:4302", + "~1.0.2 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Better Sprinklers - "ID": "SPDSprinklersMod | Speeder.BetterSprinklers", // changed in 2.3 - "UpdateKeys": [ "Nexus:41" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~2.3.1-pathoschild-update": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Speeder.BetterSprinklers", + "FormerIDs": "SPDSprinklersMod", // changed in 2.3 + "Default | UpdateKey": "Nexus:41", + "~2.3.1-pathoschild-update | Status": "AssumeBroken", // broke in SDV 1.2 + "~2.3.1-pathoschild-update | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Billboard Anywhere - "ID": "{ID:'7ad4f6f7-c3de-4729-a40f-7a11d2b2a358', Name:'Billboard Anywhere'} | Omegasis.BillboardAnywhere", // changed in 1.4; disambiguate from other mods by Alpha_Omegasis - "UpdateKeys": [ "Nexus:492" ] // added in 1.4.1 + "ID": "Omegasis.BillboardAnywhere", + "FormerIDs": "{ID:'7ad4f6f7-c3de-4729-a40f-7a11d2b2a358', Name:'Billboard Anywhere'}", // changed in 1.4; disambiguate from other mods by Alpha_Omegasis + "Default | UpdateKey": "Nexus:492" // added in 1.4.1 }, { // Birthday Mail - "ID": "005e02dc-d900-425c-9c68-1ff55c5a295d | KathrynHazuka.BirthdayMail", // changed in 1.2.3-pathoschild-update - "UpdateKeys": [ "Nexus:276" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.2.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "KathrynHazuka.BirthdayMail", + "FormerIDs": "005e02dc-d900-425c-9c68-1ff55c5a295d", // changed in 1.2.3-pathoschild-update + "Default | UpdateKey": "Nexus:276", + "~1.2.2 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.2.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Breed Like Rabbits "ID": "dycedarger.breedlikerabbits", - "UpdateKeys": [ "Nexus:948" ] + "Default | UpdateKey": "Nexus:948" }, { // Build Endurance - "ID": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'BuildEndurance'} | Omegasis.BuildEndurance", // changed in 1.4; disambiguate from other Alpha_Omegasis mods - "UpdateKeys": [ "Nexus:445" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.BuildEndurance", + "FormerIDs": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'BuildEndurance'}", // changed in 1.4; disambiguate from other Alpha_Omegasis mods + "Default | UpdateKey": "Nexus:445", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Build Health - "ID": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'BuildHealth'} | Omegasis.BuildHealth", // changed in 1.4; disambiguate from other Alpha_Omegasis mods - "UpdateKeys": [ "Nexus:446" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.BuildHealth", + "FormerIDs": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'BuildHealth'}", // changed in 1.4; disambiguate from other Alpha_Omegasis mods + "Default | UpdateKey": "Nexus:446", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Butcher Mod "ID": "DIGUS.BUTCHER", - "UpdateKeys": [ "Nexus:1538" ] + "Default | UpdateKey": "Nexus:1538" }, { // Buy Cooking Recipes "ID": "Denifia.BuyRecipes", - "UpdateKeys": [ "Nexus:1126" ], // added in 1.0.1 (2017-10-04) - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1126", // added in 1.0.1 (2017-10-04) + "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Buy Back Collectables - "ID": "BuyBackCollectables | Omegasis.BuyBackCollectables", // changed in 1.4 - "UpdateKeys": [ "Nexus:507" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.BuyBackCollectables", + "FormerIDs": "BuyBackCollectables", // changed in 1.4 + "Default | UpdateKey": "Nexus:507", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Carry Chest "ID": "spacechase0.CarryChest", - "UpdateKeys": [ "Nexus:1333" ] + "Default | UpdateKey": "Nexus:1333" }, { // Casks Anywhere "ID": "CasksAnywhere", - "UpdateKeys": [ "Nexus:878" ], - "MapLocalVersions": { - "1.1-alpha": "1.1" - } + "MapLocalVersions": { "1.1-alpha": "1.1" }, + "Default | UpdateKey": "Nexus:878" }, { // Categorize Chests "ID": "CategorizeChests", - "UpdateKeys": [ "Nexus:1300" ] + "Default | UpdateKey": "Nexus:1300" }, { // ChefsCloset "ID": "Duder.ChefsCloset", - "UpdateKeys": [ "Nexus:1030" ], - "MapLocalVersions": { - "1.3-1": "1.3" - } + "MapLocalVersions": { "1.3-1": "1.3" }, + "Default | UpdateKey": "Nexus:1030" }, { // Chest Label System - "ID": "SPDChestLabel | Speeder.ChestLabel", // changed in 1.5.1-pathoschild-update - "UpdateKeys": [ "Nexus:242" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.1 - } + "ID": "Speeder.ChestLabel", + "FormerIDs": "SPDChestLabel", // changed in 1.5.1-pathoschild-update + "Default | UpdateKey": "Nexus:242", + "~1.6 | Status": "AssumeBroken", // broke in SDV 1.1 + "~1.6 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Chest Pooling - "ID": "ChestPooling.dll | mralbobo.ChestPooling", // changed in 1.3 - "UpdateKeys": [ "GitHub:mralbobo/stardew-chest-pooling" ], - "Compatibility": { - "~1.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "mralbobo.ChestPooling", + "FormerIDs": "{EntryDLL: 'ChestPooling.dll'}", // changed in 1.3 + "Default | UpdateKey": "GitHub:mralbobo/stardew-chest-pooling", + "~1.2 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Chests Anywhere - "ID": "ChestsAnywhere | Pathoschild.ChestsAnywhere", // changed in 1.9 - "UpdateKeys": [ "Nexus:518" ], - "Compatibility": { - "~1.9-beta": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Pathoschild.ChestsAnywhere", + "FormerIDs": "ChestsAnywhere", // changed in 1.9 + "Default | UpdateKey": "Nexus:518", + "~1.9-beta | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Choose Baby Gender "ID": "ChooseBabyGender.dll", - "UpdateKeys": [ "Nexus:590" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:590", + "~1.0.2 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // CJB Automation "ID": "CJBAutomation", - "UpdateKeys": [ "Nexus:211" ], - "AlternativeUrl": "http://www.nexusmods.com/stardewvalley/mods/1063", - "Compatibility": { - "~1.4": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "Default | UpdateKey": "Nexus:211", + "~1.4 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.4 | AlternativeUrl": "http://www.nexusmods.com/stardewvalley/mods/1063" }, { // CJB Cheats Menu - "ID": "CJBCheatsMenu | CJBok.CheatsMenu", // changed in 1.14 - "UpdateKeys": [ "Nexus:4" ], - "Compatibility": { - "~1.12": { "Status": "AssumeBroken" } // broke in SDV 1.1 - } + "ID": "CJBok.CheatsMenu", + "FormerIDs": "CJBCheatsMenu", // changed in 1.14 + "Default | UpdateKey": "Nexus:4", + "~1.12 | Status": "AssumeBroken" // broke in SDV 1.1 }, { // CJB Item Spawner - "ID": "CJBItemSpawner | CJBok.ItemSpawner", // changed in 1.7 - "UpdateKeys": [ "Nexus:93" ], - "Compatibility": { - "~1.5": { "Status": "AssumeBroken" } // broke in SDV 1.1 - } + "ID": "CJBok.ItemSpawner", + "FormerIDs": "CJBItemSpawner", // changed in 1.7 + "Default | UpdateKey": "Nexus:93", + "~1.5 | Status": "AssumeBroken" // broke in SDV 1.1 }, { // CJB Show Item Sell Price - "ID": "CJBShowItemSellPrice | CJBok.ShowItemSellPrice", // changed in 1.7 - "UpdateKeys": [ "Nexus:5" ], - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "CJBok.ShowItemSellPrice", + "FormerIDs": "CJBShowItemSellPrice", // changed in 1.7 + "Default | UpdateKey": "Nexus:5", + "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Clean Farm "ID": "tstaples.CleanFarm", - "UpdateKeys": [ "Nexus:794" ] + "Default | UpdateKey": "Nexus:794" }, { // Climates of Ferngill "ID": "KoihimeNakamura.ClimatesOfFerngill", - "UpdateKeys": [ "Nexus:604" ], - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:604", + "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Cold Weather Haley "ID": "LordXamon.ColdWeatherHaleyPRO", - "UpdateKeys": [ "Nexus:1169" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1169", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Colored Chests "ID": "4befde5c-731c-4853-8e4b-c5cdf946805f", - "Compatibility": { - "~": { - "Status": "Obsolete", - "ReasonPhrase": "colored chests were added in Stardew Valley 1.1." - } - } + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "colored chests were added in Stardew Valley 1.1." }, { // Combat with Farm Implements "ID": "SPDFarmingImplementsInCombat", - "UpdateKeys": [ "Nexus:313" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:313", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Community Bundle Item Tooltip "ID": "musbah.bundleTooltip", - "UpdateKeys": [ "Nexus:1329" ] + "Default | UpdateKey": "Nexus:1329" }, { // Concentration on Farming "ID": "punyo.ConcentrationOnFarming", - "UpdateKeys": [ "Nexus:1445" ] + "Default | UpdateKey": "Nexus:1445" }, { // Configurable Machines "ID": "21da6619-dc03-4660-9794-8e5b498f5b97", - "UpdateKeys": [ "Nexus:280" ], - "MapLocalVersions": { - "1.2-beta": "1.2" - } + "MapLocalVersions": { "1.2-beta": "1.2" }, + "Default | UpdateKey": "Nexus:280" }, { // Configurable Shipping Dates "ID": "ConfigurableShippingDates", - "UpdateKeys": [ "Nexus:675" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:675", + "~1.1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Cooking Skill - "ID": "CookingSkill | spacechase0.CookingSkill", // changed in 1.0.4–6 - "UpdateKeys": [ "Nexus:522" ], - "Compatibility": { - "~1.0.6": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "spacechase0.CookingSkill", + "FormerIDs": "CookingSkill", // changed in 1.0.4–6 + "Default | UpdateKey": "Nexus:522", + "~1.0.6 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // CrabNet - "ID": "CrabNet.dll | jwdred.CrabNet", // changed in 1.0.5 - "UpdateKeys": [ "Nexus:584" ], - "Compatibility": { - "~1.0.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "jwdred.CrabNet", + "FormerIDs": "{EntryDLL: 'CrabNet.dll'}", // changed in 1.0.5 + "Default | UpdateKey": "Nexus:584", + "~1.0.4 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Current Location "ID": "CurrentLocation102120161203", - "UpdateKeys": [ "Nexus:638" ] + "Default | UpdateKey": "Nexus:638" }, { // Custom Critters "ID": "spacechase0.CustomCritters", - "UpdateKeys": [ "Nexus:1255" ] + "Default | UpdateKey": "Nexus:1255" }, { // Custom Element Handler "ID": "Platonymous.CustomElementHandler", - "UpdateKeys": [ "Nexus:1068" ] // added in 1.3.1 + "Default | UpdateKey": "Nexus:1068" // added in 1.3.1 }, { // Custom Farming "ID": "Platonymous.CustomFarming", - "UpdateKeys": [ "Nexus:991" ] // added in 0.6.1 + "Default | UpdateKey": "Nexus:991" // added in 0.6.1 }, { // Custom Farming Automate Bridge "ID": "Platonymous.CFAutomate", - "Compatibility": { - "~1.0.1": { "Status": "AssumeBroken" } // no longer compatible with Automate - }, - "AlternativeUrl": "https://www.nexusmods.com/stardewvalley/mods/991" + "~1.0.1 | Status": "AssumeBroken", // no longer compatible with Automate + "~1.0.1 | AlternativeUrl": "https://www.nexusmods.com/stardewvalley/mods/991" }, { // Custom Farm Types "ID": "spacechase0.CustomFarmTypes", - "UpdateKeys": [ "Nexus:1140" ] + "Default | UpdateKey": "Nexus:1140" }, { // Custom Furniture "ID": "Platonymous.CustomFurniture", - "UpdateKeys": [ "Nexus:1254" ] // added in 0.4.1 + "Default | UpdateKey": "Nexus:1254" // added in 0.4.1 }, { // Customize Exterior - "ID": "CustomizeExterior | spacechase0.CustomizeExterior", // changed in 1.0.3 - "UpdateKeys": [ "Nexus:1099" ], - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "spacechase0.CustomizeExterior", + "FormerIDs": "CustomizeExterior", // changed in 1.0.3 + "Default | UpdateKey": "Nexus:1099", + "~1.0.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Customizable Cart Redux "ID": "KoihimeNakamura.CCR", - "UpdateKeys": [ "Nexus:1402" ], - "MapLocalVersions": { - "1.1-20170917": "1.1" - } + "MapLocalVersions": { "1.1-20170917": "1.1" }, + "Default | UpdateKey": "Nexus:1402" }, { // Customizable Traveling Cart Days "ID": "TravelingCartYyeahdude", - "UpdateKeys": [ "Nexus:567" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:567", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Custom Linens "ID": "Mevima.CustomLinens", - "UpdateKeys": [ "Nexus:1027" ], - "MapRemoteVersions": { - "1.1": "1.0" // manifest not updated - } + "MapRemoteVersions": { "1.1": "1.0" }, // manifest not updated + "Default | UpdateKey": "Nexus:1027" }, { // Custom Shops Redux "ID": "Omegasis.CustomShopReduxGui", - "UpdateKeys": [ "Nexus:1378" ] // added in 1.4.1 + "Default | UpdateKey": "Nexus:1378" // added in 1.4.1 }, { // Custom TV "ID": "Platonymous.CustomTV", - "UpdateKeys": [ "Nexus:1139" ] // added in 1.0.6 + "Default | UpdateKey": "Nexus:1139" // added in 1.0.6 }, { // Daily Luck Message "ID": "Schematix.DailyLuckMessage", - "UpdateKeys": [ "Nexus:1327" ] + "Default | UpdateKey": "Nexus:1327" }, { // Daily News "ID": "bashNinja.DailyNews", - "UpdateKeys": [ "Nexus:1141" ], - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1141", + "~1.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Daily Quest Anywhere - "ID": "DailyQuest | Omegasis.DailyQuestAnywhere", // changed in 1.4 - "UpdateKeys": [ "Nexus:513" ] // added in 1.4.1 + "ID": "Omegasis.DailyQuestAnywhere", + "FormerIDs": "DailyQuest", // changed in 1.4 + "Default | UpdateKey": "Nexus:513" // added in 1.4.1 }, { // Debug Mode - "ID": "Pathoschild.Stardew.DebugMode | Pathoschild.DebugMode", // changed in 1.4 - "UpdateKeys": [ "Nexus:679" ] + "ID": "Pathoschild.DebugMode", + "FormerIDs": "Pathoschild.Stardew.DebugMode", // changed in 1.4 + "Default | UpdateKey": "Nexus:679" }, { // Dynamic Checklist "ID": "gunnargolf.DynamicChecklist", - "UpdateKeys": [ "Nexus:1145" ], // added in 1.0.1-pathoschild-update - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1145", // added in 1.0.1-pathoschild-update + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Dynamic Horses "ID": "Bpendragon-DynamicHorses", - "UpdateKeys": [ "Nexus:874" ], - "MapRemoteVersions": { - "1.2": "1.1-release" // manifest not updated - } + "MapRemoteVersions": { "1.2": "1.1-release" }, // manifest not updated + "Default | UpdateKey": "Nexus:874" }, { // Dynamic Machines "ID": "DynamicMachines", - "UpdateKeys": [ "Nexus:374" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "1.1": "1.1.1" - } + "MapLocalVersions": { "1.1": "1.1.1" }, + "Default | UpdateKey": "Nexus:374", + "~1.1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Dynamic NPC Sprites "ID": "BashNinja.DynamicNPCSprites", - "UpdateKeys": [ "Nexus:1183" ] + "Default | UpdateKey": "Nexus:1183" }, { // Easier Farming "ID": "cautiouswafffle.EasierFarming", - "UpdateKeys": [ "Nexus:1426" ] + "Default | UpdateKey": "Nexus:1426" }, { // Empty Hands "ID": "QuicksilverFox.EmptyHands", - "UpdateKeys": [ "Nexus:1176" ], // added in 1.0.1-pathoschild-update - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1176", // added in 1.0.1-pathoschild-update + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Enemy Health Bars - "ID": "SPDHealthBar | Speeder.HealthBars", // changed in 1.7.1-pathoschild-update - "UpdateKeys": [ "Nexus:193" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.7": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Speeder.HealthBars", + "FormerIDs": "SPDHealthBar", // changed in 1.7.1-pathoschild-update + "Default | UpdateKey": "Nexus:193", + "~1.7 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.7 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Entoarox Framework - "ID": "eacdb74b-4080-4452-b16b-93773cda5cf9 | Entoarox.EntoaroxFramework", // changed in ??? - //"UpdateKeys": [ "Chucklefish:4228" ], // Entoarox opted out of mod update checks - "Compatibility": { - "~1.7.9": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Entoarox.EntoaroxFramework", + "FormerIDs": "eacdb74b-4080-4452-b16b-93773cda5cf9", // changed in ??? + //"Default | UpdateKey": "Chucklefish:4228", // Entoarox opted out of mod update checks + "~1.7.9 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Expanded Fridge / Dynamic Expanded Fridge "ID": "Uwazouri.ExpandedFridge", - "UpdateKeys": [ "Nexus:1191" ] + "Default | UpdateKey": "Nexus:1191" }, { // Experience Bars - "ID": "ExperienceBars | spacechase0.ExperienceBars", // changed in 1.0.2 - "UpdateKeys": [ "Nexus:509" ] + "ID": "spacechase0.ExperienceBars", + "FormerIDs": "ExperienceBars", // changed in 1.0.2 + "Default | UpdateKey": "Nexus:509" }, { // Extended Bus System "ID": "ExtendedBusSystem", - "UpdateKeys": [ "Chucklefish:4373" ] + "Default | UpdateKey": "Chucklefish:4373" }, { // Extended Fridge - "ID": "Mystra007ExtendedFridge | Crystalmir.ExtendedFridge", // changed in 1.0.1 - "UpdateKeys": [ "Nexus:485" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Crystalmir.ExtendedFridge", + "FormerIDs": "Mystra007ExtendedFridge", // changed in 1.0.1 + "Default | UpdateKey": "Nexus:485", + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Extended Greenhouse "ID": "ExtendedGreenhouse", - "UpdateKeys": [ "Chucklefish:4303" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "Default | UpdateKey": "Chucklefish:4303", + "~1.0.2 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Extended Minecart - "ID": "{ID:'EntoaroxFurnitureAnywhere', Name:'Extended Minecart'} | Entoarox.ExtendedMinecart" // changed in 1.6.1 - //"UpdateKeys": [ "Chucklefish:4359" ] // Entoarox opted out of mod update checks + "ID": "Entoarox.ExtendedMinecart", + "FormerIDs": "{ID:'EntoaroxFurnitureAnywhere', Name:'Extended Minecart'}" // changed in 1.6.1 + //"Default | UpdateKey": "Chucklefish:4359" // Entoarox opted out of mod update checks }, { // Extended Reach "ID": "spacechase0.ExtendedReach", - "UpdateKeys": [ "Nexus:1493" ] + "Default | UpdateKey": "Nexus:1493" }, { // Fall 28 Snow Day - "ID": "{ID:'7ad4f6f7-c3de-4729-a40f-7a11d2b2a358', Name:'Fall28 Snow Day'} | Omegasis.Fall28SnowDay", // changed in 1.4; disambiguate from other mods by Alpha_Omegasis - "UpdateKeys": [ "Nexus:486" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.Fall28SnowDay", + "FormerIDs": "{ID:'7ad4f6f7-c3de-4729-a40f-7a11d2b2a358', Name:'Fall28 Snow Day'}", // changed in 1.4; disambiguate from other mods by Alpha_Omegasis + "Default | UpdateKey": "Nexus:486", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Farm Automation: Barn Door Automation "ID": "FarmAutomation.BarnDoorAutomation.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Farm Automation: Item Collector "ID": "FarmAutomation.ItemCollector.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Farm Automation Unofficial: Item Collector "ID": "Maddy99.FarmAutomation.ItemCollector", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~0.5": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "~0.5 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~0.5 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Farm Expansion - "ID": "3888bdfd-73f6-4776-8bb7-8ad45aea1915 | AdvizeFarmExpansionMod-2-0 | AdvizeFarmExpansionMod-2-0-5 | Advize.FarmExpansion", // changed in 2.0, 2.0.5, and 3.0 - "UpdateKeys": [ "Nexus:130" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~2.0.5": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Advize.FarmExpansion", + "FormerIDs": "3888bdfd-73f6-4776-8bb7-8ad45aea1915 | AdvizeFarmExpansionMod-2-0 | AdvizeFarmExpansionMod-2-0-5", // changed in 2.0, 2.0.5, and 3.0 + "Default | UpdateKey": "Nexus:130", + "~2.0.5 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~2.0.5 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Farm Resource Generator "ID": "FarmResourceGenerator.dll", - "UpdateKeys": [ "Nexus:647" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:647", + "~1.0.4 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.4 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Fast Animations "ID": "Pathoschild.FastAnimations", - "UpdateKeys": [ "Nexus:1089" ] + "Default | UpdateKey": "Nexus:1089" }, { // Faster Paths - "ID": "{ID:'821ce8f6-e629-41ad-9fde-03b54f68b0b6', Name:'Faster Paths'} | 615f85f8-5c89-44ee-aecc-c328f172e413 | Entoarox.FasterPaths" // changed in 1.2 and 1.3; disambiguate from Shop Expander - // "UpdateKeys": [ "Chucklefish:3641" ] // Entoarox opted out of mod update checks + "ID": "Entoarox.FasterPaths", + "FormerIDs": "{ID:'821ce8f6-e629-41ad-9fde-03b54f68b0b6', Name:'Faster Paths'} | 615f85f8-5c89-44ee-aecc-c328f172e413" // changed in 1.2 and 1.3; disambiguate from Shop Expander + // "Default | UpdateKey": "Chucklefish:3641" // Entoarox opted out of mod update checks }, { // Faster Run - "ID": "FasterRun.dll | KathrynHazuka.FasterRun", // changed in 1.1.1-pathoschild-update - "UpdateKeys": [ "Nexus:733" ], // added in 1.1.1-pathoschild-update - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "KathrynHazuka.FasterRun", + "FormerIDs": "{EntryDLL: 'FasterRun.dll'}", // changed in 1.1.1-pathoschild-update + "Default | UpdateKey": "Nexus:733", // added in 1.1.1-pathoschild-update + "~1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Fishing Adjust "ID": "shuaiz.FishingAdjustMod", - "UpdateKeys": [ "Nexus:1350" ] + "Default | UpdateKey": "Nexus:1350" }, { // Fishing Tuner Redux "ID": "HammurabiFishingTunerRedux", - "UpdateKeys": [ "Chucklefish:4578" ] + "Default | UpdateKey": "Chucklefish:4578" }, { // FlorenceMod "ID": "FlorenceMod.dll", - "UpdateKeys": [ "Nexus:591" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "1.0.1": "1.1" - } + "MapLocalVersions": { "1.0.1": "1.1" }, + "Default | UpdateKey": "Nexus:591", + "~1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Flower Color Picker "ID": "spacechase0.FlowerColorPicker", - "UpdateKeys": [ "Nexus:1229" ] + "Default | UpdateKey": "Nexus:1229" }, { // Forage at the Farm "ID": "ForageAtTheFarm", - "UpdateKeys": [ "Nexus:673" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.5.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:673", + "~1.5.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.5.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Furniture Anywhere - "ID": "{ID:'EntoaroxFurnitureAnywhere', Name:'Furniture Anywhere'} | Entoarox.FurnitureAnywhere" // changed in 1.1; disambiguate from Extended Minecart - // "UpdateKeys": [ "Chucklefish:4324" ] // Entoarox opted out of mod update checks + "ID": "Entoarox.FurnitureAnywhere", + "FormerIDs": "{ID:'EntoaroxFurnitureAnywhere', Name:'Furniture Anywhere'}" // changed in 1.1; disambiguate from Extended Minecart + // "Default | UpdateKey": "Chucklefish:4324" // Entoarox opted out of mod update checks }, { // Game Reminder "ID": "mmanlapat.GameReminder", - "UpdateKeys": [ "Nexus:1153" ] + "Default | UpdateKey": "Nexus:1153" }, { // Gate Opener - "ID": "GateOpener.dll | mralbobo.GateOpener", // changed in 1.1 - "UpdateKeys": [ "GitHub:mralbobo/stardew-gate-opener" ], - "Compatibility": { - "~1.0.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "mralbobo.GateOpener", + "FormerIDs": "{EntryDLL: 'GateOpener.dll'}", // changed in 1.1 + "Default | UpdateKey": "GitHub:mralbobo/stardew-gate-opener", + "~1.0.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // GenericShopExtender "ID": "GenericShopExtender", - "UpdateKeys": [ "Nexus:814" ], // added in 0.1.3 - "Compatibility": { - "~0.1.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:814", // added in 0.1.3 + "~0.1.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Geode Info Menu "ID": "cat.geodeinfomenu", - "UpdateKeys": [ "Nexus:1448" ] + "Default | UpdateKey": "Nexus:1448" }, { // Get Dressed - "ID": "GetDressed.dll | Advize.GetDressed", // changed in 3.3 - "UpdateKeys": [ "Nexus:331" ], - "Compatibility": { - "~3.3": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Advize.GetDressed", + "FormerIDs": "{EntryDLL: 'GetDressed.dll'}", // changed in 3.3 + "Default | UpdateKey": "Nexus:331", + "~3.3 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Giant Crop Ring "ID": "cat.giantcropring", - "UpdateKeys": [ "Nexus:1182" ] + "Default | UpdateKey": "Nexus:1182" }, { // Gift Taste Helper - "ID": "8008db57-fa67-4730-978e-34b37ef191d6 | tstaples.GiftTasteHelper", // changed in 2.5 - "UpdateKeys": [ "Nexus:229" ], - "Compatibility": { - "~2.3.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "tstaples.GiftTasteHelper", + "FormerIDs": "8008db57-fa67-4730-978e-34b37ef191d6", // changed in 2.5 + "Default | UpdateKey": "Nexus:229", + "~2.3.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Grandfather's Gift "ID": "ShadowDragon.GrandfathersGift", - "UpdateKeys": [ "Nexus:985" ] + "Default | UpdateKey": "Nexus:985" }, { // Happy Animals "ID": "HappyAnimals", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "~1.0.3 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.3 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Happy Birthday (Omegasis) - "ID": "{ID:'HappyBirthday', Author:'Alpha_Omegasis'} | Omegasis.HappyBirthday", // changed in 1.4; disambiguate from Oxyligen's fork - "UpdateKeys": [ "Nexus:520" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.HappyBirthday", + "FormerIDs": "{ID:'HappyBirthday', Author:'Alpha_Omegasis'}", // changed in 1.4; disambiguate from Oxyligen's fork + "Default | UpdateKey": "Nexus:520", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Happy Birthday (Oxyligen fork) "ID": "{ID:'HappyBirthday', Author:'Alpha_Omegasis/Oxyligen'}", // disambiguate from Oxyligen's fork - "UpdateKeys": [ "Nexus:1064" ] + "Default | UpdateKey": "Nexus:1064" }, { // Harp of Yoba Redux "ID": "Platonymous.HarpOfYobaRedux", - "UpdateKeys": [ "Nexus:914" ] // added in 2.0.3 + "Default | UpdateKey": "Nexus:914" // added in 2.0.3 }, { // Harvest Moon Witch Princess "ID": "Sasara.WitchPrincess", - "UpdateKeys": [ "Nexus:1157" ] + "Default | UpdateKey": "Nexus:1157" }, { // Harvest With Scythe "ID": "965169fd-e1ed-47d0-9f12-b104535fb4bc", - "UpdateKeys": [ "Nexus:236" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.6": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:236", + "~1.0.6 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.6 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Horse Whistle (icepuente) "ID": "icepuente.HorseWhistle", - "UpdateKeys": [ "Nexus:1131" ] + "Default | UpdateKey": "Nexus:1131" }, { // Hunger (Yyeadude) "ID": "HungerYyeadude", - "UpdateKeys": [ "Nexus:613" ] + "Default | UpdateKey": "Nexus:613" }, { // Hunger for Food (Tigerle) "ID": "HungerForFoodByTigerle", - "UpdateKeys": [ "Nexus:810" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~0.1.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:810", + "~0.1.2 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~0.1.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Hunger Mod (skn) "ID": "skn.HungerMod", - "UpdateKeys": [ "Nexus:1127" ], - "MapRemoteVersions": { - "1.2.1": "1.0" // manifest not updated - } + "MapRemoteVersions": { "1.2.1": "1.0" }, // manifest not updated + "Default | UpdateKey": "Nexus:1127" }, { // Idle Pause "ID": "Veleek.IdlePause", - "UpdateKeys": [ "Nexus:1092" ], - "MapRemoteVersions": { - "1.2": "1.1" // manifest not updated - } + "MapRemoteVersions": { "1.2": "1.1" }, // manifest not updated + "Default | UpdateKey": "Nexus:1092" }, { // Improved Quality of Life "ID": "Demiacle.ImprovedQualityOfLife", - "UpdateKeys": [ "Nexus:1025" ], - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1025", + "~1.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Instant Geode "ID": "InstantGeode", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.12": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "~1.12 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.12 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Instant Grow Trees - "ID": "dc50c58b-c7d8-4e60-86cc-e27b5d95ee59 | community.InstantGrowTrees", // changed in 1.2 - "UpdateKeys": [ "Nexus:173" ] + "ID": "community.InstantGrowTrees", + "FormerIDs": "dc50c58b-c7d8-4e60-86cc-e27b5d95ee59", // changed in 1.2 + "Default | UpdateKey": "Nexus:173" }, { // Interaction Helper "ID": "HammurabiInteractionHelper", - "UpdateKeys": [ "Chucklefish:4640" ], // added in 1.0.4-pathoschild-update - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Chucklefish:4640", // added in 1.0.4-pathoschild-update + "~1.0.3 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.3 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Item Auto Stacker "ID": "cat.autostacker", - "UpdateKeys": [ "Nexus:1184" ], - "MapRemoteVersions": { - "1.0.1": "1.0" // manifest not updated - } + "MapRemoteVersions": { "1.0.1": "1.0" }, // manifest not updated + "Default | UpdateKey": "Nexus:1184" }, { // Jiggly Junimo Bundles - "ID": "JJB.dll | Greger.JigglyJunimoBundles", // changed in 1.1.2-pathoschild-update - "UpdateKeys": [ "GitHub:gr3ger/Stardew_JJB" ], // added in 1.0.4-pathoschild-update - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" + "ID": "Greger.JigglyJunimoBundles", + "FormerIDs": "{EntryDLL: 'JJB.dll'}", // changed in 1.1.2-pathoschild-update + "Default | UpdateKey": "GitHub:gr3ger/Stardew_JJB" // added in 1.0.4-pathoschild-update }, { // Junimo Farm "ID": "Platonymous.JunimoFarm", - "UpdateKeys": [ "Nexus:984" ], // added in 1.1.3 - "MapRemoteVersions": { - "1.1.2": "1.1.1" // manifest not updated - } + "MapRemoteVersions": { "1.1.2": "1.1.1" }, // manifest not updated + "Default | UpdateKey": "Nexus:984" // added in 1.1.3 }, { // Less Strict Over-Exertion (AntiExhaustion) "ID": "BALANCEMOD_AntiExhaustion", - "UpdateKeys": [ "Nexus:637" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "0.0": "1.1" - } + "MapLocalVersions": { "0.0": "1.1" }, + "Default | UpdateKey": "Nexus:637", + "~1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Level Extender "ID": "Devin Lematty.Level Extender", - "UpdateKeys": [ "Nexus:1471" ], - "MapRemoteVersions": { - "1.1": "1.0" // manifest not updated - } + "MapRemoteVersions": { "1.1": "1.0" }, // manifest not updated + "Default | UpdateKey": "Nexus:1471" }, { // Level Up Notifications "ID": "Level Up Notifications", - "UpdateKeys": [ "Nexus:855" ] + "Default | UpdateKey": "Nexus:855" }, { // Location and Music Logging "ID": "Brandy Lover.LMlog", - "UpdateKeys": [ "Nexus:1366" ] + "Default | UpdateKey": "Nexus:1366" }, { // Longevity "ID": "RTGOAT.Longevity", - "UpdateKeys": [ "Nexus:649" ] + "Default | UpdateKey": "Nexus:649" }, { // Lookup Anything - "ID": "LookupAnything | Pathoschild.LookupAnything", // changed in 1.10.1 - "UpdateKeys": [ "Nexus:541" ], - "Compatibility": { - "~1.10.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Pathoschild.LookupAnything", + "FormerIDs": "LookupAnything", // changed in 1.10.1 + "Default | UpdateKey": "Nexus:541", + "~1.10.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Love Bubbles "ID": "LoveBubbles", - "UpdateKeys": [ "Nexus:1318" ] + "Default | UpdateKey": "Nexus:1318" }, { // Loved Labels "ID": "LovedLabels.dll", - "UpdateKeys": [ "Nexus:279" ], - "Compatibility": { - "~2.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:279", + "~2.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Luck Skill - "ID": "LuckSkill | spacechase0.LuckSkill", // changed in 0.1.4 - "UpdateKeys": [ "Nexus:521" ], - "Compatibility": { - "~0.1.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "spacechase0.LuckSkill", + "FormerIDs": "LuckSkill", // changed in 0.1.4 + "Default | UpdateKey": "Nexus:521", + "~0.1.4 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Mail Framework "ID": "DIGUS.MailFrameworkMod", - "UpdateKeys": [ "Nexus:1536" ] + "Default | UpdateKey": "Nexus:1536" }, { // MailOrderPigs - "ID": "MailOrderPigs.dll | jwdred.MailOrderPigs", // changed in 1.0.2 - "UpdateKeys": [ "Nexus:632" ], - "Compatibility": { - "~1.0.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "jwdred.MailOrderPigs", + "FormerIDs": "{EntryDLL: 'MailOrderPigs.dll'}", // changed in 1.0.2 + "Default | UpdateKey": "Nexus:632", + "~1.0.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Makeshift Multiplayer - "ID": "StardewValleyMP | spacechase0.StardewValleyMP", // changed in 0.3 - "UpdateKeys": [ "Nexus:501" ], - "Compatibility": { - "~0.3.6": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "spacechase0.StardewValleyMP", + "FormerIDs": "StardewValleyMP", // changed in 0.3 + "Default | UpdateKey": "Nexus:501", + "~0.3.6 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Map Image Exporter - "ID": "MapImageExporter | spacechase0.MapImageExporter", // changed in 1.0.2 - "UpdateKeys": [ "Nexus:1073" ] + "ID": "spacechase0.MapImageExporter", + "FormerIDs": "MapImageExporter", // changed in 1.0.2 + "Default | UpdateKey": "Nexus:1073" }, { // Message Box [API]? (ChatMod) "ID": "Kithio:ChatMod", - "UpdateKeys": [ "Chucklefish:4296" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Chucklefish:4296", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Mining at the Farm "ID": "MiningAtTheFarm", - "UpdateKeys": [ "Nexus:674" ] + "Default | UpdateKey": "Nexus:674" }, { // Mining With Explosives "ID": "MiningWithExplosives", - "UpdateKeys": [ "Nexus:770" ] + "Default | UpdateKey": "Nexus:770" }, { // Modder Serialization Utility "ID": "SerializerUtils-0-1", - "Compatibility": { - "~": { - "Status": "Obsolete", - "ReasonPhrase": "it's no longer maintained or used." - } - } + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "it's no longer maintained or used." }, { // More Artifact Spots "ID": "451", - "UpdateKeys": [ "Nexus:451" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:451", + "~1.0.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // More Map Layers "ID": "Platonymous.MoreMapLayers", - "UpdateKeys": [ "Nexus:1134" ] // added in 1.1.1 + "Default | UpdateKey": "Nexus:1134" // added in 1.1.1 }, { // More Pets - "ID": "821ce8f6-e629-41ad-9fde-03b54f68b0b6MOREPETS | Entoarox.MorePets", // changed in 1.3 - // "UpdateKeys": [ "Chucklefish:4288" ], // Entoarox opted out of mod update checks - "Compatibility": { - "~1.3.2": { "Status": "AssumeBroken" } // overhauled for SMAPI 1.11+ compatibility - } + "ID": "Entoarox.MorePets", + "FormerIDs": "821ce8f6-e629-41ad-9fde-03b54f68b0b6MOREPETS", // changed in 1.3 + // "Default | UpdateKey": "Chucklefish:4288", // Entoarox opted out of mod update checks + "~1.3.2 | Status": "AssumeBroken" // overhauled for SMAPI 1.11+ compatibility }, { // More Rain - "ID": "{ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'More_Rain'} | Omegasis.MoreRain", // changed in 1.5; disambiguate from other mods by Alpha_Omegasis - "UpdateKeys": [ "Nexus:441" ], // added in 1.5.1 - "Compatibility": { - "~1.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.MoreRain", + "FormerIDs": "{ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'More_Rain'}", // changed in 1.5; disambiguate from other mods by Alpha_Omegasis + "Default | UpdateKey": "Nexus:441", // added in 1.5.1 + "~1.4 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // More Weapons "ID": "Joco80.MoreWeapons", - "UpdateKeys": [ "Nexus:1168" ] + "Default | UpdateKey": "Nexus:1168" }, { // Move Faster "ID": "shuaiz.MoveFasterMod", - "UpdateKeys": [ "Nexus:1351" ] + "Default | UpdateKey": "Nexus:1351" }, { // Multiple Sprites and Portraits On Rotation (File Loading) "ID": "FileLoading", - "UpdateKeys": [ "Nexus:1094" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.12": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "1.1": "1.12" - } + "MapLocalVersions": { "1.1": "1.12" }, + "Default | UpdateKey": "Nexus:1094", + "~1.12 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.12 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Museum Rearranger - "ID": "{ID:'7ad4f6f7-c3de-4729-a40f-7a11d2b2a358', Name:'Museum Rearranger'} | Omegasis.MuseumRearranger", // changed in 1.4; disambiguate from other mods by Alpha_Omegasis - "UpdateKeys": [ "Nexus:428" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.MuseumRearranger", + "FormerIDs": "{ID:'7ad4f6f7-c3de-4729-a40f-7a11d2b2a358', Name:'Museum Rearranger'}", // changed in 1.4; disambiguate from other mods by Alpha_Omegasis + "Default | UpdateKey": "Nexus:428", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // New Machines "ID": "F70D4FAB-0AB2-4B78-9F1B-AF2CA2236A59", - "UpdateKeys": [ "Chucklefish:3683" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~4.2.1343": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Chucklefish:3683", + "~4.2.1343 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~4.2.1343 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Night Owl - "ID": "{ID:'SaveAnywhere', Name:'Stardew_NightOwl'} | Omegasis.NightOwl", // changed in 1.4; disambiguate from Save Anywhere - "UpdateKeys": [ "Nexus:433" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "2.1": "1.3" // 1.3 had wrong version in manifest - } + "ID": "Omegasis.NightOwl", + "FormerIDs": "{ID:'SaveAnywhere', Name:'Stardew_NightOwl'}", // changed in 1.4; disambiguate from Save Anywhere + "MapLocalVersions": { "2.1": "1.3" }, // 1.3 had wrong version in manifest + "Default | UpdateKey": "Nexus:433", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // No Kids Ever "ID": "Hangy.NoKidsEver", - "UpdateKeys": [ "Nexus:1464" ] + "Default | UpdateKey": "Nexus:1464" }, { // No Debug Mode "ID": "NoDebugMode", - "Compatibility": { - "~": { - "Status": "Obsolete", - "ReasonPhrase": "debug mode was removed in SMAPI 1.0." - } - } + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "debug mode was removed in SMAPI 1.0." }, { // No Fence Decay "ID": "cat.nofencedecay", - "UpdateKeys": [ "Nexus:1180" ] + "Default | UpdateKey": "Nexus:1180" }, { // No More Pets - "ID": "NoMorePets | Omegasis.NoMorePets", // changed in 1.4 - "UpdateKeys": [ "Nexus:506" ] // added in 1.4.1 + "ID": "Omegasis.NoMorePets", + "FormerIDs": "NoMorePets", // changed in 1.4 + "Default | UpdateKey": "Nexus:506" // added in 1.4.1 }, { // NoSoilDecay "ID": "289dee03-5f38-4d8e-8ffc-e440198e8610", - "UpdateKeys": [ "Nexus:237" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~0.5": { "Status": "AssumeBroken" } // broke in SDV 1.2, and uses Assembly.GetExecutingAssembly().Location - } + "Default | UpdateKey": "Nexus:237", + "~0.5 | Status": "AssumeBroken", // broke in SDV 1.2 and uses Assembly.GetExecutingAssembly().Location + "~0.5 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // No Soil Decay Redux "ID": "Platonymous.NoSoilDecayRedux", - "UpdateKeys": [ "Nexus:1084" ] // added in 1.1.9 + "Default | UpdateKey": "Nexus:1084" // added in 1.1.9 }, { // NPC Map Locations "ID": "NPCMapLocationsMod", - "UpdateKeys": [ "Nexus:239" ], - "Compatibility": { - "1.42~1.43": { - "Status": "AssumeBroken", - "ReasonPhrase": "this version has an update check error which crashes the game." - } - } + "Default | UpdateKey": "Nexus:239", + "1.42~1.43 | Status": "AssumeBroken", + "1.42~1.43 | StatusReasonPhrase": "this version has an update check error which crashes the game." }, { // NPC Speak "ID": "NpcEcho.dll", - "UpdateKeys": [ "Nexus:694" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:694", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Object Time Left "ID": "spacechase0.ObjectTimeLeft", - "UpdateKeys": [ "Nexus:1315" ] + "Default | UpdateKey": "Nexus:1315" }, { // OmniFarm - "ID": "BlueMod_OmniFarm | PhthaloBlue.OmniFarm", // changed in 2.0.2-pathoschild-update - "UpdateKeys": [ "GitHub:lambui/StardewValleyMod_OmniFarm" ], - "Compatibility": { - "~2.0.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "PhthaloBlue.OmniFarm", + "FormerIDs": "BlueMod_OmniFarm", // changed in 2.0.2-pathoschild-update + "Default | UpdateKey": "GitHub:lambui/StardewValleyMod_OmniFarm", + "~2.0.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Out of Season Bonuses / Seasonal Items "ID": "midoriarmstrong.seasonalitems", - "UpdateKeys": [ "Nexus:1452" ] + "Default | UpdateKey": "Nexus:1452" }, { // Part of the Community "ID": "SB_PotC", - "UpdateKeys": [ "Nexus:923" ], - "Compatibility": { - "~1.0.8": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "Default | UpdateKey": "Nexus:923", + "~1.0.8 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // PelicanFiber - "ID": "PelicanFiber.dll | jwdred.PelicanFiber", // changed in 3.0.1 - "UpdateKeys": [ "Nexus:631" ], - "Compatibility": { - "~3.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapRemoteVersions": { - "3.0.2": "3.0.1" // didn't change manifest version - } + "ID": "jwdred.PelicanFiber", + "FormerIDs": "{EntryDLL: 'PelicanFiber.dll'}", // changed in 3.0.1 + "MapRemoteVersions": { "3.0.2": "3.0.1" }, // didn't change manifest version + "Default | UpdateKey": "Nexus:631", + "~3.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // PelicanTTS "ID": "Platonymous.PelicanTTS", - "UpdateKeys": [ "Nexus:1079" ], // added in 1.6.1 - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1079", // added in 1.6.1 + "~1.6 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.6 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Persia the Mermaid - Standalone Custom NPC "ID": "63b9f419-7449-42db-ab2e-440b4d05c073", - "UpdateKeys": [ "Nexus:1419" ] + "Default | UpdateKey": "Nexus:1419" }, { // Persival's BundleMod "ID": "BundleMod.dll", - "UpdateKeys": [ "Nexus:438" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.1 - } + "Default | UpdateKey": "Nexus:438", + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.1 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Plant on Grass "ID": "Demiacle.PlantOnGrass", - "UpdateKeys": [ "Nexus:1026" ] + "Default | UpdateKey": "Nexus:1026" }, { // Point-and-Plant - "ID": "PointAndPlant.dll | jwdred.PointAndPlant", // changed in 1.0.3 - "UpdateKeys": [ "Nexus:572" ], - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "jwdred.PointAndPlant", + "FormerIDs": "{EntryDLL: 'PointAndPlant.dll'}", // changed in 1.0.3 + "Default | UpdateKey": "Nexus:572", + "~1.0.2 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Pony Weight Loss Program "ID": "BadNetCode.PonyWeightLossProgram", - "UpdateKeys": [ "Nexus:1232" ] + "Default | UpdateKey": "Nexus:1232" }, { // Portraiture "ID": "Platonymous.Portraiture", - "UpdateKeys": [ "Nexus:999" ] // added in 1.3.1 + "Default | UpdateKey": "Nexus:999" // added in 1.3.1 }, { // Prairie King Made Easy - "ID": "PrairieKingMadeEasy.dll | Mucchan.PrairieKingMadeEasy", // changed in 1.0.1 - "UpdateKeys": [ "Chucklefish:3594" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Mucchan.PrairieKingMadeEasy", + "FormerIDs": "{EntryDLL: 'PrairieKingMadeEasy.dll'}", // changed in 1.0.1 + "Default | UpdateKey": "Chucklefish:3594", + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Quest Delay "ID": "BadNetCode.QuestDelay", - "UpdateKeys": [ "Nexus:1239" ] + "Default | UpdateKey": "Nexus:1239" }, { // Rain Randomizer "ID": "RainRandomizer.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "~1.0.3 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.3 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Recatch Legendary Fish - "ID": "b3af8c31-48f0-43cf-8343-3eb08bcfa1f9 | community.RecatchLegendaryFish", // changed in 1.3 - "UpdateKeys": [ "Nexus:172" ] + "ID": "community.RecatchLegendaryFish", + "FormerIDs": "b3af8c31-48f0-43cf-8343-3eb08bcfa1f9", // changed in 1.3 + "Default | UpdateKey": "Nexus:172" }, { // Regeneration "ID": "HammurabiRegeneration", - "UpdateKeys": [ "Chucklefish:4584" ] + "Default | UpdateKey": "Chucklefish:4584" }, { // Relationship Bar UI "ID": "RelationshipBar", - "UpdateKeys": [ "Nexus:1009" ] + "Default | UpdateKey": "Nexus:1009" }, { // RelationshipsEnhanced "ID": "relationshipsenhanced", - "UpdateKeys": [ "Chucklefish:4435" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Chucklefish:4435", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Relationship Status "ID": "relationshipstatus", - "UpdateKeys": [ "Nexus:751" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.5": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapRemoteVersions": { - "1.0.5": "1.0.4" // not updated in manifest - } + "MapRemoteVersions": { "1.0.5": "1.0.4" }, // not updated in manifest + "Default | UpdateKey": "Nexus:751", + "~1.0.5 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.5 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Rented Tools "ID": "JarvieK.RentedTools", - "UpdateKeys": [ "Nexus:1307" ] + "Default | UpdateKey": "Nexus:1307" }, { // Replanter - "ID": "Replanter.dll | jwdred.Replanter", // changed in 1.0.5 - "UpdateKeys": [ "Nexus:589" ], - "Compatibility": { - "~1.0.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "jwdred.Replanter", + "FormerIDs": "{EntryDLL: 'Replanter.dll'}", // changed in 1.0.5 + "Default | UpdateKey": "Nexus:589", + "~1.0.4 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // ReRegeneration "ID": "lrsk_sdvm_rerg.0925160827", - "UpdateKeys": [ "Chucklefish:4465" ], - "MapLocalVersions": { - "1.1.2-release": "1.1.2" - } + "MapLocalVersions": { "1.1.2-release": "1.1.2" }, + "Default | UpdateKey": "Chucklefish:4465" }, { // Reseed "ID": "Roc.Reseed", - "UpdateKeys": [ "Nexus:887" ] + "Default | UpdateKey": "Nexus:887" }, { // Reusable Wallpapers and Floors (Wallpaper Retain) "ID": "dae1b553-2e39-43e7-8400-c7c5c836134b", - "UpdateKeys": [ "Nexus:356" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.5": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:356", + "~1.5 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.5 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Ring of Fire "ID": "Platonymous.RingOfFire", - "UpdateKeys": [ "Nexus:1166" ] // added in 1.0.1 + "Default | UpdateKey": "Nexus:1166" // added in 1.0.1 }, { // Rope Bridge "ID": "RopeBridge", - "UpdateKeys": [ "Nexus:824" ] + "Default | UpdateKey": "Nexus:824" }, { // Rotate Toolbar "ID": "Pathoschild.RotateToolbar", - "UpdateKeys": [ "Nexus:1100" ] + "Default | UpdateKey": "Nexus:1100" }, { // Rush Orders - "ID": "RushOrders | spacechase0.RushOrders", // changed in 1.1 - "UpdateKeys": [ "Nexus:605" ], - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "spacechase0.RushOrders", + "FormerIDs": "RushOrders", // changed in 1.1 + "Default | UpdateKey": "Nexus:605", + "~1.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Save Anywhere - "ID": "{ID:'SaveAnywhere', Name:'Save Anywhere'} | Omegasis.SaveAnywhere", // changed in 2.5; disambiguate from Night Owl - "UpdateKeys": [ "Nexus:444" ], // added in 2.6.1 - "Compatibility": { - "~2.4": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.SaveAnywhere", + "FormerIDs": "{ID:'SaveAnywhere', Name:'Save Anywhere'}", // changed in 2.5; disambiguate from Night Owl + "Default | UpdateKey": "Nexus:444", // added in 2.6.1 + "~2.4 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Save Backup - "ID": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'Stardew_Save_Backup'} | Omegasis.SaveBackup", // changed in 1.3; disambiguate from other Alpha_Omegasis mods - "UpdateKeys": [ "Nexus:435" ], // added in 1.3.1 - "Compatibility": { - "~1.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.SaveBackup", + "FormerIDs": "{ID:'4be88c18-b6f3-49b0-ba96-f94b1a5be890', Name:'Stardew_Save_Backup'}", // changed in 1.3; disambiguate from other Alpha_Omegasis mods + "Default | UpdateKey": "Nexus:435", // added in 1.3.1 + "~1.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Scroll to Blank "ID": "caraxian.scroll.to.blank", - "UpdateKeys": [ "Chucklefish:4405" ] + "Default | UpdateKey": "Chucklefish:4405" }, { // Scythe Harvesting - "ID": "ScytheHarvesting | mmanlapat.ScytheHarvesting", // changed in 1.6 - "UpdateKeys": [ "Nexus:1106" ] + "ID": "mmanlapat.ScytheHarvesting", + "FormerIDs": "ScytheHarvesting", // changed in 1.6 + "Default | UpdateKey": "Nexus:1106" }, { // Seasonal Immersion - "ID": "EntoaroxSeasonalHouse | EntoaroxSeasonalBuildings | EntoaroxSeasonalImmersion | Entoarox.SeasonalImmersion", // changed in 1.1, 1.6 or earlier, and 1.7 - // "UpdateKeys": [ "Chucklefish:4262" ], // Entoarox opted out of mod update checks - "Compatibility": { - "~1.8.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Entoarox.SeasonalImmersion", + "FormerIDs": "EntoaroxSeasonalHouse | EntoaroxSeasonalBuildings | EntoaroxSeasonalImmersion", // changed in 1.1, 1.6 or earlier, and 1.7 + // "Default | UpdateKey": "Chucklefish:4262", // Entoarox opted out of mod update checks + "~1.8.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Seed Bag "ID": "Platonymous.SeedBag", - "UpdateKeys": [ "Nexus:1133" ] // added in 1.1.2 + "Default | UpdateKey": "Nexus:1133" // added in 1.1.2 }, { // Self Service "ID": "JarvieK.SelfService", - "UpdateKeys": [ "Nexus:1304" ], - "MapRemoteVersions": { - "0.2.1": "0.2" // manifest not updated - } + "MapRemoteVersions": { "0.2.1": "0.2" }, // manifest not updated + "Default | UpdateKey": "Nexus:1304" }, { // Send Items "ID": "Denifia.SendItems", - "UpdateKeys": [ "Nexus:1087" ], // added in 1.0.3 (2017-10-04) - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1087", // added in 1.0.3 (2017-10-04) + "~1.0.2 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Shed Notifications (BuildingsNotifications) "ID": "TheCroak.BuildingsNotifications", - "UpdateKeys": [ "Nexus:620" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~0.4.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:620", + "~0.4.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~0.4.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Shenandoah Project "ID": "Shenandoah Project", - "UpdateKeys": [ "Nexus:756" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapRemoteVersions": { - "1.1.1": "1.1" // not updated in manifest - } + "MapRemoteVersions": { "1.1.1": "1.1" }, // not updated in manifest + "Default | UpdateKey": "Nexus:756", + "~1.1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Ship Anywhere "ID": "spacechase0.ShipAnywhere", - "UpdateKeys": [ "Nexus:1379" ] + "Default | UpdateKey": "Nexus:1379" }, { // Shipment Tracker "ID": "7e474181-e1a0-40f9-9c11-d08a3dcefaf3", - "UpdateKeys": [ "Nexus:321" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:321", + "~1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Shop Expander - "ID": "{ID:'821ce8f6-e629-41ad-9fde-03b54f68b0b6', Name:'Shop Expander'} | EntoaroxShopExpander | Entoarox.ShopExpander", // changed in 1.5 and 1.5.2; disambiguate from Faster Paths - // "UpdateKeys": [ "Chucklefish:4381" ], // Entoarox opted out of mod update checks - "Compatibility": { - "~1.5.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Entoarox.ShopExpander", + "FormerIDs": "{ID:'821ce8f6-e629-41ad-9fde-03b54f68b0b6', Name:'Shop Expander'} | EntoaroxShopExpander", // changed in 1.5 and 1.5.2; disambiguate from Faster Paths + // "Default | UpdateKey": "Chucklefish:4381", // Entoarox opted out of mod update checks + "~1.5.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Showcase Mod "ID": "Igorious.Showcase", - "UpdateKeys": [ "Chucklefish:4487" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~0.9": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "0.9-500": "0.9" - } + "MapLocalVersions": { "0.9-500": "0.9" }, + "Default | UpdateKey": "Chucklefish:4487", + "~0.9 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~0.9 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Shroom Spotter "ID": "TehPers.ShroomSpotter", - "UpdateKeys": [ "Nexus:908" ] + "Default | UpdateKey": "Nexus:908" }, { // Simple Crop Label "ID": "SimpleCropLabel", - "UpdateKeys": [ "Nexus:314" ] + "Default | UpdateKey": "Nexus:314" }, { // Simple Sound Manager "ID": "Omegasis.SimpleSoundManager", - "UpdateKeys": [ "Nexus:1410" ], // added in 1.0.1 - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", // can remove once 1.0.1 is published - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Nexus:1410", // added in 1.0.1 + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" // can remove once 1.0.1 is published }, { // Simple Sprinklers - "ID": "SimpleSprinkler.dll | tZed.SimpleSprinkler", // changed in 1.5 - "UpdateKeys": [ "Nexus:76" ], - "Compatibility": { - "~1.4": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "tZed.SimpleSprinkler", + "FormerIDs": "{EntryDLL: 'SimpleSprinkler.dll'}", // changed in 1.5 + "Default | UpdateKey": "Nexus:76", + "~1.4 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Siv's Marriage Mod "ID": "6266959802", - "UpdateKeys": [ "Nexus:366" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.2.2": { "Status": "AssumeBroken" } // broke in SMAPI 1.9 (has multiple Mod instances) - }, - "MapLocalVersions": { - "0.0": "1.4" - } + "MapLocalVersions": { "0.0": "1.4" }, + "Default | UpdateKey": "Nexus:366", + "~1.2.2 | Status": "AssumeBroken", // broke in SMAPI 1.9 (has multiple Mod instances) + "~1.2.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Skill Prestige - "ID": "6b843e60-c8fc-4a25-a67b-4a38ac8dcf9b | alphablackwolf.skillPrestige", // changed circa 1.2.3 - "UpdateKeys": [ "Nexus:569" ], - "Compatibility": { - "~1.0.9": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "alphablackwolf.skillPrestige", + "FormerIDs": "6b843e60-c8fc-4a25-a67b-4a38ac8dcf9b", // changed circa 1.2.3 + "Default | UpdateKey": "Nexus:569", + "~1.0.9 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Skill Prestige: Cooking Adapter - "ID": "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63 | Alphablackwolf.CookingSkillPrestigeAdapter", // changed circa 1.1 - "UpdateKeys": [ "Nexus:569" ], - "Compatibility": { - "~1.0.9": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapRemoteVersions": { - "1.2.3": "1.1" // manifest not updated - } + "ID": "Alphablackwolf.CookingSkillPrestigeAdapter", + "FormerIDs": "20d6b8a3-b6e7-460b-a6e4-07c2b0cb6c63", // changed circa 1.1 + "MapRemoteVersions": { "1.2.3": "1.1" }, // manifest not updated + "Default | UpdateKey": "Nexus:569", + "~1.0.9 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Skip Intro - "ID": "SkipIntro | Pathoschild.SkipIntro", // changed in 1.4 - "UpdateKeys": [ "Nexus:533" ] + "ID": "Pathoschild.SkipIntro", + "FormerIDs": "SkipIntro", // changed in 1.4 + "Default | UpdateKey": "Nexus:533" }, { // Skull Cavern Elevator "ID": "SkullCavernElevator", - "UpdateKeys": [ "Nexus:963" ] + "Default | UpdateKey": "Nexus:963" }, { // Skull Cave Saver - "ID": "8ac06349-26f7-4394-806c-95d48fd35774 | community.SkullCaveSaver", // changed in 1.1 - "UpdateKeys": [ "Nexus:175" ] + "ID": "community.SkullCaveSaver", + "FormerIDs": "8ac06349-26f7-4394-806c-95d48fd35774", // changed in 1.1 + "Default | UpdateKey": "Nexus:175" }, { // Sleepy Eye "ID": "spacechase0.SleepyEye", - "UpdateKeys": [ "Nexus:1152" ] + "Default | UpdateKey": "Nexus:1152" }, { // Slower Fence Decay - "ID": "SPDSlowFenceDecay | Speeder.SlowerFenceDecay", // changed in 0.5.2-pathoschild-update - "UpdateKeys": [ "Nexus:252" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~0.5.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Speeder.SlowerFenceDecay", + "FormerIDs": "SPDSlowFenceDecay", // changed in 0.5.2-pathoschild-update + "Default | UpdateKey": "Nexus:252", + "~0.5.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~0.5.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Smart Mod "ID": "KuroBear.SmartMod", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~2.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "~2.2 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~2.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Solar Eclipse Event "ID": "KoihimeNakamura.SolarEclipseEvent", - "UpdateKeys": [ "Nexus:897" ], - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "1.3-20170917": "1.3" - } + "Default | UpdateKey": "Nexus:897", + "MapLocalVersions": { "1.3-20170917": "1.3" }, + "~1.1 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // SpaceCore "ID": "spacechase0.SpaceCore", - "UpdateKeys": [ "Nexus:1348" ] + "Default | UpdateKey": "Nexus:1348" }, { // Speedster "ID": "Platonymous.Speedster", - "UpdateKeys": [ "Nexus:1102" ] // added in 1.3.1 + "Default | UpdateKey": "Nexus:1102" // added in 1.3.1 }, { // Sprinkler Range "ID": "cat.sprinklerrange", - "UpdateKeys": [ "Nexus:1179" ], - "MapRemoteVersions": { - "1.0.1": "1.0" // manifest not updated - } + "MapRemoteVersions": { "1.0.1": "1.0" }, // manifest not updated + "Default | UpdateKey": "Nexus:1179" }, { // Sprinkles "ID": "Platonymous.Sprinkles", - "UpdateKeys": [ "Chucklefish:4592" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Chucklefish:4592", + "~1.1.3 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1.3 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Sprint and Dash "ID": "SPDSprintAndDash", - "UpdateKeys": [ "Chucklefish:3531" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "Default | UpdateKey": "Chucklefish:3531", + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Sprint and Dash Redux - "ID": "lrsk_sdvm_sndr.0921161059 | littleraskol.SprintAndDashRedux", // changed in 1.3 - "UpdateKeys": [ "Chucklefish:4201" ] + "ID": "littleraskol.SprintAndDashRedux", + "FormerIDs": "lrsk_sdvm_sndr.0921161059", // changed in 1.3 + "Default | UpdateKey": "Chucklefish:4201" }, { // Sprinting Mod "ID": "a10d3097-b073-4185-98ba-76b586cba00c", - "UpdateKeys": [ "GitHub:oliverpl/SprintingMod" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~2.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - }, - "MapLocalVersions": { - "1.0": "2.1" // not updated in manifest - } + "MapLocalVersions": { "1.0": "2.1" }, // not updated in manifest + "Default | UpdateKey": "GitHub:oliverpl/SprintingMod", + "~2.1 | Status": "AssumeBroken", // broke in SDV 1.2 + "~2.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // StackSplitX - "ID": "StackSplitX.dll | tstaples.StackSplitX", // changed circa 1.3.1 - "UpdateKeys": [ "Nexus:798" ], - "Compatibility": { - "~1.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "tstaples.StackSplitX", + "FormerIDs": "{EntryDLL: 'StackSplitX.dll'}", // changed circa 1.3.1 + "Default | UpdateKey": "Nexus:798", + "~1.2 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // StaminaRegen "ID": "StaminaRegen.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "~1.0.3 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.3 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Stardew Config Menu "ID": "Juice805.StardewConfigMenu", - "UpdateKeys": [ "Nexus:1312" ] + "Default | UpdateKey": "Nexus:1312" }, { // Stardew Content Compatibility Layer (SCCL) "ID": "SCCL", - "UpdateKeys": [ "Nexus:889" ], - "Compatibility": { - "~0.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "Default | UpdateKey": "Nexus:889", + "~0.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Stardew Editor Game Integration "ID": "spacechase0.StardewEditor.GameIntegration", - "UpdateKeys": [ "Nexus:1298" ] + "Default | UpdateKey": "Nexus:1298" }, { // Stardew Notification "ID": "stardewnotification", - "UpdateKeys": [ "GitHub:monopandora/StardewNotification" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.7": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "GitHub:monopandora/StardewNotification", + "~1.7 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.7 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Stardew Symphony - "ID": "{ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'Stardew_Symphony'} | Omegasis.StardewSymphony", // changed in 1.4; disambiguate other mods by Alpha_Omegasis - "UpdateKeys": [ "Nexus:425" ], // added in 1.4.1 - "Compatibility": { - "~1.3": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "ID": "Omegasis.StardewSymphony", + "FormerIDs": "{ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'Stardew_Symphony'}", // changed in 1.4; disambiguate other mods by Alpha_Omegasis + "Default | UpdateKey": "Nexus:425", // added in 1.4.1 + "~1.3 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // StarDustCore "ID": "StarDustCore", - "Compatibility": { - "~": { - "Status": "Obsolete", - "ReasonPhrase": "it was only used by earlier versions of Save Anywhere, and is no longer used or maintained." - } - } + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "it was only used by earlier versions of Save Anywhere, and is no longer used or maintained." }, { // Starting Money - "ID": "StartingMoney | mmanlapat.StartingMoney", // changed in 1.1 - "UpdateKeys": [ "Nexus:1138" ] + "ID": "mmanlapat.StartingMoney", + "FormerIDs": "StartingMoney", // changed in 1.1 + "Default | UpdateKey": "Nexus:1138" }, { // StashItemsToChest "ID": "BlueMod_StashItemsToChest", - "UpdateKeys": [ "GitHub:lambui/StardewValleyMod_StashItemsToChest" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "GitHub:lambui/StardewValleyMod_StashItemsToChest", + "~1.0.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Stephan's Lots of Crops "ID": "stephansstardewcrops", - "UpdateKeys": [ "Chucklefish:4314" ], - "MapRemoteVersions": { - "1.41": "1.1" // manifest not updated - } + "MapRemoteVersions": { "1.41": "1.1" }, // manifest not updated + "Default | UpdateKey": "Chucklefish:4314" }, { // Stone Bridge Over Pond (PondWithBridge) "ID": "PondWithBridge.dll", - "UpdateKeys": [ "Nexus:316" ], - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - }, - "MapLocalVersions": { - "0.0": "1.0" - } + "MapLocalVersions": { "0.0": "1.0" }, + "Default | UpdateKey": "Nexus:316", + "~1.0 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // Stumps to Hardwood Stumps "ID": "StumpsToHardwoodStumps", - "UpdateKeys": [ "Nexus:691" ] + "Default | UpdateKey": "Nexus:691" }, { // Super Greenhouse Warp Modifier "ID": "SuperGreenhouse", - "UpdateKeys": [ "Chucklefish:4334" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Chucklefish:4334", + "~1.0 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Swim Almost Anywhere / Swim Suit "ID": "Platonymous.SwimSuit", - "UpdateKeys": [ "Nexus:1215" ] // added in 0.5.1 + "Default | UpdateKey": "Nexus:1215" // added in 0.5.1 }, { // Tainted Cellar "ID": "TaintedCellar.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.1 or 1.11 - } + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.1 or 1.11 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Tapper Ready "ID": "skunkkk.TapperReady", - "UpdateKeys": [ "Nexus:1219" ] + "Default | UpdateKey": "Nexus:1219" }, { // Teh's Fishing Overhaul "ID": "TehPers.FishingOverhaul", - "UpdateKeys": [ "Nexus:866" ] + "Default | UpdateKey": "Nexus:866" }, { // Teleporter "ID": "Teleporter", - "UpdateKeys": [ "Chucklefish:4374" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "Default | UpdateKey": "Chucklefish:4374", + "~1.0.2 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // The Long Night "ID": "Pathoschild.TheLongNight", - "UpdateKeys": [ "Nexus:1369" ] + "Default | UpdateKey": "Nexus:1369" }, { // Three-heart Dance Partner "ID": "ThreeHeartDancePartner", - "UpdateKeys": [ "Nexus:500" ], - "Compatibility": { - "~1.0.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "Default | UpdateKey": "Nexus:500", + "~1.0.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // TimeFreeze - "ID": "4108e859-333c-4fec-a1a7-d2e18c1019fe | Omegasis.TimeFreeze", // changed in 1.2 - "UpdateKeys": [ "Nexus:973" ] // added in 1.2.1 + "ID": "Omegasis.TimeFreeze", + "FormerIDs": "4108e859-333c-4fec-a1a7-d2e18c1019fe", // changed in 1.2 + "Default | UpdateKey": "Nexus:973" // added in 1.2.1 }, { // Time Reminder "ID": "KoihimeNakamura.TimeReminder", - "UpdateKeys": [ "Nexus:1000" ], - "MapLocalVersions": { - "1.0-20170314": "1.0.2" - } + "MapLocalVersions": { "1.0-20170314": "1.0.2" }, + "Default | UpdateKey": "Nexus:1000" }, { // TimeSpeed - "ID": "TimeSpeed.dll | {ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'TimeSpeed'} | {ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'TimeSpeed Mod (unofficial)'} | community.TimeSpeed", // changed in 2.0.3 and 2.1; disambiguate other mods by Alpha_Omegasis - "UpdateKeys": [ "Nexus:169" ], - "Compatibility": { - "~2.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "community.TimeSpeed", + "FormerIDs": "TimeSpeed.dll | {ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'TimeSpeed'} | {ID:'4108e859-333c-4fec-a1a7-d2e18c1019fe', Name:'TimeSpeed Mod (unofficial)'}", // changed in 2.0.3 and 2.1; disambiguate other mods by Alpha_Omegasis + "Default | UpdateKey": "Nexus:169", + "~2.2 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // TractorMod - "ID": "BlueMod_TractorMod | PhthaloBlue.TractorMod | community.TractorMod | Pathoschild.TractorMod", // changed in 3.2, 4.0 beta, and 4.0 - "UpdateKeys": [ "Nexus:1401" ] + "ID": "Pathoschild.TractorMod", + "FormerIDs": "BlueMod_TractorMod | PhthaloBlue.TractorMod | community.TractorMod", // changed in 3.2, 4.0 beta, and 4.0 + "Default | UpdateKey": "Nexus:1401" }, { // TrainerMod "ID": "SMAPI.TrainerMod", - "Compatibility": { - "~": { - "Status": "Obsolete", - "ReasonPhrase": "replaced by ConsoleCommands, which is added by the SMAPI installer." - } - } + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "replaced by ConsoleCommands, which is added by the SMAPI installer." }, { // Tree Transplant "ID": "TreeTransplant", - "UpdateKeys": [ "Nexus:1342" ] + "Default | UpdateKey": "Nexus:1342" }, { // UI Info Suite "ID": "Cdaragorn.UiInfoSuite", - "UpdateKeys": [ "Nexus:1150" ] + "Default | UpdateKey": "Nexus:1150" }, { // UiModSuite "ID": "Demiacle.UiModSuite", - "UpdateKeys": [ "Nexus:1023" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.2 - }, - "MapLocalVersions": { - "0.5": "1.0" // not updated in manifest - } + "MapLocalVersions": { "0.5": "1.0" }, // not updated in manifest + "Default | UpdateKey": "Nexus:1023", + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Variable Grass "ID": "dantheman999.VariableGrass", - "UpdateKeys": [ "GitHub:dantheman999301/StardewMods" ] + "Default | UpdateKey": "GitHub:dantheman999301/StardewMods" }, { // Vertical Toolbar "ID": "SB_VerticalToolMenu", - "UpdateKeys": [ "Nexus:943" ] + "Default | UpdateKey": "Nexus:943" }, { // WakeUp "ID": "WakeUp.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "~1.0.2 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.0.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // Wallpaper Fix "ID": "WallpaperFix.dll", - "UpdateKeys": [ "Chucklefish:4211" ], - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.1": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + "Default | UpdateKey": "Chucklefish:4211", + "~1.1 | Status": "AssumeBroken", // broke in SMAPI 2.0 + "~1.1 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // WarpAnimals "ID": "Symen.WarpAnimals", - "UpdateKeys": [ "Nexus:1400" ] + "Default | UpdateKey": "Nexus:1400" }, { // Weather Controller "ID": "WeatherController.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "~1.0.2 | Status": "AssumeBroken", // broke in SDV 1.2 + "~1.0.2 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // What Farm Cave / WhatAMush "ID": "WhatAMush", - "UpdateKeys": [ "Nexus:1097" ] + "Default | UpdateKey": "Nexus:1097" }, { // WHats Up "ID": "wHatsUp", - "UpdateKeys": [ "Nexus:1082" ] + "Default | UpdateKey": "Nexus:1082" }, { // Wonderful Farm Life "ID": "WonderfulFarmLife.dll", - "AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0", - "Compatibility": { - "~1.0": { "Status": "AssumeBroken" } // broke in SDV 1.1 or 1.11 - } + "~1.0 | Status": "AssumeBroken", // broke in SDV 1.1 or 1.11 + "~1.0 | AlternativeUrl": "http://stardewvalleywiki.com/Modding:SMAPI_2.0" }, { // XmlSerializerRetool "ID": "XmlSerializerRetool.dll", - "Compatibility": { - "~": { - "Status": "Obsolete", - "ReasonPhrase": "it's no longer maintained or used." - } - } + "~ | Status": "Obsolete", + "~ | StatusReasonPhrase": "it's no longer maintained or used." }, { // Xnb Loader "ID": "Entoarox.XnbLoader", - // "UpdateKeys": [ "Chucklefish:4506" ], // Entoarox opted out of mod update checks - "Compatibility": { - "~1.0.6": { "Status": "AssumeBroken" } // broke in SMAPI 2.0 - } + // "Default | UpdateKey": "Chucklefish:4506", // Entoarox opted out of mod update checks + "~1.0.6 | Status": "AssumeBroken" // broke in SMAPI 2.0 }, { // zDailyIncrease "ID": "zdailyincrease", - "UpdateKeys": [ "Chucklefish:4247" ], - "Compatibility": { - "~1.2": { "Status": "AssumeBroken" } // broke in SDV 1.2 - }, - "MapRemoteVersions": { - "1.3.5": "1.3.4" // not updated in manifest - } + "MapRemoteVersions": { "1.3.5": "1.3.4" }, // not updated in manifest + "Default | UpdateKey": "Chucklefish:4247", + "~1.2 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Zoom Out Extreme - "ID": "ZoomMod | RockinMods.ZoomMod", // changed circa 1.2.1 - "UpdateKeys": [ "Nexus:1326" ], - "Compatibility": { - "~0.1": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "RockinMods.ZoomMod", + "FormerIDs": "ZoomMod", // changed circa 1.2.1 + "Default | UpdateKey": "Nexus:1326", + "~0.1 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Zoryn's Better RNG - "ID": "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6 | Zoryn.BetterRNG", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ], - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Zoryn.BetterRNG", + "FormerIDs": "76b6d1e1-f7ba-4d72-8c32-5a1e6d2716f6", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", + "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Zoryn's Calendar Anywhere - "ID": "a41c01cd-0437-43eb-944f-78cb5a53002a | Zoryn.CalendarAnywhere", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ], - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Zoryn.CalendarAnywhere", + "FormerIDs": "a41c01cd-0437-43eb-944f-78cb5a53002a", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", + "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Zoryn's Durable Fences - "ID": "56d3439c-7b9b-497e-9496-0c4890e8a00e | Zoryn.DurableFences", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ] + "ID": "Zoryn.DurableFences", + "FormerIDs": "56d3439c-7b9b-497e-9496-0c4890e8a00e", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods" }, { // Zoryn's Health Bars - "ID": "HealthBars.dll | Zoryn.HealthBars", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ], - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Zoryn.HealthBars", + "FormerIDs": "{EntryDLL: 'HealthBars.dll'}", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", + "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Zoryn's Fishing Mod - "ID": "fa277b1f-265e-47c3-a84f-cd320cc74949 | Zoryn.FishingMod", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ] + "ID": "Zoryn.FishingMod", + "FormerIDs": "fa277b1f-265e-47c3-a84f-cd320cc74949", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods" }, { // Zoryn's Junimo Deposit Anywhere - "ID": "f93a4fe8-cade-4146-9335-b5f82fbbf7bc | Zoryn.JunimoDepositAnywhere", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ], - "Compatibility": { - "~1.7": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Zoryn.JunimoDepositAnywhere", + "FormerIDs": "f93a4fe8-cade-4146-9335-b5f82fbbf7bc", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", + "~1.7 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Zoryn's Movement Mod - "ID": "8a632929-8335-484f-87dd-c29d2ba3215d | Zoryn.MovementModifier", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ], - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Zoryn.MovementModifier", + "FormerIDs": "8a632929-8335-484f-87dd-c29d2ba3215d", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", + "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 }, { // Zoryn's Regen Mod - "ID": "dfac4383-1b6b-4f33-ae4e-37fc23e5252e | Zoryn.RegenMod", // changed in 1.6 - "UpdateKeys": [ "GitHub:Zoryn4163/SMAPI-Mods" ], - "Compatibility": { - "~1.6": { "Status": "AssumeBroken" } // broke in SDV 1.2 - } + "ID": "Zoryn.RegenMod", + "FormerIDs": "dfac4383-1b6b-4f33-ae4e-37fc23e5252e", // changed in 1.6 + "Default | UpdateKey": "GitHub:Zoryn4163/SMAPI-Mods", + "~1.6 | Status": "AssumeBroken" // broke in SDV 1.2 } ] } diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index cd6c4ac3..129c88b0 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -89,7 +89,9 @@ <Compile Include="Framework\Input\InputState.cs" /> <Compile Include="Framework\Input\InputStatus.cs" /> <Compile Include="Framework\LegacyManifestVersion.cs" /> - <Compile Include="Framework\Models\ModCompatibility.cs" /> + <Compile Include="Framework\Models\ModDataField.cs" /> + <Compile Include="Framework\Models\ModDataFieldKey.cs" /> + <Compile Include="Framework\Models\ParsedModDataRecord.cs" /> <Compile Include="Framework\ModLoading\Finders\EventFinder.cs" /> <Compile Include="Framework\ModLoading\Finders\FieldFinder.cs" /> <Compile Include="Framework\ModLoading\Finders\MethodFinder.cs" /> @@ -110,9 +112,7 @@ <Compile Include="Framework\Exceptions\SAssemblyLoadFailedException.cs" /> <Compile Include="Framework\ModLoading\AssemblyLoadStatus.cs" /> <Compile Include="Framework\Reflection\InterfaceProxyBuilder.cs" /> - <Compile Include="Framework\Serialisation\SmapiConverters\ModCompatibilityArrayConverter.cs" /> <Compile Include="Framework\Serialisation\SmapiConverters\ManifestDependencyArrayConverter.cs" /> - <Compile Include="Framework\Serialisation\SmapiConverters\ModDataIdConverter.cs" /> <Compile Include="Framework\Serialisation\SmapiConverters\SemanticVersionConverter.cs" /> <Compile Include="Framework\Serialisation\SimpleReadOnlyConverter.cs" /> <Compile Include="Framework\Serialisation\CrossplatformConverters\RectangleConverter.cs" /> @@ -154,7 +154,6 @@ <Compile Include="Framework\Utilities\Countdown.cs" /> <Compile Include="Framework\GameVersion.cs" /> <Compile Include="Framework\IModMetadata.cs" /> - <Compile Include="Framework\Models\ModDataID.cs" /> <Compile Include="Framework\ModHelpers\BaseHelper.cs" /> <Compile Include="Framework\ModHelpers\CommandHelper.cs" /> <Compile Include="Framework\ModHelpers\ContentHelper.cs" /> |