I'm trying to use Harmony 2.3.3 with Godot 4.2.1, and it works as expected within the editor, but not within an exported build.
I'm attempting to patch a prefix to a method, and within the editor the method is patched successfully, but in the exported build, the method fails to be patched and the following exception is thrown:
System.ArgumentException: GenericArguments[0], 'MonoMod.Utils.Cil.CecilILGenerator', on 'MonoMod.Utils.Cil.ILGeneratorProxy[TTarget]' violates the constraint of type 'TTarget'.
---> System.TypeLoadException: GenericArguments[0], 'MonoMod.Utils.Cil.CecilILGenerator', on 'MonoMod.Utils.Cil.ILGeneratorProxy[TTarget]' violates the constraint of type parameter 'TTarget'.
at System.RuntimeTypeHandle.Instantiate(RuntimeType inst)
at System.RuntimeType.MakeGenericType(Type[] instantiation)
--- End of inner exception stack trace ---
at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
at System.RuntimeType.MakeGenericType(Type[] instantiation)
at MonoMod.Utils.Cil.ILGeneratorShim.GetProxy()
at MonoMod.Utils.DynamicMethodDefinition.GetILGenerator()
at HarmonyLib.MethodPatcher..ctor(MethodBase original, MethodBase source, List`1 prefixes, List`1 postfixes, List`1 transpilers, List`1 finalizers, Boolean debug)
at HarmonyLib.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo)
at HarmonyLib.PatchProcessor.Patch()
at HarmonyLib.Harmony.Patch(MethodBase original, HarmonyMethod prefix, HarmonyMethod postfix, HarmonyMethod transpiler, HarmonyMethod finalizer)
at HarmonyTester.Test() in G:\Godot Projects\HarmonyExportIssue\HarmonyTesting\HarmonyTester.cs:line 24
It seems to be some kind of dependency issue, as discussed in this issue on the Harmony Github, but I have no idea where to go from here or how to fix this problem.
After researching some more, this appears to be a known issue with Godot and external .dll files. https://github.com/godotengine/godot/issues/75160
It's possible to work around this by using reflection as described in this answer: https://github.com/godotengine/godot/issues/75160#issuecomment-2071203840
public partial class ModLoader : Node
{
public override void _Ready()
{
string modPath = ProjectSettings.GlobalizePath("user://TestMod.dll");
var alc = AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly());
Assembly assembly = alc.LoadFromAssemblyPath(modPath);
Type t = assembly.GetType("TestMod.TestModInit");
t.GetMethod("Init")?.Invoke(null, null);
}
}
Do note that at runtime you will need to get the dll file using
OS.GetExecutablePath().GetBaseDir().PathJoin(file: "dllname.dll")
instead