summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web')
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs30
-rw-r--r--src/SMAPI.Web/Framework/ModSiteManager.cs39
-rw-r--r--src/SMAPI.Web/Startup.cs12
-rw-r--r--src/SMAPI.Web/Views/Mods/Index.cshtml12
-rw-r--r--src/SMAPI.Web/wwwroot/Content/js/mods.js30
5 files changed, 62 insertions, 61 deletions
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index c6e9a713..dcddaf10 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -144,7 +144,7 @@ namespace StardewModdingAPI.Web.Controllers
}
// fetch data
- ModInfoModel data = await this.GetInfoForUpdateKeyAsync(updateKey, allowNonStandardVersions, wikiEntry?.MapRemoteVersions);
+ ModInfoModel data = await this.GetInfoForUpdateKeyAsync(updateKey, allowNonStandardVersions, wikiEntry?.Overrides?.ChangeRemoteVersions);
if (data.Status != RemoteModStatus.Ok)
{
errors.Add(data.Error ?? data.Status.ToString());
@@ -195,7 +195,7 @@ namespace StardewModdingAPI.Web.Controllers
}
// get recommended update (if any)
- ISemanticVersion installedVersion = this.ModSites.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.MapLocalVersions, allowNonStandard: allowNonStandardVersions);
+ ISemanticVersion installedVersion = this.ModSites.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.Overrides?.ChangeLocalVersions, allowNonStandard: allowNonStandardVersions);
if (apiVersion != null && installedVersion != null)
{
// get newer versions
@@ -255,8 +255,8 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>Get the mod info for an update key.</summary>
/// <param name="updateKey">The namespaced update key.</param>
/// <param name="allowNonStandardVersions">Whether to allow non-standard versions.</param>
- /// <param name="mapRemoteVersions">Maps remote versions to a semantic version for update checks.</param>
- private async Task<ModInfoModel> GetInfoForUpdateKeyAsync(UpdateKey updateKey, bool allowNonStandardVersions, IDictionary<string, string> mapRemoteVersions)
+ /// <param name="mapRemoteVersions">The changes to apply to remote versions for update checks.</param>
+ private async Task<ModInfoModel> GetInfoForUpdateKeyAsync(UpdateKey updateKey, bool allowNonStandardVersions, ChangeDescriptor mapRemoteVersions)
{
// get mod page
IModPage page;
@@ -290,15 +290,12 @@ namespace StardewModdingAPI.Web.Controllers
.Distinct()
.ToList();
- // apply remove overrides from wiki
+ // apply overrides from wiki
+ if (entry?.Overrides?.ChangeUpdateKeys?.HasChanges == true)
{
- var removeKeys = new HashSet<UpdateKey>(
- from key in entry?.ChangeUpdateKeys ?? new string[0]
- where key.StartsWith('-')
- select UpdateKey.Parse(key.Substring(1))
- );
- if (removeKeys.Any())
- updateKeys.RemoveAll(removeKeys.Contains);
+ List<string> newKeys = updateKeys.Select(p => p.ToString()).ToList();
+ entry.Overrides.ChangeUpdateKeys.Apply(newKeys);
+ updateKeys = newKeys.Select(UpdateKey.Parse).ToList();
}
// if the list has both an update key (like "Nexus:2400") and subkey (like "Nexus:2400@subkey") for the same page, the subkey takes priority
@@ -348,15 +345,6 @@ namespace StardewModdingAPI.Web.Controllers
if (entry.ChucklefishID.HasValue)
yield return UpdateKey.GetString(ModSiteKey.Chucklefish, entry.ChucklefishID.ToString());
}
-
- // overrides from wiki
- foreach (string key in entry?.ChangeUpdateKeys ?? Array.Empty<string>())
- {
- if (key.StartsWith('+'))
- yield return key.Substring(1);
- else if (!key.StartsWith("-"))
- yield return key;
- }
}
}
}
diff --git a/src/SMAPI.Web/Framework/ModSiteManager.cs b/src/SMAPI.Web/Framework/ModSiteManager.cs
index 8f21d2f5..a2b92aa4 100644
--- a/src/SMAPI.Web/Framework/ModSiteManager.cs
+++ b/src/SMAPI.Web/Framework/ModSiteManager.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using StardewModdingAPI.Toolkit;
+using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
using StardewModdingAPI.Web.Framework.Clients;
@@ -55,9 +56,9 @@ namespace StardewModdingAPI.Web.Framework
/// <summary>Parse version info for the given mod page info.</summary>
/// <param name="page">The mod page info.</param>
/// <param name="subkey">The optional update subkey to match in available files. (If no file names or descriptions contain the subkey, it'll be ignored.)</param>
- /// <param name="mapRemoteVersions">Maps remote versions to a semantic version for update checks.</param>
+ /// <param name="mapRemoteVersions">The changes to apply to remote versions for update checks.</param>
/// <param name="allowNonStandardVersions">Whether to allow non-standard versions.</param>
- public ModInfoModel GetPageVersions(IModPage page, string subkey, bool allowNonStandardVersions, IDictionary<string, string> mapRemoteVersions)
+ public ModInfoModel GetPageVersions(IModPage page, string subkey, bool allowNonStandardVersions, ChangeDescriptor mapRemoteVersions)
{
// get base model
ModInfoModel model = new ModInfoModel()
@@ -79,9 +80,9 @@ namespace StardewModdingAPI.Web.Framework
/// <summary>Get a semantic local version for update checks.</summary>
/// <param name="version">The version to parse.</param>
- /// <param name="map">A map of version replacements.</param>
+ /// <param name="map">Changes to apply to the raw version, if any.</param>
/// <param name="allowNonStandard">Whether to allow non-standard versions.</param>
- public ISemanticVersion GetMappedVersion(string version, IDictionary<string, string> map, bool allowNonStandard)
+ public ISemanticVersion GetMappedVersion(string version, ChangeDescriptor map, bool allowNonStandard)
{
// try mapped version
string rawNewVersion = this.GetRawMappedVersion(version, map, allowNonStandard);
@@ -102,10 +103,10 @@ namespace StardewModdingAPI.Web.Framework
/// <param name="mod">The mod to check.</param>
/// <param name="subkey">The optional update subkey to match in available files. (If no file names or descriptions contain the subkey, it'll be ignored.)</param>
/// <param name="allowNonStandardVersions">Whether to allow non-standard versions.</param>
- /// <param name="mapRemoteVersions">Maps remote versions to a semantic version for update checks.</param>
+ /// <param name="mapRemoteVersions">The changes to apply to remote versions for update checks.</param>
/// <param name="main">The main mod version.</param>
/// <param name="preview">The latest prerelease version, if newer than <paramref name="main"/>.</param>
- private bool TryGetLatestVersions(IModPage mod, string subkey, bool allowNonStandardVersions, IDictionary<string, string> mapRemoteVersions, out ISemanticVersion main, out ISemanticVersion preview)
+ private bool TryGetLatestVersions(IModPage mod, string subkey, bool allowNonStandardVersions, ChangeDescriptor mapRemoteVersions, out ISemanticVersion main, out ISemanticVersion preview)
{
main = null;
preview = null;
@@ -179,31 +180,17 @@ namespace StardewModdingAPI.Web.Framework
/// <summary>Get a semantic local version for update checks.</summary>
/// <param name="version">The version to map.</param>
- /// <param name="map">A map of version replacements.</param>
+ /// <param name="map">Changes to apply to the raw version, if any.</param>
/// <param name="allowNonStandard">Whether to allow non-standard versions.</param>
- private string GetRawMappedVersion(string version, IDictionary<string, string> map, bool allowNonStandard)
+ private string GetRawMappedVersion(string version, ChangeDescriptor map, bool allowNonStandard)
{
- if (version == null || map == null || !map.Any())
+ if (version == null || map?.HasChanges != true)
return version;
- // match exact raw version
- if (map.ContainsKey(version))
- return map[version];
+ var mapped = new List<string> { version };
+ map.Apply(mapped);
- // match parsed version
- if (SemanticVersion.TryParse(version, allowNonStandard, out ISemanticVersion parsed))
- {
- if (map.ContainsKey(parsed.ToString()))
- return map[parsed.ToString()];
-
- foreach ((string fromRaw, string toRaw) in map)
- {
- if (SemanticVersion.TryParse(fromRaw, allowNonStandard, out ISemanticVersion target) && parsed.Equals(target) && SemanticVersion.TryParse(toRaw, allowNonStandard, out ISemanticVersion newVersion))
- return newVersion.ToString();
- }
- }
-
- return version;
+ return mapped.FirstOrDefault();
}
/// <summary>Normalize a version string.</summary>
diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs
index 2556936c..d8561172 100644
--- a/src/SMAPI.Web/Startup.cs
+++ b/src/SMAPI.Web/Startup.cs
@@ -205,15 +205,21 @@ namespace StardewModdingAPI.Web
// shortcut paths
.Add(new RedirectPathsToUrlsRule(new Dictionary<string, string>
{
+ // wiki pages
[@"^/3\.0\.?$"] = "https://stardewvalleywiki.com/Modding:Migrate_to_SMAPI_3.0",
- [@"^/(?:buildmsg|package)(?:/?(.*))$"] = "https://github.com/Pathoschild/SMAPI/blob/develop/docs/technical/mod-package.md#$1", // buildmsg deprecated, remove when SDV 1.4 is released
[@"^/community\.?$"] = "https://stardewvalleywiki.com/Modding:Community",
- [@"^/compat\.?$"] = "https://smapi.io/mods",
[@"^/docs\.?$"] = "https://stardewvalleywiki.com/Modding:Index",
[@"^/help\.?$"] = "https://stardewvalleywiki.com/Modding:Help",
[@"^/install\.?$"] = "https://stardewvalleywiki.com/Modding:Player_Guide/Getting_Started#Install_SMAPI",
[@"^/troubleshoot(.*)$"] = "https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting$1",
- [@"^/xnb\.?$"] = "https://stardewvalleywiki.com/Modding:Using_XNB_mods"
+ [@"^/xnb\.?$"] = "https://stardewvalleywiki.com/Modding:Using_XNB_mods",
+
+ // GitHub docs
+ [@"^/package(?:/?(.*))$"] = "https://github.com/Pathoschild/SMAPI/blob/develop/docs/technical/mod-package.md#$1",
+ [@"^/release(?:/?(.*))$"] = "https://github.com/Pathoschild/SMAPI/blob/develop/docs/release-notes.md#$1",
+
+ // legacy redirects
+ [@"^/compat\.?$"] = "https://smapi.io/mods"
}))
// legacy paths
diff --git a/src/SMAPI.Web/Views/Mods/Index.cshtml b/src/SMAPI.Web/Views/Mods/Index.cshtml
index 5df49afb..8a764803 100644
--- a/src/SMAPI.Web/Views/Mods/Index.cshtml
+++ b/src/SMAPI.Web/Views/Mods/Index.cshtml
@@ -16,7 +16,7 @@
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/tablesorter@2.31.3" crossorigin="anonymous"></script>
- <script src="~/Content/js/mods.js?r=20200218"></script>
+ <script src="~/Content/js/mods.js?r=20210929"></script>
<script>
$(function() {
var data = @this.ForJson(Model.Mods ?? new ModModel[0]);
@@ -65,9 +65,15 @@ else
</div>
</div>
<div id="mod-count" v-show="showAdvanced">
- <div v-if="visibleStats.total > 0">
- {{visibleStats.total}} mods shown ({{Math.round((visibleStats.compatible + visibleStats.workaround) / visibleStats.total * 100)}}% compatible or have a workaround, {{Math.round((visibleStats.soon + visibleStats.broken) / visibleStats.total * 100)}}% broken, {{Math.round(visibleStats.abandoned / visibleStats.total * 100)}}% obsolete).
+ <div v-if="visibleMainStats.total > 0">
+ {{visibleMainStats.total}} mods shown ({{visibleMainStats.percentCompatible}}% compatible or have a workaround, {{visibleMainStats.percentBroken}}% broken, {{visibleMainStats.percentObsolete}}% obsolete).
</div>
+ @if (hasBeta)
+ {
+ <div v-if="visibleBetaStats.total > 0">
+ <strong>@betaLabel</strong>: {{visibleBetaStats.total}} mods shown ({{visibleBetaStats.percentCompatible}}% compatible or have a workaround, {{visibleBetaStats.percentBroken}}% broken, {{visibleBetaStats.percentObsolete}}% obsolete).
+ </div>
+ }
<span v-else>No matching mods found.</span>
</div>
<table class="wikitable" id="mod-list">
diff --git a/src/SMAPI.Web/wwwroot/Content/js/mods.js b/src/SMAPI.Web/wwwroot/Content/js/mods.js
index ac2754a4..945f93ef 100644
--- a/src/SMAPI.Web/wwwroot/Content/js/mods.js
+++ b/src/SMAPI.Web/wwwroot/Content/js/mods.js
@@ -9,12 +9,16 @@ smapi.modList = function (mods, enableBeta) {
soon: 0,
broken: 0,
abandoned: 0,
- invalid: 0
+ invalid: 0,
+ percentCompatible: 0,
+ percentBroken: 0,
+ percentObsolete: 0
};
var data = {
mods: mods,
showAdvanced: false,
- visibleStats: $.extend({}, defaultStats),
+ visibleMainStats: $.extend({}, defaultStats),
+ visibleBetaStats: $.extend({}, defaultStats),
filters: {
source: {
value: {
@@ -124,7 +128,8 @@ smapi.modList = function (mods, enableBeta) {
var words = data.search.toLowerCase().split(" ");
// apply criteria
- var stats = data.visibleStats = $.extend({}, defaultStats);
+ var mainStats = data.visibleMainStats = $.extend({}, defaultStats);
+ var betaStats = data.visibleBetaStats = $.extend({}, defaultStats);
for (var i = 0; i < data.mods.length; i++) {
var mod = data.mods[i];
mod.Visible = true;
@@ -132,10 +137,20 @@ smapi.modList = function (mods, enableBeta) {
// check filters
mod.Visible = this.matchesFilters(mod, words);
if (mod.Visible) {
- stats.total++;
- stats[this.getCompatibilityGroup(mod)]++;
+ mainStats.total++;
+ betaStats.total++;
+
+ mainStats[this.getCompatibilityGroup(mod.Compatibility.Status)]++;
+ betaStats[this.getCompatibilityGroup(mod.LatestCompatibility.Status)]++;
}
}
+
+ // add aggregate stats
+ for (let stats of [mainStats, betaStats]) {
+ stats.percentCompatible = Math.round((stats.compatible + stats.workaround) / stats.total * 100);
+ stats.percentBroken = Math.round((stats.soon + stats.broken) / stats.total * 100);
+ stats.percentObsolete = Math.round(stats.abandoned / stats.total * 100);
+ }
},
/**
@@ -220,11 +235,10 @@ smapi.modList = function (mods, enableBeta) {
/**
* Get a mod's compatibility group for mod metrics.
- * @param {object} mod The mod to check.
+ * @param {string} mod The mod status for which to get the group.
* @returns {string} The compatibility group (one of 'compatible', 'workaround', 'soon', 'broken', 'abandoned', or 'invalid').
*/
- getCompatibilityGroup: function (mod) {
- var status = mod.LatestCompatibility.Status;
+ getCompatibilityGroup: function (status) {
switch (status) {
// obsolete
case "abandoned":