summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md3
-rw-r--r--src/SMAPI.Toolkit/SemanticVersionComparer.cs32
-rw-r--r--src/SMAPI.Web/Framework/ModSiteManager.cs73
3 files changed, 86 insertions, 22 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index d0b794ee..85971e1d 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -8,6 +8,9 @@
* Fixed error-handling for `StackOverflowException` thrown on Linux/macOS.
* Internal changes to prepare for upcoming releases.
+* For the web API:
+ * Fixed update checks recommending prerelease versions if the player has a working non-prerelease version.
+
## 3.12.2
Released 05 August 2021 for Stardew Valley 1.5.4 or later.
diff --git a/src/SMAPI.Toolkit/SemanticVersionComparer.cs b/src/SMAPI.Toolkit/SemanticVersionComparer.cs
new file mode 100644
index 00000000..9f6b57a2
--- /dev/null
+++ b/src/SMAPI.Toolkit/SemanticVersionComparer.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+
+namespace StardewModdingAPI.Toolkit
+{
+ /// <summary>A comparer for semantic versions based on the <see cref="SemanticVersion.CompareTo(ISemanticVersion)"/> field.</summary>
+ public class SemanticVersionComparer : IComparer<ISemanticVersion>
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A singleton instance of the comparer.</summary>
+ public static SemanticVersionComparer Instance { get; } = new SemanticVersionComparer();
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <inheritdoc />
+ public int Compare(ISemanticVersion x, ISemanticVersion y)
+ {
+ if (object.ReferenceEquals(x, y))
+ return 0;
+
+ if (x is null)
+ return -1;
+ if (y is null)
+ return 1;
+
+ return x.CompareTo(y);
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/ModSiteManager.cs b/src/SMAPI.Web/Framework/ModSiteManager.cs
index 68b4c6ac..8f21d2f5 100644
--- a/src/SMAPI.Web/Framework/ModSiteManager.cs
+++ b/src/SMAPI.Web/Framework/ModSiteManager.cs
@@ -110,41 +110,70 @@ namespace StardewModdingAPI.Web.Framework
main = null;
preview = null;
- ISemanticVersion ParseVersion(string raw)
+ // parse all versions from the mod page
+ IEnumerable<(string name, string description, ISemanticVersion version)> GetAllVersions()
{
- raw = this.NormalizeVersion(raw);
- return this.GetMappedVersion(raw, mapRemoteVersions, allowNonStandardVersions);
+ if (mod != null)
+ {
+ ISemanticVersion ParseAndMapVersion(string raw)
+ {
+ raw = this.NormalizeVersion(raw);
+ return this.GetMappedVersion(raw, mapRemoteVersions, allowNonStandardVersions);
+ }
+
+ // get mod version
+ ISemanticVersion modVersion = ParseAndMapVersion(mod.Version);
+ if (modVersion != null)
+ yield return (name: null, description: null, version: ParseAndMapVersion(mod.Version));
+
+ // get file versions
+ foreach (IModDownload download in mod.Downloads)
+ {
+ ISemanticVersion cur = ParseAndMapVersion(download.Version);
+ if (cur != null)
+ yield return (download.Name, download.Description, cur);
+ }
+ }
}
+ var versions = GetAllVersions()
+ .OrderByDescending(p => p.version, SemanticVersionComparer.Instance)
+ .ToArray();
- if (mod != null)
+ // get main + preview versions
+ void TryGetVersions(out ISemanticVersion mainVersion, out ISemanticVersion previewVersion, Func<(string name, string description, ISemanticVersion version), bool> filter = null)
{
- // get mod version
- if (subkey == null)
- main = ParseVersion(mod.Version);
+ mainVersion = null;
+ previewVersion = null;
- // get file versions
- foreach (IModDownload download in mod.Downloads)
+ // get latest main + preview version
+ foreach (var entry in versions)
{
- // check for subkey if specified
- if (subkey != null && download.Name?.Contains(subkey, StringComparison.OrdinalIgnoreCase) != true && download.Description?.Contains(subkey, StringComparison.OrdinalIgnoreCase) != true)
+ if (filter?.Invoke(entry) == false)
continue;
- // parse version
- ISemanticVersion cur = ParseVersion(download.Version);
- if (cur == null)
- continue;
+ if (entry.version.IsPrerelease())
+ previewVersion ??= entry.version;
+ else
+ mainVersion ??= entry.version;
- // track highest versions
- if (main == null || cur.IsNewerThan(main))
- main = cur;
- if (cur.IsPrerelease() && (preview == null || cur.IsNewerThan(preview)))
- preview = cur;
+ if (mainVersion != null)
+ break; // any other values will be older
}
- if (preview != null && !preview.IsNewerThan(main))
- preview = null;
+ // normalize values
+ if (previewVersion is not null)
+ {
+ mainVersion ??= previewVersion; // if every version is prerelease, latest one is the main version
+ if (!previewVersion.IsNewerThan(mainVersion))
+ previewVersion = null;
+ }
}
+ if (subkey is not null)
+ TryGetVersions(out main, out preview, entry => entry.name?.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true || entry.description?.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true);
+ if (main is null)
+ TryGetVersions(out main, out preview);
+
return main != null;
}