summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/StardewModdingAPI/Framework')
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs49
-rw-r--r--src/StardewModdingAPI/Framework/Models/ModCompatibility.cs55
-rw-r--r--src/StardewModdingAPI/Framework/Models/ModDataRecord.cs30
-rw-r--r--src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs22
4 files changed, 108 insertions, 48 deletions
diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
index 3674faec..31ba5bc5 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
@@ -61,14 +61,7 @@ namespace StardewModdingAPI.Framework.ModLoading
string key = !string.IsNullOrWhiteSpace(manifest.UniqueID) ? manifest.UniqueID : manifest.EntryDll;
// get data record
- dataRecord = (
- from mod in dataRecords
- where
- mod.ID.Matches(key, manifest)
- && (mod.LowerVersion == null || !manifest.Version.IsOlderThan(mod.LowerVersion))
- && (mod.UpperVersion == null || !manifest.Version.IsNewerThan(mod.UpperVersion))
- select mod
- ).FirstOrDefault();
+ dataRecord = dataRecords.FirstOrDefault(record => record.ID.Matches(key, manifest));
}
// build metadata
@@ -98,28 +91,26 @@ namespace StardewModdingAPI.Framework.ModLoading
continue;
// validate compatibility
+ ModCompatibility compatibility = mod.DataRecord?.GetCompatibility(mod.Manifest.Version);
+ switch (compatibility?.Status)
{
- ModDataRecord dataRecord = mod.DataRecord;
- switch (dataRecord?.Status)
- {
- case ModStatus.Obsolete:
- mod.SetStatus(ModMetadataStatus.Failed, $"it's obsolete: {dataRecord.ReasonPhrase}");
- continue;
-
- case ModStatus.AssumeBroken:
- {
- string reasonPhrase = dataRecord.ReasonPhrase ?? "it's no longer compatible";
- string error = $"{reasonPhrase}. Please check for a ";
- if (mod.Manifest.Version.Equals(dataRecord.UpperVersion) && dataRecord.UpperVersionLabel == null)
- error += "newer version";
- else
- error += $"version newer than {dataRecord.UpperVersionLabel ?? dataRecord.UpperVersion.ToString()}";
- error += " at " + string.Join(" or ", dataRecord.UpdateUrls);
-
- mod.SetStatus(ModMetadataStatus.Failed, error);
- continue;
- }
- }
+ case ModStatus.Obsolete:
+ mod.SetStatus(ModMetadataStatus.Failed, $"it's obsolete: {compatibility.ReasonPhrase}");
+ continue;
+
+ case ModStatus.AssumeBroken:
+ {
+ string reasonPhrase = compatibility.ReasonPhrase ?? "it's no longer compatible";
+ string error = $"{reasonPhrase}. Please check for a ";
+ if (mod.Manifest.Version.Equals(compatibility.UpperVersion))
+ error += "newer version";
+ else
+ error += $"version newer than {compatibility.UpperVersion}";
+ error += " at " + string.Join(" or ", mod.DataRecord.UpdateUrls);
+
+ mod.SetStatus(ModMetadataStatus.Failed, error);
+ }
+ continue;
}
// validate SMAPI version
diff --git a/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs
new file mode 100644
index 00000000..54737e6c
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/Models/ModCompatibility.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace StardewModdingAPI.Framework.Models
+{
+ /// <summary>Specifies the compatibility of a given mod version range.</summary>
+ internal class ModCompatibility
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The lowest version in the range, or <c>null</c> for all past versions.</summary>
+ public ISemanticVersion LowerVersion { get; }
+
+ /// <summary>The highest version in the range, or <c>null</c> for all future versions.</summary>
+ public ISemanticVersion UpperVersion { get; }
+
+ /// <summary>The mod compatibility.</summary>
+ public ModStatus Status { get; }
+
+ /// <summary>The reason phrase to show in log output, or <c>null</c> to use the default value.</summary>
+ /// <example>For example, "this version is incompatible with the latest version of the game".</example>
+ public string ReasonPhrase { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="versionRange">A version range, which consists of two version strings separated by a '~' character. Either side can be left blank for an unbounded range.</param>
+ /// <param name="status">The mod compatibility.</param>
+ /// <param name="reasonPhrase">The reason phrase to show in log output, or <c>null</c> to use the default value.</param>
+ public ModCompatibility(string versionRange, ModStatus status, string reasonPhrase)
+ {
+ // extract version strings
+ string[] versions = versionRange.Split('~');
+ if (versions.Length != 2)
+ throw new FormatException($"Could not parse '{versionRange}' as a version range. It must have two version strings separated by a '~' character (either side can be left blank for an unbounded range).");
+
+ // initialise
+ this.LowerVersion = !string.IsNullOrWhiteSpace(versions[0]) ? new SemanticVersion(versions[0]) : null;
+ this.UpperVersion = !string.IsNullOrWhiteSpace(versions[1]) ? new SemanticVersion(versions[1]) : null;
+ this.Status = status;
+ this.ReasonPhrase = reasonPhrase;
+ }
+
+ /// <summary>Get whether a given version is contained within this compatibility range.</summary>
+ /// <param name="version">The version to check.</param>
+ public bool MatchesVersion(ISemanticVersion version)
+ {
+ return
+ (this.LowerVersion == null || !version.IsOlderThan(this.LowerVersion))
+ && (this.UpperVersion == null || !version.IsNewerThan(this.UpperVersion));
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs b/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs
index 8126022d..de219076 100644
--- a/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs
+++ b/src/StardewModdingAPI/Framework/Models/ModDataRecord.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using Newtonsoft.Json;
using StardewModdingAPI.Framework.Serialisation;
@@ -16,25 +17,22 @@ namespace StardewModdingAPI.Framework.Models
/// <summary>The mod name.</summary>
public string Name { get; set; }
- /// <summary>The oldest incompatible mod version, or <c>null</c> for all past versions.</summary>
- [JsonConverter(typeof(SFieldConverter))]
- public ISemanticVersion LowerVersion { get; set; }
-
- /// <summary>The most recent incompatible mod version.</summary>
- [JsonConverter(typeof(SFieldConverter))]
- public ISemanticVersion UpperVersion { get; set; }
-
- /// <summary>A label to show to the user instead of <see cref="UpperVersion"/>, when the manifest version differs from the user-facing version.</summary>
- public string UpperVersionLabel { get; set; }
-
/// <summary>The URLs the user can check for a newer version.</summary>
public string[] UpdateUrls { get; set; }
- /// <summary>The reason phrase to show in the warning, or <c>null</c> to use the default value.</summary>
- /// <example>"this version is incompatible with the latest version of the game"</example>
- public string ReasonPhrase { get; set; }
+ /// <summary>The compatibility of given mod versions (if any).</summary>
+ [JsonConverter(typeof(SFieldConverter))]
+ public ModCompatibility[] Compatibility { get; set; } = new ModCompatibility[0];
+
- /// <summary>Indicates how SMAPI should treat the mod.</summary>
- public ModStatus Status { get; set; } = ModStatus.AssumeBroken;
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Get the compatibility record for a given version, if any.</summary>
+ /// <param name="version">The mod version to check.</param>
+ public ModCompatibility GetCompatibility(ISemanticVersion version)
+ {
+ return this.Compatibility.FirstOrDefault(p => p.MatchesVersion(version));
+ }
}
}
diff --git a/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs
index d71e138b..ffece081 100644
--- a/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs
+++ b/src/StardewModdingAPI/Framework/Serialisation/SFieldConverter.cs
@@ -27,7 +27,8 @@ namespace StardewModdingAPI.Framework.Serialisation
return
objectType == typeof(ISemanticVersion)
|| objectType == typeof(IManifestDependency[])
- || objectType == typeof(ModDataID);
+ || objectType == typeof(ModDataID)
+ || objectType == typeof(ModCompatibility[]);
}
/// <summary>Reads the JSON representation of the object.</summary>
@@ -68,7 +69,7 @@ namespace StardewModdingAPI.Framework.Serialisation
}
}
- // manifest dependency
+ // manifest dependencies
if (objectType == typeof(IManifestDependency[]))
{
List<IManifestDependency> result = new List<IManifestDependency>();
@@ -82,13 +83,28 @@ namespace StardewModdingAPI.Framework.Serialisation
return result.ToArray();
}
- // mod compatibility ID
+ // mod data ID
if (objectType == typeof(ModDataID))
{
JToken token = JToken.Load(reader);
return new ModDataID(token.Value<string>());
}
+ // mod compatibility records
+ if (objectType == typeof(ModCompatibility[]))
+ {
+ List<ModCompatibility> result = new List<ModCompatibility>();
+ foreach (JProperty property in JObject.Load(reader).Properties())
+ {
+ string range = property.Name;
+ ModStatus status = property.Value.Value<ModStatus>(nameof(ModCompatibility.Status));
+ string reasonPhrase = property.Value.Value<string>(nameof(ModCompatibility.ReasonPhrase));
+
+ result.Add(new ModCompatibility(range, status, reasonPhrase));
+ }
+ return result.ToArray();
+ }
+
// unknown
throw new NotSupportedException($"Unknown type '{objectType?.FullName}'.");
}