summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Framework/ModLoading
diff options
context:
space:
mode:
Diffstat (limited to 'src/StardewModdingAPI/Framework/ModLoading')
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs15
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs24
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs9
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs28
4 files changed, 59 insertions, 17 deletions
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs
new file mode 100644
index 00000000..11be19fc
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs
@@ -0,0 +1,15 @@
+namespace StardewModdingAPI.Framework.ModLoading
+{
+ /// <summary>Indicates the result of an assembly load.</summary>
+ internal enum AssemblyLoadStatus
+ {
+ /// <summary>The assembly was loaded successfully.</summary>
+ Okay = 1,
+
+ /// <summary>The assembly could not be loaded.</summary>
+ Failed = 2,
+
+ /// <summary>The assembly is already loaded.</summary>
+ AlreadyLoaded = 3
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
index 406d49e1..b14ae56f 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -6,6 +6,7 @@ using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
using StardewModdingAPI.AssemblyRewriters;
+using StardewModdingAPI.Framework.Exceptions;
namespace StardewModdingAPI.Framework.ModLoading
{
@@ -65,16 +66,27 @@ namespace StardewModdingAPI.Framework.ModLoading
AssemblyDefinitionResolver resolver = new AssemblyDefinitionResolver();
HashSet<string> visitedAssemblyNames = new HashSet<string>(AppDomain.CurrentDomain.GetAssemblies().Select(p => p.GetName().Name)); // don't try loading assemblies that are already loaded
assemblies = this.GetReferencedLocalAssemblies(new FileInfo(assemblyPath), visitedAssemblyNames, resolver).ToArray();
- if (!assemblies.Any())
- throw new InvalidOperationException($"Could not load '{assemblyPath}' because it doesn't exist.");
- resolver.Add(assemblies.Select(p => p.Definition).ToArray());
}
+ // validate load
+ if (!assemblies.Any() || assemblies[0].Status == AssemblyLoadStatus.Failed)
+ {
+ throw new SAssemblyLoadFailedException(!File.Exists(assemblyPath)
+ ? $"Could not load '{assemblyPath}' because it doesn't exist."
+ : $"Could not load '{assemblyPath}'."
+ );
+ }
+ if (assemblies[0].Status == AssemblyLoadStatus.AlreadyLoaded)
+ throw new SAssemblyLoadFailedException($"Could not load '{assemblyPath}' because it was already loaded. Do you have two copies of this mod?");
+
// rewrite & load assemblies in leaf-to-root order
bool oneAssembly = assemblies.Length == 1;
Assembly lastAssembly = null;
foreach (AssemblyParseResult assembly in assemblies)
{
+ if (assembly.Status == AssemblyLoadStatus.AlreadyLoaded)
+ continue;
+
bool changed = this.RewriteAssembly(assembly.Definition, assumeCompatible, logPrefix: " ");
if (changed)
{
@@ -143,7 +155,7 @@ namespace StardewModdingAPI.Framework.ModLoading
// skip if already visited
if (visitedAssemblyNames.Contains(assembly.Name.Name))
- yield break;
+ yield return new AssemblyParseResult(file, null, AssemblyLoadStatus.AlreadyLoaded);
visitedAssemblyNames.Add(assembly.Name.Name);
// yield referenced assemblies
@@ -155,7 +167,7 @@ namespace StardewModdingAPI.Framework.ModLoading
}
// yield assembly
- yield return new AssemblyParseResult(file, assembly);
+ yield return new AssemblyParseResult(file, assembly, AssemblyLoadStatus.Okay);
}
/****
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs
index 69c99afe..b56a776c 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs
@@ -15,6 +15,9 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>The assembly definition.</summary>
public readonly AssemblyDefinition Definition;
+ /// <summary>The result of the assembly load.</summary>
+ public AssemblyLoadStatus Status;
+
/*********
** Public methods
@@ -22,10 +25,12 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>Construct an instance.</summary>
/// <param name="file">The original assembly file.</param>
/// <param name="assembly">The assembly definition.</param>
- public AssemblyParseResult(FileInfo file, AssemblyDefinition assembly)
+ /// <param name="status">The result of the assembly load.</param>
+ public AssemblyParseResult(FileInfo file, AssemblyDefinition assembly, AssemblyLoadStatus status)
{
this.File = file;
this.Definition = assembly;
+ this.Status = status;
}
}
-} \ No newline at end of file
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
index b75453b7..6b19db5c 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs
@@ -71,9 +71,9 @@ namespace StardewModdingAPI.Framework.ModLoading
compatibility = (
from mod in compatibilityRecords
where
- mod.ID.Contains(key, StringComparer.InvariantCultureIgnoreCase)
- && (mod.LowerSemanticVersion == null || !manifest.Version.IsOlderThan(mod.LowerSemanticVersion))
- && !manifest.Version.IsNewerThan(mod.UpperSemanticVersion)
+ mod.ID.Any(p => p.Matches(key, manifest))
+ && (mod.LowerVersion == null || !manifest.Version.IsOlderThan(mod.LowerVersion))
+ && !manifest.Version.IsNewerThan(mod.UpperVersion)
select mod
).FirstOrDefault();
}
@@ -109,15 +109,25 @@ namespace StardewModdingAPI.Framework.ModLoading
ModCompatibility compatibility = mod.Compatibility;
if (compatibility?.Compatibility == ModCompatibilityType.AssumeBroken)
{
- bool hasOfficialUrl = !string.IsNullOrWhiteSpace(mod.Compatibility.UpdateUrl);
- bool hasUnofficialUrl = !string.IsNullOrWhiteSpace(mod.Compatibility.UnofficialUpdateUrl);
+#if SMAPI_1_x
+ bool hasOfficialUrl = mod.Compatibility.UpdateUrls.Length > 0;
+ bool hasUnofficialUrl = mod.Compatibility.UpdateUrls.Length > 1;
string reasonPhrase = compatibility.ReasonPhrase ?? "it's not compatible with the latest version of the game or SMAPI";
- string error = $"{reasonPhrase}. Please check for a version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion} here:";
+ string error = $"{reasonPhrase}. Please check for a version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion.ToString()} here:";
if (hasOfficialUrl)
- error += !hasUnofficialUrl ? $" {compatibility.UpdateUrl}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrl}";
+ error += !hasUnofficialUrl ? $" {compatibility.UpdateUrls[0]}" : $"{Environment.NewLine}- official mod: {compatibility.UpdateUrls[0]}";
if (hasUnofficialUrl)
- error += $"{Environment.NewLine}- unofficial update: {compatibility.UnofficialUpdateUrl}";
+ error += $"{Environment.NewLine}- unofficial update: {compatibility.UpdateUrls[1]}";
+#else
+ string reasonPhrase = compatibility.ReasonPhrase ?? "it's no longer compatible";
+ string error = $"{reasonPhrase}. Please check for a ";
+ if (mod.Manifest.Version.Equals(compatibility.UpperVersion) && compatibility.UpperVersionLabel == null)
+ error += "newer version";
+ else
+ error += $"version newer than {compatibility.UpperVersionLabel ?? compatibility.UpperVersion.ToString()}";
+ error += " at " + string.Join(" or ", compatibility.UpdateUrls);
+#endif
mod.SetStatus(ModMetadataStatus.Failed, error);
continue;
@@ -161,7 +171,7 @@ namespace StardewModdingAPI.Framework.ModLoading
#if !SMAPI_1_x
{
var duplicatesByID = mods
- .GroupBy(mod => mod.Manifest.UniqueID?.Trim(), mod => mod, StringComparer.InvariantCultureIgnoreCase)
+ .GroupBy(mod => mod.Manifest?.UniqueID?.Trim(), mod => mod, StringComparer.InvariantCultureIgnoreCase)
.Where(p => p.Count() > 1);
foreach (var group in duplicatesByID)
{