mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Scripting Hotfix for .NET (#2061)
* fix scripting being broken scripting got broken due to being compiled as a single file application. * move invoke to a helper function also move MinecraftClient assembly fetch into the main assembly fetch loop * Downgrade version of SingleFileExtractor 1.1.0 changed their internal API, so we use 1.0.1 to reduce the amount of reflection we need to do * add exception messages
This commit is contained in:
parent
aa1f54d0d8
commit
fd7f79402f
2 changed files with 89 additions and 8 deletions
|
|
@ -35,6 +35,7 @@
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="5.0.2" />
|
<PackageReference Include="Microsoft.Windows.Compatibility" Version="5.0.2" />
|
||||||
|
<PackageReference Include="SingleFileExtractor.Core" Version="1.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="config\ChatBots\AutoLook.cs" />
|
<Compile Remove="config\ChatBots\AutoLook.cs" />
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,14 @@ https://github.com/laurentkempe/DynamicRun/blob/master/LICENSE
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.MemoryMappedFiles;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
using MinecraftClient;
|
using MinecraftClient;
|
||||||
|
using SingleFileExtractor.Core;
|
||||||
|
|
||||||
namespace DynamicRun.Builder
|
namespace DynamicRun.Builder
|
||||||
{
|
{
|
||||||
|
|
@ -58,16 +60,82 @@ namespace DynamicRun.Builder
|
||||||
|
|
||||||
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
|
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>
|
var references = new List<MetadataReference>
|
||||||
{
|
{
|
||||||
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
|
MetadataReference.CreateFromFile(A),
|
||||||
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
|
MetadataReference.CreateFromFile(B)
|
||||||
MetadataReference.CreateFromFile(typeof(ChatBot).Assembly.Location)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Assembly.GetEntryAssembly()?.GetReferencedAssemblies().ToList()
|
// We're on a Single File Application, so we need to extract the executable to get the assembly.
|
||||||
.ForEach(a => references.Add(MetadataReference.CreateFromFile(Assembly.Load(a).Location)));
|
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",
|
return CSharpCompilation.Create($"{fileName}.dll",
|
||||||
new[] { parsedSyntaxTree },
|
new[] { parsedSyntaxTree },
|
||||||
references: references,
|
references: references,
|
||||||
|
|
@ -76,6 +144,18 @@ namespace DynamicRun.Builder
|
||||||
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));
|
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 struct CompileResult {
|
||||||
internal byte[]? Assembly;
|
internal byte[]? Assembly;
|
||||||
internal bool HasCompiledSucecssfully;
|
internal bool HasCompiledSucecssfully;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue