summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md1
-rw-r--r--src/SMAPI/Framework/IModMetadata.cs4
-rw-r--r--src/SMAPI/Framework/ModLoading/ModMetadata.cs21
-rw-r--r--src/SMAPI/Framework/SCore.cs48
4 files changed, 61 insertions, 13 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md
index dc0f584d..d31b95e5 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -6,6 +6,7 @@
* For players:
* Added friendly log message for save file-not-found errors.
* Updated for the 'Force Off' gamepad mode added in Stardew Valley 1.4.1.
+ * The 'skipped mods' list now shows broken dependencies first, so it's easier to see which ones to fix first.
* Fixed compatibility with Linux Mint 18 (thanks to techge!) and Arch Linux.
* Fixed compatibility with Linux systems which have libhybris-utils installed.
* Fixes for the bundled Console Commands mod:
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 6ee7df69..37927482 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -105,6 +105,10 @@ namespace StardewModdingAPI.Framework
/// <param name="validOnly">Only return valid update keys.</param>
IEnumerable<UpdateKey> GetUpdateKeys(bool validOnly = true);
+ /// <summary>Get the mod IDs that must be installed to load this mod.</summary>
+ /// <param name="includeOptional">Whether to include optional dependencies.</param>
+ IEnumerable<string> GetRequiredModIds(bool includeOptional = false);
+
/// <summary>Whether the mod has at least one valid update key set.</summary>
bool HasValidUpdateKeys();
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index 7f788d17..0e90362e 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -188,6 +188,27 @@ namespace StardewModdingAPI.Framework.ModLoading
}
}
+ /// <summary>Get the mod IDs that must be installed to load this mod.</summary>
+ /// <param name="includeOptional">Whether to include optional dependencies.</param>
+ public IEnumerable<string> GetRequiredModIds(bool includeOptional = false)
+ {
+ HashSet<string> required = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+
+ // yield dependencies
+ if (this.Manifest?.Dependencies != null)
+ {
+ foreach (var entry in this.Manifest?.Dependencies)
+ {
+ if ((entry.IsRequired || includeOptional) && required.Add(entry.UniqueID))
+ yield return entry.UniqueID;
+ }
+ }
+
+ // yield content pack parent
+ if (this.Manifest?.ContentPackFor?.UniqueID != null && required.Add(this.Manifest.ContentPackFor.UniqueID))
+ yield return this.Manifest.ContentPackFor.UniqueID;
+ }
+
/// <summary>Whether the mod has at least one valid update key set.</summary>
public bool HasValidUpdateKeys()
{
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index c9c9d14a..a89c14d7 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -1046,26 +1046,48 @@ namespace StardewModdingAPI.Framework
// log skipped mods
if (skippedMods.Any())
{
+ // get logging logic
+ HashSet<string> logged = new HashSet<string>();
+ void LogSkippedMod(IModMetadata mod, string errorReason, string errorDetails)
+ {
+ string message = $" - {mod.DisplayName}{(mod.Manifest?.Version != null ? " " + mod.Manifest.Version.ToString() : "")} because {errorReason}";
+
+ if (logged.Add($"{message}|{errorDetails}"))
+ {
+ this.Monitor.Log(message, LogLevel.Error);
+ if (errorDetails != null)
+ this.Monitor.Log($" ({errorDetails})", LogLevel.Trace);
+ }
+ }
+
+ // find skipped dependencies
+ KeyValuePair<IModMetadata, Tuple<string, string>>[] skippedDependencies;
+ {
+ HashSet<string> skippedDependencyIds = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+ HashSet<string> skippedModIds = new HashSet<string>(from mod in skippedMods where mod.Key.HasID() select mod.Key.Manifest.UniqueID, StringComparer.InvariantCultureIgnoreCase);
+ foreach (IModMetadata mod in skippedMods.Keys)
+ {
+ foreach (string requiredId in skippedModIds.Intersect(mod.GetRequiredModIds()))
+ skippedDependencyIds.Add(requiredId);
+ }
+ skippedDependencies = skippedMods.Where(p => p.Key.HasID() && skippedDependencyIds.Contains(p.Key.Manifest.UniqueID)).ToArray();
+ }
+
+ // log skipped mods
this.Monitor.Log(" Skipped mods", LogLevel.Error);
this.Monitor.Log(" " + "".PadRight(50, '-'), LogLevel.Error);
this.Monitor.Log(" These mods could not be added to your game.", LogLevel.Error);
this.Monitor.Newline();
- HashSet<string> logged = new HashSet<string>();
- foreach (var pair in skippedMods.OrderBy(p => p.Key.DisplayName))
+ if (skippedDependencies.Any())
{
- IModMetadata mod = pair.Key;
- string errorReason = pair.Value.Item1;
- string errorDetails = pair.Value.Item2;
- string message = $" - {mod.DisplayName}{(mod.Manifest?.Version != null ? " " + mod.Manifest.Version.ToString() : "")} because {errorReason}";
-
- if (!logged.Add($"{message}|{errorDetails}"))
- continue; // skip duplicate messages (e.g. if multiple copies of the mod are installed)
-
- this.Monitor.Log(message, LogLevel.Error);
- if (errorDetails != null)
- this.Monitor.Log($" ({errorDetails})", LogLevel.Trace);
+ foreach (var pair in skippedDependencies.OrderBy(p => p.Key.DisplayName))
+ LogSkippedMod(pair.Key, pair.Value.Item1, pair.Value.Item2);
+ this.Monitor.Newline();
}
+
+ foreach (var pair in skippedMods.OrderBy(p => p.Key.DisplayName))
+ LogSkippedMod(pair.Key, pair.Value.Item1, pair.Value.Item2);
this.Monitor.Newline();
}