add support for unofficial version in update checks (#532)

This commit is contained in:
Jesse Plamondon-Willard 2018-06-28 22:30:34 -04:00
parent 3f5a5e5404
commit c9fedebaf3
6 changed files with 58 additions and 3 deletions

View File

@ -10,6 +10,7 @@
* Improved update checks:
* added beta update channel;
* added support for optional files on Nexus;
* added support for unofficial updates from the wiki (only if the installed version is incompatible);
* added console warning for mods which don't have update checks configured;
* fixed mod update checks failing if a mod only has prerelease versions on GitHub;
* fixed Nexus mod update alerts not showing HTTPS links.

View File

@ -10,6 +10,7 @@ using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Toolkit.Framework.Clients.WebApi;
using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
using StardewModdingAPI.Toolkit.Framework.ModData;
using StardewModdingAPI.Web.Framework.Clients.Chucklefish;
using StardewModdingAPI.Web.Framework.Clients.GitHub;
@ -45,6 +46,9 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>The internal mod metadata list.</summary>
private readonly ModDatabase ModDatabase;
/// <summary>The web URL for the wiki compatibility list.</summary>
private readonly string WikiCompatibilityPageUrl;
/*********
** Public methods
@ -60,6 +64,7 @@ namespace StardewModdingAPI.Web.Controllers
{
this.ModDatabase = new ModToolkit().GetModDatabase(Path.Combine(environment.WebRootPath, "StardewModdingAPI.metadata.json"));
ModUpdateCheckConfig config = configProvider.Value;
this.WikiCompatibilityPageUrl = config.WikiCompatibilityPageUrl;
this.Cache = cache;
this.SuccessCacheMinutes = config.SuccessCacheMinutes;
@ -84,6 +89,9 @@ namespace StardewModdingAPI.Web.Controllers
ISemanticVersion apiVersion = this.GetApiVersion();
ModSearchEntryModel[] searchMods = this.GetSearchMods(model, apiVersion).ToArray();
// fetch wiki data
WikiCompatibilityEntry[] wikiData = await this.GetWikiDataAsync();
// perform checks
IDictionary<string, ModEntryModel> mods = new Dictionary<string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);
foreach (ModSearchEntryModel mod in searchMods)
@ -123,7 +131,7 @@ namespace StardewModdingAPI.Web.Controllers
continue;
}
if (result.Main == null || result.Main.Version.IsOlderThan(version))
if (this.IsNewer(version, result.Main?.Version))
{
result.Name = data.Name;
result.Main = new ModEntryVersionModel(version, data.Url);
@ -139,7 +147,7 @@ namespace StardewModdingAPI.Web.Controllers
continue;
}
if (result.Optional == null || result.Optional.Version.IsOlderThan(version))
if (this.IsNewer(version, result.Optional?.Version))
{
result.Name = result.Name ?? data.Name;
result.Optional = new ModEntryVersionModel(version, data.Url);
@ -147,6 +155,13 @@ namespace StardewModdingAPI.Web.Controllers
}
}
// get unofficial version
{
WikiCompatibilityEntry wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(result.ID.Trim(), StringComparer.InvariantCultureIgnoreCase));
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);
}
// fallback to preview if latest is invalid
if (result.Main == null && result.Optional != null)
{
@ -199,6 +214,14 @@ namespace StardewModdingAPI.Web.Controllers
return true;
}
/// <summary>Get whether a <paramref name="current"/> version is newer than an <paramref name="other"/> version.</summary>
/// <param name="current">The current version.</param>
/// <param name="other">The other version.</param>
private bool IsNewer(ISemanticVersion current, ISemanticVersion other)
{
return current != null && (other == null || other.IsOlderThan(current));
}
/// <summary>Get the mods for which the API should return data.</summary>
/// <param name="model">The search model.</param>
/// <param name="apiVersion">The requested API version.</param>
@ -222,6 +245,26 @@ namespace StardewModdingAPI.Web.Controllers
}
}
/// <summary>Get mod data from the wiki compatibility list.</summary>
private async Task<WikiCompatibilityEntry[]> GetWikiDataAsync()
{
ModToolkit toolkit = new ModToolkit();
return await this.Cache.GetOrCreateAsync($"_wiki", async entry =>
{
try
{
WikiCompatibilityEntry[] entries = await toolkit.GetWikiCompatibilityListAsync();
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.SuccessCacheMinutes);
return entries;
}
catch
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.ErrorCacheMinutes);
return new WikiCompatibilityEntry[0];
}
});
}
/// <summary>Get the mod info for an update key.</summary>
/// <param name="updateKey">The namespaced update key.</param>
private async Task<ModInfoModel> GetInfoForUpdateKeyAsync(string updateKey)

View File

@ -24,5 +24,8 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/// <summary>The repository key for Nexus Mods.</summary>
public string NexusKey { get; set; }
/// <summary>The web URL for the wiki compatibility list.</summary>
public string WikiCompatibilityPageUrl { get; set; }
}
}

View File

@ -46,6 +46,8 @@
"ChucklefishKey": "Chucklefish",
"GitHubKey": "GitHub",
"NexusKey": "Nexus"
"NexusKey": "Nexus",
"WikiCompatibilityPageUrl": "https://smapi.io/compat"
}
}

View File

@ -675,12 +675,15 @@ namespace StardewModdingAPI
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;
// show update alerts
if (this.IsValidUpdate(localVersion, latestVersion, useBetaChannel: true))
updates.Add(Tuple.Create(mod, latestVersion, result.Main?.Url));
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));
}
// show update errors

View File

@ -20,6 +20,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>The latest optional version, if newer than <see cref="Main"/>.</summary>
public ModEntryVersionModel Optional { get; set; }
/// <summary>The latest unofficial version, if newer than <see cref="Main"/> and <see cref="Optional"/>.</summary>
public ModEntryVersionModel Unofficial { get; set; }
/// <summary>The errors that occurred while fetching update data.</summary>
public string[] Errors { get; set; } = new string[0];