diff options
Diffstat (limited to 'src/SMAPI.Web')
-rw-r--r-- | src/SMAPI.Web/Controllers/ModsApiController.cs | 30 | ||||
-rw-r--r-- | src/SMAPI.Web/Framework/ModSiteManager.cs | 39 | ||||
-rw-r--r-- | src/SMAPI.Web/Startup.cs | 12 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/Mods/Index.cshtml | 12 | ||||
-rw-r--r-- | src/SMAPI.Web/wwwroot/Content/js/mods.js | 30 |
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": |