Worked on ModScriptCompilerPatch
I can load into a test world successfully, and the mods seemingly compile fine. Scripts compile when loading into the world, but no longer compile when the world is loaded (not sure why). Pressing the CheckCode button never finishes either, and it seems to get stuck at the end of CompileAsync For some reason, mod assemblies don't unload after reloading the world, even when disabling other plugins to test.
This commit is contained in:
@@ -32,6 +32,7 @@ public static class ModScriptCompilerPatch
|
||||
{
|
||||
private static readonly ILogger Log = LogManager.GetCurrentClassLogger();
|
||||
private static AssemblyLoadContext _modContext = new(null, true);
|
||||
private static readonly HashSet<string> LoadedModAssemblyNames = new();
|
||||
|
||||
private static readonly ConditionalWeakTable<MyProgrammableBlock, AssemblyLoadContext> LoadContexts = new();
|
||||
|
||||
@@ -63,12 +64,17 @@ public static class ModScriptCompilerPatch
|
||||
EmitDiagnostics = AccessTools.MethodDelegate<Func<CompilationWithAnalyzers, EmitResult, List<Message>, bool, Task<bool>>>(
|
||||
AccessTools.Method(typeof(MyScriptCompiler), "EmitDiagnostics"), MyScriptCompiler.Static);
|
||||
|
||||
MakeAssemblyName =
|
||||
AccessTools.MethodDelegate<Func<string, string>>(AccessTools.Method(typeof(MyScriptCompiler),
|
||||
"MakeAssemblyName"));
|
||||
|
||||
CreateInstanceMethod = AccessTools.Method(typeof(MyProgrammableBlock), "CreateInstance");
|
||||
SetDetailedInfoMethod = AccessTools.Method(typeof(MyProgrammableBlock), "SetDetailedInfo");
|
||||
}
|
||||
|
||||
private static void OnUnloaded()
|
||||
{
|
||||
LoadedModAssemblyNames.Clear();
|
||||
if (!_modContext.Assemblies.Any())
|
||||
return;
|
||||
|
||||
@@ -100,18 +106,28 @@ public static class ModScriptCompilerPatch
|
||||
MyScreenManager.AddScreen(progress);
|
||||
|
||||
if (__instance.Description.Text.Length > 0)
|
||||
CompileAsync(__instance, ___m_compilerErrors, __instance.Description.Text.ToString(), progress);
|
||||
CompileAsync(__instance, ___m_compilerErrors, __instance.Description.Text.ToString(), progress).Wait();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MyScriptCompiler), nameof(MyScriptCompiler.Compile))]
|
||||
[HarmonyPrefix]
|
||||
private static bool Prefix(ref Task<Assembly?> __result, MyApiTarget target, string assemblyName, IEnumerable<Script> scripts,
|
||||
List<Message> messages, string friendlyName, bool enableDebugInformation = false)
|
||||
{
|
||||
__result = CompileAsync(_modContext, target, assemblyName, scripts, messages, friendlyName,
|
||||
enableDebugInformation);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async void CompileAsync(MyGuiScreenEditor editor, List<string> errors, string program, MyGuiScreenProgress progress)
|
||||
private static async Task CompileAsync(MyGuiScreenEditor editor, List<string> errors, string program, MyGuiScreenProgress progress)
|
||||
{
|
||||
var context = new AssemblyLoadContext(null, true);
|
||||
var messages = new List<Message>();
|
||||
var script = MyVRage.Platform.Scripting.GetIngameScript(program, "Program", nameof(MyGridProgram));
|
||||
var assembly = await CompileAsync(context, MyApiTarget.Ingame, "check", new[] { script }, messages,
|
||||
"PB Code Editor");
|
||||
await CompileAsync(context, MyApiTarget.Ingame, "check", new[] { script }, messages,
|
||||
"PB Code Editor");
|
||||
|
||||
errors.AddRange(messages.OrderBy(b => b.IsError ? 0 : 1).Select(b => b.Text));
|
||||
context.Unload();
|
||||
@@ -191,6 +207,7 @@ public static class ModScriptCompilerPatch
|
||||
bool enableDebugInformation = false)
|
||||
{
|
||||
friendlyName ??= "<No Name>";
|
||||
var assemblyFileName = MakeAssemblyName(assemblyName);
|
||||
Func<CSharpCompilation, SyntaxTree, SyntaxTree>? syntaxTreeInjector;
|
||||
DiagnosticAnalyzer? whitelistAnalyzer;
|
||||
switch (target)
|
||||
@@ -204,6 +221,13 @@ public static class ModScriptCompilerPatch
|
||||
var modId = MyModWatchdog.AllocateModId(friendlyName);
|
||||
whitelistAnalyzer = ModWhitelistAnalyzer;
|
||||
syntaxTreeInjector = (c, st) => InjectMod(c, st, modId);
|
||||
|
||||
//skip if name exists already
|
||||
if (!LoadedModAssemblyNames.Add(assemblyFileName))
|
||||
{
|
||||
Console.WriteLine($"{assemblyFileName} is already loaded, skipping");
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MyApiTarget.Ingame:
|
||||
@@ -213,11 +237,10 @@ public static class ModScriptCompilerPatch
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(target), target, "Invalid compilation target");
|
||||
}
|
||||
|
||||
var compilation = CreateCompilation(assemblyName, scripts);
|
||||
var compilation = CreateCompilation(assemblyFileName, scripts);
|
||||
var compilationWithoutInjection = compilation;
|
||||
var injectionFailed = false;
|
||||
|
||||
|
||||
if (syntaxTreeInjector != null)
|
||||
{
|
||||
SyntaxTree[]? newSyntaxTrees = null;
|
||||
@@ -233,7 +256,7 @@ public static class ModScriptCompilerPatch
|
||||
var compilation1 = compilation;
|
||||
newSyntaxTrees = await Task
|
||||
.WhenAll(syntaxTrees.Select(
|
||||
x => Task.Run(() => syntaxTreeInjector(compilation1, x))));
|
||||
x => Task.Run(() => syntaxTreeInjector(compilation1, x)))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -269,19 +292,21 @@ public static class ModScriptCompilerPatch
|
||||
}
|
||||
else
|
||||
{
|
||||
success = await EmitDiagnostics(analyticCompilation, emitResult, messages, success);
|
||||
success = await EmitDiagnostics(analyticCompilation, emitResult, messages, success).ConfigureAwait(false);
|
||||
assemblyStream.Seek(0, SeekOrigin.Begin);
|
||||
if (injectionFailed) return null;
|
||||
if (success)
|
||||
return context.LoadFromStream(assemblyStream);
|
||||
|
||||
await EmitDiagnostics(analyticCompilation, compilationWithoutInjection.Emit(assemblyStream), messages, false);
|
||||
await EmitDiagnostics(analyticCompilation, compilationWithoutInjection.Emit(assemblyStream), messages,
|
||||
false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static readonly CSharpCompilationOptions CompilationOptions = new(OutputKind.DynamicallyLinkedLibrary);
|
||||
private static readonly CSharpCompilationOptions CompilationOptions =
|
||||
new(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release, platform: Platform.X64);
|
||||
private static readonly CSharpParseOptions ParseOptions = new(LanguageVersion.CSharp11, DocumentationMode.None);
|
||||
private static readonly DiagnosticAnalyzer ModWhitelistAnalyzer;
|
||||
private static readonly DiagnosticAnalyzer ScriptWhitelistAnalyzer;
|
||||
@@ -289,19 +314,22 @@ public static class ModScriptCompilerPatch
|
||||
private static readonly Func<CSharpCompilation, SyntaxTree, int, SyntaxTree> InjectMod;
|
||||
private static readonly Func<CSharpCompilation, SyntaxTree, SyntaxTree> InjectInstructionCounter;
|
||||
private static readonly Func<CompilationWithAnalyzers, EmitResult, List<Message>, bool, Task<bool>> EmitDiagnostics;
|
||||
private static readonly Func<string, string> MakeAssemblyName;
|
||||
private static readonly MethodInfo CreateInstanceMethod;
|
||||
private static readonly MethodInfo SetDetailedInfoMethod;
|
||||
|
||||
private static CSharpCompilation CreateCompilation(string assemblyFileName, IEnumerable<Script>? scripts)
|
||||
|
||||
|
||||
private static CSharpCompilation CreateCompilation(string assemblyFile, IEnumerable<Script>? scripts)
|
||||
{
|
||||
if (scripts == null)
|
||||
return CSharpCompilation.Create(assemblyFileName, null, MetadataReferences,
|
||||
return CSharpCompilation.Create(assemblyFile, null, MetadataReferences,
|
||||
CompilationOptions);
|
||||
|
||||
|
||||
|
||||
var parseOptions = ParseOptions.WithPreprocessorSymbols(MyScriptCompiler.Static.ConditionalCompilationSymbols);
|
||||
var enumerable = scripts.Select(s => CSharpSyntaxTree.ParseText(s.Code, parseOptions, s.Name, Encoding.UTF8));
|
||||
|
||||
return CSharpCompilation.Create(assemblyFileName, enumerable, MetadataReferences, CompilationOptions);
|
||||
return CSharpCompilation.Create(assemblyFile, enumerable, MetadataReferences, CompilationOptions);
|
||||
}
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user