add unloading for mod assemblies
This commit is contained in:
67
CringeLauncher/Patches/ModScriptCompilerPatch.cs
Normal file
67
CringeLauncher/Patches/ModScriptCompilerPatch.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using CringeLauncher.Utils;
|
||||||
|
using HarmonyLib;
|
||||||
|
using NLog;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using VRage.Scripting;
|
||||||
|
|
||||||
|
namespace CringeLauncher.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch]
|
||||||
|
public static class ModScriptCompilerPatch
|
||||||
|
{
|
||||||
|
private static ILogger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private static AssemblyLoadContext _context = new(null, true);
|
||||||
|
|
||||||
|
static ModScriptCompilerPatch()
|
||||||
|
{
|
||||||
|
MySession.OnUnloaded += OnUnloaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnUnloaded()
|
||||||
|
{
|
||||||
|
if (!_context.Assemblies.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Log.Info("Unloading mod scripts context");
|
||||||
|
_context.Unload();
|
||||||
|
|
||||||
|
_context = new(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodInfo TargetMethod()
|
||||||
|
{
|
||||||
|
return MethodTools.AsyncMethodBody(
|
||||||
|
AccessTools.Method(typeof(MyScriptCompiler), nameof(MyScriptCompiler.Compile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||||
|
{
|
||||||
|
var ins = instructions.ToList();
|
||||||
|
|
||||||
|
for (var i = 0; i < ins.Count; i++)
|
||||||
|
{
|
||||||
|
var instruction = ins[i];
|
||||||
|
|
||||||
|
if (instruction.Calls(AccessTools.Method(typeof(Assembly), nameof(Assembly.Load),
|
||||||
|
new[] { typeof(byte[]) })))
|
||||||
|
{
|
||||||
|
ins[i - 1] = new(OpCodes.Nop); // memStream toArray call
|
||||||
|
ins[i] = CodeInstruction.CallClosure((Stream assembly) => _context.LoadFromStream(assembly));
|
||||||
|
}
|
||||||
|
else if (instruction.Calls(AccessTools.Method(typeof(Assembly), nameof(Assembly.Load),
|
||||||
|
new[] { typeof(byte[]), typeof(byte[]) })))
|
||||||
|
{
|
||||||
|
ins[i - 4] = new(OpCodes.Nop); // memStream toArray calls
|
||||||
|
ins[i - 1] = new(OpCodes.Nop);
|
||||||
|
ins[i] = CodeInstruction.CallClosure((Stream assembly, Stream assemblySymbols) =>
|
||||||
|
_context.LoadFromStream(assembly, assemblySymbols));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ins;
|
||||||
|
}
|
||||||
|
}
|
20
CringeLauncher/Utils/MethodTools.cs
Normal file
20
CringeLauncher/Utils/MethodTools.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MonoMod.Utils;
|
||||||
|
|
||||||
|
namespace CringeLauncher.Utils;
|
||||||
|
|
||||||
|
public static class MethodTools
|
||||||
|
{
|
||||||
|
public static MethodInfo AsyncMethodBody(MethodInfo method)
|
||||||
|
{
|
||||||
|
var (_, operand) = PatchProcessor.ReadMethodBody(method).First();
|
||||||
|
|
||||||
|
if (operand is not LocalVariableInfo localVar)
|
||||||
|
throw new InvalidOperationException($"Method {method.GetID()} does not contain a valid async state machine");
|
||||||
|
|
||||||
|
return AccessTools.Method(localVar.LocalType, "MoveNext") ??
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Async State machine of method {method.GetID()} does not contain a valid MoveNext method");
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user