summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/StardewModdingAPI.Tests/Core/ModResolverTests.cs20
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs21
2 files changed, 41 insertions, 0 deletions
diff --git a/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs b/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs
index e6ec632a..9b46c5d2 100644
--- a/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs
+++ b/src/StardewModdingAPI.Tests/Core/ModResolverTests.cs
@@ -179,6 +179,26 @@ namespace StardewModdingAPI.Tests.Core
mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
}
+#if SMAPI_2_0
+ [Test(Description = "Assert that validation fails when multiple mods have the same unique ID.")]
+ public void ValidateManifests_DuplicateUniqueID_Fails()
+ {
+ // arrange
+ Mock<IModMetadata> modA = this.GetMetadata("Mod A", new string[0], allowStatusChange: true);
+ Mock<IModMetadata> modB = this.GetMetadata(this.GetManifest("Mod A", "1.0", manifest => manifest.Name = "Mod B"), allowStatusChange: true);
+ Mock<IModMetadata> modC = this.GetMetadata("Mod C", new string[0], allowStatusChange: false);
+ foreach (Mock<IModMetadata> mod in new[] { modA, modB, modC })
+ this.SetupMetadataForValidation(mod);
+
+ // act
+ new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"));
+
+ // 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.");
+ modB.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the second mod with a unique ID.");
+ }
+#endif
+
[Test(Description = "Assert that validation fails when the manifest references a DLL that does not exist.")]
public void ValidateManifests_Valid_Passes()
{
diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
index 38dddce7..69ef0b63 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
@@ -95,6 +95,9 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <param name="apiVersion">The current SMAPI version.</param>
public void ValidateManifests(IEnumerable<IModMetadata> mods, ISemanticVersion apiVersion)
{
+ mods = mods.ToArray();
+
+ // validate each manifest
foreach (IModMetadata mod in mods)
{
// skip if already failed
@@ -153,6 +156,24 @@ namespace StardewModdingAPI.Framework.ModLoading
}
#endif
}
+
+ // validate IDs are unique
+#if SMAPI_2_0
+ {
+ var duplicatesByID = mods
+ .GroupBy(mod => mod.Manifest.UniqueID?.Trim(), mod => mod, StringComparer.InvariantCultureIgnoreCase)
+ .Where(p => p.Count() > 1);
+ foreach (var group in duplicatesByID)
+ {
+ foreach (IModMetadata mod in group)
+ {
+ if (mod.Status == ModMetadataStatus.Failed)
+ continue; // don't replace metadata error
+ mod.SetStatus(ModMetadataStatus.Failed, $"its unique ID '{mod.Manifest.UniqueID}' is used by multiple mods ({string.Join(", ", group.Select(p => p.DisplayName))}).");
+ }
+ }
+ }
+#endif
}
/// <summary>Sort the given mods by the order they should be loaded.</summary>