summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md4
-rw-r--r--docs/technical/web.md209
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs18
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs24
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs11
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs (renamed from src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSeachModel.cs)18
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs8
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs26
-rw-r--r--src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs7
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs6
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs31
-rw-r--r--src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs24
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs162
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMod.cs24
-rw-r--r--src/SMAPI.Web/Views/Index/Privacy.cshtml2
-rw-r--r--src/SMAPI.Web/wwwroot/SMAPI.metadata.json97
-rw-r--r--src/SMAPI/Framework/SCore.cs57
17 files changed, 458 insertions, 270 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index eace8be5..5f643f07 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -18,6 +18,9 @@ For players:
* **Improved mod scanning.**
SMAPI now supports some non-standard mod structures automatically, improves compatibility with the Vortex mod manager, and improves various error/skip messages related to mod loading.
+* **Overhauled update checks.**
+ SMAPI update checks are now handled entirely on the web server and support community-defined version mappings. For example, that means false update alerts can now be solved by the community for all players.
+
* **Fixed many bugs and edge cases.**
For modders:
@@ -50,6 +53,7 @@ For modders:
* Added update checks for CurseForge mods.
* Added support for configuring console colors via `smapi-internal/config.json` (intended for players with unusual consoles).
* Added support for specifying SMAPI command-line arguments as environment variables for Linux/Mac compatibility.
+ * Overhauled update checks and added community-defined version mapping.
* Improved launch script compatibility on Linux (thanks to kurumushi and toastal!).
* Made error messages more user-friendly in some cases.
* Save Backup now works in the background, to avoid affecting startup time for players with a large number of saves.
diff --git a/docs/technical/web.md b/docs/technical/web.md
index 719b9433..78d93625 100644
--- a/docs/technical/web.md
+++ b/docs/technical/web.md
@@ -116,54 +116,201 @@ SMAPI provides a web API at `api.smapi.io` for use by SMAPI and external tools.
accessible but not officially released; it may change at any time.
### `/mods` endpoint
-The API has one `/mods` endpoint. This provides mod info, including official versions and URLs
-(from Chucklefish, GitHub, or Nexus), unofficial versions from the wiki, and optional mod metadata
-from the wiki and SMAPI's internal data. This is used by SMAPI to perform update checks, and by
-external tools to fetch mod data.
+The API has one `/mods` endpoint. This crossreferences the mod against a variety of sources (e.g.
+the wiki, Chucklefish, CurseForge, ModDrop, and Nexus) to provide metadata mainly intended for
+update checks.
-The API accepts a `POST` request with the mods to match, each of which **must** specify an ID and
-may _optionally_ specify [update keys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest#Update_checks).
-The API will automatically try to fetch known update keys from the wiki and internal data based on
-the given ID.
+The API accepts a `POST` request with these fields:
-```
-POST https://api.smapi.io/v2.0/mods
+<table>
+<tr>
+<th>field</th>
+<th>summary</th>
+</tr>
+
+<tr>
+<td><code>mods</code></td>
+<td>
+
+The mods for which to fetch metadata. Included fields:
+
+
+field | summary
+----- | -------
+`id` | The unique ID in the mod's `manifest.json`. This is used to crossreference with the wiki, and to index mods in the response. If it's unknown (e.g. you just have an update key), you can use a unique fake ID like `FAKE.Nexus.2400`.
+`updateKeys` | _(optional)_ [Update keys](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest#Update_checks) which specify the mod pages to check, in addition to any mod pages linked to the `ID`.
+`installedVersion` | _(optional)_ The installed version of the mod. If not specified, the API won't recommend an update.
+`isBroken` | _(optional)_ Whether SMAPI failed to load the installed version of the mod, e.g. due to incompatibility. If true, the web API will be more permissive when recommending updates (e.g. allowing a stable → prerelease update).
+
+</td>
+</tr>
+
+<tr>
+<td><code>apiVersion</code></td>
+<td>
+
+_(optional)_ The installed version of SMAPI. If not specified, the API won't recommend an update.
+
+</td>
+</tr>
+
+<tr>
+<td><code>gameVersion</code></td>
+<td>
+
+_(optional)_ The installed version of Stardew Valley. This may be used to select updates.
+
+</td>
+</tr>
+
+<tr>
+<td><code>platform</code></td>
+<td>
+
+_(optional)_ The player's OS (`Android`, `Linux`, `Mac`, or `Windows`). This may be used to select updates.
+
+</td>
+</tr>
+
+<tr>
+<td><code>includeExtendedMetadata</code></td>
+<td>
+
+_(optional)_ Whether to include extra metadata that's not needed for SMAPI update checks, but which
+may be useful to external tools.
+
+</td>
+</table>
+
+Example request:
+```js
+POST https://api.smapi.io/v3.0/mods
{
"mods": [
{
- "id": "Pathoschild.LookupAnything",
- "updateKeys": [ "nexus:541", "chucklefish:4250" ]
+ "id": "Pathoschild.ContentPatcher",
+ "updateKeys": [ "nexus:1915" ],
+ "installedVersion": "1.9.2",
+ "isBroken": false
}
],
+ "apiVersion": "3.0.0",
+ "gameVersion": "1.4.0",
+ "platform": "Windows",
"includeExtendedMetadata": true
}
```
-The API will automatically aggregate versions and errors. Each mod will include...
-* an `id` (matching what you passed in);
-* up to three versions: `main` (e.g. 'latest version' field on Nexus), `optional` if newer (e.g.
- optional files on Nexus), and `unofficial` if newer (from the wiki);
-* `metadata` with mod info crossreferenced from the wiki and internal data (only if you specified
- `includeExtendedMetadata: true`);
-* and `errors` containing any error messages that occurred while fetching data.
-
-For example:
+Response fields:
+
+<table>
+<tr>
+<th>field</th>
+<th>summary</th>
+</tr>
+
+<tr>
+<td><code>id</code></td>
+<td>
+
+The mod ID you specified in the request.
+
+</td>
+</tr>
+
+<tr>
+<td><code>suggestedUpdate</code></td>
+<td>
+
+The update version recommended by the web API, if any. This is based on some internal rules (e.g.
+it won't recommend a prerelease update if the player has a working stable version) and context
+(e.g. whether the player is in the game beta channel). Choosing an update version yourself isn't
+recommended, but you can set `includeExtendedMetadata: true` and check the `metadata` field if you
+really want to do that.
+
+</td>
+</tr>
+
+<tr>
+<td><code>errors</code></td>
+<td>
+
+Human-readable errors that occurred fetching the version info (e.g. if a mod page has an invalid
+version).
+
+</td>
+</tr>
+
+<tr>
+<td><code>metadata</code></td>
+<td>
+
+Extra metadata that's not needed for SMAPI update checks but which may be useful to external tools,
+if you set `includeExtendedMetadata: true` in the request. Included fields:
+
+field | summary
+----- | -------
+`id` | The known `manifest.json` unique IDs for this mod defined on the wiki, if any. That includes historical versions of the mod.
+`name` | The normalised name for this mod based on the crossreferenced sites.
+`nexusID` | The mod ID on [Nexus Mods](https://www.nexusmods.com/stardewvalley/), if any.
+`chucklefishID` | The mod ID in the [Chucklefish mod repo](https://community.playstarbound.com/resources/categories/stardew-valley.22/), if any.
+`curseForgeID` | The mod project ID on [CurseForge](https://www.curseforge.com/stardewvalley), if any.
+`curseForgeKey` | The mod key on [CurseForge](https://www.curseforge.com/stardewvalley), if any. This is used in the mod page URL.
+`modDropID` | The mod ID on [ModDrop](https://www.moddrop.com/stardew-valley), if any.
+`gitHubRepo` | The GitHub repository containing the mod code, if any. Specified in the `Owner/Repo` form.
+`customSourceUrl` | The custom URL to the mod code, if any. This is used for mods which aren't stored in a GitHub repo.
+`customUrl` | The custom URL to the mod page, if any. This is used for mods which aren't stored on one of the standard mod sites covered by the ID fields.
+`main` | The primary mod version, if any. This depends on the mod site, but it's typically either the version of the mod itself or of its latest non-optional download.
+`optional` | The latest optional download version, if any.
+`unofficial` | The version of the unofficial update defined on the wiki for this mod, if any.
+`unofficialForBeta` | Equivalent to `unofficial`, but for beta versions of SMAPI or Stardew Valley.
+`hasBetaInfo` | Whether there's an ongoing Stardew Valley or SMAPI beta which may affect update checks.
+`compatibilityStatus` | The compatibility status for the mod for the stable version of the game, as defined on the wiki, if any. See [possible values](https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs).
+`compatibilitySummary` | The human-readable summary of the mod's compatibility in HTML format, if any.
+`brokeIn` | The SMAPI or Stardew Valley version that broke this mod, if any.
+`betaCompatibilityStatus`<br />`betaCompatibilitySummary`<br />`betaBrokeIn` | Equivalent to the preceding fields, but for beta versions of SMAPI or Stardew Valley.
+
+
+</td>
+</tr>
+</table>
+
+Example response with `includeExtendedMetadata: false`:
+```js
+[
+ {
+ "id": "Pathoschild.ContentPatcher",
+ "suggestedUpdate": {
+ "version": "1.10.0",
+ "url": "https://www.nexusmods.com/stardewvalley/mods/1915"
+ },
+ "errors": []
+ }
+]
```
+
+Example response with `includeExtendedMetadata: true`:
+```js
[
{
- "id": "Pathoschild.LookupAnything",
- "main": {
- "version": "1.19",
- "url": "https://www.nexusmods.com/stardewvalley/mods/541"
+ "id": "Pathoschild.ContentPatcher",
+ "suggestedUpdate": {
+ "version": "1.10.0",
+ "url": "https://www.nexusmods.com/stardewvalley/mods/1915"
},
"metadata": {
- "id": [
- "Pathoschild.LookupAnything",
- "LookupAnything"
- ],
- "name": "Lookup Anything",
- "nexusID": 541,
+ "id": [ "Pathoschild.ContentPatcher" ],
+ "name": "Content Patcher",
+ "nexusID": 1915,
+ "curseForgeID": 309243,
+ "curseForgeKey": "content-patcher",
+ "modDropID": 470174,
"gitHubRepo": "Pathoschild/StardewMods",
+ "main": {
+ "version": "1.10",
+ "url": "https://www.nexusmods.com/stardewvalley/mods/1915"
+ },
+ "hasBetaInfo": true,
"compatibilityStatus": "Ok",
"compatibilitySummary": "✓ use latest version."
},
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
index 8a9c0a25..f1bcfccc 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
{
/// <summary>Metadata about a mod.</summary>
@@ -9,23 +11,31 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>The mod's unique ID (if known).</summary>
public string ID { get; set; }
+ /// <summary>The update version recommended by the web API based on its version update and mapping rules.</summary>
+ public ModEntryVersionModel SuggestedUpdate { get; set; }
+
+ /// <summary>Optional extended data which isn't needed for update checks.</summary>
+ public ModExtendedMetadataModel Metadata { get; set; }
+
/// <summary>The main version.</summary>
+ [Obsolete]
public ModEntryVersionModel Main { get; set; }
/// <summary>The latest optional version, if newer than <see cref="Main"/>.</summary>
+ [Obsolete]
public ModEntryVersionModel Optional { get; set; }
/// <summary>The latest unofficial version, if newer than <see cref="Main"/> and <see cref="Optional"/>.</summary>
+ [Obsolete]
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>
+ [Obsolete]
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; }
+ [Obsolete]
+ 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/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
index 8074210c..4a697585 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs
@@ -46,6 +46,17 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>The custom mod page URL (if applicable).</summary>
public string CustomUrl { get; set; }
+ /// <summary>The main version.</summary>
+ public ModEntryVersionModel Main { get; set; }
+
+ /// <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 latest unofficial version for the current Stardew Valley or SMAPI beta, if any (see <see cref="HasBetaInfo"/>).</summary>
+ public ModEntryVersionModel UnofficialForBeta { get; set; }
/****
** Stable compatibility
@@ -60,7 +71,6 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>The game or SMAPI version which broke this mod, if applicable.</summary>
public string BrokeIn { get; set; }
-
/****
** Beta compatibility
****/
@@ -84,8 +94,18 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>Construct an instance.</summary>
/// <param name="wiki">The mod metadata from the wiki (if available).</param>
/// <param name="db">The mod metadata from SMAPI's internal DB (if available).</param>
- public ModExtendedMetadataModel(WikiModEntry wiki, ModDataRecord db)
+ /// <param name="main">The main version.</param>
+ /// <param name="optional">The latest optional version, if newer than <paramref name="main"/>.</param>
+ /// <param name="unofficial">The latest unofficial version, if newer than <paramref name="main"/> and <paramref name="optional"/>.</param>
+ /// <param name="unofficialForBeta">The latest unofficial version for the current Stardew Valley or SMAPI beta, if any.</param>
+ public ModExtendedMetadataModel(WikiModEntry wiki, ModDataRecord db, ModEntryVersionModel main, ModEntryVersionModel optional, ModEntryVersionModel unofficial, ModEntryVersionModel unofficialForBeta)
{
+ // versions
+ this.Main = main;
+ this.Optional = optional;
+ this.Unofficial = unofficial;
+ this.UnofficialForBeta = unofficialForBeta;
+
// wiki data
if (wiki != null)
{
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
index 886cd5a1..bf81e102 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs
@@ -12,6 +12,12 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>The namespaced mod update keys (if available).</summary>
public string[] UpdateKeys { get; set; }
+ /// <summary>The mod version installed by the local player. This is used for version mapping in some cases.</summary>
+ public ISemanticVersion InstalledVersion { get; set; }
+
+ /// <summary>Whether the installed version is broken or could not be loaded.</summary>
+ public bool IsBroken { get; set; }
+
/*********
** Public methods
@@ -24,10 +30,13 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>Construct an instance.</summary>
/// <param name="id">The unique mod ID.</param>
+ /// <param name="installedVersion">The version installed by the local player. This is used for version mapping in some cases.</param>
/// <param name="updateKeys">The namespaced mod update keys (if available).</param>
- public ModSearchEntryModel(string id, string[] updateKeys)
+ /// <param name="isBroken">Whether the installed version is broken or could not be loaded.</param>
+ public ModSearchEntryModel(string id, ISemanticVersion installedVersion, string[] updateKeys, bool isBroken = false)
{
this.ID = id;
+ this.InstalledVersion = installedVersion;
this.UpdateKeys = updateKeys ?? new string[0];
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSeachModel.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
index a2eaad16..73698173 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSeachModel.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using StardewModdingAPI.Toolkit.Utilities;
namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
{
@@ -14,6 +15,15 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>Whether to include extended metadata for each mod.</summary>
public bool IncludeExtendedMetadata { get; set; }
+ /// <summary>The SMAPI version installed by the player. This is used for version mapping in some cases.</summary>
+ public ISemanticVersion ApiVersion { get; set; }
+
+ /// <summary>The Stardew Valley version installed by the player.</summary>
+ public ISemanticVersion GameVersion { get; set; }
+
+ /// <summary>The OS on which the player plays.</summary>
+ public Platform? Platform { get; set; }
+
/*********
** Public methods
@@ -26,10 +36,16 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>Construct an instance.</summary>
/// <param name="mods">The mods to search.</param>
+ /// <param name="apiVersion">The SMAPI version installed by the player. If this is null, the API won't provide a recommended update.</param>
+ /// <param name="gameVersion">The Stardew Valley version installed by the player.</param>
+ /// <param name="platform">The OS on which the player plays.</param>
/// <param name="includeExtendedMetadata">Whether to include extended metadata for each mod.</param>
- public ModSearchModel(ModSearchEntryModel[] mods, bool includeExtendedMetadata)
+ public ModSearchModel(ModSearchEntryModel[] mods, ISemanticVersion apiVersion, ISemanticVersion gameVersion, Platform platform, bool includeExtendedMetadata)
{
this.Mods = mods.ToArray();
+ this.ApiVersion = apiVersion;
+ this.GameVersion = gameVersion;
+ this.Platform = platform;
this.IncludeExtendedMetadata = includeExtendedMetadata;
}
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
index 80c8f62b..f0a7c82a 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/WebApi/WebApiClient.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Net;
using Newtonsoft.Json;
using StardewModdingAPI.Toolkit.Serialization;
+using StardewModdingAPI.Toolkit.Utilities;
namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
{
@@ -37,12 +38,15 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi
/// <summary>Get metadata about a set of mods from the web API.</summary>
/// <param name="mods">The mod keys for which to fetch the latest version.</param>
+ /// <param name="apiVersion">The SMAPI version installed by the player. If this is null, the API won't provide a recommended update.</param>
+ /// <param name="gameVersion">The Stardew Valley version installed by the player.</param>
+ /// <param name="platform">The OS on which the player plays.</param>
/// <param name="includeExtendedMetadata">Whether to include extended metadata for each mod.</param>
- public IDictionary<string, ModEntryModel> GetModInfo(ModSearchEntryModel[] mods, bool includeExtendedMetadata = false)
+ public IDictionary<string, ModEntryModel> GetModInfo(ModSearchEntryModel[] mods, ISemanticVersion apiVersion, ISemanticVersion gameVersion, Platform platform, bool includeExtendedMetadata = false)
{
return this.Post<ModSearchModel, ModEntryModel[]>(
$"v{this.Version}/mods",
- new ModSearchModel(mods, includeExtendedMetadata)
+ new ModSearchModel(mods, apiVersion, gameVersion, platform, includeExtendedMetadata)
).ToDictionary(p => p.ID);
}
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
index 610e14f1..384f23fc 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs
@@ -102,6 +102,8 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
string anchor = this.GetAttribute(node, "id");
string contentPackFor = this.GetAttribute(node, "data-content-pack-for");
string devNote = this.GetAttribute(node, "data-dev-note");
+ IDictionary<string, string> mapLocalVersions = this.GetAttributeAsVersionMapping(node, "data-map-local-versions");
+ IDictionary<string, string> mapRemoteVersions = this.GetAttributeAsVersionMapping(node, "data-map-remote-versions");
// parse stable compatibility
WikiCompatibilityInfo compatibility = new WikiCompatibilityInfo
@@ -159,6 +161,8 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
Warnings = warnings,
MetadataLinks = metadataLinks.ToArray(),
DevNote = devNote,
+ MapLocalVersions = mapLocalVersions,
+ MapRemoteVersions = mapRemoteVersions,
Anchor = anchor
};
}
@@ -223,6 +227,28 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
return null;
}
+ /// <summary>Get an attribute value and parse it as a version mapping.</summary>
+ /// <param name="element">The element whose attributes to read.</param>
+ /// <param name="name">The attribute name.</param>
+ private IDictionary<string, string> GetAttributeAsVersionMapping(HtmlNode element, string name)
+ {
+ // get raw value
+ string raw = this.GetAttribute(element, name);
+ if (raw?.Contains("→") != true)
+ return null;
+
+ // parse
+ // Specified on the wiki in the form "remote version → mapped version; another remote version → mapped version"
+ IDictionary<string, string> map = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+ foreach (string pair in raw.Split(';'))
+ {
+ string[] versions = pair.Split('→');
+ if (versions.Length == 2 && !string.IsNullOrWhiteSpace(versions[0]) && !string.IsNullOrWhiteSpace(versions[1]))
+ map[versions[0].Trim()] = versions[1].Trim();
+ }
+ return map;
+ }
+
/// <summary>Get the text of an element with the given class name.</summary>
/// <param name="container">The metadata container.</param>
/// <param name="className">The field name.</param>
diff --git a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
index 51bb2336..931dcd43 100644
--- a/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
+++ b/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
{
@@ -62,6 +63,12 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
/// <summary>Special notes intended for developers who maintain unofficial updates or submit pull requests. </summary>
public string DevNote { get; set; }
+ /// <summary>Maps local versions to a semantic version for update checks.</summary>
+ public IDictionary<string, string> MapLocalVersions { get; set; }
+
+ /// <summary>Maps remote versions to a semantic version for update checks.</summary>
+ public IDictionary<string, string> MapRemoteVersions { get; set; }
+
/// <summary>The link anchor for the mod entry in the wiki compatibility list.</summary>
public string Anchor { get; set; }
}
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs
index dd0bd07b..8b40c301 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataModel.cs
@@ -25,12 +25,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// </remarks>
public string FormerIDs { get; set; }
- /// <summary>Maps local versions to a semantic version for update checks.</summary>
- public IDictionary<string, string> MapLocalVersions { get; set; } = new Dictionary<string, string>();
-
- /// <summary>Maps remote versions to a semantic version for update checks.</summary>
- public IDictionary<string, string> MapRemoteVersions { get; set; } = new Dictionary<string, string>();
-
/// <summary>The mod warnings to suppress, even if they'd normally be shown.</summary>
public ModWarning SuppressWarnings { get; set; }
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
index f01ada7c..c892d820 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecord.cs
@@ -22,12 +22,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// <summary>The mod warnings to suppress, even if they'd normally be shown.</summary>
public ModWarning SuppressWarnings { get; set; }
- /// <summary>Maps local versions to a semantic version for update checks.</summary>
- public IDictionary<string, string> MapLocalVersions { get; }
-
- /// <summary>Maps remote versions to a semantic version for update checks.</summary>
- public IDictionary<string, string> MapRemoteVersions { get; }
-
/// <summary>The versioned field data.</summary>
public ModDataField[] Fields { get; }
@@ -44,8 +38,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
this.ID = model.ID;
this.FormerIDs = model.GetFormerIDs().ToArray();
this.SuppressWarnings = model.SuppressWarnings;
- this.MapLocalVersions = new Dictionary<string, string>(model.MapLocalVersions, StringComparer.InvariantCultureIgnoreCase);
- this.MapRemoteVersions = new Dictionary<string, string>(model.MapRemoteVersions, StringComparer.InvariantCultureIgnoreCase);
this.Fields = model.GetFields().ToArray();
}
@@ -67,29 +59,6 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
return false;
}
- /// <summary>Get a semantic local version for update checks.</summary>
- /// <param name="version">The remote version to normalize.</param>
- public ISemanticVersion GetLocalVersionForUpdateChecks(ISemanticVersion version)
- {
- return this.MapLocalVersions != null && this.MapLocalVersions.TryGetValue(version.ToString(), out string newVersion)
- ? new SemanticVersion(newVersion)
- : version;
- }
-
- /// <summary>Get a semantic remote version for update checks.</summary>
- /// <param name="version">The remote version to normalize.</param>
- public string GetRemoteVersionForUpdateChecks(string version)
- {
- // normalize version if possible
- if (SemanticVersion.TryParse(version, out ISemanticVersion parsed))
- version = parsed.ToString();
-
- // fetch remote version
- return this.MapRemoteVersions != null && this.MapRemoteVersions.TryGetValue(version, out string newVersion)
- ? newVersion
- : version;
- }
-
/// <summary>Get the possible mod IDs.</summary>
public IEnumerable<string> GetIDs()
{
diff --git a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
index 9e22990d..598da66a 100644
--- a/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
+++ b/src/SMAPI.Toolkit/Framework/ModData/ModDataRecordVersionedFields.cs
@@ -26,29 +26,5 @@ namespace StardewModdingAPI.Toolkit.Framework.ModData
/// <summary>The upper version for which the <see cref="Status"/> applies (if any).</summary>
public ISemanticVersion StatusUpperVersion { get; set; }
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Get a semantic local version for update checks.</summary>
- /// <param name="version">The remote version to normalize.</param>
- public ISemanticVersion GetLocalVersionForUpdateChecks(ISemanticVersion version)
- {
- return this.DataRecord.GetLocalVersionForUpdateChecks(version);
- }
-
- /// <summary>Get a semantic remote version for update checks.</summary>
- /// <param name="version">The remote version to normalize.</param>
- public ISemanticVersion GetRemoteVersionForUpdateChecks(ISemanticVersion version)
- {
- if (version == null)
- return null;
-
- string rawVersion = this.DataRecord.GetRemoteVersionForUpdateChecks(version.ToString());
- return rawVersion != null
- ? new SemanticVersion(rawVersion)
- : version;
- }
}
}
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index f65b164f..fe220eb5 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -80,7 +80,7 @@ namespace StardewModdingAPI.Web.Controllers
new IModRepository[]
{
new ChucklefishRepository(chucklefish),
- new CurseForgeRepository(curseForge),
+ new CurseForgeRepository(curseForge),
new GitHubRepository(github),
new ModDropRepository(modDrop),
new NexusRepository(nexus)
@@ -90,12 +90,15 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>Fetch version metadata for the given mods.</summary>
/// <param name="model">The mod search criteria.</param>
+ /// <param name="version">The requested API version.</param>
[HttpPost]
- public async Task<IEnumerable<ModEntryModel>> PostAsync([FromBody] ModSearchModel model)
+ public async Task<IEnumerable<ModEntryModel>> PostAsync([FromBody] ModSearchModel model, [FromRoute] string version)
{
if (model?.Mods == null)
return new ModEntryModel[0];
+ bool legacyMode = SemanticVersion.TryParse(version, out ISemanticVersion parsedVersion) && parsedVersion.IsOlderThan("3.0.0-beta.20191109");
+
// fetch wiki data
WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.GetModel()).ToArray();
IDictionary<string, ModEntryModel> mods = new Dictionary<string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);
@@ -104,7 +107,25 @@ namespace StardewModdingAPI.Web.Controllers
if (string.IsNullOrWhiteSpace(mod.ID))
continue;
- ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata);
+ ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata || legacyMode, model.ApiVersion);
+ if (legacyMode)
+ {
+ result.Main = result.Metadata.Main;
+ result.Optional = result.Metadata.Optional;
+ result.Unofficial = result.Metadata.Unofficial;
+ result.UnofficialForBeta = result.Metadata.UnofficialForBeta;
+ result.HasBetaInfo = result.Metadata.BetaCompatibilityStatus != null;
+ result.SuggestedUpdate = null;
+ if (!model.IncludeExtende