public void TestMethod5() { var originalClass = typeof(Class5); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Method5"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class5Patch); var prefix = patchClass.GetMethod("Prefix"); Assert.IsNotNull(prefix); var postfix = patchClass.GetMethod("Postfix"); Assert.IsNotNull(postfix); Class5Patch.ResetTest(); var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.IsNotNull(patcher); patcher.AddPrefix(prefix); patcher.AddPostfix(postfix); patcher.Patch(); (new Class5()).Method5("foo"); Assert.IsTrue(Class5Patch.prefixed, "Prefix was not executed"); Assert.IsTrue(Class5Patch.postfixed, "Prefix was not executed"); }
public void TestPatchUnpatch() { var originalClass = typeof(Class9); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("ToString"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class9Patch); var prefix = patchClass.GetMethod("Prefix"); Assert.IsNotNull(prefix); var postfix = patchClass.GetMethod("Postfix"); Assert.IsNotNull(postfix); var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.IsNotNull(patcher); patcher.AddPrefix(prefix); patcher.AddPostfix(postfix); patcher.Patch(); var instanceB = new Harmony("test"); Assert.IsNotNull(instanceB); instanceB.UnpatchAll("test"); }
public void Test_InjectingBaseClassField() { var testInstance = new InjectFieldSubClass(); testInstance.Method("foo"); Assert.AreEqual("foo", testInstance.TestValue); var originalClass = testInstance.GetType(); Assert.NotNull(originalClass); var originalMethod = originalClass.GetMethod("Method"); Assert.NotNull(originalMethod); var patchClass = typeof(InjectFieldSubClass_Patch); var postfix = patchClass.GetMethod("Postfix"); Assert.NotNull(postfix); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPostfix(postfix); Assert.NotNull(patcher); _ = patcher.Patch(); testInstance.Method("bar"); Assert.AreEqual("patched", testInstance.TestValue); }
public void TestMethod0() { var originalClass = typeof(Class0); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Method0"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class0Patch); var postfix = patchClass.GetMethod("Postfix"); Assert.IsNotNull(postfix); var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.IsNotNull(patcher); patcher.AddPostfix(postfix); patcher.Patch(); var result = new Class0().Method0(); Assert.AreEqual("patched", result); }
public void Test_Method7() { var originalClass = typeof(Class7); Assert.NotNull(originalClass); var originalMethod = originalClass.GetMethod("Method7"); Assert.NotNull(originalMethod); var patchClass = typeof(Class7Patch); var postfix = patchClass.GetMethod("Postfix"); Assert.NotNull(postfix); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPostfix(postfix); _ = patcher.Patch(); var instance7 = new Class7(); var result = instance7.Method7("parameter"); Assert.AreEqual("parameter", instance7.state1); Assert.AreEqual(10, result.a); Assert.AreEqual(20, result.b); }
public void TestMethod10() { var originalClass = typeof(Class10); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Method10"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class10Patch); var postfix = patchClass.GetMethod("Postfix"); Assert.IsNotNull(postfix); var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.IsNotNull(patcher); patcher.AddPostfix(postfix); patcher.Patch(); new Class10().Method10(); Assert.IsTrue(Class10Patch.postfixed); Assert.IsTrue(Class10Patch.originalResult); }
public void Test_Method8() { var originalClass = typeof(Class8); Assert.NotNull(originalClass); var originalMethod = originalClass.GetMethod("Method8"); Assert.NotNull(originalMethod); var patchClass = typeof(Class8Patch); var postfix = patchClass.GetMethod("Postfix"); Assert.NotNull(postfix); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPostfix(postfix); Assert.NotNull(patcher); _ = patcher.Patch(); var result = Class8.Method8("patched"); Assert.True(Class8.mainRun); Assert.AreEqual(10, result.a); Assert.AreEqual(20, result.b); }
public static ProcessorInfo Create(Harmony instance, MethodBase original) { return(new ProcessorInfo { instance = instance, original = original, processor = instance.CreateProcessor(original) }); }
public void TestMethod1() { var originalClass = typeof(Class1); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Method1"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class1Patch); var prefix = patchClass.GetMethod("Prefix"); var postfix = patchClass.GetMethod("Postfix"); var transpiler = patchClass.GetMethod("Transpiler"); Assert.IsNotNull(prefix); Assert.IsNotNull(postfix); Assert.IsNotNull(transpiler); Class1Patch.ResetTest(); var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.IsNotNull(patcher); patcher.AddPrefix(prefix); patcher.AddPostfix(postfix); patcher.AddTranspiler(transpiler); var originalMethodStartPre = Memory.GetMethodStart(originalMethod, out _); patcher.Patch(); var originalMethodStartPost = Memory.GetMethodStart(originalMethod, out _); Assert.AreEqual(originalMethodStartPre, originalMethodStartPost); unsafe { var patchedCode = *(byte *)originalMethodStartPre; if (IntPtr.Size == sizeof(long)) { Assert.IsTrue(patchedCode == 0x48); } else { Assert.IsTrue(patchedCode == 0x68); } } Class1.Method1(); Assert.IsTrue(Class1Patch.prefixed, "Prefix was not executed"); Assert.IsTrue(Class1Patch.originalExecuted, "Original was not executed"); Assert.IsTrue(Class1Patch.postfixed, "Postfix was not executed"); }
public static void Patch() { var testMethod = TestContext.CurrentContext.Test.Name; var parts = testMethod.Split('_'); var originalType = AccessTools.TypeByName("HarmonyLibTests.Assets." + parts[1]); var patchType = AccessTools.TypeByName("HarmonyLibTests.Assets." + parts[2]); Assert.NotNull(originalType, nameof(originalType)); var originalMethod = originalType.GetMethod("Method"); Assert.NotNull(originalMethod, nameof(originalMethod)); Assert.NotNull(patchType, nameof(patchType)); var finalizer = patchType.GetMethod("Finalizer"); Assert.NotNull(finalizer, nameof(finalizer)); var instance = new Harmony("finalizer-test"); instance.UnpatchAll("finalizer-test"); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher, nameof(patcher)); _ = patcher.AddFinalizer(finalizer); _ = patcher.Patch(); var trv = Traverse.Create(patchType); _ = trv.Field("finalized").SetValue(false); _ = trv.Field("exception").SetValue(new NullReferenceException("replace-me")); var obj = Activator.CreateInstance(originalType); var m_method = AccessTools.Method(originalType, "Method"); Assert.NotNull(m_method, nameof(m_method)); info = new Dictionary <string, object>(); try { if (m_method.ReturnType == typeof(void)) { _ = m_method.Invoke(obj, null); } else { info["result"] = m_method.Invoke(obj, new object[0]); } info["outerexception"] = null; } catch (TargetInvocationException e) { info["outerexception"] = e.InnerException; } trv.Fields().ForEach(name => info[name] = trv.Field(name).GetValue()); Assert.True((bool)info["finalized"], "Finalizer not called"); }
public void Test_GenericsOriginalMethod() { var originalMethod = typeof(Class13 <int>).GetMethod(nameof(Class13 <int> .Add)); Assert.NotNull(originalMethod); var patchClass = typeof(Class13Patch); var prefix = patchClass.GetMethod("Prefix"); Assert.NotNull(prefix); var list1 = new Class13 <int> { 1, 2, 3 }; list1.Add(1000); var e1 = list1.GetEnumerator(); _ = e1.MoveNext(); _ = e1.MoveNext(); _ = e1.MoveNext(); _ = e1.MoveNext(); Assert.AreEqual(1000, e1.Current); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPrefix(prefix); _ = patcher.Patch(); Class13Patch.method = null; Class13Patch.result = 0; var list2 = new Class13 <int> { 1, 2, 3 }; list2.Add(1000); var e2 = list2.GetEnumerator(); _ = e2.MoveNext(); _ = e2.MoveNext(); _ = e2.MoveNext(); _ = e2.MoveNext(); Assert.AreEqual(999, e2.Current); Assert.AreEqual(1000, Class13Patch.result); Assert.AreEqual(originalMethod, Class13Patch.method); }
public override void LoadPlugin() { base.LoadPlugin(); HarmonyInstance = new Harmony("PlayerIPPatch"); Logger.Log("Player IP Patch by ShimmyMySherbet (great job nelson!)"); Logger.Log("Patching IP Methods..."); MethodInfo PatchBase = typeof(SteamGameServerNetworking).GetMethod("GetP2PSessionState"); PatchProcessor Processor = HarmonyInstance.CreateProcessor(PatchBase); Processor.AddPostfix(new HarmonyMethod(typeof(PlayerIPPatch).GetMethod("PostFixObj"))); Processor.Patch(); Logger.Log("Patched!"); }
public override void LoadPlugin() { base.LoadPlugin(); Harmony = new Harmony("PluginPerformanceProfiler"); MethodInfo EStart = typeof(PluginPerformanceProfiler).GetMethod("OnExcecuteStart", BindingFlags.Public | BindingFlags.Static); MethodInfo EEnd = typeof(PluginPerformanceProfiler).GetMethod("OnExcecuteEnd", BindingFlags.Public | BindingFlags.Static); MethodInfo E = typeof(RocketCommandManager).GetMethod("Execute", BindingFlags.Instance | BindingFlags.Public); var patcher = Harmony.CreateProcessor(E); patcher.AddPrefix(EStart); patcher.AddPostfix(EEnd); patcher.Patch(); Console.WriteLine("Patched!"); }
/// <summary> /// Initializes the RPCDetectorCore. /// </summary> public static void Init(Harmony harmony, RPCDetectorPlugin instance) { instance.m_Logger.LogInformation("Loading RPCDetectorCore..."); Plugin = instance; Logger = new RPCLogger(); Dictionary <MethodInfo, MethodInfo> patchMappings = new Dictionary <MethodInfo, MethodInfo>(); foreach (MethodInfo patchMethod in typeof(RPCDetectorCore).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Where(x => Attribute.IsDefined(x, typeof(MPatch)))) { var patchParameters = patchMethod.GetParameters().ToList(); patchParameters.RemoveAll(x => x.Name.StartsWith("__")); foreach (MethodInfo BaseMethod in typeof(SteamChannel).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) { var baseParameters = BaseMethod.GetParameters(); if (patchMethod.Name.Equals(BaseMethod.Name, StringComparison.InvariantCultureIgnoreCase) && patchParameters.Count == baseParameters.Length) { bool equal = true; for (int i = 0; i < baseParameters.Length; i++) { if (patchParameters[i].ParameterType != baseParameters[i].ParameterType) { equal = false; } } if (equal && !patchMappings.ContainsKey(patchMethod)) { patchMappings.Add(patchMethod, BaseMethod); } } } } foreach (var patchMapping in patchMappings) { harmony.CreateProcessor(patchMapping.Value) .AddPrefix(patchMapping.Key) .Patch(); } lock (Logger) Logger.CallReceived += Logger_CallReceived; Plugin.m_Logger.Log(LogLevel.Information, "RPCDetectorCore loaded; Created {0} patches.", patchMappings.Count); }
public void Test_GenericStructReturnTypes() { var originalClass = typeof(MyList <>).MakeGenericType(typeof(int)); Assert.NotNull(originalClass); var originalMethod = originalClass.GetMethod("GetEnumerator"); Assert.NotNull(originalMethod); var patchClass = typeof(TestGenericStructReturnTypes_Patch); var postfix = patchClass.GetMethod("Postfix"); Assert.NotNull(postfix); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPostfix(postfix); _ = patcher.Patch(); var list = new MyList <int> { list = new List <int>() { 1, 2, 3 } }; var enumerator = list.GetEnumerator(); var result = new List <int>(); while (enumerator.MoveNext()) { result.Add(enumerator.Current); } Assert.AreEqual(3, result.Count); Assert.AreEqual(result[0], 100); Assert.AreEqual(result[1], 200); Assert.AreEqual(result[2], 300); }
public void Test_Method2() { var originalClass = typeof(Class2); Assert.NotNull(originalClass); var originalMethod = originalClass.GetMethod("Method2"); Assert.NotNull(originalMethod); var patchClass = typeof(Class2Patch); var prefix = patchClass.GetMethod("Prefix"); var postfix = patchClass.GetMethod("Postfix"); var transpiler = patchClass.GetMethod("Transpiler"); Assert.NotNull(prefix); Assert.NotNull(postfix); Assert.NotNull(transpiler); Class2Patch.ResetTest(); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPrefix(prefix); _ = patcher.AddPostfix(postfix); _ = patcher.AddTranspiler(transpiler); var originalMethodStartPre = Memory.GetMethodStart(originalMethod, out _); _ = patcher.Patch(); var originalMethodStartPost = Memory.GetMethodStart(originalMethod, out _); Assert.AreEqual(originalMethodStartPre, originalMethodStartPost); new Class2().Method2(); Assert.True(Class2Patch.prefixed, "Prefix was not executed"); Assert.True(Class2Patch.originalExecuted, "Original was not executed"); Assert.True(Class2Patch.postfixed, "Postfix was not executed"); }
public void Test_ReverseTranspilerPatching() { var class0 = new Class0Reverse(); var result1 = class0.Method("al-gin-Ori", 123); Assert.AreEqual("Original123Prolog", result1); var originalClass = typeof(Class0Reverse); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Method"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class0ReversePatch); var postfix = patchClass.GetMethod("Postfix"); Assert.IsNotNull(postfix); //Harmony.DEBUG = true; var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = instance.CreateProcessor(originalMethod); _ = patcher.AddPostfix(new HarmonyMethod(postfix)); _ = patcher.Patch(); var standin = new HarmonyMethod(patchClass.GetMethod("StringOperation")); Assert.IsNotNull(standin); Assert.IsNotNull(standin.method); var reversePatcher = instance.CreateReversePatcher(originalMethod, standin); _ = reversePatcher.Patch(); var result2 = class0.Method("al-gin-Ori", 456); Assert.AreEqual("EpilogOriginal", result2); }
public void Test_Method6() { var originalClass = typeof(Class6); Assert.NotNull(originalClass); var originalMethod = originalClass.GetMethod("Method6"); Assert.NotNull(originalMethod); var patchClass = typeof(Class6Patch); var prefix = patchClass.GetMethod("Prefix"); Assert.NotNull(prefix); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPrefix(prefix); Assert.NotNull(patcher); _ = patcher.Patch(); var instance6 = new Class6 { someFloat = 999, someString = "original", someStruct = new Class6Struct() { d1 = 1, d2 = 2, d3 = 3 } }; var res = instance6.Method6(); Assert.AreEqual(res[0], 123); Assert.AreEqual(res[1], "patched"); Assert.AreEqual(((Class6Struct)res[2]).d1, 10.0); }
public void Test_Method4() { var originalClass = typeof(Class4); Assert.NotNull(originalClass); var originalMethod = originalClass.GetMethod("Method4"); Assert.NotNull(originalMethod); var patchClass = typeof(Class4Patch); var prefix = patchClass.GetMethod("Prefix"); Assert.NotNull(prefix); Class4Patch.ResetTest(); var instance = new Harmony("test"); Assert.NotNull(instance); var patcher = instance.CreateProcessor(originalMethod); Assert.NotNull(patcher); _ = patcher.AddPrefix(prefix); var originalMethodStartPre = Memory.GetMethodStart(originalMethod, out _); _ = patcher.Patch(); var originalMethodStartPost = Memory.GetMethodStart(originalMethod, out _); Assert.AreEqual(originalMethodStartPre, originalMethodStartPost); (new Class4()).Method4("foo"); Assert.True(Class4Patch.prefixed, "Prefix was not executed"); Assert.True(Class4Patch.originalExecuted, "Original was not executed"); Assert.AreEqual(Class4Patch.senderValue, "foo"); }
public override void LoadPlugin() { base.LoadPlugin(); HarmonyInstance = new Harmony("uEssentialsAntiTrack"); try { AssemblyRef asm = AssemblyRef.Load("UEssentials"); if (asm == null) { throw new FileNotFoundException(); } Type anylitics = asm.GetType("Essentials.Misc.Analytics"); if (anylitics != null) { MethodInfo sendAnylticicsEvent = anylitics.GetMethod("SendEvent", BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (sendAnylticicsEvent != null) { HarmonyInstance.CreateProcessor(sendAnylticicsEvent).AddPrefix(typeof(AntiTracker).GetMethod("BlockMethod")).Patch(); Logger.Log("uEssentials analytics blocked."); } else { Logger.Log("Failed to find Analytics SendEvent Method."); } } else { Logger.Log("Failed to find Analytics Type."); } } catch (FileNotFoundException) { Logger.Log("uEssentials is not installed."); } }
public void AttemptRemoveHarmonyModPatch(Harmony h) { /* Needs to run after Harmony is OnEnabled() */ testName = "TEST/" + "API/ACL3/" + typeof(HarmonyHelper).Assembly.GetName().Name + " " + typeof(HarmonyHelper).Assembly.GetName().Version; PatchProcessor processor = null; MethodInfo prohibitedPatch = null; try { var targetName = "SetEntry"; MethodInfo target = typeof(PackageEntry).GetMethod(targetName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (target == null) { throw new TestFailed($"Target fn ({targetName}) was not found."); } processor = h.CreateProcessor(target); if (processor == null) { UnityEngine.Debug.Log($"[{testName}] INFO - Processor=null. BAD"); throw new TestFailed($"Failed to create processor."); } var patches = Harmony.GetPatchInfo(target); if (patches == null) { throw new TestFailed($"Failed to get '{target}' patches."); } if (patches.Prefixes != null) { UnityEngine.Debug.Log($"[{testName}] INFO - found {patches.Prefixes.Count} prefix patches to {targetName}"); } if (patches.Postfixes != null) { UnityEngine.Debug.Log($"[{testName}] INFO - found {patches.Postfixes.Count} postfix patches to {targetName}"); } if (patches.Transpilers != null) { UnityEngine.Debug.Log($"[{testName}] INFO - found {patches.Transpilers.Count} transpiler patches to {targetName}"); } if (patches.Finalizers != null) { UnityEngine.Debug.Log($"[{testName}] INFO - found {patches.Finalizers.Count} finalizer patches to {targetName}"); } patches.Postfixes.Do((p) => UnityEngine.Debug.Log($"[{testName}] INFO - found patch to {targetName} = {p.PatchMethod.Name} by {p.owner}")); bool found = false; patches.Postfixes.DoIf((p) => p.owner.Contains("org.ohmi.harmony"), (p) => { found = true; processor.Unpatch(p.PatchMethod); }); if (!found) { throw new TestFailed($"Mod's patch to {targetName} not found."); } throw new TestFailed("Removing a Mod's patch did not throw HarmonyModACLException"); } catch (TestFailed ex) { UnityEngine.Debug.Log($"[{testName}] ERROR : {ex.Message}. BAD"); throw ex; } catch (Exception ex) { if (ex.GetType().Name != "HarmonyModACLException") { throw new TestFailed($"Unpatching the mod failed but not because of ACL: {ex.Message}", ex); } UnityEngine.Debug.Log($"[{testName}] INFO - Attempting to unpatch the mod was blocked ({ex.Message}). OK."); } }
public void AttemptProhibitedPatch(Harmony h) { testName = "TEST/" + "API/ACL2/" + typeof(HarmonyHelper).Assembly.GetName().Name + " " + typeof(HarmonyHelper).Assembly.GetName().Version; PatchProcessor processor = null; MethodInfo prohibitedPatch = null; try { var targetName = "CompareTo"; MethodInfo target = typeof(Patch).GetMethod(targetName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (target == null) { throw new TestFailed($"Target fn ({targetName}) was not found."); } processor = h.CreateProcessor(target); if (processor == null) { UnityEngine.Debug.Log($"[{testName}] INFO - Processor=null. BAD"); throw new TestFailed($"Failed to create processor."); } prohibitedPatch = typeof(ACLPatchDefinitions).GetMethod("ProhibitedPatch", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (prohibitedPatch == null) { throw new TestFailed("Patch fn (ProhibitedPatch) was not found."); } processor.AddPostfix(prohibitedPatch); processor.Patch(); throw new TestFailed("Installing a prohibited patch did not throw HarmonyModACLException"); } catch (TestFailed ex) { UnityEngine.Debug.Log($"[{testName}] ERROR : {ex.Message}. BAD"); throw ex; } catch (Exception ex) { if (ex.GetType().Name != "HarmonyModACLException") { throw new TestFailed($"Installing a prohibited patch failed but not because of ACL: {ex.Message}"); } UnityEngine.Debug.Log($"[{testName}] INFO - Attempting to install a prohibited patch was blocked ({ex.Message}). OK."); } finally { if (processor != null && prohibitedPatch != null) { try { processor.Unpatch(prohibitedPatch); } catch (Exception ex) { if (ex.GetType().Name != "HarmonyModACLException") { throw new TestFailed($"Uninstalling a prohibited patch failed but not because of ACL: {ex.Message}"); } UnityEngine.Debug.Log($"[{testName}] INFO - Attempting to remove a prohibited patch was blocked ({ex.Message}). OK."); } } else { throw new TestFailed($"Prohibited patch test is broken. processor={processor != null} patch={prohibitedPatch != null}"); } } }
public static void Install(Harmony harmony, object vanilla, Type vanillaInterfaceType) { Instance = Activator.CreateInstance <TInstance>(); var vanillaImplType = vanilla.GetType(); var fullInterfaceType = typeof(TService); Log.Debug.PrintLine($"Installing {Instance.GetType()} -> {fullInterfaceType} -> {vanillaImplType} -> {vanillaInterfaceType}"); var stubs = new List <MethodBase>(); var patches = new Dictionary <MethodBase, ServicePatch>(); foreach (var method in typeof(TInstance).GetMethods(InstanceBinding)) { var replace = method.GetCustomAttribute <HarmonyReplaceAttribute>(); if (replace != null) { var patch = GetPatch(); int stubId = stubs.Count; var interfaceMethod = patch.GetImplementedInterface(vanillaInterfaceType); if (interfaceMethod != null) { stubs.Add(interfaceMethod); } else { //This is not method from vanilla interface var extendedInterfaceMethod = patch.Target.FindMatchingMethod(fullInterfaceType.GetAllInterfaceMethods(), false); if (extendedInterfaceMethod != null) { //We can proxy it via extended interface stubs.Add(extendedInterfaceMethod); } else { //Or don't proxy it at all if there is no match in extended interface either stubId = -1; } } patch.VanillaReplace(method, stubId); continue; } //TODO: Fix for `Vanilla` call var prefix = method.GetCustomAttribute <HarmonyPrefix>(); if (prefix != null) { GetPatch().Patch.AddPrefix(new HarmonyMethod(method)); continue; } var postfix = method.GetCustomAttribute <HarmonyPostfix>(); if (postfix != null) { GetPatch().Patch.AddPostfix(new HarmonyMethod(method)); continue; } var finalizer = method.GetCustomAttribute <HarmonyFinalizer>(); if (finalizer != null) { GetPatch().Patch.AddFinalizer(new HarmonyMethod(method)); continue; } ServicePatch GetPatch() { var target = method.FindMatchingMethod(vanillaImplType.GetMethods(InstanceBinding), true, IsTypeMatching); //Path real implementation, not V-table stub target = target.GetDeclaredMember(); if (patches.TryGetValue(target, out var patch) == false) { patch = new ServicePatch(target, harmony.CreateProcessor(target)); patches.Add(target, patch); } return(patch); bool IsTypeMatching(Type real, Type general) { if (real == general) { return(true); } if (real.IsClass && general == typeof(object)) { return(true); } if (real.IsByRef && (general == typeof(IntPtr) || general == typeof(void *))) { return(true); } return(false); } } } foreach (var(_, patch) in patches) { patch.Patch.Patch(); } if (stubs.Count > 0 || vanillaInterfaceType != fullInterfaceType) { StubIndirections = new bool[stubs.Count]; var proxy = PatchHelpers.CreateDynamicType($"{fullInterfaceType.Name}_Proxy"); proxy.SetParent(typeof(object)); proxy.AddInterfaceImplementation(fullInterfaceType); var stubsField = AccessTools.Field(typeof(CreativeService <TInstance, TService>), "StubIndirections"); var vanillaInstance = proxy.DefineField("__Vanilla", vanillaImplType, FieldAttributes.Public | FieldAttributes.InitOnly); var proxyDelegates = new List <(Delegate Delegate, FieldInfo Field)>(); var vanillaMethods = vanillaInterfaceType.GetAllInterfaceMethods().ToHashSet(); var declaredVanillaMethods = vanillaImplType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (var method in fullInterfaceType.GetAllInterfaceMethods()) { var args = method.GetParameters(); var proxyMethod = proxy.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual); proxyMethod.SetReturnType(method.ReturnType); proxyMethod.SetParameters(args.Select(x => x.ParameterType).ToArray()); proxy.DefineMethodOverride(proxyMethod, method); var IL = proxyMethod.GetILGenerator(); var stubId = stubs.IndexOf(method); if (stubId >= 0) { //Set stub mark IL.Emit(OpCodes.Ldsfld, stubsField); IL.Emit(OpCodes.Ldc_I4, stubId); IL.Emit(OpCodes.Ldc_I4_1); IL.Emit(OpCodes.Stelem_I1); } MethodInfo directCallTarget = null; var isInterfaceCall = vanillaMethods.Contains(method); if (isInterfaceCall == false) { directCallTarget = method.FindMatchingMethod(declaredVanillaMethods, true); if (directCallTarget.IsAssemblyPublic() == false) { //Note: Nasty hack follows! //Dynamic type has to follow all visibility rules normal assembly would so it can't call private methods directly //Runtime delegates can reflect, call and expose private members, but can't instantiate interface //=> Call proxy delegate inside proxy type to expose private method via public interface. We need to go deeper :) var delegateType = DelegateCreator.NewDelegateType(directCallTarget, unboundInstanceCall: true); var proxyDelegate = Delegate.CreateDelegate(delegateType, directCallTarget); var fieldName = $"__ProxyDelegate_{directCallTarget.Name}_{proxyDelegates.Count}"; var delegateField = proxy.DefineField(fieldName, delegateType, FieldAttributes.Private | FieldAttributes.InitOnly); IL.Emit(OpCodes.Ldarg_0); //This IL.Emit(OpCodes.Ldfld, delegateField); proxyDelegates.Add((proxyDelegate, delegateField)); directCallTarget = delegateType.GetMethod("Invoke"); } } if (directCallTarget == null || directCallTarget.IsStatic == false) { IL.Emit(OpCodes.Ldarg_0); //This IL.Emit(OpCodes.Ldfld, vanillaInstance); } var argCount = args.Length; for (int i = 1; i <= argCount; i++) { IL.Emit(OpCodes.Ldarg, i); } if (isInterfaceCall) { IL.Emit(OpCodes.Callvirt, method); } else { IL.Emit(OpCodes.Call, directCallTarget); } IL.Emit(OpCodes.Ret); } var ctorArgTypes = new[] { vanillaImplType }.Concat(proxyDelegates.Select(x => x.Field.FieldType)); var ctor = proxy.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, ctorArgTypes.ToArray()); var cIL = ctor.GetILGenerator(); cIL.Emit(OpCodes.Ldarg_0); cIL.Emit(OpCodes.Call, proxy.BaseType.GetConstructor(Type.EmptyTypes)); cIL.Emit(OpCodes.Ldarg_0); cIL.Emit(OpCodes.Ldarg_1); cIL.Emit(OpCodes.Stfld, vanillaInstance); for (int i = 0; i < proxyDelegates.Count; i++) { cIL.Emit(OpCodes.Ldarg_0); cIL.Emit(OpCodes.Ldarg, i + 2); cIL.Emit(OpCodes.Stfld, proxyDelegates[i].Field); } cIL.Emit(OpCodes.Ret); var ctorArgs = new object[] { vanilla }.Concat(proxyDelegates.Select(x => x.Delegate)); Instance.Vanilla = (TService)Activator.CreateInstance(proxy.CreateType(), ctorArgs.ToArray()); } else { Instance.Vanilla = (TService)vanilla; } foreach (var property in typeof(TInstance).GetProperties(InstanceBinding | BindingFlags.Static)) { if (property.HasAttribute <HarmonyPropertyAttribute>()) { property.InjectVanillaData(vanillaImplType, harmony); } } }