diff --git a/MinecraftClient/Scripting/AssemblyResolver.cs b/MinecraftClient/Scripting/AssemblyResolver.cs new file mode 100644 index 00000000..3ecdd02c --- /dev/null +++ b/MinecraftClient/Scripting/AssemblyResolver.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace MinecraftClient.Scripting; + +public static class AssemblyResolver { + private static Dictionary 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); + } +} \ No newline at end of file diff --git a/MinecraftClient/Scripting/CSharpRunner.cs b/MinecraftClient/Scripting/CSharpRunner.cs index 48f52a45..87857b5f 100644 --- a/MinecraftClient/Scripting/CSharpRunner.cs +++ b/MinecraftClient/Scripting/CSharpRunner.cs @@ -105,7 +105,7 @@ namespace MinecraftClient ConsoleIO.WriteLogLine($"[Script] Starting compilation for {scriptName}..."); //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 if (result.Failures != null) { diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs index 5da4f15d..4bb02b07 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs @@ -15,16 +15,17 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using MinecraftClient; +using MinecraftClient.Scripting; using SingleFileExtractor.Core; namespace DynamicRun.Builder { internal class Compiler { - public CompileResult Compile(string filepath, string fileName) + public CompileResult Compile(string filepath, string fileName, List additionalAssemblies) { using var peStream = new MemoryStream(); - var result = GenerateCode(filepath, fileName).Emit(peStream); + var result = GenerateCode(filepath, fileName, additionalAssemblies).Emit(peStream); 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 additionalAssemblies) { var codeString = SourceText.From(sourceCode); var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options); + + var references = new List(); - 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. var SystemPrivateCoreLib = typeof(object).Assembly.Location; // System.Private.CoreLib var SystemConsole = typeof(Console).Assembly.Location; // System.Console var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll - - var references = new List(); - + // We're on a self-contained binary, so we need to extract the executable to get the assemblies. if (string.IsNullOrEmpty(MinecraftClientDll)) {