From 5f19e4f2035c36f9c6c882da3767d6f29409db1c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 27 Jun 2018 00:05:53 -0400 Subject: move mod DB parsing into toolkit (#532) --- src/SMAPI/Constants.cs | 26 ---- src/SMAPI/Framework/IModMetadata.cs | 2 +- src/SMAPI/Framework/ModData/ModDataField.cs | 82 ------------ src/SMAPI/Framework/ModData/ModDataFieldKey.cs | 18 --- src/SMAPI/Framework/ModData/ModDataRecord.cs | 146 --------------------- src/SMAPI/Framework/ModData/ModDatabase.cs | 140 -------------------- src/SMAPI/Framework/ModData/ModStatus.cs | 18 --- src/SMAPI/Framework/ModData/ParsedModDataRecord.cs | 51 ------- src/SMAPI/Framework/ModLoading/ModMetadata.cs | 2 +- src/SMAPI/Framework/ModLoading/ModResolver.cs | 2 +- src/SMAPI/Framework/Models/SMetadata.cs | 15 --- src/SMAPI/Program.cs | 9 +- src/SMAPI/StardewModdingAPI.csproj | 7 - 13 files changed, 8 insertions(+), 510 deletions(-) delete mode 100644 src/SMAPI/Framework/ModData/ModDataField.cs delete mode 100644 src/SMAPI/Framework/ModData/ModDataFieldKey.cs delete mode 100644 src/SMAPI/Framework/ModData/ModDataRecord.cs delete mode 100644 src/SMAPI/Framework/ModData/ModDatabase.cs delete mode 100644 src/SMAPI/Framework/ModData/ModStatus.cs delete mode 100644 src/SMAPI/Framework/ModData/ParsedModDataRecord.cs delete mode 100644 src/SMAPI/Framework/Models/SMetadata.cs (limited to 'src/SMAPI') diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 01b99d62..c4a3813e 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -22,14 +21,6 @@ namespace StardewModdingAPI /// Whether the directory containing the current save's data exists on disk. private static bool SavePathReady => Context.IsSaveLoaded && Directory.Exists(Constants.RawSavePath); - /// Maps vendor keys (like Nexus) to their mod URL template (where {0} is the mod ID). This doesn't affect update checks, which defer to the remote web API. - private static readonly IDictionary VendorModUrls = new Dictionary(StringComparer.InvariantCultureIgnoreCase) - { - ["Chucklefish"] = "https://community.playstarbound.com/resources/{0}", - ["GitHub"] = "https://github.com/{0}/releases", - ["Nexus"] = "https://www.nexusmods.com/stardewvalley/mods/{0}" - }; - /********* ** Accessors @@ -159,23 +150,6 @@ namespace StardewModdingAPI return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences, targetAssemblies); } - /// Get an update URL for an update key (if valid). - /// The update key. - internal static string GetUpdateUrl(string updateKey) - { - string[] parts = updateKey.Split(new[] { ':' }, 2); - if (parts.Length != 2) - return null; - - string vendorKey = parts[0].Trim(); - string modID = parts[1].Trim(); - - if (Constants.VendorModUrls.TryGetValue(vendorKey, out string urlTemplate)) - return string.Format(urlTemplate, modID); - - return null; - } - /********* ** Private methods diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs index 6281c052..5a8689de 100644 --- a/src/SMAPI/Framework/IModMetadata.cs +++ b/src/SMAPI/Framework/IModMetadata.cs @@ -1,6 +1,6 @@ -using StardewModdingAPI.Framework.ModData; using StardewModdingAPI.Framework.ModLoading; using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.ModData; namespace StardewModdingAPI.Framework { diff --git a/src/SMAPI/Framework/ModData/ModDataField.cs b/src/SMAPI/Framework/ModData/ModDataField.cs deleted file mode 100644 index df906103..00000000 --- a/src/SMAPI/Framework/ModData/ModDataField.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Linq; - -namespace StardewModdingAPI.Framework.ModData -{ - /// A versioned mod metadata field. - internal class ModDataField - { - /********* - ** Accessors - *********/ - /// The field key. - public ModDataFieldKey Key { get; } - - /// The field value. - public string Value { get; } - - /// Whether this field should only be applied if it's not already set. - public bool IsDefault { get; } - - /// The lowest version in the range, or null for all past versions. - public ISemanticVersion LowerVersion { get; } - - /// The highest version in the range, or null for all future versions. - public ISemanticVersion UpperVersion { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The field key. - /// The field value. - /// Whether this field should only be applied if it's not already set. - /// The lowest version in the range, or null for all past versions. - /// The highest version in the range, or null for all future versions. - 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; - } - - /// Get whether this data field applies for the given manifest. - /// The mod manifest. - 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 - *********/ - /// Get whether a manifest field has a meaningful value for the purposes of enforcing . - /// The mod manifest. - /// The field key matching . - private bool HasFieldValue(IManifest manifest, ModDataFieldKey key) - { - switch (key) - { - // update key - case ModDataFieldKey.UpdateKey: - return manifest.UpdateKeys != null && manifest.UpdateKeys.Any(p => !string.IsNullOrWhiteSpace(p)); - - // non-manifest fields - case ModDataFieldKey.AlternativeUrl: - case ModDataFieldKey.StatusReasonPhrase: - case ModDataFieldKey.Status: - return false; - - default: - return false; - } - } - } -} diff --git a/src/SMAPI/Framework/ModData/ModDataFieldKey.cs b/src/SMAPI/Framework/ModData/ModDataFieldKey.cs deleted file mode 100644 index f68f575c..00000000 --- a/src/SMAPI/Framework/ModData/ModDataFieldKey.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace StardewModdingAPI.Framework.ModData -{ - /// The valid field keys. - public enum ModDataFieldKey - { - /// A manifest update key. - UpdateKey, - - /// An alternative URL the player can check for an updated version. - AlternativeUrl, - - /// The mod's predefined compatibility status. - Status, - - /// A reason phrase for the , or null to use the default reason. - StatusReasonPhrase - } -} diff --git a/src/SMAPI/Framework/ModData/ModDataRecord.cs b/src/SMAPI/Framework/ModData/ModDataRecord.cs deleted file mode 100644 index 56275f53..00000000 --- a/src/SMAPI/Framework/ModData/ModDataRecord.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace StardewModdingAPI.Framework.ModData -{ - /// Raw mod metadata from SMAPI's internal mod list. - internal class ModDataRecord - { - /********* - ** Properties - *********/ - /// This field stores properties that aren't mapped to another field before they're parsed into . - [JsonExtensionData] - private IDictionary ExtensionData; - - - /********* - ** Accessors - *********/ - /// The mod's current unique ID. - public string ID { get; set; } - - /// The former mod IDs (if any). - /// - /// 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 - /// | 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. - /// - public string FormerIDs { get; set; } - - /// Maps local versions to a semantic version for update checks. - public IDictionary MapLocalVersions { get; set; } = new Dictionary(); - - /// Maps remote versions to a semantic version for update checks. - public IDictionary MapRemoteVersions { get; set; } = new Dictionary(); - - /// The versioned field data. - /// - /// This maps field names to values. This should be accessed via . - /// Format notes: - /// - Each key consists of a field name prefixed with any combination of version range - /// and Default, separated by pipes (whitespace trimmed). For example, Name - /// will always override the name, Default | Name will only override a blank - /// name, and ~1.1 | Default | Name will override blank names up to version 1.1. - /// - The version format is min~max (where either side can be blank for unbounded), or - /// a single version number. - /// - The field name itself corresponds to a value. - /// - public IDictionary Fields { get; set; } = new Dictionary(); - - - /********* - ** Public methods - *********/ - /// Get a parsed representation of the . - public IEnumerable GetFields() - { - foreach (KeyValuePair 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); - } - } - - /// Get a semantic local version for update checks. - /// The remote version to normalise. - public ISemanticVersion GetLocalVersionForUpdateChecks(ISemanticVersion version) - { - return this.MapLocalVersions != null && this.MapLocalVersions.TryGetValue(version.ToString(), out string newVersion) - ? new SemanticVersion(newVersion) - : version; - } - - /// Get a semantic remote version for update checks. - /// The remote version to normalise. - public string GetRemoteVersionForUpdateChecks(string version) - { - // normalise version if possible - if (SemanticVersion.TryParse(version, out ISemanticVersion parsed)) - version = parsed.ToString(); - - // fetch remote version - return this.MapRemoteVersions != null && this.MapRemoteVersions.TryGetValue(version, out string newVersion) - ? newVersion - : version; - } - - - /********* - ** Private methods - *********/ - /// The method invoked after JSON deserialisation. - /// The deserialisation context. - [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; - } - } - } -} diff --git a/src/SMAPI/Framework/ModData/ModDatabase.cs b/src/SMAPI/Framework/ModData/ModDatabase.cs deleted file mode 100644 index 62f37d9b..00000000 --- a/src/SMAPI/Framework/ModData/ModDatabase.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace StardewModdingAPI.Framework.ModData -{ - /// Handles access to SMAPI's internal mod metadata list. - internal class ModDatabase - { - /********* - ** Properties - *********/ - /// The underlying mod data records indexed by default display name. - private readonly IDictionary Records; - - /// Get an update URL for an update key (if valid). - private readonly Func GetUpdateUrl; - - - /********* - ** Public methods - *********/ - /// Construct an empty instance. - public ModDatabase() - : this(new Dictionary(), key => null) { } - - /// Construct an instance. - /// The underlying mod data records indexed by default display name. - /// Get an update URL for an update key (if valid). - public ModDatabase(IDictionary records, Func getUpdateUrl) - { - this.Records = records; - this.GetUpdateUrl = getUpdateUrl; - } - - /// Get a parsed representation of the which match a given manifest. - /// The manifest to match. - public ParsedModDataRecord GetParsed(IManifest manifest) - { - // get raw record - if (!this.TryGetRaw(manifest?.UniqueID, out string displayName, out ModDataRecord record)) - return null; - - // parse fields - ParsedModDataRecord parsed = new ParsedModDataRecord { DisplayName = displayName, DataRecord = record }; - foreach (ModDataField field in record.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; - } - - /// Get the display name for a given mod ID (if available). - /// The unique mod ID. - public string GetDisplayNameFor(string id) - { - return this.TryGetRaw(id, out string displayName, out ModDataRecord _) - ? displayName - : null; - } - - /// Get the mod page URL for a mod (if available). - /// The unique mod ID. - public string GetModPageUrlFor(string id) - { - // get raw record - if (!this.TryGetRaw(id, out string _, out ModDataRecord record)) - return null; - - // get update key - ModDataField updateKeyField = record.GetFields().FirstOrDefault(p => p.Key == ModDataFieldKey.UpdateKey); - if (updateKeyField == null) - return null; - - // get update URL - return this.GetUpdateUrl(updateKeyField.Value); - } - - - /********* - ** Private models - *********/ - /// Get a raw data record. - /// The mod ID to match. - /// The mod's default display name. - /// The raw mod record. - private bool TryGetRaw(string id, out string displayName, out ModDataRecord record) - { - if (!string.IsNullOrWhiteSpace(id)) - { - foreach (var entry in this.Records) - { - displayName = entry.Key; - record = entry.Value; - - // try main ID - if (record.ID != null && record.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase)) - return true; - - // try former IDs - if (record.FormerIDs != null) - { - foreach (string part in record.FormerIDs.Split('|')) - { - if (part.Trim().Equals(id, StringComparison.InvariantCultureIgnoreCase)) - return true; - } - } - } - } - - displayName = null; - record = null; - return false; - } - } -} diff --git a/src/SMAPI/Framework/ModData/ModStatus.cs b/src/SMAPI/Framework/ModData/ModStatus.cs deleted file mode 100644 index 0e1d94d4..00000000 --- a/src/SMAPI/Framework/ModData/ModStatus.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace StardewModdingAPI.Framework.ModData -{ - /// Indicates how SMAPI should treat a mod. - internal enum ModStatus - { - /// Don't override the status. - None, - - /// The mod is obsolete and shouldn't be used, regardless of version. - Obsolete, - - /// Assume the mod is not compatible, even if SMAPI doesn't detect any incompatible code. - AssumeBroken, - - /// Assume the mod is compatible, even if SMAPI detects incompatible code. - AssumeCompatible - } -} diff --git a/src/SMAPI/Framework/ModData/ParsedModDataRecord.cs b/src/SMAPI/Framework/ModData/ParsedModDataRecord.cs deleted file mode 100644 index 3801fac3..00000000 --- a/src/SMAPI/Framework/ModData/ParsedModDataRecord.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace StardewModdingAPI.Framework.ModData -{ - /// A parsed representation of the fields from a for a specific manifest. - internal class ParsedModDataRecord - { - /********* - ** Accessors - *********/ - /// The underlying data record. - public ModDataRecord DataRecord { get; set; } - - /// The default mod name to display when the name isn't available (e.g. during dependency checks). - public string DisplayName { get; set; } - - /// The update key to apply. - public string UpdateKey { get; set; } - - /// The alternative URL the player can check for an updated version. - public string AlternativeUrl { get; set; } - - /// The predefined compatibility status. - public ModStatus Status { get; set; } = ModStatus.None; - - /// A reason phrase for the , or null to use the default reason. - public string StatusReasonPhrase { get; set; } - - /// The upper version for which the applies (if any). - public ISemanticVersion StatusUpperVersion { get; set; } - - - /********* - ** Public methods - *********/ - /// Get a semantic local version for update checks. - /// The remote version to normalise. - public ISemanticVersion GetLocalVersionForUpdateChecks(ISemanticVersion version) - { - return this.DataRecord.GetLocalVersionForUpdateChecks(version); - } - - /// Get a semantic remote version for update checks. - /// The remote version to normalise. - public ISemanticVersion GetRemoteVersionForUpdateChecks(string version) - { - string rawVersion = this.DataRecord.GetRemoteVersionForUpdateChecks(version); - return rawVersion != null - ? new SemanticVersion(rawVersion) - : null; - } - } -} diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs index 3a412009..4db25932 100644 --- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs @@ -1,7 +1,7 @@ using System; using System.Linq; -using StardewModdingAPI.Framework.ModData; using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.ModData; namespace StardewModdingAPI.Framework.ModLoading { diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index fde921e6..c1bc51ec 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; -using StardewModdingAPI.Framework.ModData; +using StardewModdingAPI.Toolkit.Framework.ModData; using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Serialisation.Models; using StardewModdingAPI.Toolkit.Utilities; diff --git a/src/SMAPI/Framework/Models/SMetadata.cs b/src/SMAPI/Framework/Models/SMetadata.cs deleted file mode 100644 index 9ff495e9..00000000 --- a/src/SMAPI/Framework/Models/SMetadata.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using StardewModdingAPI.Framework.ModData; - -namespace StardewModdingAPI.Framework.Models -{ - /// The SMAPI predefined metadata. - internal class SMetadata - { - /******** - ** Accessors - ********/ - /// Extra metadata about mods. - public IDictionary ModData { get; set; } - } -} diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs index 09d9969c..6f1fe761 100644 --- a/src/SMAPI/Program.cs +++ b/src/SMAPI/Program.cs @@ -21,7 +21,6 @@ using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Events; using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.Logging; -using StardewModdingAPI.Framework.ModData; using StardewModdingAPI.Framework.Models; using StardewModdingAPI.Framework.ModHelpers; using StardewModdingAPI.Framework.ModLoading; @@ -29,7 +28,9 @@ using StardewModdingAPI.Framework.Patching; using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Framework.Serialisation; using StardewModdingAPI.Internal; +using StardewModdingAPI.Toolkit; using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.ModData; using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Serialisation.Converters; using StardewModdingAPI.Toolkit.Utilities; @@ -413,8 +414,8 @@ namespace StardewModdingAPI this.Monitor.Log("SMAPI found problems in your game's content files which are likely to cause errors or crashes. Consider uninstalling XNB mods or reinstalling the game.", LogLevel.Error); // load mod data - SMetadata metadata = JsonConvert.DeserializeObject(File.ReadAllText(Constants.ApiMetadataPath)); - ModDatabase modDatabase = new ModDatabase(metadata.ModData, Constants.GetUpdateUrl); + ModToolkit toolkit = new ModToolkit(); + ModDatabase modDatabase = toolkit.GetModDatabase(Constants.ApiMetadataPath, toolkit.GetUpdateUrl); // load mods { @@ -423,7 +424,7 @@ namespace StardewModdingAPI // load manifests IModMetadata[] mods = resolver.ReadManifests(Constants.ModPath, this.JsonHelper, modDatabase).ToArray(); - resolver.ValidateManifests(mods, Constants.ApiVersion, Constants.GetUpdateUrl); + resolver.ValidateManifests(mods, Constants.ApiVersion, toolkit.GetUpdateUrl); // process dependencies mods = resolver.ProcessDependencies(mods, modDatabase).ToArray(); diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index 4852f70c..f849ee53 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -132,11 +132,6 @@ - - - - - @@ -239,7 +234,6 @@ - @@ -263,7 +257,6 @@ - -- cgit