diff options
author | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-06-05 20:22:46 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <github@jplamondonw.com> | 2018-06-05 20:22:46 -0400 |
commit | 625c538f244519700f3942b2b2969845db9a99b0 (patch) | |
tree | d13596220a62616838310cbcf0f0004e2f843da3 /src | |
parent | 265ce35fd1db677230ddb16483b4d92e7c13a777 (diff) | |
download | SMAPI-625c538f244519700f3942b2b2969845db9a99b0.tar.gz SMAPI-625c538f244519700f3942b2b2969845db9a99b0.tar.bz2 SMAPI-625c538f244519700f3942b2b2969845db9a99b0.zip |
move manifest parsing into toolkit (#532)
Diffstat (limited to 'src')
27 files changed, 343 insertions, 189 deletions
diff --git a/src/SMAPI.Tests/Core/ModResolverTests.cs b/src/SMAPI.Tests/Core/ModResolverTests.cs index d63eb1a2..2fbeb9b6 100644 --- a/src/SMAPI.Tests/Core/ModResolverTests.cs +++ b/src/SMAPI.Tests/Core/ModResolverTests.cs @@ -9,7 +9,7 @@ using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.ModData; using StardewModdingAPI.Framework.Models; using StardewModdingAPI.Framework.ModLoading; -using StardewModdingAPI.Framework.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation; namespace StardewModdingAPI.Tests.Core { @@ -93,8 +93,8 @@ namespace StardewModdingAPI.Tests.Core Assert.IsNotNull(mod, "The loaded manifest shouldn't be null."); Assert.AreEqual(null, mod.DataRecord, "The data record should be null since we didn't provide one."); Assert.AreEqual(modFolder, mod.DirectoryPath, "The directory path doesn't match."); - Assert.AreEqual(ModMetadataStatus.Found, mod.Status, "The status doesn't match."); Assert.AreEqual(null, mod.Error, "The error should be null since parsing should have succeeded."); + Assert.AreEqual(ModMetadataStatus.Found, mod.Status, "The status doesn't match."); Assert.AreEqual(original[nameof(IManifest.Name)], mod.DisplayName, "The display name should use the manifest name."); Assert.AreEqual(original[nameof(IManifest.Name)], mod.Manifest.Name, "The manifest's name doesn't match."); @@ -160,7 +160,7 @@ namespace StardewModdingAPI.Tests.Core { // arrange Mock<IModMetadata> mock = this.GetMetadata("Mod A", new string[0], allowStatusChange: true); - mock.Setup(p => p.Manifest).Returns(this.GetManifest(m => m.MinimumApiVersion = new SemanticVersion("1.1"))); + mock.Setup(p => p.Manifest).Returns(this.GetManifest(minimumApiVersion: "1.1")); this.SetupMetadataForValidation(mock); // act @@ -174,7 +174,7 @@ namespace StardewModdingAPI.Tests.Core public void ValidateManifests_MissingEntryDLL_Fails() { // arrange - Mock<IModMetadata> mock = this.GetMetadata(this.GetManifest("Mod A", "1.0", manifest => manifest.EntryDll = "Missing.dll"), allowStatusChange: true); + Mock<IModMetadata> mock = this.GetMetadata(this.GetManifest(id: "Mod A", version: "1.0", entryDll: "Missing.dll"), allowStatusChange: true); this.SetupMetadataForValidation(mock); // act @@ -189,7 +189,7 @@ namespace StardewModdingAPI.Tests.Core { // arrange Mock<IModMetadata> modA = this.GetMetadata("Mod A", new string[0], allowStatusChange: true); - Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest("Mod A", "1.0", manifest => manifest.Name = "Mod B"), allowStatusChange: true); + Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest(id: "Mod A", name: "Mod B", version: "1.0"), allowStatusChange: true); Mock<IModMetadata> modC = this.GetMetadata("Mod C", new string[0], allowStatusChange: false); foreach (Mock<IModMetadata> mod in new[] { modA, modB, modC }) this.SetupMetadataForValidation(mod); @@ -398,8 +398,8 @@ namespace StardewModdingAPI.Tests.Core { // arrange // A 1.0 ◀── B (need A 1.1) - Mock<IModMetadata> modA = this.GetMetadata(this.GetManifest("Mod A", "1.0")); - Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest("Mod B", "1.0", new ManifestDependency("Mod A", "1.1")), allowStatusChange: true); + Mock<IModMetadata> modA = this.GetMetadata(this.GetManifest(id: "Mod A", version: "1.0")); + Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest(id: "Mod B", version: "1.0", dependencies: new IManifestDependency[] { new ManifestDependency("Mod A", "1.1") }), allowStatusChange: true); // act IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modA.Object, modB.Object }, new ModDatabase()).ToArray(); @@ -414,8 +414,8 @@ namespace StardewModdingAPI.Tests.Core { // arrange // A 1.0 ◀── B (need A 1.0-beta) - Mock<IModMetadata> modA = this.GetMetadata(this.GetManifest("Mod A", "1.0")); - Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest("Mod B", "1.0", new ManifestDependency("Mod A", "1.0-beta")), allowStatusChange: false); + Mock<IModMetadata> modA = this.GetMetadata(this.GetManifest(id: "Mod A", version: "1.0")); + Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest(id: "Mod B", version: "1.0", dependencies: new IManifestDependency[] { new ManifestDependency("Mod A", "1.0-beta") }), allowStatusChange: false); // act IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modA.Object, modB.Object }, new ModDatabase()).ToArray(); @@ -431,8 +431,8 @@ namespace StardewModdingAPI.Tests.Core { // arrange // A ◀── B - Mock<IModMetadata> modA = this.GetMetadata(this.GetManifest("Mod A", "1.0")); - Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest("Mod B", "1.0", new ManifestDependency("Mod A", "1.0", required: false)), allowStatusChange: false); + Mock<IModMetadata> modA = this.GetMetadata(this.GetManifest(id: "Mod A", version: "1.0")); + Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest(id: "Mod B", version: "1.0", dependencies: new IManifestDependency[] { new ManifestDependency("Mod A", "1.0", required: false) }), allowStatusChange: false); // act IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modB.Object, modA.Object }, new ModDatabase()).ToArray(); @@ -448,7 +448,7 @@ namespace StardewModdingAPI.Tests.Core { // arrange // A ◀── B where A doesn't exist - Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest("Mod B", "1.0", new ManifestDependency("Mod A", "1.0", required: false)), allowStatusChange: false); + Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest(id: "Mod B", version: "1.0", dependencies: new IManifestDependency[] { new ManifestDependency("Mod A", "1.0", required: false) }), allowStatusChange: false); // act IModMetadata[] mods = new ModResolver().ProcessDependencies(new[] { modB.Object }, new ModDatabase()).ToArray(); @@ -463,46 +463,26 @@ namespace StardewModdingAPI.Tests.Core ** Private methods *********/ /// <summary>Get a randomised basic manifest.</summary> - /// <param name="adjust">Adjust the generated manifest.</param> - private Manifest GetManifest(Action<Manifest> adjust = null) - { - Manifest manifest = new Manifest - { - Name = Sample.String(), - Author = Sample.String(), - Version = new SemanticVersion(Sample.Int(), Sample.Int(), Sample.Int(), Sample.String()), - Description = Sample.String(), - UniqueID = $"{Sample.String()}.{Sample.String()}", - EntryDll = $"{Sample.String()}.dll" - }; - adjust?.Invoke(manifest); - return manifest; - } - - /// <summary>Get a randomised basic manifest.</summary> - /// <param name="uniqueID">The mod's name and unique ID.</param> - /// <param name="version">The mod version.</param> - /// <param name="adjust">Adjust the generated manifest.</param> - /// <param name="dependencies">The dependencies this mod requires.</param> - private IManifest GetManifest(string uniqueID, string version, Action<Manifest> adjust, params IManifestDependency[] dependencies) - { - return this.GetManifest(manifest => - { - manifest.Name = uniqueID; - manifest.UniqueID = uniqueID; - manifest.Version = new SemanticVersion(version); - manifest.Dependencies = dependencies; - adjust?.Invoke(manifest); - }); - } - - /// <summary>Get a randomised basic manifest.</summary> - /// <param name="uniqueID">The mod's name and unique ID.</param> - /// <param name="version">The mod version.</param> - /// <param name="dependencies">The dependencies this mod requires.</param> - private IManifest GetManifest(string uniqueID, string version, params IManifestDependency[] dependencies) + /// <param name="id">The <see cref="IManifest.UniqueID"/> value, or <c>null</c> for a generated value.</param> + /// <param name="name">The <see cref="IManifest.Name"/> value, or <c>null</c> for a generated value.</param> + /// <param name="version">The <see cref="IManifest.Version"/> value, or <c>null</c> for a generated value.</param> + /// <param name="entryDll">The <see cref="IManifest.EntryDll"/> value, or <c>null</c> for a generated value.</param> + /// <param name="contentPackForID">The <see cref="IManifest.ContentPackFor"/> value.</param> + /// <param name="minimumApiVersion">The <see cref="IManifest.MinimumApiVersion"/> value.</param> + /// <param name="dependencies">The <see cref="IManifest.Dependencies"/> value.</param> + private Manifest GetManifest(string id = null, string name = null, string version = null, string entryDll = null, string contentPackForID = null, string minimumApiVersion = null, IManifestDependency[] dependencies = null) { - return this.GetManifest(uniqueID, version, null, dependencies); + return new Manifest( + uniqueID: id ?? $"{Sample.String()}.{Sample.String()}", + name: name ?? id ?? Sample.String(), + author: Sample.String(), + description: Sample.String(), + version: version != null ? new SemanticVersion(version) : new SemanticVersion(Sample.Int(), Sample.Int(), Sample.Int(), Sample.String()), + entryDll: entryDll ?? $"{Sample.String()}.dll", + contentPackFor: contentPackForID != null ? new ManifestContentPackFor(contentPackForID) : null, + minimumApiVersion: minimumApiVersion != null ? new SemanticVersion(minimumApiVersion) : null, + dependencies: dependencies + ); } /// <summary>Get a randomised basic manifest.</summary> @@ -518,7 +498,7 @@ namespace StardewModdingAPI.Tests.Core /// <param name="allowStatusChange">Whether the code being tested is allowed to change the mod status.</param> private Mock<IModMetadata> GetMetadata(string uniqueID, string[] dependencies, bool allowStatusChange = false) { - IManifest manifest = this.GetManifest(uniqueID, "1.0", dependencies?.Select(dependencyID => (IManifestDependency)new ManifestDependency(dependencyID, null)).ToArray()); + IManifest manifest = this.GetManifest(id: uniqueID, version: "1.0", dependencies: dependencies?.Select(dependencyID => (IManifestDependency)new ManifestDependency(dependencyID, null)).ToArray()); return this.GetMetadata(manifest, allowStatusChange); } diff --git a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs index f1a72012..b797393b 100644 --- a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs +++ b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; using NUnit.Framework; using StardewModdingAPI.Framework; +using StardewModdingAPI.Toolkit.Serialisation.Models; namespace StardewModdingAPI.Tests.Utilities { diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs index ee6df1ec..4a4adb90 100644 --- a/src/SMAPI/Framework/ContentPack.cs +++ b/src/SMAPI/Framework/ContentPack.cs @@ -2,7 +2,7 @@ using System; using System.IO; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; -using StardewModdingAPI.Framework.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Utilities; using xTile; diff --git a/src/SMAPI/Framework/InternalExtensions.cs b/src/SMAPI/Framework/InternalExtensions.cs index bff4807c..ff3925fb 100644 --- a/src/SMAPI/Framework/InternalExtensions.cs +++ b/src/SMAPI/Framework/InternalExtensions.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Reflection; using Microsoft.Xna.Framework.Graphics; -using Newtonsoft.Json.Linq; using StardewModdingAPI.Framework.Reflection; using StardewValley; @@ -91,20 +90,5 @@ namespace StardewModdingAPI.Framework // get result return reflection.GetField<bool>(Game1.spriteBatch, fieldName).GetValue(); } - - /**** - ** Json.NET - ****/ - /// <summary>Get a JSON field value from a case-insensitive field name. This will check for an exact match first, then search without case sensitivity.</summary> - /// <typeparam name="T">The value type.</typeparam> - /// <param name="obj">The JSON object to search.</param> - /// <param name="fieldName">The field name.</param> - public static T ValueIgnoreCase<T>(this JObject obj, string fieldName) - { - JToken token = obj.GetValue(fieldName, StringComparison.InvariantCultureIgnoreCase); - return token != null - ? token.Value<T>() - : default(T); - } } } diff --git a/src/SMAPI/Framework/ModHelpers/ModHelper.cs b/src/SMAPI/Framework/ModHelpers/ModHelper.cs index e8726938..18904857 100644 --- a/src/SMAPI/Framework/ModHelpers/ModHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModHelper.cs @@ -5,7 +5,7 @@ using System.Linq; using StardewModdingAPI.Events; using StardewModdingAPI.Framework.Input; using StardewModdingAPI.Framework.Models; -using StardewModdingAPI.Framework.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Utilities; namespace StardewModdingAPI.Framework.ModHelpers @@ -179,16 +179,14 @@ namespace StardewModdingAPI.Framework.ModHelpers throw new ArgumentException($"Can't create content pack for directory path '{directoryPath}' because no such directory exists."); // create manifest - IManifest manifest = new Manifest - { - Name = name, - Author = author, - Description = description, - Version = version, - UniqueID = id, - UpdateKeys = new string[0], - ContentPackFor = new ManifestContentPackFor { UniqueID = this.ModID } - }; + IManifest manifest = new Manifest( + uniqueID: id, + name: name, + author: author, + description: description, + version: version, + contentPackFor: new ManifestContentPackFor(this.ModID) + ); // create content pack return this.CreateContentPack(directoryPath, manifest); diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index d46caa55..ddc8650c 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; -using StardewModdingAPI.Framework.Exceptions; using StardewModdingAPI.Framework.ModData; using StardewModdingAPI.Framework.Models; -using StardewModdingAPI.Framework.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Utilities; +using ToolkitManifest = StardewModdingAPI.Toolkit.Serialisation.Models.Manifest; namespace StardewModdingAPI.Framework.ModLoading { @@ -28,25 +28,28 @@ namespace StardewModdingAPI.Framework.ModLoading { // read file Manifest manifest = null; - string path = Path.Combine(modDir.FullName, "manifest.json"); string error = null; - try { - manifest = jsonHelper.ReadJsonFile<Manifest>(path); - if (manifest == null) + string path = Path.Combine(modDir.FullName, "manifest.json"); + try { - error = File.Exists(path) - ? "its manifest is invalid." - : "it doesn't have a manifest."; + ToolkitManifest rawManifest = jsonHelper.ReadJsonFile<ToolkitManifest>(path); + if (rawManifest == null) + { + error = File.Exists(path) + ? "its manifest is invalid." + : "it doesn't have a manifest."; + } + manifest = new Manifest(rawManifest); + } + catch (SParseException ex) + { + error = $"parsing its manifest failed: {ex.Message}"; + } + catch (Exception ex) + { + error = $"parsing its manifest failed:\n{ex.GetLogSummary()}"; } - } - catch (SParseException ex) - { - error = $"parsing its manifest failed: {ex.Message}"; - } - catch (Exception ex) - { - error = $"parsing its manifest failed:\n{ex.GetLogSummary()}"; } // parse internal data record (if any) diff --git a/src/SMAPI/Framework/Models/Manifest.cs b/src/SMAPI/Framework/Models/Manifest.cs index f5867cf3..92ffe0dc 100644 --- a/src/SMAPI/Framework/Models/Manifest.cs +++ b/src/SMAPI/Framework/Models/Manifest.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; -using StardewModdingAPI.Framework.Serialisation.SmapiConverters; namespace StardewModdingAPI.Framework.Models { @@ -11,39 +11,87 @@ namespace StardewModdingAPI.Framework.Models ** Accessors *********/ /// <summary>The mod name.</summary> - public string Name { get; set; } + public string Name { get; } /// <summary>A brief description of the mod.</summary> - public string Description { get; set; } + public string Description { get; } /// <summary>The mod author's name.</summary> - public string Author { get; set; } + public string Author { get; } /// <summary>The mod version.</summary> - public ISemanticVersion Version { get; set; } + public ISemanticVersion Version { get; } /// <summary>The minimum SMAPI version required by this mod, if any.</summary> - public ISemanticVersion MinimumApiVersion { get; set; } + public ISemanticVersion MinimumApiVersion { get; } /// <summary>The name of the DLL in the directory that has the <see cref="IMod.Entry"/> method. Mutually exclusive with <see cref="ContentPackFor"/>.</summary> - public string EntryDll { get; set; } + public string EntryDll { get; } /// <summary>The mod which will read this as a content pack. Mutually exclusive with <see cref="IManifest.EntryDll"/>.</summary> - [JsonConverter(typeof(ManifestContentPackForConverter))] - public IManifestContentPackFor ContentPackFor { get; set; } + public IManifestContentPackFor ContentPackFor { get; } /// <summary>The other mods that must be loaded before this mod.</summary> - [JsonConverter(typeof(ManifestDependencyArrayConverter))] - public IManifestDependency[] Dependencies { get; set; } + public IManifestDependency[] Dependencies { get; } /// <summary>The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</summary> public string[] UpdateKeys { get; set; } /// <summary>The unique mod ID.</summary> - public string UniqueID { get; set; } + public string UniqueID { get; } /// <summary>Any manifest fields which didn't match a valid field.</summary> [JsonExtensionData] - public IDictionary<string, object> ExtraFields { get; set; } + public IDictionary<string, object> ExtraFields { get; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="manifest">The toolkit manifest.</param> + public Manifest(Toolkit.Serialisation.Models.Manifest manifest) + : this( + uniqueID: manifest.UniqueID, + name: manifest.Name, + author: manifest.Author, + description: manifest.Description, + version: manifest.Version != null ? new SemanticVersion(manifest.Version) : null, + entryDll: manifest.EntryDll, + minimumApiVersion: manifest.MinimumApiVersion != null ? new SemanticVersion(manifest.MinimumApiVersion) : null, + contentPackFor: manifest.ContentPackFor != null ? new ManifestContentPackFor(manifest.ContentPackFor) : null, + dependencies: manifest.Dependencies?.Select(p => p != null ? (IManifestDependency)new ManifestDependency(p) : null).ToArray(), + updateKeys: manifest.UpdateKeys, + extraFields: manifest.ExtraFields + ) + { } + + /// <summary>Construct an instance for a transitional content pack.</summary> + /// <param name="uniqueID">The unique mod ID.</param> + /// <param name="name">The mod name.</param> + /// <param name="author">The mod author's name.</param> + /// <param name="description">A brief description of the mod.</param> + /// <param name="version">The mod version.</param> + /// <param name="entryDll">The name of the DLL in the directory that has the <see cref="IMod.Entry"/> method. Mutually exclusive with <paramref name="contentPackFor"/>.</param> + /// <param name="minimumApiVersion">The minimum SMAPI version required by this mod, if any.</param> + /// <param name="contentPackFor">The modID which will read this as a content pack. Mutually exclusive with <paramref name="entryDll"/>.</param> + /// <param name="dependencies">The other mods that must be loaded before this mod.</param> + /// <param name="updateKeys">The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</param> + /// <param name="extraFields">Any manifest fields which didn't match a valid field.</param> + public Manifest(string uniqueID, string name, string author, string description, ISemanticVersion version, string entryDll = null, ISemanticVersion minimumApiVersion = null, IManifestContentPackFor contentPackFor = null, IManifestDependency[] dependencies = null, string[] updateKeys = null, IDictionary<string, object> extraFields = null) + { + this.Name = name; + this.Author = author; + this.Description = description; + this.Version = version; + this.UniqueID = uniqueID; + this.UpdateKeys = new string[0]; + this.EntryDll = entryDll; + this.ContentPackFor = contentPackFor; + this.MinimumApiVersion = minimumApiVersion; + this.Dependencies = dependencies ?? new IManifestDependency[0]; + this.UpdateKeys = updateKeys ?? new string[0]; + this.ExtraFields = extraFields; + } } } diff --git a/src/SMAPI/Framework/Models/ManifestContentPackFor.cs b/src/SMAPI/Framework/Models/ManifestContentPackFor.cs index 7836bbcc..cdad8893 100644 --- a/src/SMAPI/Framework/Models/ManifestContentPackFor.cs +++ b/src/SMAPI/Framework/Models/ManifestContentPackFor.cs @@ -7,9 +7,30 @@ namespace StardewModdingAPI.Framework.Models ** Accessors *********/ /// <summary>The unique ID of the mod which can read this content pack.</summary> - public string UniqueID { get; set; } + public string UniqueID { get; } /// <summary>The minimum required version (if any).</summary> - public ISemanticVersion MinimumVersion { get; set; } + public ISemanticVersion MinimumVersion { get; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="contentPackFor">The toolkit instance.</param> + public ManifestContentPackFor(Toolkit.Serialisation.Models.ManifestContentPackFor contentPackFor) + { + this.UniqueID = contentPackFor.UniqueID; + this.MinimumVersion = new SemanticVersion(contentPackFor.MinimumVersion); + } + + /// <summary>Construct an instance.</summary> + /// <param name="uniqueID">The unique ID of the mod which can read this content pack.</param> + /// <param name="minimumVersion">The minimum required version (if any).</param> + public ManifestContentPackFor(string uniqueID, ISemanticVersion minimumVersion = null) + { + this.UniqueID = uniqueID; + this.MinimumVersion = minimumVersion; + } } } diff --git a/src/SMAPI/Framework/Models/ManifestDependency.cs b/src/SMAPI/Framework/Models/ManifestDependency.cs index 97f0775a..e92597f3 100644 --- a/src/SMAPI/Framework/Models/ManifestDependency.cs +++ b/src/SMAPI/Framework/Models/ManifestDependency.cs @@ -7,19 +7,24 @@ namespace StardewModdingAPI.Framework.Models ** Accessors *********/ /// <summary>The unique mod ID to require.</summary> - public string UniqueID { get; set; } + public string UniqueID { get; } /// <summary>The minimum required version (if any).</summary> - public ISemanticVersion MinimumVersion { get; set; } + public ISemanticVersion MinimumVersion { get; } /// <summary>Whether the dependency must be installed to use the mod.</summary> - public bool IsRequired { get; set; } + public bool IsRequired { get; } /********* ** Public methods *********/ /// <summary>Construct an instance.</summary> + /// <param name="dependency">The toolkit instance.</param> + public ManifestDependency(Toolkit.Serialisation.Models.ManifestDependency dependency) + : this(dependency.UniqueID, dependency.MinimumVersion?.ToString(), dependency.IsRequired) { } + + /// <summary>Construct an instance.</summary> /// <param name="uniqueID">The unique mod ID to require.</param> /// <param name="minimumVersion">The minimum required version (if any).</param> /// <param name="required">Whether the dependency must be installed to use the mod.</param> diff --git a/src/SMAPI/Framework/Serialisation/CrossplatformConverters/ColorConverter.cs b/src/SMAPI/Framework/Serialisation/ColorConverter.cs index f1b2f04f..c27065bf 100644 --- a/src/SMAPI/Framework/Serialisation/CrossplatformConverters/ColorConverter.cs +++ b/src/SMAPI/Framework/Serialisation/ColorConverter.cs @@ -1,9 +1,10 @@ using System; using Microsoft.Xna.Framework; using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Exceptions; +using StardewModdingAPI.Toolkit.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation.Converters; -namespace StardewModdingAPI.Framework.Serialisation.CrossplatformConverters +namespace StardewModdingAPI.Framework.Serialisation { /// <summary>Handles deserialisation of <see cref="Color"/> for crossplatform compatibility.</summary> /// <remarks> diff --git a/src/SMAPI/Framework/Serialisation/CrossplatformConverters/PointConverter.cs b/src/SMAPI/Framework/Serialisation/PointConverter.cs index 434b7ea5..fbc857d2 100644 --- a/src/SMAPI/Framework/Serialisation/CrossplatformConverters/PointConverter.cs +++ b/src/SMAPI/Framework/Serialisation/PointConverter.cs @@ -1,9 +1,10 @@ using System; using Microsoft.Xna.Framework; using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Exceptions; +using StardewModdingAPI.Toolkit.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation.Converters; -namespace StardewModdingAPI.Framework.Serialisation.CrossplatformConverters +namespace StardewModdingAPI.Framework.Serialisation { /// <summary>Handles deserialisation of <see cref="PointConverter"/> for crossplatform compatibility.</summary> /// <remarks> diff --git a/src/SMAPI/Framework/Serialisation/CrossplatformConverters/RectangleConverter.cs b/src/SMAPI/Framework/Serialisation/RectangleConverter.cs index 62bc8637..4f55cc32 100644 --- a/src/SMAPI/Framework/Serialisation/CrossplatformConverters/RectangleConverter.cs +++ b/src/SMAPI/Framework/Serialisation/RectangleConverter.cs @@ -2,9 +2,10 @@ using System; using System.Text.RegularExpressions; using Microsoft.Xna.Framework; using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Exceptions; +using StardewModdingAPI.Toolkit.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation.Converters; -namespace StardewModdingAPI.Framework.Serialisation.CrossplatformConverters +namespace StardewModdingAPI.Framework.Serialisation { /// <summary>Handles deserialisation of <see cref="Rectangle"/> for crossplatform compatibility.</summary> /// <remarks> diff --git a/src/SMAPI/IManifest.cs b/src/SMAPI/IManifest.cs index 183ac105..6c07d374 100644 --- a/src/SMAPI/IManifest.cs +++ b/src/SMAPI/IManifest.cs @@ -36,7 +36,7 @@ namespace StardewModdingAPI IManifestDependency[] Dependencies { get; } /// <summary>The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</summary> - string[] UpdateKeys { get; set; } + string[] UpdateKeys { get; } /// <summary>Any manifest fields which didn't match a valid field.</summary> IDictionary<string, object> ExtraFields { get; } diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs index 5c5137ef..e55a96b2 100644 --- a/src/SMAPI/Program.cs +++ b/src/SMAPI/Program.cs @@ -10,6 +10,7 @@ using System.Runtime.ExceptionServices; using System.Security; using System.Text.RegularExpressions; using System.Threading; +using Microsoft.Xna.Framework.Input; #if SMAPI_FOR_WINDOWS using System.Windows.Forms; #endif @@ -27,8 +28,11 @@ using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Framework.Serialisation; using StardewModdingAPI.Internal; using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Serialisation; +using StardewModdingAPI.Toolkit.Serialisation.Converters; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; +using Keys = System.Windows.Forms.Keys; using Monitor = StardewModdingAPI.Framework.Monitor; using SObject = StardewValley.Object; using ThreadState = System.Threading.ThreadState; @@ -148,6 +152,18 @@ namespace StardewModdingAPI }; this.EventManager = new EventManager(this.Monitor, this.ModRegistry); + // init JSON parser + JsonConverter[] converters = { + new StringEnumConverter<Buttons>(), + new StringEnumConverter<Keys>(), + new StringEnumConverter<SButton>(), + new ColorConverter(), + new PointConverter(), + new RectangleConverter() + }; + foreach (JsonConverter converter in converters) + this.JsonHelper.JsonSettings.Converters.Add(converter); + // hook up events ContentEvents.Init(this.EventManager); ControlEvents.Init(this.EventManager); @@ -1093,7 +1109,7 @@ namespace StardewModdingAPI /// <param name="mods">The mods for which to reload translations.</param> private void ReloadTranslations(IEnumerable<IModMetadata> mods) { - JsonHelper jsonHelper = new JsonHelper(); + JsonHelper jsonHelper = this.JsonHelper; foreach (IModMetadata metadata in mods) { if (metadata.IsContentPack) diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index a5ccc62d..3c953ec5 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -103,6 +103,9 @@ <Compile Include="Framework\ContentManagers\GameContentManager.cs" /> <Compile Include="Framework\ContentManagers\IContentManager.cs" /> <Compile Include="Framework\ContentManagers\ModContentManager.cs" /> + <Compile Include="Framework\Serialisation\ColorConverter.cs" /> + <Compile Include="Framework\Serialisation\PointConverter.cs" /> + <Compile Include="Framework\Serialisation\RectangleConverter.cs" /> <Compile Include="Framework\Events\ModEventsBase.cs" /> <Compile Include="Framework\Events\EventManager.cs" /> <Compile Include="Events\IModEvents.cs" /> @@ -114,16 +117,17 @@ <Compile Include="Framework\Events\ModEvents.cs" /> <Compile Include="Framework\Events\ModInputEvents.cs" /> <Compile Include="Framework\Input\GamePadStateBuilder.cs" /> + <Compile Include="Framework\Models\Manifest.cs" /> + <Compile Include="Framework\Models\ManifestContentPackFor.cs" /> + <Compile Include="Framework\Models\ManifestDependency.cs" /> <Compile Include="Framework\ModHelpers\InputHelper.cs" /> <Compile Include="IInputHelper.cs" /> <Compile Include="Framework\Input\SInputState.cs" /> <Compile Include="Framework\Input\InputStatus.cs" /> - <Compile Include="Framework\LegacyManifestVersion.cs" /> <Compile Include="Framework\ModData\ModDatabase.cs" /> <Compile Include="Framework\ModData\ModDataField.cs" /> <Compile Include="Framework\ModData\ModDataFieldKey.cs" /> <Compile Include="Framework\ModData\ParsedModDataRecord.cs" /> - <Compile Include="Framework\Models\ManifestContentPackFor.cs" /> <Compile Include="Framework\Models\SMetadata.cs" /> <Compile Include="Framework\ModHelpers\MultiplayerHelper.cs" /> <Compile Include="Framework\ModLoading\Finders\EventFinder.cs" /> @@ -151,13 +155,6 @@ <Compile Include="Framework\Reflection\InterfaceProxyBuilder.cs" /> <Compile Include="Framework\Reflection\InterfaceProxyFactory.cs" /> <Compile Include="Framework\RewriteFacades\SpriteBatchMethods.cs" /> - <Compile Include="Framework\Serialisation\SmapiConverters\ManifestContentPackForConverter.cs" /> - <Compile Include="Framework\Serialisation\SmapiConverters\ManifestDependencyArrayConverter.cs" /> - <Compile Include="Framework\Serialisation\SmapiConverters\SemanticVersionConverter.cs" /> - <Compile Include="Framework\Serialisation\SimpleReadOnlyConverter.cs" /> - <Compile Include="Framework\Serialisation\CrossplatformConverters\RectangleConverter.cs" /> - <Compile Include="Framework\Serialisation\CrossplatformConverters\ColorConverter.cs" /> - <Compile Include="Framework\Serialisation\CrossplatformConverters\PointConverter.cs" /> <Compile Include="Framework\SMultiplayer.cs" /> <Compile Include="Framework\StateTracking\Comparers\EquatableComparer.cs" /> <Compile Include="Framework\StateTracking\Comparers\ObjectReferenceComparer.cs" /> @@ -235,16 +232,12 @@ <Compile Include="Context.cs" /> <Compile Include="Framework\Logging\ConsoleInterceptionManager.cs" /> <Compile Include="Framework\Logging\InterceptingTextWriter.cs" /> - <Compile Include="Framework\Models\ManifestDependency.cs" /> <Compile Include="Framework\ModData\ModStatus.cs" /> <Compile Include="Framework\Models\SConfig.cs" /> <Compile Include="Framework\ModLoading\ModMetadata.cs" /> <Compile Include="Framework\Reflection\ReflectedProperty.cs" /> <Compile Include="Framework\RequestExitDelegate.cs" /> <Compile Include="Framework\ContentCoordinator.cs" /> - <Compile Include="Framework\Exceptions\SParseException.cs" /> - <Compile Include="Framework\Serialisation\JsonHelper.cs" /> - <Compile Include="Framework\Serialisation\SmapiConverters\StringEnumConverter.cs" /> <Compile Include="IAssetEditor.cs" /> <Compile Include="IAssetInfo.cs" /> <Compile Include="IAssetLoader.cs" /> @@ -283,7 +276,6 @@ <Compile Include="Events\ChangeType.cs" /> <Compile Include="Events\ItemStackChange.cs" /> <Compile Include="Framework\Monitor.cs" /> - <Compile Include="Framework\Models\Manifest.cs" /> <Compile Include="Metadata\InstructionMetadata.cs" /> <Compile Include="Mod.cs" /> <Compile Include="PatchMode.cs" /> diff --git a/src/SMAPI/Framework/Serialisation/SmapiConverters/ManifestContentPackForConverter.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/ManifestContentPackForConverter.cs index af7558f6..232c22a7 100644 --- a/src/SMAPI/Framework/Serialisation/SmapiConverters/ManifestContentPackForConverter.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/ManifestContentPackForConverter.cs @@ -1,11 +1,11 @@ using System; using Newtonsoft.Json; -using StardewModdingAPI.Framework.Models; +using StardewModdingAPI.Toolkit.Serialisation.Models; -namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters +namespace StardewModdingAPI.Toolkit.Serialisation.Converters { - /// <summary>Handles deserialisation of <see cref="IManifestContentPackFor"/> arrays.</summary> - internal class ManifestContentPackForConverter : JsonConverter + /// <summary>Handles deserialisation of <see cref="ManifestContentPackFor"/> arrays.</summary> + public class ManifestContentPackForConverter : JsonConverter { /********* ** Accessors @@ -21,7 +21,7 @@ namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters /// <param name="objectType">The object type.</param> public override bool CanConvert(Type objectType) { - return objectType == typeof(IManifestContentPackFor[]); + return objectType == typeof(ManifestContentPackFor[]); } diff --git a/src/SMAPI/Framework/Serialisation/SmapiConverters/ManifestDependencyArrayConverter.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/ManifestDependencyArrayConverter.cs index 4150d5fb..0a304ee3 100644 --- a/src/SMAPI/Framework/Serialisation/SmapiConverters/ManifestDependencyArrayConverter.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/ManifestDependencyArrayConverter.cs @@ -2,11 +2,11 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Models; +using StardewModdingAPI.Toolkit.Serialisation.Models; -namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters +namespace StardewModdingAPI.Toolkit.Serialisation.Converters { - /// <summary>Handles deserialisation of <see cref="IManifestDependency"/> arrays.</summary> + /// <summary>Handles deserialisation of <see cref="ManifestDependency"/> arrays.</summary> internal class ManifestDependencyArrayConverter : JsonConverter { /********* @@ -23,7 +23,7 @@ namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters /// <param name="objectType">The object type.</param> public override bool CanConvert(Type objectType) { - return objectType == typeof(IManifestDependency[]); + return objectType == typeof(ManifestDependency[]); } @@ -37,12 +37,12 @@ namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters /// <param name="serializer">The calling serializer.</param> public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - List<IManifestDependency> result = new List<IManifestDependency>(); + List<ManifestDependency> result = new List<ManifestDependency>(); foreach (JObject obj in JArray.Load(reader).Children<JObject>()) { - string uniqueID = obj.ValueIgnoreCase<string>(nameof(IManifestDependency.UniqueID)); - string minVersion = obj.ValueIgnoreCase<string>(nameof(IManifestDependency.MinimumVersion)); - bool required = obj.ValueIgnoreCase<bool?>(nameof(IManifestDependency.IsRequired)) ?? true; + string uniqueID = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.UniqueID)); + string minVersion = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.MinimumVersion)); + bool required = obj.ValueIgnoreCase<bool?>(nameof(ManifestDependency.IsRequired)) ?? true; result.Add(new ManifestDependency(uniqueID, minVersion, required)); } return result.ToArray(); diff --git a/src/SMAPI/Framework/Serialisation/SmapiConverters/SemanticVersionConverter.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/SemanticVersionConverter.cs index 7ee7e29b..2075d27e 100644 --- a/src/SMAPI/Framework/Serialisation/SmapiConverters/SemanticVersionConverter.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/SemanticVersionConverter.cs @@ -1,10 +1,10 @@ using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Exceptions; +using StardewModdingAPI.Toolkit.Serialisation.Models; -namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters +namespace StardewModdingAPI.Toolkit.Serialisation.Converters { /// <summary>Handles deserialisation of <see cref="SemanticVersion"/>.</summary> - internal class SemanticVersionConverter : SimpleReadOnlyConverter<ISemanticVersion> + internal class SemanticVersionConverter : SimpleReadOnlyConverter<SemanticVersion> { /********* ** Protected methods @@ -12,25 +12,25 @@ namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters /// <summary>Read a JSON object.</summary> /// <param name="obj">The JSON object to read.</param> /// <param name="path">The path to the current JSON node.</param> - protected override ISemanticVersion ReadObject(JObject obj, string path) + protected override SemanticVersion ReadObject(JObject obj, string path) { - int major = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MajorVersion)); - int minor = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MinorVersion)); - int patch = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.PatchVersion)); - string build = obj.ValueIgnoreCase<string>(nameof(ISemanticVersion.Build)); + int major = obj.ValueIgnoreCase<int>("MajorVersion"); + int minor = obj.ValueIgnoreCase<int>("MinorVersion"); + int patch = obj.ValueIgnoreCase<int>("PatchVersion"); + string build = obj.ValueIgnoreCase<string>("Build"); return new LegacyManifestVersion(major, minor, patch, build); } /// <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 ISemanticVersion ReadString(string str, string path) + protected override SemanticVersion ReadString(string str, string path) { if (string.IsNullOrWhiteSpace(str)) return null; if (!SemanticVersion.TryParse(str, out ISemanticVersion version)) throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta (path: {path})."); - return version; + return (SemanticVersion)version; } } } diff --git a/src/SMAPI/Framework/Serialisation/SimpleReadOnlyConverter.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/SimpleReadOnlyConverter.cs index 5765ad96..5e0b0f4a 100644 --- a/src/SMAPI/Framework/Serialisation/SimpleReadOnlyConverter.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/SimpleReadOnlyConverter.cs @@ -1,9 +1,8 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using StardewModdingAPI.Framework.Exceptions; -namespace StardewModdingAPI.Framework.Serialisation +namespace StardewModdingAPI.Toolkit.Serialisation.Converters { /// <summary>The base implementation for simplified converters which deserialise <typeparamref name="T"/> without overriding serialisation.</summary> /// <typeparam name="T">The type to deserialise.</typeparam> diff --git a/src/SMAPI/Framework/Serialisation/SmapiConverters/StringEnumConverter.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/StringEnumConverter.cs index c88ac834..13e6e3a1 100644 --- a/src/SMAPI/Framework/Serialisation/SmapiConverters/StringEnumConverter.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Converters/StringEnumConverter.cs @@ -1,7 +1,7 @@ using System; using Newtonsoft.Json.Converters; -namespace StardewModdingAPI.Framework.Serialisation.SmapiConverters +namespace StardewModdingAPI.Toolkit.Serialisation.Converters { /// <summary>A variant of <see cref="StringEnumConverter"/> which only converts a specified enum.</summary> /// <typeparam name="T">The enum type.</typeparam> diff --git a/src/StardewModdingAPI.Toolkit/Serialisation/InternalExtensions.cs b/src/StardewModdingAPI.Toolkit/Serialisation/InternalExtensions.cs new file mode 100644 index 00000000..12b2c933 --- /dev/null +++ b/src/StardewModdingAPI.Toolkit/Serialisation/InternalExtensions.cs @@ -0,0 +1,21 @@ +using System; +using Newtonsoft.Json.Linq; + +namespace StardewModdingAPI.Toolkit.Serialisation +{ + /// <summary>Provides extension methods for parsing JSON.</summary> + public static class JsonExtensions + { + /// <summary>Get a JSON field value from a case-insensitive field name. This will check for an exact match first, then search without case sensitivity.</summary> + /// <typeparam name="T">The value type.</typeparam> + /// <param name="obj">The JSON object to search.</param> + /// <param name="fieldName">The field name.</param> + public static T ValueIgnoreCase<T>(this JObject obj, string fieldName) + { + JToken token = obj.GetValue(fieldName, StringComparison.InvariantCultureIgnoreCase); + return token != null + ? token.Value<T>() + : default(T); + } + } +} diff --git a/src/SMAPI/Framework/Serialisation/JsonHelper.cs b/src/StardewModdingAPI.Toolkit/Serialisation/JsonHelper.cs index 6cba343e..00f334ad 100644 --- a/src/SMAPI/Framework/Serialisation/JsonHelper.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/JsonHelper.cs @@ -1,39 +1,23 @@ using System; using System.Collections.Generic; using System.IO; -using Microsoft.Xna.Framework.Input; using Newtonsoft.Json; -using StardewModdingAPI.Framework.Serialisation.CrossplatformConverters; -using StardewModdingAPI.Framework.Serialisation.SmapiConverters; +using StardewModdingAPI.Toolkit.Serialisation.Converters; -namespace StardewModdingAPI.Framework.Serialisation +namespace StardewModdingAPI.Toolkit.Serialisation { /// <summary>Encapsulates SMAPI's JSON file parsing.</summary> - internal class JsonHelper + public class JsonHelper { /********* ** Accessors *********/ /// <summary>The JSON settings to use when serialising and deserialising files.</summary> - private readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings + public JsonSerializerSettings JsonSettings { get; } = new JsonSerializerSettings { Formatting = Formatting.Indented, ObjectCreationHandling = ObjectCreationHandling.Replace, // avoid issue where default ICollection<T> values are duplicated each time the config is loaded - Converters = new List<JsonConverter> - { - // SMAPI types - new SemanticVersionConverter(), - - // enums - new StringEnumConverter<Buttons>(), - new StringEnumConverter<Keys>(), - new StringEnumConverter<SButton>(), - - // crossplatform compatibility - new ColorConverter(), - new PointConverter(), - new RectangleConverter() - } + Converters = new List<JsonConverter> { new SemanticVersionConverter() } }; diff --git a/src/SMAPI/Framework/LegacyManifestVersion.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Models/LegacyManifestVersion.cs index 454b9137..12f6755b 100644 --- a/src/SMAPI/Framework/LegacyManifestVersion.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Models/LegacyManifestVersion.cs @@ -1,9 +1,9 @@ using Newtonsoft.Json; -namespace StardewModdingAPI.Framework +namespace StardewModdingAPI.Toolkit.Serialisation.Models { - /// <summary>An implementation of <see cref="ISemanticVersion"/> that hamdles the legacy <see cref="IManifest"/> version format.</summary> - internal class LegacyManifestVersion : SemanticVersion + /// <summary>An implementation of <see cref="ISemanticVersion"/> that hamdles the legacy <see cref="Manifest"/> version format.</summary> + public class LegacyManifestVersion : SemanticVersion { /********* ** Public methods diff --git a/src/StardewModdingAPI.Toolkit/Serialisation/Models/Manifest.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Models/Manifest.cs new file mode 100644 index 00000000..68987dd1 --- /dev/null +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Models/Manifest.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using StardewModdingAPI.Toolkit.Serialisation.Converters; + +namespace StardewModdingAPI.Toolkit.Serialisation.Models +{ + /// <summary>A manifest which describes a mod for SMAPI.</summary> + public class Manifest + { + /********* + ** Accessors + *********/ + /// <summary>The mod name.</summary> + public string Name { get; set; } + + /// <summary>A brief description of the mod.</summary> + public string Description { get; set; } + + /// <summary>The mod author's name.</summary> + public string Author { get; set; } + + /// <summary>The mod version.</summary> + public SemanticVersion Version { get; set; } + + /// <summary>The minimum SMAPI version required by this mod, if any.</summary> + public SemanticVersion MinimumApiVersion { get; set; } + + /// <summary>The name of the DLL in the directory that has the <c>Entry</c> method. Mutually exclusive with <see cref="ContentPackFor"/>.</summary> + public string EntryDll { get; set; } + + /// <summary>The mod which will read this as a content pack. Mutually exclusive with <see cref="Manifest.EntryDll"/>.</summary> + [JsonConverter(typeof(ManifestContentPackForConverter))] + public ManifestContentPackFor ContentPackFor { get; set; } + + /// <summary>The other mods that must be loaded before this mod.</summary> + [JsonConverter(typeof(ManifestDependencyArrayConverter))] + public ManifestDependency[] Dependencies { get; set; } + + /// <summary>The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</summary> + public string[] UpdateKeys { get; set; } + + /// <summary>The unique mod ID.</summary> + public string UniqueID { get; set; } + + /// <summary>Any manifest fields which didn't match a valid field.</summary> + [JsonExtensionData] + public IDictionary<string, object> ExtraFields { get; set; } + } +} diff --git a/src/StardewModdingAPI.Toolkit/Serialisation/Models/ManifestContentPackFor.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Models/ManifestContentPackFor.cs new file mode 100644 index 00000000..00546533 --- /dev/null +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Models/ManifestContentPackFor.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Toolkit.Serialisation.Models +{ + /// <summary>Indicates which mod can read the content pack represented by the containing manifest.</summary> + public class ManifestContentPackFor + { + /********* + ** Accessors + *********/ + /// <summary>The unique ID of the mod which can read this content pack.</summary> + public string UniqueID { get; set; } + + /// <summary>The minimum required version (if any).</summary> + public SemanticVersion MinimumVersion { get; set; } + } +} diff --git a/src/StardewModdingAPI.Toolkit/Serialisation/Models/ManifestDependency.cs b/src/StardewModdingAPI.Toolkit/Serialisation/Models/ManifestDependency.cs new file mode 100644 index 00000000..d902f9ac --- /dev/null +++ b/src/StardewModdingAPI.Toolkit/Serialisation/Models/ManifestDependency.cs @@ -0,0 +1,35 @@ +namespace StardewModdingAPI.Toolkit.Serialisation.Models +{ + /// <summary>A mod dependency listed in a mod manifest.</summary> + public class ManifestDependency + { + /********* + ** Accessors + *********/ + /// <summary>The unique mod ID to require.</summary> + public string UniqueID { get; set; } + + /// <summary>The minimum required version (if any).</summary> + public SemanticVersion MinimumVersion { get; set; } + + /// <summary>Whether the dependency must be installed to use the mod.</summary> + public bool IsRequired { get; set; } + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="uniqueID">The unique mod ID to require.</param> + /// <param name="minimumVersion">The minimum required version (if any).</param> + /// <param name="required">Whether the dependency must be installed to use the mod.</param> + public ManifestDependency(string uniqueID, string minimumVersion, bool required = true) + { + this.UniqueID = uniqueID; + this.MinimumVersion = !string.IsNullOrWhiteSpace(minimumVersion) + ? new SemanticVersion(minimumVersion) + : null; + this.IsRequired = required; + } + } +} diff --git a/src/SMAPI/Framework/Exceptions/SParseException.cs b/src/StardewModdingAPI.Toolkit/Serialisation/SParseException.cs index f7133ee7..61a7b305 100644 --- a/src/SMAPI/Framework/Exceptions/SParseException.cs +++ b/src/StardewModdingAPI.Toolkit/Serialisation/SParseException.cs @@ -1,6 +1,6 @@ -using System; +using System; -namespace StardewModdingAPI.Framework.Exceptions +namespace StardewModdingAPI.Toolkit.Serialisation { /// <summary>A format exception which provides a user-facing error message.</summary> internal class SParseException : FormatException |