Merge branch 'master' into rpc

This commit is contained in:
Anon 2022-10-24 20:45:40 +00:00 committed by GitHub
commit 477da50fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1230 additions and 116 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

@ -26,7 +26,7 @@ namespace MinecraftClient
/// <param name="run">Set to false to compile and cache the script without launching it</param>
/// <exception cref="CSharpException">Thrown if an error occured</exception>
/// <returns>Result of the execution, returned by the script</returns>
public static object? Run(ChatBot apiHandler, string[] lines, string[] args, Dictionary<string, object>? localVars, bool run = true)
public static object? Run(ChatBot apiHandler, string[] lines, string[] args, Dictionary<string, object>? localVars, bool run = true, string scriptName = "Unknown Script")
{
//Script compatibility check for handling future versions differently
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
@ -102,13 +102,24 @@ 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)
throw new CSharpException(CSErrorType.LoadError,
new InvalidOperationException(result.Failures[0].GetMessage()));
if (result.Failures != null) {
ConsoleIO.WriteLogLine("[Script] Compilation failed with error(s):");
foreach (var failure in result.Failures) {
ConsoleIO.WriteLogLine($"[Script] Error in {scriptName}, line:col{failure.Location.GetMappedLineSpan()}: [{failure.Id}] {failure.GetMessage()}");
}
throw new CSharpException(CSErrorType.InvalidScript, new InvalidProgramException("Compilation failed due to error."));
}
ConsoleIO.WriteLogLine("[Script] Compilation done with no errors.");
//Retrieve compiled assembly
assembly = result.Assembly;
@ -385,7 +396,7 @@ namespace MinecraftClient
{
throw new CSharpException(CSErrorType.FileReadError, e);
}
return CSharpRunner.Run(this, lines, args, localVars);
return CSharpRunner.Run(this, lines, args, localVars, scriptName: script);
}
}
}

View file

@ -835,10 +835,11 @@ namespace MinecraftClient
/// <param name="text">Log text to write</param>
protected void LogToConsole(object? text)
{
string botName = Translations.GetOrNull("botname." + GetType().Name) ?? GetType().Name;
if (_handler == null || master == null)
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", GetType().Name, text));
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text));
else
Handler.Log.Info(String.Format("[{0}] {1}", GetType().Name, text));
Handler.Log.Info(String.Format("[{0}] {1}", botName, text));
string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile);
if (!String.IsNullOrEmpty(logfile))
@ -913,7 +914,10 @@ namespace MinecraftClient
protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0)
{
if (Settings.Config.Logging.DebugMessages)
ConsoleIO.WriteLogLine(Translations.Get("chatbot.reconnect", GetType().Name));
{
string botName = Translations.GetOrNull("botname." + GetType().Name) ?? GetType().Name;
ConsoleIO.WriteLogLine(Translations.Get("chatbot.reconnect", botName));
}
McClient.ReconnectionAttemptsLeft = ExtraAttempts;
Program.Restart(delaySeconds);
}
@ -1129,14 +1133,15 @@ namespace MinecraftClient
/// </summary>
protected static string GetTimestamp()
{
DateTime time = DateTime.Now;
return String.Format("{0}-{1}-{2} {3}:{4}:{5}",
time.Year.ToString("0000"),
time.Month.ToString("00"),
time.Day.ToString("00"),
time.Hour.ToString("00"),
time.Minute.ToString("00"),
time.Second.ToString("00"));
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
/// <summary>
/// Get a h:m:s timestamp representing the current system time
/// </summary>
protected static string GetShortTimestamp()
{
return DateTime.Now.ToString("HH:mm:ss");
}
/// <summary>

View file

@ -24,7 +24,7 @@ namespace DynamicRun.Builder
GC.WaitForPendingFinalizers();
}
ConsoleIO.WriteLogLine(assemblyLoadContextWeakRef.Item1.IsAlive ? "Script continues to run." : "Script finished!");
ConsoleIO.WriteLogLine(assemblyLoadContextWeakRef.Item1.IsAlive ? "[Script] Script continues to run." : "[Script] Script finished!");
return assemblyLoadContextWeakRef.Item2;
}

View file

@ -15,23 +15,20 @@ 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<string> additionalAssemblies)
{
ConsoleIO.WriteLogLine($"Starting compilation...");
using var peStream = new MemoryStream();
var result = GenerateCode(filepath, fileName).Emit(peStream);
var result = GenerateCode(filepath, fileName, additionalAssemblies).Emit(peStream);
if (!result.Success)
{
ConsoleIO.WriteLogLine("Compilation done with error.");
var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
return new CompileResult()
@ -41,9 +38,7 @@ namespace DynamicRun.Builder
Failures = failures.ToList()
};
}
ConsoleIO.WriteLogLine("Compilation done without any error.");
peStream.Seek(0, SeekOrigin.Begin);
return new CompileResult()
@ -54,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 options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9);
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.
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<MetadataReference>();
// We're on a self-contained binary, so we need to extract the executable to get the assemblies.
if (string.IsNullOrEmpty(MinecraftClientDll))
{