diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj
index 46d57543..e7947ff9 100644
--- a/MinecraftClient/MinecraftClient.csproj
+++ b/MinecraftClient/MinecraftClient.csproj
@@ -35,6 +35,7 @@
all
+
diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs
index dc83df0a..3bc7c2ae 100644
--- a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs
+++ b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs
@@ -7,16 +7,18 @@ https://github.com/laurentkempe/DynamicRun/blob/master/LICENSE
using System;
using System.Collections.Generic;
using System.IO;
+using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using MinecraftClient;
+using SingleFileExtractor.Core;
namespace DynamicRun.Builder
{
- internal class Compiler
+ internal class Compiler
{
public CompileResult Compile(string filepath, string fileName)
{
@@ -58,23 +60,101 @@ namespace DynamicRun.Builder
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
+ var mods = Assembly.GetEntryAssembly().GetModules();
+
+#pragma warning disable IL3000
+ // System.Private.CoreLib
+ var A = typeof(object).Assembly.Location;
+ // System.Console
+ var B = typeof(Console).Assembly.Location;
+ // The path to MinecraftClient.dll
+ var C = typeof(Program).Assembly.Location;
+
var references = new List
{
- MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
- MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
- MetadataReference.CreateFromFile(typeof(ChatBot).Assembly.Location)
+ MetadataReference.CreateFromFile(A),
+ MetadataReference.CreateFromFile(B)
};
-
- Assembly.GetEntryAssembly()?.GetReferencedAssemblies().ToList()
- .ForEach(a => references.Add(MetadataReference.CreateFromFile(Assembly.Load(a).Location)));
+ // We're on a Single File Application, so we need to extract the executable to get the assembly.
+ if (string.IsNullOrEmpty(C))
+ {
+ // Create a temporary file to copy the executable to.
+ var executableDir = System.AppContext.BaseDirectory;
+ var executablePath = Path.Combine(executableDir, "MinecraftClient.exe");
+ var tempFileName = Path.GetTempFileName();
+ if (File.Exists(executablePath))
+ {
+ // Copy the executable to a temporary path.
+ ExecutableReader e = new();
+ File.Delete(tempFileName);
+ File.Copy(executablePath, tempFileName);
+
+ // Access the contents of the executable.
+ var viewAccessor = MemoryMappedFile.CreateFromFile(tempFileName, FileMode.Open).CreateViewAccessor();
+ var manifest = e.ReadManifest(viewAccessor);
+ var files = manifest.Files;
+
+ Stream? assemblyStream;
+
+ var assemblyrefs = Assembly.GetEntryAssembly()?.GetReferencedAssemblies().ToList();
+ assemblyrefs.Add(new ("MinecraftClient"));
+
+ foreach (var refs in assemblyrefs) {
+ var loadedAssembly = Assembly.Load(refs);
+ if (string.IsNullOrEmpty(loadedAssembly.Location))
+ {
+ // Check if we can access the file from the executable.
+ var reference = files.FirstOrDefault(x => x.RelativePath.Remove(x.RelativePath.Length - 4) == refs.Name);
+ var refCount = files.Count(x => x.RelativePath.Remove(x.RelativePath.Length - 4) == refs.Name);
+ if (refCount > 1)
+ {
+ // Safety net for the case where the assembly is referenced multiple times.
+ // Should not happen normally, but we can make exceptions when it does happen.
+ throw new InvalidOperationException("Too many references to the same assembly. Assembly name: " + refs.Name);
+ }
+ if (reference == null)
+ {
+ throw new InvalidOperationException("The executable does not contain a referenced assembly. Assembly name: " + refs.Name);
+ }
+
+ assemblyStream = GetStreamForFileEntry(viewAccessor, reference);
+ references.Add(MetadataReference.CreateFromStream(assemblyStream));
+ continue;
+ }
+ references.Add(MetadataReference.CreateFromFile(loadedAssembly.Location));
+ }
+
+ // Cleanup.
+ viewAccessor.Flush();
+ viewAccessor.Dispose();
+ }
+ }
+ else
+ {
+ references.Add(MetadataReference.CreateFromFile(C));
+ Assembly.GetEntryAssembly()?.GetReferencedAssemblies().ToList().ForEach(a => references.Add(MetadataReference.CreateFromFile(Assembly.Load(a).Location)));
+ }
+#pragma warning restore IL3000
return CSharpCompilation.Create($"{fileName}.dll",
- new[] { parsedSyntaxTree },
+ new[] { parsedSyntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));
}
+
+ private static Stream? GetStreamForFileEntry(MemoryMappedViewAccessor viewAccessor, FileEntry file)
+ {
+ var stream = typeof(BundleExtractor).GetMethod("GetStreamForFileEntry", BindingFlags.NonPublic | BindingFlags.Static)!.Invoke(null, new object[] { viewAccessor, file }) as Stream;
+
+ if (stream == null)
+ {
+ throw new InvalidOperationException("The executable does not contain the assembly. Assembly name: " + file.RelativePath);
+ }
+
+ return stream;
+ }
internal struct CompileResult {
internal byte[]? Assembly;