show friendlier error when players have two copies of a mod
This commit is contained in:
parent
723ddc255e
commit
80fe706f19
|
@ -6,6 +6,7 @@ For players:
|
|||
* The SMAPI console is now much simpler and easier to read.
|
||||
* The SMAPI console now adjusts its colors when you have a light terminal background.
|
||||
* Updated compatibility list.
|
||||
* Improved errors when a mod DLL can't be loaded.
|
||||
|
||||
For mod developers:
|
||||
* Added new APIs to edit, inject, and reload XNB assets loaded by the game at any time.
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace StardewModdingAPI.Framework.Exceptions
|
||||
{
|
||||
/// <summary>An exception thrown when an assembly can't be loaded by SMAPI, with all the relevant details in the message.</summary>
|
||||
internal class SAssemblyLoadFailedException : Exception
|
||||
{
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="message">The error message.</param>
|
||||
public SAssemblyLoadFailedException(string message)
|
||||
: base(message) { }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/****
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ using Newtonsoft.Json;
|
|||
using StardewModdingAPI.AssemblyRewriters;
|
||||
using StardewModdingAPI.Events;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Framework.Exceptions;
|
||||
using StardewModdingAPI.Framework.Logging;
|
||||
using StardewModdingAPI.Framework.Models;
|
||||
using StardewModdingAPI.Framework.ModHelpers;
|
||||
|
@ -655,6 +656,11 @@ namespace StardewModdingAPI
|
|||
#endif
|
||||
continue;
|
||||
}
|
||||
catch (SAssemblyLoadFailedException ex)
|
||||
{
|
||||
TrackSkip(metadata, $"its DLL '{manifest.EntryDll}' couldn't be loaded: {ex.Message}");
|
||||
continue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TrackSkip(metadata, $"its DLL '{manifest.EntryDll}' couldn't be loaded:\n{ex.GetLogSummary()}");
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
<Link>Properties\GlobalAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Command.cs" />
|
||||
<Compile Include="Framework\Exceptions\SAssemblyLoadFailedException.cs" />
|
||||
<Compile Include="Framework\ModLoading\AssemblyLoadStatus.cs" />
|
||||
<Compile Include="Framework\Utilities\ContextHash.cs" />
|
||||
<Compile Include="Metadata\CoreAssets.cs" />
|
||||
<Compile Include="ContentSource.cs" />
|
||||
|
|
Loading…
Reference in New Issue