summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md1
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs17
-rw-r--r--src/SMAPI/Framework/SCore.cs5
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs6
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs21
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityClient.cs66
-rw-r--r--src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityEntry.cs32
7 files changed, 121 insertions, 27 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index bf0abae3..b9cee4c6 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -24,6 +24,7 @@
* **Breaking change:** most SMAPI files have been moved into a `smapi-internal` subfolder. This won't affect compiled mods, but you'll need to update the mod build config NuGet package when compiling mods.
* For SMAPI developers:
+ * Added support for parallel stable/beta unofficial updates in update checks.
* Added a 'paranoid warnings' option which reports mods using potentially sensitive .NET APIs (like file or shell access) in the mod issues list.
* Adjusted `SaveBackup` mod to make it easier to account for custom mod subfolders in the installer.
* Installer no longer special-cases Omegasis' older `SaveBackup` mod (now named `AdvancedSaveBackup`).
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index 5ca8c94c..592c8f97 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -165,6 +165,23 @@ namespace StardewModdingAPI.Web.Controllers
if (wikiEntry?.UnofficialVersion != null && this.IsNewer(wikiEntry.UnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.UnofficialVersion, result.Optional?.Version))
result.Unofficial = new ModEntryVersionModel(wikiEntry.UnofficialVersion, this.WikiCompatibilityPageUrl);
+ // get unofficial version for beta
+ if (wikiEntry?.HasBetaInfo == true)
+ {
+ result.HasBetaInfo = true;
+ if (wikiEntry.BetaStatus == WikiCompatibilityStatus.Unofficial)
+ {
+ if (wikiEntry.BetaUnofficialVersion != null)
+ {
+ result.UnofficialForBeta = (wikiEntry.BetaUnofficialVersion != null && this.IsNewer(wikiEntry.BetaUnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.BetaUnofficialVersion, result.Optional?.Version))
+ ? new ModEntryVersionModel(wikiEntry.BetaUnofficialVersion, this.WikiCompatibilityPageUrl)
+ : null;
+ }
+ else
+ result.UnofficialForBeta = result.Unofficial;
+ }
+ }
+
// fallback to preview if latest is invalid
if (result.Main == null && result.Optional != null)
{
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 59ce3be7..00e608c1 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -609,10 +609,11 @@ namespace StardewModdingAPI.Framework
}
// parse versions
+ bool useBetaInfo = result.HasBetaInfo && Constants.ApiVersion.IsPrerelease();
ISemanticVersion localVersion = mod.DataRecord?.GetLocalVersionForUpdateChecks(mod.Manifest.Version) ?? mod.Manifest.Version;
ISemanticVersion latestVersion = mod.DataRecord?.GetRemoteVersionForUpdateChecks(result.Main?.Version) ?? result.Main?.Version;
ISemanticVersion optionalVersion = mod.DataRecord?.GetRemoteVersionForUpdateChecks(result.Optional?.Version) ?? result.Optional?.Version;
- ISemanticVersion unofficialVersion = result.Unofficial?.Version;
+ ISemanticVersion unofficialVersion = useBetaInfo ? result.UnofficialForBeta?.Version : result.Unofficial?.Version;
// show update alerts
if (this.IsValidUpdate(localVersion, latestVersion, useBetaChannel: true))
@@ -620,7 +621,7 @@ namespace StardewModdingAPI.Framework
else if (this.IsValidUpdate(localVersion, optionalVersion, useBetaChannel: localVersion.IsPrerelease()))
updates.Add(Tuple.Create(mod, optionalVersion, result.Optional?.Url));
else if (this.IsValidUpdate(localVersion, unofficialVersion, useBetaChannel: mod.Status == ModMetadataStatus.Failed))
- updates.Add(Tuple.Create(mod, unofficialVersion, result.Unofficial?.Url));
+ updates.Add(Tuple.Create(mod, unofficialVersion, useBetaInfo ? result.UnofficialForBeta?.Url : result.Unofficial?.Url));
}
// show update errors
diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
index 2aafe199..8a9c0a25 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
@@ -18,9 +18,15 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>The latest unofficial version, if newer than <see cref="Main"/> and <see cref="Optional"/>.</summary>
public ModEntryVersionModel Unofficial { get; set; }
+ /// <summary>The latest unofficial version for the current Stardew Valley or SMAPI beta, if any (see <see cref="HasBetaInfo"/>).</summary>
+ public ModEntryVersionModel UnofficialForBeta { get; set; }
+
/// <summary>Optional extended data which isn't needed for update checks.</summary>
public ModExtendedMetadataModel Metadata { get; set; }
+ /// <summary>Whether a Stardew Valley or SMAPI beta which affects mod compatibility is in progress. If this is true, <see cref="UnofficialForBeta"/> should be used for beta versions of SMAPI instead of <see cref="Unofficial"/>.</summary>
+ public bool HasBetaInfo { get; set; }
+
/// <summary>The errors that occurred while fetching update data.</summary>
public string[] Errors { get; set; } = new string[0];
}
diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
index 21376b36..0f3cb26f 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
@@ -13,6 +13,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/*********
** Accessors
*********/
+ /****
+ ** Mod info
+ ****/
/// <summary>The mod's unique ID. A mod may have multiple current IDs in rare cases (e.g. due to parallel releases or unofficial updates).</summary>
public string[] ID { get; set; } = new string[0];
@@ -34,6 +37,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>The custom mod page URL (if applicable).</summary>
public string CustomUrl { get; set; }
+ /****
+ ** Stable compatibility
+ ****/
/// <summary>The compatibility status.</summary>
[JsonConverter(typeof(StringEnumConverter))]
public WikiCompatibilityStatus? CompatibilityStatus { get; set; }
@@ -42,6 +48,17 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
public string CompatibilitySummary { get; set; }
+ /****
+ ** Beta compatibility
+ ****/
+ /// <summary>The compatibility status for the Stardew Valley beta (if any).</summary>
+ [JsonConverter(typeof(StringEnumConverter))]
+ public WikiCompatibilityStatus? BetaCompatibilityStatus { get; set; }
+
+ /// <summary>The human-readable summary of the compatibility status or workaround for the Stardew Valley beta (if any), without HTML formatitng.</summary>
+ public string BetaCompatibilitySummary { get; set; }
+
+
/*********
** Public methods
*********/
@@ -63,8 +80,12 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
this.GitHubRepo = wiki.GitHubRepo;
this.CustomSourceUrl = wiki.CustomSourceUrl;
this.CustomUrl = wiki.CustomUrl;
+
this.CompatibilityStatus = wiki.Status;
this.CompatibilitySummary = wiki.Summary;
+
+ this.BetaCompatibilityStatus = wiki.BetaStatus;
+ this.BetaCompatibilitySummary = wiki.BetaSummary;
}
// internal DB data
diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityClient.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityClient.cs
index d0da42df..929284c3 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityClient.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityClient.cs
@@ -73,26 +73,8 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
foreach (HtmlNode node in nodes)
{
- // parse status
- WikiCompatibilityStatus status;
- {
- string rawStatus = node.GetAttributeValue("data-status", null);
- if (rawStatus == null)
- continue; // not a mod node?
- if (!Enum.TryParse(rawStatus, true, out status))
- throw new InvalidOperationException($"Unknown status '{rawStatus}' when parsing compatibility list.");
- }
-
- // parse unofficial version
- ISemanticVersion unofficialVersion = null;
- {
- string rawUnofficialVersion = node.GetAttributeValue("data-unofficial-version", null);
- SemanticVersion.TryParse(rawUnofficialVersion, out unofficialVersion);
- }
-
- // parse other fields
+ // parse mod info
string name = node.Descendants("td").FirstOrDefault()?.InnerText?.Trim();
- string summary = node.Descendants("td").FirstOrDefault(p => p.GetAttributeValue("class", null) == "summary")?.InnerText.Trim();
string[] ids = this.GetAttribute(node, "data-id")?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray() ?? new string[0];
int? nexusID = this.GetNullableIntAttribute(node, "data-nexus-id");
int? chucklefishID = this.GetNullableIntAttribute(node, "data-chucklefish-id");
@@ -100,23 +82,65 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
string customSourceUrl = this.GetAttribute(node, "data-custom-source");
string customUrl = this.GetAttribute(node, "data-custom-url");
+ // parse stable compatibility
+ WikiCompatibilityStatus status = this.GetStatusAttribute(node, "data-status") ?? WikiCompatibilityStatus.Ok;
+ ISemanticVersion unofficialVersion = this.GetSemanticVersionAttribute(node, "data-unofficial-version");
+ string summary = node.Descendants().FirstOrDefault(p => p.HasClass("data-summary"))?.InnerText.Trim();
+
+ // parse beta compatibility
+ WikiCompatibilityStatus? betaStatus = this.GetStatusAttribute(node, "data-beta-status");
+ ISemanticVersion betaUnofficialVersion = betaStatus.HasValue ? this.GetSemanticVersionAttribute(node, "data-beta-unofficial-version") : null;
+ string betaSummary = betaStatus.HasValue ? node.Descendants().FirstOrDefault(p => p.HasClass("data-beta-summary"))?.InnerText.Trim() : null;
+
// yield model
yield return new WikiCompatibilityEntry
{
+ // mod info
ID = ids,
Name = name,
- Status = status,
NexusID = nexusID,
ChucklefishID = chucklefishID,
GitHubRepo = githubRepo,
CustomSourceUrl = customSourceUrl,
CustomUrl = customUrl,
+
+ // stable compatibility
+ Status = status,
+ Summary = summary,
UnofficialVersion = unofficialVersion,
- Summary = summary
+
+ // beta compatibility
+ BetaStatus = betaStatus,
+ BetaSummary = betaSummary,
+ BetaUnofficialVersion = betaUnofficialVersion
};
}
}
+ /// <summary>Get a compatibility status attribute value.</summary>
+ /// <param name="node">The HTML node.</param>
+ /// <param name="attributeName">The attribute name.</param>
+ private WikiCompatibilityStatus? GetStatusAttribute(HtmlNode node, string attributeName)
+ {
+ string raw = node.GetAttributeValue(attributeName, null);
+ if (raw == null)
+ return null; // not a mod node?
+ if (!Enum.TryParse(raw, true, out WikiCompatibilityStatus status))
+ throw new InvalidOperationException($"Unknown status '{raw}' when parsing compatibility list.");
+ return status;
+ }
+
+ /// <summary>Get a semantic version attribute value.</summary>
+ /// <param name="node">The HTML node.</param>
+ /// <param name="attributeName">The attribute name.</param>
+ private ISemanticVersion GetSemanticVersionAttribute(HtmlNode node, string attributeName)
+ {
+ string raw = node.GetAttributeValue(attributeName, null);
+ return SemanticVersion.TryParse(raw, out ISemanticVersion version)
+ ? version
+ : null;
+ }
+
/// <summary>Get a nullable integer attribute value.</summary>
/// <param name="node">The HTML node.</param>
/// <param name="attributeName">The attribute name.</param>
diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityEntry.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityEntry.cs
index 8bc66e20..3cb9c97c 100644
--- a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityEntry.cs
+++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityEntry.cs
@@ -3,6 +3,12 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <summary>An entry in the mod compatibility list.</summary>
public class WikiCompatibilityEntry
{
+ /*********
+ ** Accessors
+ *********/
+ /****
+ ** Mod info
+ ****/
/// <summary>The mod's unique ID. A mod may have multiple current IDs in rare cases (e.g. due to parallel releases or unofficial updates).</summary>
public string[] ID { get; set; }
@@ -24,13 +30,31 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <summary>The custom mod page URL (if applicable).</summary>
public string CustomUrl { get; set; }
- /// <summary>The version of the latest unofficial update, if applicable.</summary>
- public ISemanticVersion UnofficialVersion { get; set; }
-
+ /****
+ ** Stable compatibility
+ ****/
/// <summary>The compatibility status.</summary>
public WikiCompatibilityStatus Status { get; set; }
- /// <summary>The human-readable summary of the compatibility status or workaround, without HTML formatitng.</summary>
+ /// <summary>The human-readable summary of the compatibility status or workaround, without HTML formatting.</summary>
public string Summary { get; set; }
+
+ /// <summary>The version of the latest unofficial update, if applicable.</summary>
+ public ISemanticVersion UnofficialVersion { get; set; }
+
+ /****
+ ** Beta compatibility
+ ****/
+ /// <summary>Whether a Stardew Valley or SMAPI beta which affects mod compatibility is in progress. If this is true, <see cref="BetaUnofficialVersion"/> should be used for beta versions of SMAPI instead of <see cref="UnofficialVersion"/>.</summary>
+ public bool HasBetaInfo => this.BetaStatus != null;
+
+ /// <summary>The compatibility status for the Stardew Valley beta, if a beta is in progress.</summary>
+ public WikiCompatibilityStatus? BetaStatus { get; set; }
+
+ /// <summary>The human-readable summary of the compatibility status or workaround for the Stardew Valley beta (if any), without HTML formatting.</summary>
+ public string BetaSummary { get; set; }
+
+ /// <summary>The version of the latest unofficial update for the Stardew Valley beta (if any), if applicable.</summary>
+ public ISemanticVersion BetaUnofficialVersion { get; set; }
}
}