Fix externally referenced DLLs in scripts

External DLL references weren't being used, this commit fixes them so they are used.
This commit is contained in:
breadbyte 2022-10-21 00:29:46 +08:00
parent 78f9c35800
commit e006943535
No known key found for this signature in database
GPG key ID: 81897669486FFAFC
3 changed files with 60 additions and 9 deletions

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace MinecraftClient.Scripting;
public static class AssemblyResolver {
private static Dictionary<string, string> ScriptAssemblies = new();
static AssemblyResolver() {
// Manually resolve assemblies that .NET can't resolve automatically.
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var asmReqName = new AssemblyName(args.Name);
// Check the script-referenced assemblies if we have the DLL that is required.
foreach (var dll in ScriptAssemblies)
{
// If we have the assembly, load it.
if (asmReqName.FullName == dll.Key)
{
return Assembly.LoadFile(dll.Value);
}
}
ConsoleIO.WriteLogLine($"[Script Error] Failed to resolve assembly {args.Name} (are you missing a DLL file?)");
return null;
};
}
internal static void AddAssembly(string AssemblyFullName, string AssemblyPath)
{
if (ScriptAssemblies.ContainsKey(AssemblyFullName))
return;
ScriptAssemblies.Add(AssemblyFullName, AssemblyPath);
}
}

View file

@ -105,7 +105,7 @@ namespace MinecraftClient
ConsoleIO.WriteLogLine($"[Script] Starting compilation for {scriptName}..."); ConsoleIO.WriteLogLine($"[Script] Starting compilation for {scriptName}...");
//Compile the C# class in memory using all the currently loaded assemblies //Compile the C# class in memory using all the currently loaded assemblies
var result = compiler.Compile(code, Guid.NewGuid().ToString()); var result = compiler.Compile(code, Guid.NewGuid().ToString(), dlls);
//Process compile warnings and errors //Process compile warnings and errors
if (result.Failures != null) { if (result.Failures != null) {

View file

@ -15,16 +15,17 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using MinecraftClient; using MinecraftClient;
using MinecraftClient.Scripting;
using SingleFileExtractor.Core; using SingleFileExtractor.Core;
namespace DynamicRun.Builder namespace DynamicRun.Builder
{ {
internal class Compiler internal class Compiler
{ {
public CompileResult Compile(string filepath, string fileName) public CompileResult Compile(string filepath, string fileName, List<string> additionalAssemblies)
{ {
using var peStream = new MemoryStream(); using var peStream = new MemoryStream();
var result = GenerateCode(filepath, fileName).Emit(peStream); var result = GenerateCode(filepath, fileName, additionalAssemblies).Emit(peStream);
if (!result.Success) if (!result.Success)
{ {
@ -48,24 +49,37 @@ namespace DynamicRun.Builder
}; };
} }
private static CSharpCompilation GenerateCode(string sourceCode, string fileName) private static CSharpCompilation GenerateCode(string sourceCode, string fileName, List<string> additionalAssemblies)
{ {
var codeString = SourceText.From(sourceCode); var codeString = SourceText.From(sourceCode);
var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9);
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options); var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
var references = new List<MetadataReference>();
var mods = Assembly.GetEntryAssembly()!.GetModules(); // Find if any additional assembly DLL exists in the base directory where the .exe exists.
foreach (var assembly in additionalAssemblies)
{
var dllPath = Path.Combine(AppContext.BaseDirectory, assembly);
if (File.Exists(dllPath))
{
references.Add(MetadataReference.CreateFromFile(dllPath));
// Store the reference in our Assembly Resolver for future reference.
AssemblyResolver.AddAssembly(Assembly.LoadFile(dllPath).FullName!, dllPath);
}
else
{
ConsoleIO.WriteLogLine($"[Script Error] {assembly} is defined in script, but cannot find DLL! Script may not run.");
}
}
#pragma warning disable IL3000 // We determine if we are in a self-contained binary by checking specifically if the Assembly file path is null. #pragma warning disable IL3000 // We determine if we are in a self-contained binary by checking specifically if the Assembly file path is null.
var SystemPrivateCoreLib = typeof(object).Assembly.Location; // System.Private.CoreLib var SystemPrivateCoreLib = typeof(object).Assembly.Location; // System.Private.CoreLib
var SystemConsole = typeof(Console).Assembly.Location; // System.Console var SystemConsole = typeof(Console).Assembly.Location; // System.Console
var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll
var references = new List<MetadataReference>();
// We're on a self-contained binary, so we need to extract the executable to get the assemblies. // We're on a self-contained binary, so we need to extract the executable to get the assemblies.
if (string.IsNullOrEmpty(MinecraftClientDll)) if (string.IsNullOrEmpty(MinecraftClientDll))
{ {