add logic to detect incompatible mod instructions & reject mod load (#247)
This commit is contained in:
parent
b0fab4a076
commit
6d2d90b768
|
@ -135,6 +135,14 @@ namespace StardewModdingAPI
|
|||
return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences, targetAssemblies);
|
||||
}
|
||||
|
||||
/// <summary>Get finders which match incompatible CIL instructions in mod assemblies.</summary>
|
||||
internal static IEnumerable<IInstructionFinder> GetIncompatibilityFinders()
|
||||
{
|
||||
return new IInstructionFinder[]
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Get rewriters which fix incompatible CIL instructions in mod assemblies.</summary>
|
||||
internal static IEnumerable<IInstructionRewriter> GetRewriters()
|
||||
{
|
||||
|
|
|
@ -55,6 +55,7 @@ namespace StardewModdingAPI.Framework
|
|||
/// <summary>Preprocess and load an assembly.</summary>
|
||||
/// <param name="assemblyPath">The assembly file path.</param>
|
||||
/// <returns>Returns the rewrite metadata for the preprocessed assembly.</returns>
|
||||
/// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
|
||||
public Assembly Load(string assemblyPath)
|
||||
{
|
||||
// get referenced local assemblies
|
||||
|
@ -159,6 +160,7 @@ namespace StardewModdingAPI.Framework
|
|||
/// <summary>Rewrite the types referenced by an assembly.</summary>
|
||||
/// <param name="assembly">The assembly to rewrite.</param>
|
||||
/// <returns>Returns whether the assembly was modified.</returns>
|
||||
/// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
|
||||
private bool RewriteAssembly(AssemblyDefinition assembly)
|
||||
{
|
||||
ModuleDefinition module = assembly.MainModule;
|
||||
|
@ -189,6 +191,22 @@ namespace StardewModdingAPI.Framework
|
|||
this.ChangeTypeScope(type);
|
||||
}
|
||||
|
||||
// throw exception if assembly contains incompatible instructions can't be rewritten
|
||||
{
|
||||
IInstructionFinder[] finders = Constants.GetIncompatibilityFinders().ToArray();
|
||||
foreach (MethodDefinition method in this.GetMethods(module))
|
||||
{
|
||||
foreach (Instruction instruction in method.Body.Instructions)
|
||||
{
|
||||
foreach (IInstructionFinder finder in finders)
|
||||
{
|
||||
if (finder.IsMatch(instruction, platformChanged))
|
||||
throw new IncompatibleInstructionException(finder.NounPhrase, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rewrite incompatible instructions
|
||||
bool anyRewritten = false;
|
||||
IInstructionRewriter[] rewriters = Constants.GetRewriters().ToArray();
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace StardewModdingAPI.Framework
|
||||
{
|
||||
/// <summary>An exception raised when an incompatible instruction is found while loading a mod assembly.</summary>
|
||||
internal class IncompatibleInstructionException : Exception
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>A brief noun phrase which describes the incompatible instruction that was found.</summary>
|
||||
public string NounPhrase { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="nounPhrase">A brief noun phrase which describes the incompatible instruction that was found.</param>
|
||||
/// <param name="message">A message which describes the error.</param>
|
||||
public IncompatibleInstructionException(string nounPhrase, string message)
|
||||
: base(message)
|
||||
{
|
||||
this.NounPhrase = nounPhrase;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -453,6 +453,11 @@ namespace StardewModdingAPI
|
|||
{
|
||||
modAssembly = modAssemblyLoader.Load(assemblyPath);
|
||||
}
|
||||
catch (IncompatibleInstructionException ex)
|
||||
{
|
||||
this.Monitor.Log($"{skippedPrefix} because it's not compatible with the latest version of the game (detected {ex.NounPhrase}). Please check for a newer version of the mod (you have v{manifest.Version}).", LogLevel.Error);
|
||||
continue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Monitor.Log($"{skippedPrefix} because its DLL '{manifest.EntryDll}' couldn't be loaded.\n{ex.GetLogSummary()}", LogLevel.Error);
|
||||
|
|
|
@ -154,6 +154,7 @@
|
|||
<Compile Include="Framework\Content\ContentEventHelper.cs" />
|
||||
<Compile Include="Framework\Content\ContentEventHelperForDictionary.cs" />
|
||||
<Compile Include="Framework\Content\ContentEventHelperForImage.cs" />
|
||||
<Compile Include="Framework\IncompatibleInstructionException.cs" />
|
||||
<Compile Include="Framework\Logging\ConsoleInterceptionManager.cs" />
|
||||
<Compile Include="Framework\Logging\InterceptingTextWriter.cs" />
|
||||
<Compile Include="Framework\CommandHelper.cs" />
|
||||
|
|
Loading…
Reference in New Issue