parent
4fd3fdc0d8
commit
5cb183e16d
|
@ -119,7 +119,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
[Test(Description = "Assert that validation doesn't fail if there are no mods installed.")]
|
||||
public void ValidateManifests_NoMods_DoesNothing()
|
||||
{
|
||||
new ModResolver().ValidateManifests(new ModMetadata[0], apiVersion: new SemanticVersion("1.0"));
|
||||
new ModResolver().ValidateManifests(new ModMetadata[0], apiVersion: new SemanticVersion("1.0"), vendorModUrls: new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
[Test(Description = "Assert that validation skips manifests that have already failed without calling any other properties.")]
|
||||
|
@ -130,7 +130,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
mock.Setup(p => p.Status).Returns(ModMetadataStatus.Failed);
|
||||
|
||||
// act
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"));
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), vendorModUrls: new Dictionary<string, string>());
|
||||
|
||||
// assert
|
||||
mock.VerifyGet(p => p.Status, Times.Once, "The validation did not check the manifest status.");
|
||||
|
@ -148,7 +148,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
});
|
||||
|
||||
// act
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"));
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), vendorModUrls: new Dictionary<string, string>());
|
||||
|
||||
// assert
|
||||
mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
|
||||
|
@ -163,7 +163,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
this.SetupMetadataForValidation(mock);
|
||||
|
||||
// act
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"));
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), vendorModUrls: new Dictionary<string, string>());
|
||||
|
||||
// assert
|
||||
mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
|
||||
|
@ -177,7 +177,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
this.SetupMetadataForValidation(mock);
|
||||
|
||||
// act
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"));
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), vendorModUrls: new Dictionary<string, string>());
|
||||
|
||||
// assert
|
||||
mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
|
||||
|
@ -194,7 +194,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
this.SetupMetadataForValidation(mod);
|
||||
|
||||
// act
|
||||
new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"));
|
||||
new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"), vendorModUrls: new Dictionary<string, string>());
|
||||
|
||||
// assert
|
||||
modA.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the first mod with a unique ID.");
|
||||
|
@ -220,7 +220,7 @@ namespace StardewModdingAPI.Tests.Core
|
|||
mock.Setup(p => p.DirectoryPath).Returns(modFolder);
|
||||
|
||||
// act
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"));
|
||||
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), vendorModUrls: new Dictionary<string, string>());
|
||||
|
||||
// assert
|
||||
// if Moq doesn't throw a method-not-setup exception, the validation didn't override the status.
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace StardewModdingAPI.Web.Controllers
|
|||
[HttpGet]
|
||||
public async Task<IDictionary<string, ModInfoModel>> GetAsync(string modKeys)
|
||||
{
|
||||
string[] modKeysArray = modKeys?.Split(',').Select(p => p.Trim()).ToArray();
|
||||
string[] modKeysArray = modKeys?.Split(',').ToArray();
|
||||
if (modKeysArray == null || !modKeysArray.Any())
|
||||
return new Dictionary<string, ModInfoModel>();
|
||||
|
||||
|
@ -154,8 +154,8 @@ namespace StardewModdingAPI.Web.Controllers
|
|||
}
|
||||
|
||||
// parse
|
||||
vendorKey = parts[0];
|
||||
modID = parts[1];
|
||||
vendorKey = parts[0].Trim();
|
||||
modID = parts[1].Trim();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
@ -86,6 +87,14 @@ namespace StardewModdingAPI
|
|||
Platform.Mono;
|
||||
#endif
|
||||
|
||||
/// <summary>Maps vendor keys (like <c>Nexus</c>) to their mod URL template (where <c>{0}</c> is the mod ID) during mod compatibility checks. This doesn't affect update checks, which defer to the remote web API.</summary>
|
||||
internal static readonly IDictionary<string, string> VendorModUrls = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
["Chucklefish"] = "https://community.playstarbound.com/resources/{0}",
|
||||
["Nexus"] = "http://nexusmods.com/stardewvalley/mods/{0}",
|
||||
["GitHub"] = "https://github.com/{0}/releases"
|
||||
};
|
||||
|
||||
|
||||
/*********
|
||||
** Internal methods
|
||||
|
|
|
@ -61,13 +61,9 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
dataRecord = dataRecords.FirstOrDefault(record => record.ID.Matches(key, manifest));
|
||||
}
|
||||
|
||||
// apply defaults
|
||||
if (dataRecord?.Defaults != null)
|
||||
{
|
||||
manifest.ChucklefishID = manifest.ChucklefishID ?? dataRecord.Defaults.ChucklefishID;
|
||||
manifest.GitHubProject = manifest.GitHubProject ?? dataRecord.Defaults.GitHubProject;
|
||||
manifest.NexusID = manifest.NexusID ?? dataRecord.Defaults.NexusID;
|
||||
}
|
||||
// add default update keys
|
||||
if (manifest != null && manifest.UpdateKeys == null && dataRecord?.UpdateKeys != null)
|
||||
manifest.UpdateKeys = dataRecord.UpdateKeys;
|
||||
|
||||
// build metadata
|
||||
string displayName = !string.IsNullOrWhiteSpace(manifest?.Name)
|
||||
|
@ -84,7 +80,8 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
/// <summary>Validate manifest metadata.</summary>
|
||||
/// <param name="mods">The mod manifests to validate.</param>
|
||||
/// <param name="apiVersion">The current SMAPI version.</param>
|
||||
public void ValidateManifests(IEnumerable<IModMetadata> mods, ISemanticVersion apiVersion)
|
||||
/// <param name="vendorModUrls">Maps vendor keys (like <c>Nexus</c>) to their mod URL template (where <c>{0}</c> is the mod ID).</param>
|
||||
public void ValidateManifests(IEnumerable<IModMetadata> mods, ISemanticVersion apiVersion, IDictionary<string, string> vendorModUrls)
|
||||
{
|
||||
mods = mods.ToArray();
|
||||
|
||||
|
@ -110,12 +107,18 @@ namespace StardewModdingAPI.Framework.ModLoading
|
|||
|
||||
// get update URLs
|
||||
List<string> updateUrls = new List<string>();
|
||||
if (!string.IsNullOrWhiteSpace(mod.Manifest.ChucklefishID))
|
||||
updateUrls.Add($"https://community.playstarbound.com/resources/{mod.Manifest.ChucklefishID}");
|
||||
if (!string.IsNullOrWhiteSpace(mod.Manifest.NexusID))
|
||||
updateUrls.Add($"http://nexusmods.com/stardewvalley/mods/{mod.Manifest.NexusID}");
|
||||
if (!string.IsNullOrWhiteSpace(mod.Manifest.GitHubProject))
|
||||
updateUrls.Add($"https://github.com/{mod.Manifest.GitHubProject}/releases");
|
||||
foreach (string key in mod.Manifest.UpdateKeys ?? new string[0])
|
||||
{
|
||||
string[] parts = key.Split(new[] { ':' }, 2);
|
||||
if (parts.Length != 2)
|
||||
continue;
|
||||
|
||||
string vendorKey = parts[0].Trim();
|
||||
string modID = parts[1].Trim();
|
||||
|
||||
if (vendorModUrls.TryGetValue(vendorKey, out string urlTemplate))
|
||||
updateUrls.Add(string.Format(urlTemplate, modID));
|
||||
}
|
||||
if (mod.DataRecord.AlternativeUrl != null)
|
||||
updateUrls.Add(mod.DataRecord.AlternativeUrl);
|
||||
|
||||
|
|
|
@ -34,14 +34,8 @@ namespace StardewModdingAPI.Framework.Models
|
|||
[JsonConverter(typeof(SFieldConverter))]
|
||||
public IManifestDependency[] Dependencies { get; set; }
|
||||
|
||||
/// <summary>The mod's unique ID in the Chucklefish mod site (if any), used for update checks.</summary>
|
||||
public string ChucklefishID { get; set; }
|
||||
|
||||
/// <summary>The mod's unique ID in Nexus Mods (if any), used for update checks.</summary>
|
||||
public string NexusID { get; set; }
|
||||
|
||||
/// <summary>The mod's organisation and project name on GitHub (if any), used for update checks.</summary>
|
||||
public string GitHubProject { get; set; }
|
||||
/// <summary>The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</summary>
|
||||
public string[] UpdateKeys { get; set; }
|
||||
|
||||
/// <summary>The unique mod ID.</summary>
|
||||
public string UniqueID { get; set; }
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
namespace StardewModdingAPI.Framework.Models
|
||||
{
|
||||
/// <summary>Default values for support fields to inject into the manifest.</summary>
|
||||
internal class ModDataDefaults
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The mod's unique ID in the Chucklefish mod site (if any), used for update checks.</summary>
|
||||
public string ChucklefishID { get; set; }
|
||||
|
||||
/// <summary>The mod's unique ID in Nexus Mods (if any), used for update checks.</summary>
|
||||
public string NexusID { get; set; }
|
||||
|
||||
/// <summary>The mod's organisation and project name on GitHub (if any), used for update checks.</summary>
|
||||
public string GitHubProject { get; set; }
|
||||
}
|
||||
}
|
|
@ -15,8 +15,8 @@ namespace StardewModdingAPI.Framework.Models
|
|||
[JsonConverter(typeof(SFieldConverter))]
|
||||
public ModDataID ID { get; set; }
|
||||
|
||||
/// <summary>Default values for support fields to inject into the manifest.</summary>
|
||||
public ModDataDefaults Defaults { get; set; }
|
||||
/// <summary>A value to inject into <see cref="IManifest.UpdateKeys"/> field if it's not already set.</summary>
|
||||
public string[] UpdateKeys { get; set; }
|
||||
|
||||
/// <summary>The URL where the player can get an unofficial or alternative version of the mod if the official version isn't compatible.</summary>
|
||||
public string AlternativeUrl { get; set; }
|
||||
|
|
|
@ -32,14 +32,8 @@ namespace StardewModdingAPI
|
|||
/// <summary>The other mods that must be loaded before this mod.</summary>
|
||||
IManifestDependency[] Dependencies { get; }
|
||||
|
||||
/// <summary>The mod's unique ID in the Chucklefish mod site (if any), used for update checks.</summary>
|
||||
string ChucklefishID { get; }
|
||||
|
||||
/// <summary>The mod's unique ID in Nexus Mods (if any), used for update checks.</summary>
|
||||
string NexusID { get; }
|
||||
|
||||
/// <summary>The mod's organisation and project name on GitHub (if any), used for update checks.</summary>
|
||||
string GitHubProject { get; }
|
||||
/// <summary>The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</summary>
|
||||
string[] UpdateKeys { get; set; }
|
||||
|
||||
/// <summary>Any manifest fields which didn't match a valid field.</summary>
|
||||
IDictionary<string, object> ExtraFields { get; }
|
||||
|
|
|
@ -347,7 +347,7 @@ namespace StardewModdingAPI
|
|||
|
||||
// load manifests
|
||||
IModMetadata[] mods = resolver.ReadManifests(Constants.ModPath, new JsonHelper(), this.Settings.ModData).ToArray();
|
||||
resolver.ValidateManifests(mods, Constants.ApiVersion);
|
||||
resolver.ValidateManifests(mods, Constants.ApiVersion, Constants.VendorModUrls);
|
||||
|
||||
// process dependencies
|
||||
mods = resolver.ProcessDependencies(mods).ToArray();
|
||||
|
@ -530,25 +530,12 @@ namespace StardewModdingAPI
|
|||
}
|
||||
|
||||
// add update keys
|
||||
bool hasUpdateKeys = false;
|
||||
if (!string.IsNullOrWhiteSpace(mod.Manifest.ChucklefishID))
|
||||
if (mod.Manifest.UpdateKeys?.Any() == true)
|
||||
{
|
||||
hasUpdateKeys = true;
|
||||
modsByKey[$"Chucklefish:{mod.Manifest.ChucklefishID}"] = mod;
|
||||
foreach (string updateKey in mod.Manifest.UpdateKeys)
|
||||
modsByKey[updateKey] = mod;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(mod.Manifest.NexusID))
|
||||
{
|
||||
hasUpdateKeys = true;
|
||||
modsByKey[$"Nexus:{mod.Manifest.NexusID}"] = mod;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(mod.Manifest.GitHubProject))
|
||||
{
|
||||
hasUpdateKeys = true;
|
||||
modsByKey[$"GitHub:{mod.Manifest.GitHubProject}"] = mod;
|
||||
}
|
||||
|
||||
// log
|
||||
if (!hasUpdateKeys)
|
||||
else
|
||||
this.VerboseLog($" {mod.DisplayName}: no update keys.");
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -91,7 +91,6 @@
|
|||
<Link>Properties\GlobalAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Framework\Models\ModCompatibility.cs" />
|
||||
<Compile Include="Framework\Models\ModDataDefaults.cs" />
|
||||
<Compile Include="Framework\ModLoading\Finders\EventFinder.cs" />
|
||||
<Compile Include="Framework\ModLoading\Finders\FieldFinder.cs" />
|
||||
<Compile Include="Framework\ModLoading\Finders\MethodFinder.cs" />
|
||||
|
|
Loading…
Reference in New Issue