From 74d29321b4155736e971783263205d0eb8a4e3cc Mon Sep 17 00:00:00 2001 From: breadbyte Date: Tue, 28 Mar 2023 21:06:56 +0800 Subject: [PATCH 1/2] Scripting Hotfix for #2458 (#2460) * Scripting hotfix - Fixes #2458 - Change error wording around scripting - Prevent duplicating the binary for scripting * Create temporary folder if it doesn't exist --- .../Scripting/DynamicRun/Builder/Compiler.cs | 109 ++++++++++-------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs index 63e312391b..9bd865c2e2 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/Compiler.cs @@ -79,60 +79,77 @@ private static CSharpCompilation GenerateCode(string sourceCode, string fileName var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll // 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)) { // Create a temporary file to copy the executable to. - var executableDir = AppContext.BaseDirectory; - var executablePath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Path.Combine(executableDir, "MinecraftClient.exe") : Path.Combine(executableDir, "MinecraftClient"); - var tempFileName = Path.GetTempFileName(); - if (File.Exists(executablePath)) + var executablePath = Environment.ProcessPath; + var tempPath = Path.Combine(Path.GetTempPath(), "mcc-scripting"); + Directory.CreateDirectory(tempPath); + + var tempFile = Path.Combine(tempPath, "mcc-executable"); + var useExisting = false; + + // Check if we already have the executable in the temporary path. + foreach (var file in Directory.EnumerateFiles(tempPath)) { - // 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; + if (file.EndsWith("mcc-executable")) + { + useExisting = true; + break; + } + } + + if (!File.Exists(executablePath)) + { + throw new FileNotFoundException("[Script Error] Could not locate the current folder of MCC for scripting."); + } - var assemblyrefs = Assembly.GetEntryAssembly()?.GetReferencedAssemblies().ToList()!; - assemblyrefs.Add(new("MinecraftClient")); - assemblyrefs.Add(new("System.Private.CoreLib")); + // Copy the executable to a temporary path. + if (!useExisting) + File.Copy(executablePath, tempFile); + + // Access the contents of the executable. + ExecutableReader e = new(); + var viewAccessor = MemoryMappedFile.CreateFromFile(tempFile, FileMode.Open).CreateViewAccessor(); + var manifest = e.ReadManifest(viewAccessor); + var files = manifest.Files; + + Stream? assemblyStream; + + var assemblyrefs = Assembly.GetEntryAssembly()?.GetReferencedAssemblies().ToList()!; + assemblyrefs.Add(new("MinecraftClient")); + assemblyrefs.Add(new("System.Private.CoreLib")); + + 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( + "[Script Error] Too many references to the same assembly. Assembly name: " + refs.Name); + } - 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; + if (reference == null) { + throw new InvalidOperationException( + "[Script Error] The executable does not contain a referenced assembly. Assembly name: " + refs.Name); } - references.Add(MetadataReference.CreateFromFile(loadedAssembly.Location)); + + assemblyStream = GetStreamForFileEntry(viewAccessor, reference); + references.Add(MetadataReference.CreateFromStream(assemblyStream!)); + continue; } - // Cleanup. - viewAccessor.Flush(); - viewAccessor.Dispose(); + references.Add(MetadataReference.CreateFromFile(loadedAssembly.Location)); } + + // Cleanup. + viewAccessor.Flush(); + viewAccessor.Dispose(); } else { @@ -154,7 +171,7 @@ private static CSharpCompilation GenerateCode(string sourceCode, string fileName private static Stream? GetStreamForFileEntry(MemoryMappedViewAccessor viewAccessor, FileEntry file) { if (typeof(BundleExtractor).GetMethod("GetStreamForFileEntry", BindingFlags.NonPublic | BindingFlags.Static)!.Invoke(null, new object[] { viewAccessor, file }) is not Stream stream) - throw new InvalidOperationException("The executable does not contain the assembly. Assembly name: " + file.RelativePath); + throw new InvalidOperationException("[Script Error] The executable does not contain the assembly. Assembly name: " + file.RelativePath); return stream; } From cd39c1ec12d729c96f2d64b001ae0e42096c1720 Mon Sep 17 00:00:00 2001 From: breadbyte Date: Wed, 29 Mar 2023 20:15:22 +0800 Subject: [PATCH 2/2] Make UpgradeHelper compliant with .NET RID catalog Fetching updates return an error because the UpgradeHelper returns an invalid RID, and we use the RID to create automated builds. This updates the UpgradeHelper to be compliant with the catalog. --- MinecraftClient/UpgradeHelper.cs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/MinecraftClient/UpgradeHelper.cs b/MinecraftClient/UpgradeHelper.cs index 3843824b66..8c53da1692 100644 --- a/MinecraftClient/UpgradeHelper.cs +++ b/MinecraftClient/UpgradeHelper.cs @@ -241,27 +241,27 @@ private static string GetOSIdentifier() { string OSPlatformName; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - OSPlatformName = "Windows"; + OSPlatformName = "win"; else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - OSPlatformName = "Linux"; + OSPlatformName = "linux"; else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - OSPlatformName = "OSX"; + OSPlatformName = "osx"; else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - OSPlatformName = "FreeBSD"; + OSPlatformName = "freebsd"; else return string.Empty; string architecture = RuntimeInformation.ProcessArchitecture switch { - Architecture.X86 => "X86", - Architecture.X64 => "X64", - Architecture.Arm => "Arm32", - Architecture.Arm64 => "Arm64", - Architecture.Wasm => "Wasm", - Architecture.S390x => "S390x", - Architecture.LoongArch64 => "LoongArch64", - Architecture.Armv6 => "Armv6", - Architecture.Ppc64le => "Ppc64le", + Architecture.X86 => "x86", + Architecture.X64 => "x64", + Architecture.Arm => "arm32", + Architecture.Arm64 => "arm64", + Architecture.Wasm => "wasm", + Architecture.S390x => "s390x", + Architecture.LoongArch64 => "loongarch64", + Architecture.Armv6 => "armv6", + Architecture.Ppc64le => "ppc64le", _ => RuntimeInformation.ProcessArchitecture.ToString(), };