Giter Site home page Giter Site logo

pardeike / harmony Goto Github PK

View Code? Open in Web Editor NEW
4.8K 94.0 466.0 16.43 MB

A library for patching, replacing and decorating .NET and Mono methods during runtime

Home Page: https://www.patreon.com/pardeike

License: MIT License

C# 99.88% Shell 0.01% PowerShell 0.12%
csharp patcher detour detours runtime mono dotnet unity cil monkey-patching non-destructive

harmony's Introduction

Harmony
Version 2.3
A library for patching, replacing and decorating
.NET and Mono methods during runtime.

About

Harmony gives you an elegant and high level way to alter the functionality in applications written in C#. It works great in games and is well established in titles like

Rust
Rimworld
7 Days To Die
Stardew Valley
Subnautica
Oxygen Not Included
Besiege
Cities:Skylines
Kerbal Space Program
Resonite
BattleTech
Slime Rancher

and others like Ravenfield, Sheltered, Staxel, The Ultimate Nerd Game, Total Miner, Unturned, SCP: Secret Laboratory ...

It is also used in unit testing WPF controls at Microsoft and Google and in many other areas.

How it works

If you develop in C# and your code is loaded as a module/plugin into a host application, you can use Harmony to alter the functionality of all the available assemblies of that application. Where other patch libraries simply allow you to replace the original method, Harmony goes one step further and gives you:

• A way to keep the original method intact
• Execute your code before and/or after the original method
• Modify the original with IL code processors
• Multiple Harmony patches co-exist and don't conflict with each other
• Works at runtime and does not touch any files

Installation

If you want a single file, dependency-merged assembly, you should use the Lib.Harmony nuget package. This is the preferred way.

If you instead want to supply the dependencies yourself, you should use the Lib.Harmony.Thin nuget package. You get more control but you are responsible to make all references available at runtime.

Documentation

Please check out the documentation and join the official discord server.

Contribute

I put thousands of hours into this project and its support. So every little action helps:

• Become a GitHub sponsor or a Patreon
• Upvote this stackoverflow answer
• Spread the word in your developer communities

This project uses the great MonoMod.Core library by 0x0ade and nike4613.

Harmony 1

Harmony 1 is deprecated and not under active development anymore. The latest version of it (v1.2.0.1) is stable and contains only minor bugs. Keep using it if you are in an environment that exclusively uses Harmony 1. Currently Harmony 1.x and 2.x are NOT COMPATIBLE with each other and SHOULD NOT BE MIXED. The old documentation can still be found at the Wiki.


 

harmony's People

Contributors

0x0ade avatar alextd avatar aragas avatar asquared31415 avatar banane9 avatar bbepis avatar bugproof avatar cybershadow avatar denjur avatar failedshack avatar hcoona avatar kian738 avatar kittyfisto avatar kohanis avatar lbmaian avatar logandark avatar mehni avatar pardeike avatar pardeike-bot avatar pathoschild avatar pengweiqhca avatar qkrisi avatar rrazgriz avatar rube200 avatar simplywiri avatar sineswiper avatar stackoverflowexcept1on avatar tarbisu avatar wasabii avatar windows10ce avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

harmony's Issues

Use Harmony in Xamarin?

I'm trying to use harmony in a xamarin project, have no idea how to make it work, I get nullref exceptions no matter how I use the library.

Patching instance methods

I'm trying to use Harmony to overwrite the behaviour of MouseEventArgs.GetPosition() method. However applying the following patch:

	[HarmonyPatch(typeof(MouseEventArgs))]
	[HarmonyPatch("GetPosition")]
	sealed class PatchMouseEventArgsGetPosition
	{
		private static void Postfix(MouseEventArgs __instance, IInputElement relativeTo, ref Point __result)
		{
			Positions.TryGetValue(relativeTo, out __result);
		}
	}

throws a System.NotSupportedException exception with the following message : "Wrong MethodAttributes or CallingConventions for DynamicMethod. Only public, static, standard supported".

According to documentation, the only supported combination is Public | Static which is why I don't understand the following code inside DynamicTools.CreateDynamicMethod:

		var method = new DynamicMethod(
			patchName,
			MethodAttributes.Public | (original.IsStatic ? MethodAttributes.Static : 0),
			CallingConventions.Standard,
			AccessTools.GetReturnedType(original),
			paramTypes,
			original.DeclaringType,
			true
		);

Is there a case (or a platform) where creating a non-static DynamicMethod is ever possible?
I've changed it locally to just MethodAttributes.Public | MethodAttributes.Static which caused my problem to go away, but I may be missing something here.

[RimWorld] Exception thrown when using __state in postfix patch

Maybe I don't fully understand how to use the __state parameter, but this is what my code looks like:

namespace Xnope.Patches
{
    // Prefix patch:
    // stores the old (dead) leader's name for use in postfix
    [HarmonyPatch(typeof(Faction), "GenerateNewLeader")]
    public static class Prefix_GenerateNewLeader
    {
        [HarmonyPrefix]
        public static void Prefix(Faction __instance, string __state)
        {
            __state = "";
            if (__instance.leader != null)
                __state = __instance.leader.NameStringShort;
            else
                __state = "LNAME";
        }
    }



    // Postfix patch:
    // dynamically adjusts a faction's name with its leader's name
    [HarmonyPatch(typeof(Faction), "GenerateNewLeader")]
    public static class Postfix_GenerateNewLeader
    {
        [HarmonyPostfix]
        public static void Postfix(Faction __instance, string __state)
        {

            if (__instance.IsDynamicallyNamed())
                ResolveFactionName(__instance, __state);
        }


        private static void ResolveFactionName(Faction f, string oldLeaderName)
        {
            f.Name = f.Name.Replace(oldLeaderName, f.leader.NameStringShort);
        }
    }
}

I only get the error when I try to use the __state parameter in the postfix. It doesn't matter if I use the __state paremeter anywhere in the postfix code, just if it exists as a parameter.

Here is the full error trace:

An exception was thrown by the type initializer for Xnope.XnopeCoreMod ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
  at System.Collections.Generic.Dictionary`2[System.String,System.Reflection.Emit.LocalBuilder].get_Item (System.String key) [0x00000] in <filename unknown>:0 
  at Harmony.MethodPatcher.EmitCallParameter (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Reflection.MethodInfo patch, System.Collections.Generic.Dictionary`2 variables) [0x00000] in <filename unknown>:0 
  at Harmony.MethodPatcher+<>c__DisplayClass7_0.<AddPostfixes>b__0 (System.Reflection.MethodInfo fix) [0x00000] in <filename unknown>:0 
  at System.Collections.Generic.List`1[System.Reflection.MethodInfo].ForEach (System.Action`1 action) [0x00000] in <filename unknown>:0 
  at Harmony.MethodPatcher.AddPostfixes (System.Reflection.Emit.ILGenerator il, System.Reflection.MethodBase original, System.Collections.Generic.List`1 postfixes, System.Collections.Generic.Dictionary`2 variables) [0x00000] in <filename unknown>:0 
  at Harmony.MethodPatcher.CreatePatchedMethod (System.Reflection.MethodBase original, System.Collections.Generic.List`1 prefixes, System.Collections.Generic.List`1 postfixes, System.Collections.Generic.List`1 transpilers) [0x00000] in <filename unknown>:0 
  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo) [0x00000] in <filename unknown>:0 
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.<PatchAll>b__6_0 (System.Type type) [0x00000] in <filename unknown>:0 
  at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0 
  at Xnope.XnopeCoreMod..cctor () [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at Xnope.Defs.BackstoryDef.ResolveReferences () [0x00000] in <filename unknown>:0 
  at Verse.DefDatabase`1[Xnope.Defs.BackstoryDef].ResolveAllReferences () [0x00000] in <filename unknown>:0 
Verse.Log:Error(String)
Verse.DefDatabase`1:ResolveAllReferences()
System.Reflection.MonoMethod:InternalInvoke(Object, Object[], Exception&)
System.Reflection.MonoMethod:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
System.Reflection.MethodBase:Invoke(Object, Object[])
Verse.GenGeneric:InvokeStaticMethodOnGenericType(Type, Type, String)
Verse.PlayDataLoader:DoPlayLoad()
Verse.PlayDataLoader:LoadAllPlayData(Boolean)
Verse.Root:<Start>m__853()
Verse.LongEventHandler:RunEventFromAnotherThread(Action)
Verse.LongEventHandler:<UpdateCurrentAsynchronousEvent>m__851()

Any guidance would be helpful.

[Suggestion] Unpatching methods

I went through the wiki and some of the source code and I can't find any method for doing this. Would it be feasible to implement an interface for removing a hook from a method?

Request: Function like postfix, but for Thrown Exceptions

I know there's been some discussion about dealing with exception code within code being modified, I'm not sure if this is the same or different.

We have prefix, transpiler, and postfix. Can we get a function for catching thrown exceptions? I would imagine that once the exception is hijacked, that we'd also want the ability to send it on its merry way to where it was originally going.

Potential Bug in MethodBodyReader

MethodBodyReader exposes a static function GetInstructions(MethodBase). If this is called, there is potential to throw a null pointer for ShortInlineVar and InlineVar operand types because "variables" will be undefined. I fixed this locally by setting "reader.locals = null" in GetInstructions; however, I do not believe this is the correct long term fix.

Suggestion - Prefix ref __result

Does __result have a purpose for prefix? It would be really nice if it was a pass by reference so we can return a nice result to the original caller if the method was skipped.

InvalidCastException inside MethodBodyReader.ReadOperand

Hi, I've been testing this in Kerbal Space Program and it seems to be working except for a small possible oversight. Here's the stack trace:

InvalidCastException: Cannot cast from source type to destination type.
	Harmony.ILCopying.MethodBodyReader.ReadOperand (Harmony.ILCopying.ILInstruction instruction)
	Harmony.ILCopying.MethodBodyReader.ReadInstructions ()
	Harmony.ILCopying.MethodCopier..ctor (System.Reflection.MethodBase fromMethod, System.Reflection.Emit.DynamicMethod toDynamicMethod, System.Reflection.Emit.LocalBuilder[] existingVariables)
	Harmony.MethodPatcher.CreatePatchedMethod (System.Reflection.MethodBase original, System.Collections.Generic.List`1 prefixes, System.Collections.Generic.List`1 postfixes, System.Collections.Generic.List`1 processors)
	Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo)
	Harmony.PatchProcessor.Patch ()
	Harmony.HarmonyInstance.<PatchAll>b__5_0 (System.Type type)
	Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action)
	Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly)

The specific issue is in OperandType.InlineTok in ReadOperand
instruction.argument = (Type) instruction.operand;
instruction.operand is of type MonoMethod and the cast fails

Here's the target method's IL:

.method private hidebysig 
	instance void startGame () cil managed 
{
	// Method begins at RVA 0x1b4fc
	// Code size 26 (0x1a)
	.maxstack 8

	IL_0000: ldarg.0
	IL_0001: ldfld class MainMenuEnvLogic MainMenu::envLogic
	IL_0006: call bool [UnityEngine]UnityEngine.Object::op_Implicit(class [UnityEngine]UnityEngine.Object)
	IL_000b: brfalse.s IL_0019

	IL_000d: ldarg.0
	IL_000e: ldfld class MainMenuEnvLogic MainMenu::envLogic
	IL_0013: ldc.i4.1
	IL_0014: callvirt instance void MainMenuEnvLogic::GoToStage(int32)

	IL_0019: ret
} // end of method MainMenu::startGame

Here's the test patch class:

    [HarmonyPatch(typeof(MainMenu))]
    [HarmonyPatch("startGame")]
    class MainMenu_StartGameBreaker
    {
        private static int _counter = 0;

        private static bool Prefix(MainMenu __instance)
        {
            Debug.LogWarning("User clicked on start game! Cancelled. " + _counter++);
            return false;
        }
    }

I solved the issue by testing for MethodInfo and casting to that instead before assigning to instruction.operand but what the IL is doing exactly at this level is a little beyond me so I'm not sure if that's the proper solution.

Let me know if you need any more details

Obfuscated parameters

What if the method has obfuscated parameters that begin with # ?

How could I access them?

Also how does it deal with multiple overloads?

FileLog is not thread-safe

I'm running multiple threads, and it appears that FileLog doesn't properly handle multiple log requests at once. It would be nice to have this be thread safe.

As an aside, really excited for the next release of Harmony. Is there anything I can do to help?

System.IO.IOException: Sharing violation on path C:\Users\XXXXXX\Desktop\harmony.log.txt
  at System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean anonymous, FileOptions options) [0x00000] in <filename unknown>:0 
  at System.IO.FileStream..ctor (System.String path, FileMode mode, FileAccess access, FileShare share) [0x00000] in <filename unknown>:0 
  at (wrapper remoting-invoke-with-check) System.IO.FileStream:.ctor (string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare)
  at System.IO.StreamWriter..ctor (System.String path, Boolean append, System.Text.Encoding encoding, Int32 bufferSize) [0x00000] in <filename unknown>:0 
  at System.IO.StreamWriter..ctor (System.String path, Boolean append) [0x00000] in <filename unknown>:0 
  at (wrapper remoting-invoke-with-check) System.IO.StreamWriter:.ctor (string,bool)
  at System.IO.File.AppendText (System.String path) [0x00000] in <filename unknown>:0 
  at Harmony.FileLog.Log (System.String str) [0x00000] in <filename unknown>:0 
  at _9thFingerThreadingMod.Replacement_Functions.ReachabilityFunctionHolder.hijackCanReach (Verse.Reachability oldReacher, IntVec3 start, LocalTargetInfo dest, PathEndMode peMode, TraverseParms traverseParams) [0x00000] in <filename unknown>:0

[SUGGEST] Cross platform support

I find it very hard to compile it under other platform. (Linux, Mac, etc.)

I would suggest to convert the project into DotNet Core projects & replace the testing framework with NUnit/xUnit etc.

UnitTest fail

There are 4 unit tests failed for latest master branch (05e5b66), please fix them to ensure the quality.

  • Traverse_Types
  • Traverse_SilentFailures
  • Traverse_Missing_Method
  • AccessCache_Method

Not transforming Leave_S jumps

MethodCopier transforms various short opcodes into their long versions, but not Leave_S. This can cause invalid instructions to be emitted.

Trouble patching properties.

Hello!
First off, I want to thank you for creating Harmony :)

Anyhow... please bear with me while I try to explain what the problem is. I am a newbie modder and my knowledge is very limited, so I apologize in advance if my issue is silly or stupid or it has an easy solution which I, in my ignorance, am unaware of.

Anyway, I'm trying to patch the property of a struct type.

Here is a simplification of the struct and the property im trying to patch.

public struct TreeInstance
{
        public TreeInfo Info
	{
		get
		{
			return PrefabCollection<TreeInfo>.GetPrefab((uint)this.m_infoIndex);
		}
		set
		{
			this.m_infoIndex = (ushort)Mathf.Clamp(value.m_prefabDataIndex, 0, 65535);
		}
	}
}

And here is the code I'm using for patching...

[HarmonyPatch(typeof(TreeInstance))]
 [HarmonyPatch("get_Info")]
 public class TreeInstanceGetInfoPatch
 {
     public static bool Prefix(ref TreeInstance __instance, ref TreeInfo __result)
     {
         var info = PrefabCollection<TreeInfo>.GetPrefab(__instance.m_infoIndex);
         float[] array;
         Vector3 pos;
         pos.x = __instance.m_posX;
         pos.y = __instance.m_posY;
         pos.z = __instance.m_posZ;
         if (UniqueTrees.instance.Trees.TryGetValue(pos, out array))
         {
             info.m_minScale = array[(int)TreeProperty.MinScale];
             info.m_maxScale = array[(int)TreeProperty.MaxScale];
             info.m_minBrightness = array[(int)TreeProperty.MinBrightness];
             info.m_maxBrightness = array[(int)TreeProperty.MaxBrightness];
             info.m_createRuining = array[(int)TreeProperty.CreateRuining] == 0f ? false : true;
             info.m_lodRenderDistance = array[(int)TreeProperty.LodRenderDistance];
             __result = info;
             return false;
         }
         __result = info;
         return true;
     }
 }

I must be doing something wrong somewhere because I get this error:

InvalidProgramException: Invalid IL code in (wrapper dynamic-method) TreeInstance.get_Info_Patch1(object):IL_0007: call 0x00000001

I tried a few variations, but it just doesn't work... again. I don't really know much about programming except the little I've taught myself, so it's probably something fairly obvious. For example, could it be that Harmony just doesn't support patching get_Property methods? Or maybe I can't use the __instance parameter when working with structs? Anyway, if you know how to resolve this issue, it would be amazing. Thanks again for sharing this amazing library with the modding community.

ps. When I try to compile Harmony in VS 2015 it tells me there is an error in MethodCopier, cannot cast from MemberInfo to EventInfo or something along those lines. Intellisense tells me the cast is redundant and the error goes away if I remove the cast. I thought maybe this was the problem so I'm currently working with a dll version just to be sure but it doesnt fix the issue. Still, I thought I'd mention that ;)

[Unity][Android][Mono] Cannot find instruction for 4 for prefix method

Method for patch (Type "coa"):

	protected void w(cnn acrk, cnz acrl, cny acrm)
	{
		int acsd = 0;
		if (acrl != null)
		{
			acsd = acrl.e.c;
		}
		else if (null != acrk)
		{
			acsd = acrk.a;
		}
		if (acrm.h > 0)
		{
			int n = this.a.ac.n;
			this.a.ac.n -= acrm.h;
			this.a.OnDamage(acrl, n, this.a.ac.n);
			if (this.a.gz())
			{
				Scene.mainSceneStar.m(acrk, acrm.e, 0, this.a.af.bn);
			}
			if (this.ag(acsd, false))
			{
				hn<dl>.a.a(cvc.a(this.a.e), acrm.e, this.a.h(), acrm.b, acrm.k, acrm.c);
			}
			if (this.a.ac.n <= 0)
			{
				this.a.hg(acrk);
				if (acrl != null)
				{
					this.a.OnDie(acrl, false);
					return;
				}
			}
		}
		if (acrm.i > 0)
		{
			if (this.a.ac.bf)
			{
				(this.a.ac as cpi).em -= acrm.i;
			}
			else
			{
				if (!this.a.ac.cz)
				{
					return;
				}
				(this.a.ac as cpf).em -= acrm.i;
			}
		}
		if (acrm.j > 0 && this.a.ac.n > 0)
		{
			this.a.ac.n += acrm.j;
			if (this.a.gz())
			{
				Scene.mainSceneStar.m(acrk, 0, acrm.j, false);
			}
			if (this.ag(acsd, true))
			{
				hn<dl>.a.a(cvc.a(this.a.e), -acrm.j, this.a.h(), acrm.b, false, false);
			}
		}
		if (acrl != null)
		{
			if (acrl.d != null)
			{
				acrl.d.c(acrm.f);
			}
		}
	}

The prefix:

		public static void w(cnn acrk, cnz acrl, cny acrm)
		{
			Debug.Log("[MY] Patch!");
		}

Patching:

  	MethodBase patcher= harmonyInstance.GetMethod("Patch", BindingFlags.Instance | BindingFlags.Public);
  	MethodInfo original = typeof(coa).GetMethod("w", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  	MethodInfo prefixTarget = typeof(My).GetMethod("w", BindingFlags.Static | BindingFlags.Public);
  	ConstructorInfo constructor = harmonyMethod.GetConstructor(new Type[]
  	{
  		typeof(MethodInfo)
  	});
  	Debug.Log("[MY] 5");
  	object prefix = constructor.Invoke(new object[]
  	{
  		prefixTarget
  	});
12-16 18:01:02.842 I/Unity   (3798): Exception: Cannot find instruction for 4
12-16 18:01:02.842 I/Unity   (3798):   at Harmony.ILCopying.MethodBodyReader.GetInstruction (Int32 offset) [0x00000] in <filename unknown>:0 
12-16 18:01:02.842 I/Unity   (3798):   at Harmony.ILCopying.MethodBodyReader.ResolveBranches () [0x00000] in <filename unknown>:0 
12-16 18:01:02.842 I/Unity   (3798):   at Harmony.ILCopying.MethodCopier..ctor (System.Reflection.MethodBase fromMethod, System.Reflection.Emit.DynamicMethod toDynamicMethod, System.Reflection.Emit.LocalBuilder[] existingVariables) [0x00000] in <filename unknown>:0 
12-16 18:01:02.842 I/Unity   (3798):   at Harmony.MethodPatcher.CreatePatchedMethod (System.Reflection.MethodBase original, System.Collections.Generic.List`1 prefixes, System.Collections.Generic.List`1 postfixes, System.Collections.Generic.List`1 transpilers) [0x00000] in <filename unknown>:0 
12-16 18:01:02.842 I/Unity   (3798):   at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo) [0x00000] in <filename unknown>:0 
12-16 18:01:02.842 I/Unity   (3798):   at Harmony.PatchProcessor.Patch () [0x00000] in <fil

Assembly-CSharp.zip

Is impossible to describe method containing ref or out argument using HarmonyPatchAttribute

By Ref argument type defined as typeof(SomeType).MakeByRefType()

For example:

public static bool TryParse(string s, out Int32 result);
new Type[] { typeof(string), typeof(Int32).MakeByRefType() }

But it will not work with attributes, because it wants constant expression:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type

Error trying to patch base virtual method

I am trying to patch a base virtual method, but the following error occurs when I do so:

System.NotSupportedException: Incorrect MethodAttributes or CallingConventions for DynamicMethod. Only public, static and standard are supported
   by System.Reflection.Emit.DynamicMethod.CheckConsistency(MethodAttributes attributes, CallingConventions callingConvention)
   by System.Reflection.Emit.DynamicMethod.Init(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] signature, Type owner, Module m, Boolean skipVisibility, Boolean transparentMethod, StackCrawlMark& stackMark)
   by System.Reflection.Emit.DynamicMethod..ctor(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type owner, Boolean skipVisibility)
   by Harmony.DynamicTools.CreateDynamicMethod(MethodBase original, String suffix)
   by Harmony.MethodPatcher.CreatePatchedMethod(MethodBase original, List`1 prefixes, List`1 postfixes, List`1 transpilers)
   by Harmony.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo)
   by Harmony.PatchProcessor.Patch()
   by Harmony.HarmonyInstance.<PatchAll>b__6_0(Type type)
   by Harmony.CollectionExtensions.Do[T](IEnumerable`1 sequence, Action`1 action)
   by Harmony.HarmonyInstance.PatchAll(Assembly assembly)
   by Entoarox.FurnitureAnywhere.FurnitureAnywhereMod.Entry(IModHelper helper)
   by StardewModdingAPI.Program.LoadMods(IModMetadata[] mods, JsonHelper jsonHelper, SContentManager contentManager) in C:\source\_Stardew\SMAPI\src\SMAPI\Program.cs:line 808

(Please excuse any translation errors in the error message, I had to translate the error message to english so it might not be 100% correct)

The patch code is as follows:

    [HarmonyPatch(typeof(GameLocation))]
    [HarmonyPatch("isCollidingPosition")]
    [HarmonyPatch(new Type[] { typeof(Microsoft.Xna.Framework.Rectangle), typeof(xTile.Dimensions.Rectangle), typeof(bool), typeof(int), typeof(bool), typeof(Character), typeof(bool), typeof(bool), typeof(bool) })]
    static class LocationPatch
    {
        public static bool Prefix(ref bool __return, GameLocation __instance, Microsoft.Xna.Framework.Rectangle position)
        {
            foreach (AnywhereFurniture current in __instance.objects.Values.Select(a => a as AnywhereFurniture))
                if (current.furniture_type != 12 && current.getBoundingBox(current.tileLocation).Intersects(position))
                {
                    __return = true;
                    return true;
                }
            return false;
        }
    }

I am probably making some mistake here on my end, but the wiki has been useless in helping solve this, so I am stuck.

HarmonyInstance.DEBUG log incomplete: manually added local variables missing

If you use a Transpiler with an ILGenerator parameter and then add local variables through ILGenerator.DeclareLocal those variables do not show in the method header block in the debug log.
The log only shows the original parameters.

PATCHING Foo(Bar)
L_0000: Local var #0 Int32
L_0000: Local var #1 Baz
L_0000: ...
...
L_...: stloc 2

Opcodes will reference the locals, they're just not part of the header.

Generic Classes Bug

Hi. The following attached C# console program illustrates a bug in the processing of generic classes in C#.

The program was compiled and tested from the most recent source release of Harmony, using Visual Studio Enterprise 2017.

Program.zip

Harmony should support MS .NET and not only Mono. (was: Error trying to override int getter)

Target class:

public sealed class VisualFind
{
    private int _waitForElementsTimeout;

    public int WaitOnElementsTimeout
    {
      get
      {
        return this._waitForElementsTimeout;
      }
      set
      {
        this._waitForElementsTimeout = value;
    }
}

Patcher:

internal class HarmonyPatcher
{
        private static readonly Logger _Log = LogManager.GetCurrentClassLogger();

        public static HarmonyInstance Initialize()
        {
            _Log.Info($"Initializing harmony patches");
            HarmonyInstance harmony = HarmonyInstance.Create("com.test");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
            return harmony;
        }
}

Patch:

[HarmonyPatch(typeof(VisualFind))]
[HarmonyPatch("get_WaitOnElementsTimeout")]
public class VisualFindPatch
{
        private static readonly Logger _Log = LogManager.GetCurrentClassLogger();

        [HarmonyPrefix]
        static void BeforeGet()
        {

        }
}

I get the following error:

2017-02-06 14:18:45.0133 INFO Test.Ui.Patches.HarmonyPatcher.Initialize(Main.cs:19)   Initializing harmony patches
2017-02-06 14:18:45.0653 FATAL Test.Ui.Utility.Test.BaseTest.Initialize(BaseTest.cs:114) System.NotSupportedException: Wrong MethodAttributes or CallingConventions for DynamicMethod. Only public, static, standard supported
   at System.Reflection.Emit.DynamicMethod.CheckConsistency(MethodAttributes attributes, CallingConventions callingConvention)
   at System.Reflection.Emit.DynamicMethod.Init(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] signature, Type owner, Module m, Boolean skipVisibility, Boolean transparentMethod, StackCrawlMark& stackMark)
   at System.Reflection.Emit.DynamicMethod..ctor(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type owner, Boolean skipVisibility)
   at Harmony.DynamicTools.CreateDynamicMethod(MethodBase original, String suffix) in C:\repo\Harmony\Harmony\Tools\DynamicTools.cs:line 22
   at Harmony.MethodPatcher.CreatePatchedMethod(MethodBase original, List`1 prefixes, List`1 postfixes, List`1 processors) in C:\repo\Harmony\Harmony\MethodPatcher.cs:line 51
   at Harmony.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo) in C:\repo\Harmony\Harmony\PatchFunctions.cs:line 70
   at Harmony.PatchProcessor.Patch() in C:\repo\Harmony\Harmony\PatchProcessor.cs:line 57
   at Harmony.HarmonyInstance.<PatchAll>b__5_0(Type type) in C:\repo\Harmony\Harmony\HarmonyInstance.cs:line 60
   at Harmony.CollectionExtensions.Do[T](IEnumerable`1 sequence, Action`1 action) in C:\repo\Harmony\Harmony\Tools\Extensions.cs:line 45
   at Harmony.HarmonyInstance.PatchAll(Assembly assembly) in C:\repo\Harmony\Harmony\HarmonyInstance.cs:line 53
   at Test.Ui.Patches.HarmonyPatcher.Initialize() in C:\repo\Test.Ui\Patches\Main.cs:line 21

To my knowledge, it should be working properly. What am I missing?

I have also tried changing the signature to static bool BeforeGetWait and static bool AfterGetWait, with same behavior and error message.

Publish on NuGet

Have you published Harmony on NuGet? That would make it simpler to add Harmony to an existing project..

Doesn't patch properly?

I'm trying to compile a test executable with this code:

using System;
using System.Reflection;
using Harmony;

public class Testing {
    public void Hello() {
        Console.WriteLine("Hello world!");
    }
}

namespace HarmonyTest {
    class MainClass {
        public static void Main() {
            var h = HarmonyInstance.Create("abc.abc.bc");
            var original = typeof(Testing).GetMethod("Hello");
            var prefix = typeof(Patch).GetMethod("Prefix");
            var postfix = typeof(Patch).GetMethod("Prefix");

            h.Patch(original, new HarmonyMethod(prefix), new HarmonyMethod(postfix));

            var t = new Testing();
            t.Hello();
        }
    }

    class Patch {
        static void Prefix() {
            Console.WriteLine("ABC");
        }
    }
}

the output is

Hello world!

instead of the expected

ABC
Hello world!
ABC

Problem compiling Transpilers.cs with Visual Studio 2017

Using Visual Studio 2017 Enterprise, when attempting to compile Transpilers.cs, it issues he following error:

Severity Code Description Project File Line Suppression State
Error CS0252 Possible unintended reference comparison; to get a value comparison, cast the left hand side to type 'MethodBase' Harmony H:\joe.kraska\Artifacts\Mods\Harmony-master\Harmony\Transpilers.cs 13 Active

How to add an if check with a transpiler?

I'm very new to this so an example of this would be most helpful.
The code goes like this

protected virtual bool InputKeyProc()
{
    // things that should run normally
    if(speed > 0f) // new check
    {
        // things I want block sometimes
    }
    // more things that should run normally
}

And thanks for this great library of course, it's gonna make a lot of things easier.

AccessViolation with .NET 4.6

While trying to create a test case for a different issue, I stumbled upon the following:

Patching the System.Windows.Input.Keyboard.IsKeyDown() function crashes with an System.AccessViolationException in a simple console application:

at Harmony.ILCopying.Memory.WriteByte(Int64 memory, Byte value)
at Harmony.ILCopying.Memory.WriteBytes(Int64 memory, Byte[] values)
at Harmony.ILCopying.Memory.WriteJump(Int64 memory, Int64 destination)
at Harmony.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo)
at Harmony.PatchProcessor.Patch()
at Harmony.HarmonyInstance.b__6_0(Type type)
at Harmony.CollectionExtensions.Do[T](IEnumerable1 sequence, Action1 action)
at Harmony.HarmonyInstance.PatchAll(Assembly assembly)
at Console.App.Main() in C:\Users\Simon\Documents\Visual Studio 2015\Projects\Console\Console\App.cs:line 19
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

I've attached a project that exhibits this problem on my computer.
Console.zip

So far I've found out that this access violation does NOT happen when I change the following variables:

  • Patch a non framework class (does this happen because the PresentationCore.dll assembly is part of the GAC and likely ngen'd?)
  • Execute the same code from within an nunit unit test in the Debug configuration: nunit.zip

For the moment I've got some questions that need answering in order to provide a fix for this issue, such as:

  • (Assuming the retrieved address is correct) Why is this function's code page write protected? Is it because the underlying image is ngen'd?
  • Is it enough to simply mess with VirtualProtect to change the corresponding pages to be writable?
  • Why does it only happen in some environments? The one provided by the nunit runner shouldn't differ as far as the .NET framework is concerned as both projects are targeting the same framework

I'll report back once I've made some progress, but any input is appreciated!

Discussion on a new field injection feature

Hi,

I am thinking of adding more argument injections to patch methods. Currently, we have

  • original method arguments (by exact name and type)
  • __instance to get to this for non-static methods
  • __state to create a local variable holding state between prefix and postfix
  • __result to access the original methods result value

What often happens is that you want to access private fields of the original methods class but it adds a lot of overhead in form of performance and code to your patch methods.

So my proposal is that you can add arguments to the patch methods that match the type and name of a field and Harmony will inject those for you.

Example:

class Foo
{
	private List<String> aList;
	
	private void SomeMethod(String foo)
	{
		aList.Add(foo);
		// ...
	}
}

could be patched with

[HarmonyPatch(typeof(Foo))]
[HarmonyPatch("SomeMethod")]
static class FooPatch
{
	static void Prefix(ref String foo, List<String> aList)
	{
		// ...
	}
}

Some reflections:

  • Naming could be an issue. I don't want to overcomplicate things with a required name prefix and I assume giving the original method argument names that are the same as its fields will cause conflicts anyway, or are there edge cases?
  • Are there other cases of injections that are useful?

Anyone care to discuss this?

support for general purpose param object[]

i'm integrating harmony with jurrassic javascript runtime intepreter and i stuck at problem

for example original method is test(int a, string b)

but i would like to patch it with testPrefix(object[] o) so o[0]=a and o[1]=b

but its not possible currently in harmony

NullReferenceException thrown inside Harmony.PatchTools.GetPatches when processors parameter invalid

NullReferenceException: Object reference not set to an instance of an object
  at Harmony.PatchTools.GetPatches (System.Type patchType, System.Reflection.MethodBase original, System.Reflection.MethodInfo& prefix, System.Reflection.MethodInfo& postfix, System.Reflection.MethodInfo& processors) [0x00000] in <filename unknown>:0 
  at Harmony.PatchProcessor.ProcessType () [0x00000] in <filename unknown>:0 
  at Harmony.PatchProcessor..ctor (Harmony.HarmonyInstance instance, System.Type type, Harmony.HarmonyMethod attributes) [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.<PatchAll>b__0 (System.Type type) [0x00000] in <filename unknown>:0 
  at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0 
  at HarmonyKSPTesting.HarmonyBootstrapper+<Start>d__0.MoveNext () [0x00062] in d:\For New Computer\KSPCustomMods\Harmony-master\HarmonyKSPTesting\HarmonyBootstrapper.cs:29 
  at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00020] in C:\buildslave\unity\build\Runtime\Export\Coroutines.cs:17 

Patch:

    [HarmonyPatch(typeof(MainMenu))]
    [HarmonyPatch("startGame")]
    class MainMenu_StartGameBreaker
    {
        private static int _counter = 0;

        private static bool Prefix(MainMenu __instance)
        {
            Debug.LogWarning("User clicked on start game! Cancelled. " + _counter++);
            return false;
        }
    }

This NullRef actually occurs inside PatchProcessor.PatchType when it passes a field from a null HarmonyPatch to GetPatches as an out parameter. The moment any type of assignment occurs, the exception is thrown.

In the above case, the "infix" field of PatchProcessor can sometimes be null which results in this error. Here's a little reproduction:

[KSPAddon(KSPAddon.Startup.MainMenu, true)]
class WhenIsNREThrown : MonoBehaviour
{
    private void Awake()
    {
        HarmonyMethod patch = null;

        FindOutWhatHappens(out patch.method); // exception is not thrown here
    }

    private static void FindOutWhatHappens(out MethodInfo infix)
    {
        infix = null; // exception is thrown here, regardless of assigned value
    }
}

Integration with Unity

It is not really an Issue...

I am considering modernization of Unity.Interception and looking at different libraries for technologies and inspirations.

I was wondering if Harmony could be used for that purpose and if it has any advantages over other implementations?

InvalidProgramException

I am trying to make a mod for a different game using Unity and have run into problems with using Harmony. Whenever I try to patch a method, whether in the unity editor or in the game, I get the error:
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) TargetScript:DoMyThing_Patch2 (object): IL_0000: call 0x00000001

The test code I am using is as follows.
PatcherScript.cs

using UnityEngine;
using System.Reflection;
using Harmony;

public class PatcherScript : MonoBehaviour {

	void OnGUI()
    {
        if (GUI.Button(new Rect(0,0,100,200), "GO!"))
        {
            DoHack();
        }
    }
    void DoHack()
    {
        HarmonyInstance.DEBUG = true;
        var harm = HarmonyInstance.Create("testInst");
        MethodInfo target = typeof(TargetScript).GetMethod("DoMyThing", BindingFlags.Public | BindingFlags.Instance);
        MethodInfo M_prefix = GetType().GetMethod("prefix");
        MethodInfo M_suffix = GetType().GetMethod("suffix");
        Debug.LogFormat("{0}   ;   {1}    ;    {2}", target, M_prefix, M_suffix);
        harm.Patch(target, new HarmonyMethod(M_prefix), new HarmonyMethod(M_suffix));
    }
    public void prefix()
    {
        Debug.Log("Injected Code Prefix");
    }
    public void suffix()
    {
        Debug.Log("Injected Code Suffix");
    }
}

(the debug logs in here confirm all of my method references are correct: Void DoMyThing() ; Void prefix() ; Void suffix())

TargetScript.cs

using UnityEngine;

public class TargetScript : MonoBehaviour
{

    void OnGUI()
    {
        if (GUI.Button(new Rect(300,300,100,200), "Target"))
        {
            DoMyThing();
        }
    }
    public void DoMyThing()
    {
        Debug.Log("Button Clicked!");
    }
}

The full error is:

InvalidProgramException: Invalid IL code in (wrapper dynamic-method) TargetScript:DoMyThing_Patch2 (object): IL_0000: call      0x00000001


System.RuntimeMethodHandle.GetFunctionPointer () (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/RuntimeMethodHandle.cs:96)
Harmony.ILCopying.Memory.GetMethodStart (System.Reflection.MethodBase method)
Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo)
Harmony.PatchProcessor.Patch ()
Harmony.HarmonyInstance.Patch (System.Reflection.MethodBase original, Harmony.HarmonyMethod prefix, Harmony.HarmonyMethod postfix, Harmony.HarmonyMethod transpiler)
PatcherScript.DoHack () (at Assets/Scripts/PatcherScript.cs:22)
PatcherScript.OnGUI () (at Assets/Scripts/PatcherScript.cs:11)

and the log output is:

PATCHING TargetScript Void DoMyThing()
L_0000: call Void prefix()
L_0005: ldstr "Button Clicked!"
L_000a: call Void Log(System.Object)
L_000f: br Label #0
L_0014: Label #0
L_0014: call Void suffix()
L_0019: ret
DONE

Using Unity 5.4.5f1 Personal

Implement Reverse Patching [was: Disable Harmony for specific calls]

I'm working on some parallelization, and I need to be able to call the patched function from within the patch itself without creating a recursive loop of patches. Can this be done? Or would it require a complete bypass of the original function? If that's requried, can we make an annotation for function bypassing as an alternative to transpilling?

Failed to work for Net45 on VS2017 + Win10

I'm following the Wiki to get start Harmony but failed with following exception (full log at end):

Unhandled Exception: System.NotSupportedException: Wrong MethodAttributes or CallingConventions for DynamicMethod. Only public, static, standard supported

Environment:

  • Harmony 1.0.9.1
  • Windows 10
  • VS 2017
  • .NET Framework 4.5

Code:

using System;
using System.Globalization;
using System.Reflection;
using System.Threading;
using Harmony;

namespace TestHarmony
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
            Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
            var harmony = HarmonyInstance.Create("test");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
        }

        [HarmonyPatch(typeof(String))]
        [HarmonyPatch("IndexOf")]
        [HarmonyPatch(new Type[] { typeof(char), typeof(int) })]
        class Patch
        {
            static void Prefix()
            {
            }
        }
    }
}

Full console error message:

Unhandled Exception: System.NotSupportedException: Wrong MethodAttributes or CallingConventions for DynamicMethod. Only public, static, standard supported
   at System.Reflection.Emit.DynamicMethod.CheckConsistency(MethodAttributes attributes, CallingConventions callingConvention)
   at System.Reflection.Emit.DynamicMethod.Init(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] signature, Type owner, Module m, Boolean skipVisibility, Boolean transparentMethod, StackCrawlMark& stackMark)
   at System.Reflection.Emit.DynamicMethod..ctor(String name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type owner, Boolean skipVisibility)
   at Harmony.DynamicTools.CreateDynamicMethod(MethodBase original, String suffix) in C:\Users\Admin\Source\Repos\Harmony\Harmony\Tools\DynamicTools.cs:line 23
   at Harmony.MethodPatcher.CreatePatchedMethod(MethodBase original, List`1 prefixes, List`1 postfixes, List`1 transpilers) in C:\Users\Admin\Source\Repos\Harmony\Harmony\MethodPatcher.cs:line 23
   at Harmony.PatchFunctions.UpdateWrapper(MethodBase original, PatchInfo patchInfo) in C:\Users\Admin\Source\Repos\Harmony\Harmony\PatchFunctions.cs:line 59
   at Harmony.PatchProcessor.Patch() in C:\Users\Admin\Source\Repos\Harmony\Harmony\PatchProcessor.cs:line 63
   at Harmony.HarmonyInstance.<PatchAll>b__6_0(Type type) in C:\Users\Admin\Source\Repos\Harmony\Harmony\HarmonyInstance.cs:line 68
   at Harmony.CollectionExtensions.Do[T](IEnumerable`1 sequence, Action`1 action) in C:\Users\Admin\Source\Repos\Harmony\Harmony\Tools\Extensions.cs:line 45
   at Harmony.HarmonyInstance.PatchAll(Assembly assembly) in C:\Users\Admin\Source\Repos\Harmony\Harmony\HarmonyInstance.cs:line 61
   at TestHarmony.Program.Main(String[] args) in D:\VsProjects\TestHarmony\TestHarmony\Program.cs:line 20

Bug in ILInstruction Class

There is a bug in the ILInstruction class's GetCodeInstruction() function. The function does not directly copy the operand to the given code instruction.

The code instruction object is initialized with the following statement on line 24:

var instr = new CodeInstruction(opcode, argument);

As you can see, it passes 'argument' in as the operand. 'argument' is not guaranteed to contain the correct operand value in all codes paths because operand is a public member variable that is modified outside of this class.

An example of where this blows up is in MethodCopier.cs in ResolveBranches() line 120. ILInstruction's public member variable 'operand' is directly written to - this will cause problem later for anyone wanting to convert the ILInstructions into CodeInstructions.

Request: CodeInstruction generator

It would be really nice to have a function that took a Method and returned a list/enumberable of CodeInstructions for use in Transpilers.

Request: Type level functionality

This may be a special interest request, but...

It'd be nice to be able to annotate an entire type, and then have all of the pre-patches/post-patches apply to every function (maybe allow excluding the constructor). Naturally, these patches would be highly limited in what variables they have access to, but it'd reduce a lot of the boilerplate code I'm using to place objects under concurrency wrappers.

Question about Harmony and generic class methods.

Hi, I am a modder in Stardew Valley, and have come across the situation where I need to patch a method in a class, where the class itself has 2 generic arguments that I need to work with in that same method.

How, if at all, could I do such a thing with harmony?

TargetMethod Documentation incorrect?

Your documented example for HarmonyTargetMethod uses MethodInfo:

static MethodInfo TargetMethod()
static MethodInfo TargetMethod(HarmonyInstance instance)
// or
[HarmonyTargetMethod]
static MethodInfo CalculateMethod(...)

Should it not be listed as return a MethodBase object? Allows for ConstructorInfo in addition to MethodInfo.

System.InvalidProgramException: Invalid IL code

Hi

I am trying to mod Cities Skylines and came across your lib last week replacing Detours.
Just testing I am prefixing a simple function however a System.InvalidProgramException exeption is thrown.

I assume this is something similar to a previous issue... #3 as L_0750 is a function call with refs?
L_0750: callvirt Void GetNodeBuilding(UInt16, NetNode ByRef, BuildingInfo ByRef, Single ByRef)

Thanks in advance,
-Adrian

System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) NetNode:CalculateNode_Patch1 (object,uint16): IL_0750: callvirt  0x0000011b
 at (wrapper managed-to-native) System.RuntimeMethodHandle:GetFunctionPointer (intptr)
 at System.RuntimeMethodHandle.GetFunctionPointer () [0x00000] in <filename unknown>:0 
 at Harmony.ILCopying.Memory.GetMethodStart (System.Reflection.MethodBase method) [0x00000] in <filename unknown>:0 
 at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo) [0x00000] in <filename unknown>:0 
 at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0 
 at Harmony.HarmonyInstance.Patch (System.Reflection.MethodBase original, Harmony.HarmonyMethod prefix, Harmony.HarmonyMethod postfix, Harmony.HarmonyMethod transpiler) [0x00000] in <filename unknown>:0 
 at TollRoad.Node.Hook () [0x00000] in <filename unknown>:0 
PATCHING NetNode Void CalculateNode(UInt16)
L_0000: Local var 0 NetManager
L_0000: Local var 1 UnityEngine.Vector3
L_0000: Local var 2 System.Int32
L_0000: Local var 3 System.Int32
L_0000: Local var 4 System.Boolean
L_0000: Local var 5 System.Boolean
L_0000: Local var 6 System.Boolean
L_0000: Local var 7 System.Boolean
L_0000: Local var 8 System.Boolean
L_0000: Local var 9 System.Boolean
L_0000: Local var 10 System.Boolean
L_0000: Local var 11 System.Boolean
L_0000: Local var 12 System.Boolean
L_0000: Local var 13 System.Boolean
L_0000: Local var 14 System.Boolean
L_0000: Local var 15 System.Boolean
L_0000: Local var 16 NetInfo
L_0000: Local var 17 System.Int32
L_0000: Local var 18 System.Int32
L_0000: Local var 19 NetInfo
L_0000: Local var 20 System.Single
L_0000: Local var 21 System.Int32
L_0000: Local var 22 System.UInt16
L_0000: Local var 23 NetInfo
L_0000: Local var 24 System.Single
L_0000: Local var 25 System.Boolean
L_0000: Local var 26 System.Int32
L_0000: Local var 27 System.UInt16
L_0000: Local var 28 System.UInt16
L_0000: Local var 29 System.UInt16
L_0000: Local var 30 UnityEngine.Vector3
L_0000: Local var 31 UnityEngine.Vector3
L_0000: Local var 32 System.Boolean
L_0000: Local var 33 UnityEngine.Vector3
L_0000: Local var 34 NetInfo
L_0000: Local var 35 ItemClass
L_0000: Local var 36 System.Int32
L_0000: Local var 37 System.Int32
L_0000: Local var 38 System.Int32
L_0000: Local var 39 System.UInt16
L_0000: Local var 40 NetInfo
L_0000: Local var 41 ItemClass
L_0000: Local var 42 System.Boolean
L_0000: Local var 43 UnityEngine.Vector3
L_0000: Local var 44 System.Single
L_0000: Local var 45 System.Single
L_0000: Local var 46 UnityEngine.Vector3
L_0000: Local var 47 UnityEngine.Vector3
L_0000: Local var 48 System.Single
L_0000: Local var 49 NetNode+Flags
L_0000: Local var 50 BuildingInfo
L_0000: Local var 51 System.Single
L_0000: call Void CalculateNode_Prefix()
L_0005: ldarg.0
L_0006: ldfld NetNode+Flags m_flags
L_000b: brtrue Label 2
L_0010: br Label 0
L_0015: Label 2
L_0015: call NetManager get_instance()
L_001a: stloc.0
L_001b: call Vector3 get_zero()
L_0020: stloc.1
L_0021: ldc.i4.0
L_0022: stloc.2
L_0023: ldc.i4.0
L_0024: stloc.3
L_0025: ldc.i4.0
L_0026: stloc.s 4 (System.Boolean)
L_0028: ldc.i4.0
L_0029: stloc.s 5 (System.Boolean)
L_002b: ldc.i4.0
L_002c: stloc.s 6 (System.Boolean)
L_002e: ldc.i4.0
L_002f: stloc.s 7 (System.Boolean)
L_0031: ldc.i4.0
L_0032: stloc.s 8 (System.Boolean)
L_0034: ldc.i4.0
L_0035: stloc.s 9 (System.Boolean)
L_0037: ldc.i4.0
L_0038: stloc.s 10 (System.Boolean)
L_003a: ldc.i4.0
L_003b: stloc.s 11 (System.Boolean)
L_003d: ldc.i4.0
L_003e: stloc.s 12 (System.Boolean)
L_0040: ldc.i4.1
L_0041: stloc.s 13 (System.Boolean)
L_0043: ldc.i4.1
L_0044: stloc.s 14 (System.Boolean)
L_0046: call TerrainManager get_instance()
L_004b: ldarg.0
L_004c: ldfld UnityEngine.Vector3 m_position
L_0051: callvirt Boolean HasDetailMapping(Vector3)
L_0056: stloc.s 15 (System.Boolean)
L_0058: ldnull
L_0059: stloc.s 16 (NetInfo)
L_005b: ldc.i4.0
L_005c: stloc.s 17 (System.Int32)
L_005e: ldc.i4.0
L_005f: stloc.s 18 (System.Int32)
L_0061: ldnull
L_0062: stloc.s 19 (NetInfo)
L_0064: ldc.r4 -1E+07
L_0069: stloc.s 20 (System.Single)
L_006b: ldc.i4.0
L_006c: stloc.s 21 (System.Int32)
L_006e: br Label 3
L_0073: Label 6
L_0073: ldarg.0
L_0074: ldloc.s 21 (System.Int32)
L_0076: call UInt16 GetSegment(Int32)
L_007b: stloc.s 22 (System.UInt16)
L_007d: ldloc.s 22 (System.UInt16)
L_007f: brfalse Label 4
L_0084: ldloc.0
L_0085: ldfld Array16`1[NetSegment] m_segments
L_008a: ldfld NetSegment[] m_buffer
L_008f: ldloc.s 22 (System.UInt16)
L_0091: ldelema NetSegment
L_0096: call NetInfo get_Info()
L_009b: stloc.s 23 (NetInfo)
L_009d: ldloc.s 23 (NetInfo)
L_009f: ldfld NetAI m_netAI
L_00a4: ldloc.s 22 (System.UInt16)
L_00a6: ldloc.0
L_00a7: ldfld Array16`1[NetSegment] m_segments
L_00ac: ldfld NetSegment[] m_buffer
L_00b1: ldloc.s 22 (System.UInt16)
L_00b3: ldelema NetSegment
L_00b8: callvirt Single GetNodeInfoPriority(UInt16, NetSegment ByRef)
L_00bd: stloc.s 24 (System.Single)
L_00bf: ldloc.s 24 (System.Single)
L_00c1: ldloc.s 20 (System.Single)
L_00c3: ble.un Label 5
L_00c8: ldloc.s 23 (NetInfo)
L_00ca: stloc.s 19 (NetInfo)
L_00cc: ldloc.s 24 (System.Single)
L_00ce: stloc.s 20 (System.Single)
L_00d0: Label 4
L_00d0: Label 5
L_00d0: ldloc.s 21 (System.Int32)
L_00d2: ldc.i4.1
L_00d3: add
L_00d4: stloc.s 21 (System.Int32)
L_00d6: Label 3
L_00d6: ldloc.s 21 (System.Int32)
L_00d8: ldc.i4.8
L_00d9: blt Label 6
L_00de: ldloc.s 19 (NetInfo)
L_00e0: brtrue Label 7
L_00e5: ldarg.0
L_00e6: call NetInfo get_Info()
L_00eb: stloc.s 19 (NetInfo)
L_00ed: Label 7
L_00ed: ldloc.s 19 (NetInfo)
L_00ef: ldarg.0
L_00f0: call NetInfo get_Info()
L_00f5: beq Label 8
L_00fa: ldarg.0
L_00fb: ldloc.s 19 (NetInfo)
L_00fd: call Void set_Info(NetInfo)
L_0102: call NetManager get_instance()
L_0107: ldarg.1
L_0108: callvirt Void UpdateNodeColors(UInt16)
L_010d: ldloc.s 19 (NetInfo)
L_010f: ldfld System.Boolean m_canDisable
L_0114: brtrue Label 9
L_0119: ldarg.0
L_011a: dup
L_011b: ldfld NetNode+Flags m_flags
L_0120: ldc.i4.s -9
L_0122: and
L_0123: stfld NetNode+Flags m_flags
L_0128: Label 8
L_0128: Label 9
L_0128: ldc.i4.0
L_0129: stloc.s 25 (System.Boolean)
L_012b: ldc.i4.0
L_012c: stloc.s 26 (System.Int32)
L_012e: br Label 10
L_0133: Label 58
L_0133: ldarg.0
L_0134: ldloc.s 26 (System.Int32)
L_0136: call UInt16 GetSegment(Int32)
L_013b: stloc.s 27 (System.UInt16)
L_013d: ldloc.s 27 (System.UInt16)
L_013f: brfalse Label 11
L_0144: ldloc.2
L_0145: ldc.i4.1
L_0146: add
L_0147: stloc.2
L_0148: ldloc.0
L_0149: ldfld Array16`1[NetSegment] m_segments
L_014e: ldfld NetSegment[] m_buffer
L_0153: ldloc.s 27 (System.UInt16)
L_0155: ldelema NetSegment
L_015a: ldfld System.UInt16 m_startNode
L_015f: stloc.s 28 (System.UInt16)
L_0161: ldloc.0
L_0162: ldfld Array16`1[NetSegment] m_segments
L_0167: ldfld NetSegment[] m_buffer
L_016c: ldloc.s 27 (System.UInt16)
L_016e: ldelema NetSegment
L_0173: ldfld System.UInt16 m_endNode
L_0178: stloc.s 29 (System.UInt16)
L_017a: ldloc.0
L_017b: ldfld Array16`1[NetSegment] m_segments
L_0180: ldfld NetSegment[] m_buffer
L_0185: ldloc.s 27 (System.UInt16)
L_0187: ldelema NetSegment
L_018c: ldfld UnityEngine.Vector3 m_startDirection
L_0191: stloc.s 30 (UnityEngine.Vector3)
L_0193: ldloc.0
L_0194: ldfld Array16`1[NetSegment] m_segments
L_0199: ldfld NetSegment[] m_buffer
L_019e: ldloc.s 27 (System.UInt16)
L_01a0: ldelema NetSegment
L_01a5: ldfld UnityEngine.Vector3 m_endDirection
L_01aa: stloc.s 31 (UnityEngine.Vector3)
L_01ac: ldarg.1
L_01ad: ldloc.s 28 (System.UInt16)
L_01af: ceq
L_01b1: stloc.s 32 (System.Boolean)
L_01b3: ldloc.s 32 (System.Boolean)
L_01b5: brfalse Label 12
L_01ba: ldloc.s 30 (UnityEngine.Vector3)
L_01bc: br Label 13
L_01c1: Label 12
L_01c1: ldloc.s 31 (UnityEngine.Vector3)
L_01c3: Label 13
L_01c3: stloc.s 33 (UnityEngine.Vector3)
L_01c5: ldloc.0
L_01c6: ldfld Array16`1[NetSegment] m_segments
L_01cb: ldfld NetSegment[] m_buffer
L_01d0: ldloc.s 27 (System.UInt16)
L_01d2: ldelema NetSegment
L_01d7: call NetInfo get_Info()
L_01dc: stloc.s 34 (NetInfo)
L_01de: ldloc.s 34 (NetInfo)
L_01e0: callvirt ItemClass GetConnectionClass()
L_01e5: stloc.s 35 (ItemClass)
L_01e7: ldloc.s 34 (NetInfo)
L_01e9: ldfld NetAI m_netAI
L_01ee: callvirt Boolean CanModify()
L_01f3: brtrue Label 14
L_01f8: ldc.i4.0
L_01f9: stloc.s 14 (System.Boolean)
L_01fb: Label 14
L_01fb: ldloc.s 32 (System.Boolean)
L_01fd: ldloc.0
L_01fe: ldfld Array16`1[NetSegment] m_segments
L_0203: ldfld NetSegment[] m_buffer
L_0208: ldloc.s 27 (System.UInt16)
L_020a: ldelema NetSegment
L_020f: ldfld NetSegment+Flags m_flags
L_0214: ldc.i4.s 16
L_0216: and
L_0217: ldc.i4.0
L_0218: ceq
L_021a: ldc.i4.0
L_021b: ceq
L_021d: bne.un Label 15
L_0222: ldloc.s 34 (NetInfo)
L_0224: ldfld System.Int32 m_backwardVehicleLaneCount
L_0229: stloc.s 36 (System.Int32)
L_022b: ldloc.s 34 (NetInfo)
L_022d: ldfld System.Int32 m_forwardVehicleLaneCount
L_0232: stloc.s 37 (System.Int32)
L_0234: br Label 16
L_0239: Label 15
L_0239: ldloc.s 34 (NetInfo)
L_023b: ldfld System.Int32 m_forwardVehicleLaneCount
L_0240: stloc.s 36 (System.Int32)
L_0242: ldloc.s 34 (NetInfo)
L_0244: ldfld System.Int32 m_backwardVehicleLaneCount
L_0249: stloc.s 37 (System.Int32)
L_024b: Label 16
L_024b: ldloc.s 26 (System.Int32)
L_024d: ldc.i4.1
L_024e: add
L_024f: stloc.s 38 (System.Int32)
L_0251: br Label 17
L_0256: Label 33
L_0256: ldarg.0
L_0257: ldloc.s 38 (System.Int32)
L_0259: call UInt16 GetSegment(Int32)
L_025e: stloc.s 39 (System.UInt16)
L_0260: ldloc.s 39 (System.UInt16)
L_0262: brfalse Label 18
L_0267: ldloc.0
L_0268: ldfld Array16`1[NetSegment] m_segments
L_026d: ldfld NetSegment[] m_buffer
L_0272: ldloc.s 39 (System.UInt16)
L_0274: ldelema NetSegment
L_0279: call NetInfo get_Info()
L_027e: stloc.s 40 (NetInfo)
L_0280: ldloc.s 40 (NetInfo)
L_0282: callvirt ItemClass GetConnectionClass()
L_0287: stloc.s 41 (ItemClass)
L_0289: ldloc.s 41 (ItemClass)
L_028b: ldfld ItemClass+Service m_service
L_0290: ldloc.s 35 (ItemClass)
L_0292: ldfld ItemClass+Service m_service
L_0297: beq Label 19
L_029c: ldloc.s 40 (NetInfo)
L_029e: ldfld NetInfo+ConnectGroup m_nodeConnectGroups
L_02a3: ldloc.s 34 (NetInfo)
L_02a5: ldfld NetInfo+ConnectGroup m_connectGroup
L_02aa: and
L_02ab: brtrue Label 20
L_02b0: ldloc.s 34 (NetInfo)
L_02b2: ldfld NetInfo+ConnectGroup m_nodeConnectGroups
L_02b7: ldloc.s 40 (NetInfo)
L_02b9: ldfld NetInfo+ConnectGroup m_connectGroup
L_02be: and
L_02bf: brfalse Label 21
L_02c4: Label 19
L_02c4: Label 20
L_02c4: ldarg.1
L_02c5: ldloc.0
L_02c6: ldfld Array16`1[NetSegment] m_segments
L_02cb: ldfld NetSegment[] m_buffer
L_02d0: ldloc.s 39 (System.UInt16)
L_02d2: ldelema NetSegment
L_02d7: ldfld System.UInt16 m_startNode
L_02dc: ceq
L_02de: stloc.s 42 (System.Boolean)
L_02e0: ldloc.s 42 (System.Boolean)
L_02e2: brfalse Label 22
L_02e7: ldloc.0
L_02e8: ldfld Array16`1[NetSegment] m_segments
L_02ed: ldfld NetSegment[] m_buffer
L_02f2: ldloc.s 39 (System.UInt16)
L_02f4: ldelema NetSegment
L_02f9: ldfld UnityEngine.Vector3 m_startDirection
L_02fe: br Label 23
L_0303: Label 22
L_0303: ldloc.0
L_0304: ldfld Array16`1[NetSegment] m_segments
L_0309: ldfld NetSegment[] m_buffer
L_030e: ldloc.s 39 (System.UInt16)
L_0310: ldelema NetSegment
L_0315: ldfld UnityEngine.Vector3 m_endDirection
L_031a: Label 23
L_031a: stloc.s 43 (UnityEngine.Vector3)
L_031c: ldloca.s 33 (UnityEngine.Vector3)
L_031e: ldfld System.Single x
L_0323: ldloca.s 43 (UnityEngine.Vector3)
L_0325: ldfld System.Single x
L_032a: mul
L_032b: ldloca.s 33 (UnityEngine.Vector3)
L_032d: ldfld System.Single z
L_0332: ldloca.s 43 (UnityEngine.Vector3)
L_0334: ldfld System.Single z
L_0339: mul
L_033a: add
L_033b: stloc.s 44 (System.Single)
L_033d: ldc.r4 0.01
L_0342: ldloc.s 34 (NetInfo)
L_0344: ldfld System.Single m_maxTurnAngleCos
L_0349: ldloc.s 40 (NetInfo)
L_034b: ldfld System.Single m_maxTurnAngleCos
L_0350: call Single Min(Single, Single)
L_0355: sub
L_0356: stloc.s 45 (System.Single)
L_0358: ldloc.s 44 (System.Single)
L_035a: ldloc.s 45 (System.Single)
L_035c: bge.un Label 24
L_0361: ldloc.s 34 (NetInfo)
L_0363: ldfld System.Boolean m_requireDirectRenderers
L_0368: brfalse Label 25
L_036d: ldloc.s 34 (NetInfo)
L_036f: ldfld NetInfo+ConnectGroup m_nodeConnectGroups
L_0374: brfalse Label 26
L_0379: ldloc.s 34 (NetInfo)
L_037b: ldfld NetInfo+ConnectGroup m_nodeConnectGroups
L_0380: ldloc.s 40 (NetInfo)
L_0382: ldfld NetInfo+ConnectGroup m_connectGroup
L_0387: and
L_0388: brtrue Label 27
L_038d: Label 25
L_038d: ldloc.s 40 (NetInfo)
L_038f: ldfld System.Boolean m_requireDirectRenderers
L_0394: brfalse Label 28
L_0399: ldloc.s 40 (NetInfo)
L_039b: ldfld NetInfo+ConnectGroup m_nodeConnectGroups
L_03a0: brfalse Label 29
L_03a5: ldloc.s 40 (NetInfo)
L_03a7: ldfld NetInfo+ConnectGroup m_nodeConnectGroups
L_03ac: ldloc.s 34 (NetInfo)
L_03ae: ldfld NetInfo+ConnectGroup m_connectGroup
L_03b3: and
L_03b4: brfalse Label 30
L_03b9: Label 26
L_03b9: Label 27
L_03b9: Label 29
L_03b9: ldloc.3
L_03ba: ldc.i4.1
L_03bb: add
L_03bc: stloc.3
L_03bd: Label 28
L_03bd: Label 30
L_03bd: br Label 31
L_03c2: Label 24
L_03c2: ldc.i4.1
L_03c3: stloc.s 9 (System.Boolean)
L_03c5: Label 31
L_03c5: br Label 32
L_03ca: Label 21
L_03ca: ldc.i4.1
L_03cb: stloc.s 9 (System.Boolean)
L_03cd: Label 18
L_03cd: Label 32
L_03cd: ldloc.s 38 (System.Int32)
L_03cf: ldc.i4.1
L_03d0: add
L_03d1: stloc.s 38 (System.Int32)
L_03d3: Label 17
L_03d3: ldloc.s 38 (System.Int32)
L_03d5: ldc.i4.8
L_03d6: blt Label 33
L_03db: ldloc.0
L_03dc: ldfld Array16`1[NetNode] m_nodes
L_03e1: ldfld NetNode[] m_buffer
L_03e6: ldloc.s 28 (System.UInt16)
L_03e8: ldelema NetNode
L_03ed: ldfld System.Byte m_elevation
L_03f2: ldloc.0
L_03f3: ldfld Array16`1[NetNode] m_nodes
L_03f8: ldfld NetNode[] m_buffer
L_03fd: ldloc.s 29 (System.UInt16)
L_03ff: ldelema NetNode
L_0404: ldfld System.Byte m_elevation
L_0409: beq Label 34
L_040e: ldc.i4.0
L_040f: stloc.s 13 (System.Boolean)
L_0411: Label 34
L_0411: ldloc.0
L_0412: ldfld Array16`1[NetNode] m_nodes
L_0417: ldfld NetNode[] m_buffer
L_041c: ldloc.s 28 (System.UInt16)
L_041e: ldelema NetNode
L_0423: ldfld UnityEngine.Vector3 m_position
L_0428: stloc.s 46 (UnityEngine.Vector3)
L_042a: ldloc.0
L_042b: ldfld Array16`1[NetNode] m_nodes
L_0430: ldfld NetNode[] m_buffer
L_0435: ldloc.s 29 (System.UInt16)
L_0437: ldelema NetNode
L_043c: ldfld UnityEngine.Vector3 m_position
L_0441: stloc.s 47 (UnityEngine.Vector3)
L_0443: ldloc.s 32 (System.Boolean)
L_0445: brfalse Label 35
L_044a: ldloc.s 15 (System.Boolean)
L_044c: brfalse Label 36
L_0451: call TerrainManager get_instance()
L_0456: ldloc.s 47 (UnityEngine.Vector3)
L_0458: callvirt Boolean HasDetailMapping(Vector3)
L_045d: br Label 37
L_0462: Label 36
L_0462: ldc.i4.0
L_0463: Label 37
L_0463: stloc.s 15 (System.Boolean)
L_0465: br Label 38
L_046a: Label 35
L_046a: ldloc.s 15 (System.Boolean)
L_046c: brfalse Label 39
L_0471: call TerrainManager get_instance()
L_0476: ldloc.s 46 (UnityEngine.Vector3)
L_0478: callvirt Boolean HasDetailMapping(Vector3)
L_047d: br Label 40
L_0482: Label 39
L_0482: ldc.i4.0
L_0483: Label 40
L_0483: stloc.s 15 (System.Boolean)
L_0485: Label 38
L_0485: ldloc.s 46 (UnityEngine.Vector3)
L_0487: ldloc.s 30 (UnityEngine.Vector3)
L_0489: ldloc.s 47 (UnityEngine.Vector3)
L_048b: ldloc.s 31 (UnityEngine.Vector3)
L_048d: call Boolean IsStraight(Vector3, Vector3, Vector3, Vector3)
L_0492: brfalse Label 41
L_0497: ldc.i4.1
L_0498: stloc.s 11 (System.Boolean)
L_049a: br Label 42
L_049f: Label 41
L_049f: ldc.i4.1
L_04a0: stloc.s 10 (System.Boolean)
L_04a2: Label 42
L_04a2: ldloc.2
L_04a3: ldc.i4.1
L_04a4: bne.un Label 43
L_04a9: ldloc.s 32 (System.Boolean)
L_04ab: stloc.s 25 (System.Boolean)
L_04ad: ldloc.s 33 (UnityEngine.Vector3)
L_04af: stloc.1
L_04b0: ldc.i4.1
L_04b1: stloc.s 4 (System.Boolean)
L_04b3: br Label 44
L_04b8: Label 43
L_04b8: ldloc.2
L_04b9: ldc.i4.2
L_04ba: bne.un Label 45
L_04bf: ldloc.s 34 (NetInfo)
L_04c1: ldloc.s 16 (NetInfo)
L_04c3: callvirt Boolean IsCombatible(NetInfo)
L_04c8: brfalse Label 46
L_04cd: ldloc.s 34 (NetInfo)
L_04cf: ldloc.s 19 (NetInfo)
L_04d1: callvirt Boolean IsCombatible(NetInfo)
L_04d6: brfalse Label 47
L_04db: ldloc.s 36 (System.Int32)
L_04dd: ldc.i4.0
L_04de: ceq
L_04e0: ldc.i4.0
L_04e1: ceq
L_04e3: ldloc.s 18 (System.Int32)
L_04e5: ldc.i4.0
L_04e6: ceq
L_04e8: ldc.i4.0
L_04e9: ceq
L_04eb: bne.un Label 48
L_04f0: ldloc.s 37 (System.Int32)
L_04f2: ldc.i4.0
L_04f3: ceq
L_04f5: ldc.i4.0
L_04f6: ceq
L_04f8: ldloc.s 17 (System.Int32)
L_04fa: ldc.i4.0
L_04fb: ceq
L_04fd: ldc.i4.0
L_04fe: ceq
L_0500: bne.un Label 49
L_0505: ldloca.s 1 (UnityEngine.Vector3)
L_0507: ldfld System.Single x
L_050c: ldloca.s 33 (UnityEngine.Vector3)
L_050e: ldfld System.Single x
L_0513: mul
L_0514: ldloca.s 1 (UnityEngine.Vector3)
L_0516: ldfld System.Single z
L_051b: ldloca.s 33 (UnityEngine.Vector3)
L_051d: ldfld System.Single z
L_0522: mul
L_0523: add
L_0524: stloc.s 48 (System.Single)
L_0526: ldloc.s 36 (System.Int32)
L_0528: ldloc.s 18 (System.Int32)
L_052a: bne.un Label 50
L_052f: ldloc.s 37 (System.Int32)
L_0531: ldloc.s 17 (System.Int32)
L_0533: beq Label 51
L_0538: Label 50
L_0538: ldloc.s 36 (System.Int32)
L_053a: ldloc.s 37 (System.Int32)
L_053c: ble Label 52
L_0541: ldc.i4.1
L_0542: stloc.s 7 (System.Boolean)
L_0544: ldc.i4.1
L_0545: stloc.s 6 (System.Boolean)
L_0547: br Label 53
L_054c: Label 52
L_054c: ldc.i4.1
L_054d: stloc.s 8 (System.Boolean)
L_054f: ldc.i4.1
L_0550: stloc.s 6 (System.Boolean)
L_0552: Label 53
L_0552: br Label 54
L_0557: Label 51
L_0557: ldloc.s 48 (System.Single)
L_0559: ldc.r4 -0.999
L_055e: bge.un Label 55
L_0563: ldc.i4.1
L_0564: stloc.s 5 (System.Boolean)
L_0566: br Label 56
L_056b: Label 55
L_056b: ldc.i4.1
L_056c: stloc.s 6 (System.Boolean)
L_056e: Label 54
L_056e: Label 56
L_056e: ldloc.s 32 (System.Boolean)
L_0570: ldloc.s 25 (System.Boolean)
L_0572: ceq
L_0574: ldc.i4.0
L_0575: ceq
L_0577: stloc.s 12 (System.Boolean)
L_0579: br Label 57
L_057e: Label 45
L_057e: Label 46
L_057e: Label 47
L_057e: Label 48
L_057e: Label 49
L_057e: ldc.i4.1
L_057f: stloc.s 9 (System.Boolean)
L_0581: Label 44
L_0581: Label 57
L_0581: ldloc.s 34 (NetInfo)
L_0583: stloc.s 16 (NetInfo)
L_0585: ldloc.s 36 (System.Int32)
L_0587: stloc.s 17 (System.Int32)
L_0589: ldloc.s 37 (System.Int32)
L_058b: stloc.s 18 (System.Int32)
L_058d: Label 11
L_058d: ldloc.s 26 (System.Int32)
L_058f: ldc.i4.1
L_0590: add
L_0591: stloc.s 26 (System.Int32)
L_0593: Label 10
L_0593: ldloc.s 26 (System.Int32)
L_0595: ldc.i4.8
L_0596: blt Label 58
L_059b: ldloc.s 19 (NetInfo)
L_059d: ldfld System.Boolean m_enableMiddleNodes
L_05a2: ldc.i4.0
L_05a3: ceq
L_05a5: ldloc.s 5 (System.Boolean)
L_05a7: and
L_05a8: brfalse Label 59
L_05ad: ldc.i4.1
L_05ae: stloc.s 6 (System.Boolean)
L_05b0: Label 59
L_05b0: ldloc.s 19 (NetInfo)
L_05b2: ldfld System.Boolean m_enableBendingNodes
L_05b7: ldc.i4.0
L_05b8: ceq
L_05ba: ldloc.s 6 (System.Boolean)
L_05bc: and
L_05bd: brfalse Label 60
L_05c2: ldc.i4.1
L_05c3: stloc.s 9 (System.Boolean)
L_05c5: Label 60
L_05c5: ldloc.s 19 (NetInfo)
L_05c7: ldfld System.Boolean m_requireContinuous
L_05cc: brfalse Label 61
L_05d1: ldarg.0
L_05d2: ldfld NetNode+Flags m_flags
L_05d7: ldc.i4 512
L_05dc: and
L_05dd: brfalse Label 62
L_05e2: ldc.i4.1
L_05e3: stloc.s 9 (System.Boolean)
L_05e5: Label 61
L_05e5: Label 62
L_05e5: ldloc.s 19 (NetInfo)
L_05e7: ldfld System.Boolean m_requireContinuous
L_05ec: brfalse Label 63
L_05f1: ldloc.s 12 (System.Boolean)
L_05f3: brtrue Label 64
L_05f8: ldloc.s 5 (System.Boolean)
L_05fa: brtrue Label 65
L_05ff: ldloc.s 6 (System.Boolean)
L_0601: brfalse Label 66
L_0606: Label 65
L_0606: ldc.i4.1
L_0607: stloc.s 9 (System.Boolean)
L_0609: Label 63
L_0609: Label 64
L_0609: Label 66
L_0609: ldarg.0
L_060a: ldfld NetNode+Flags m_flags
L_060f: ldc.i4 -1610613233
L_0614: and
L_0615: stloc.s 49 (NetNode+Flags)
L_0617: ldloc.s 49 (NetNode+Flags)
L_0619: ldc.i4 1024
L_061e: and
L_061f: brfalse Label 67
L_0624: ldarg.0
L_0625: ldloc.s 49 (NetNode+Flags)
L_0627: stfld NetNode+Flags m_flags
L_062c: br Label 68
L_0631: Label 67
L_0631: ldloc.s 9 (System.Boolean)
L_0633: brfalse Label 69
L_0638: ldarg.0
L_0639: ldloc.s 49 (NetNode+Flags)
L_063b: ldc.i4 128
L_0640: or
L_0641: stfld NetNode+Flags m_flags
L_0646: br Label 70
L_064b: Label 69
L_064b: ldloc.s 6 (System.Boolean)
L_064d: brfalse Label 71
L_0652: ldloc.s 7 (System.Boolean)
L_0654: brfalse Label 72
L_0659: ldloc.s 49 (NetNode+Flags)
L_065b: ldc.i4 536870912
L_0660: or
L_0661: stloc.s 49 (NetNode+Flags)
L_0663: Label 72
L_0663: ldloc.s 8 (System.Boolean)
L_0665: brfalse Label 73
L_066a: ldloc.s 49 (NetNode+Flags)
L_066c: ldc.i4 1073741824
L_0671: or
L_0672: stloc.s 49 (NetNode+Flags)
L_0674: Label 73
L_0674: ldarg.0
L_0675: ldloc.s 49 (NetNode+Flags)
L_0677: ldc.i4.s 64
L_0679: or
L_067a: stfld NetNode+Flags m_flags
L_067f: br Label 74
L_0684: Label 71
L_0684: ldloc.s 5 (System.Boolean)
L_0686: brfalse Label 75
L_068b: ldloc.s 10 (System.Boolean)
L_068d: brfalse Label 76
L_0692: ldloc.s 11 (System.Boolean)
L_0694: brtrue Label 77
L_0699: Label 76
L_0699: ldarg.0
L_069a: ldfld NetNode+Flags m_flags
L_069f: ldc.i4 4608
L_06a4: and
L_06a5: brtrue Label 78
L_06aa: ldloc.s 13 (System.Boolean)
L_06ac: brfalse Label 79
L_06b1: ldloc.s 14 (System.Boolean)
L_06b3: brfalse Label 80
L_06b8: ldloc.s 49 (NetNode+Flags)
L_06ba: ldc.i4 256
L_06bf: or
L_06c0: stloc.s 49 (NetNode+Flags)
L_06c2: Label 77
L_06c2: Label 78
L_06c2: Label 79
L_06c2: Label 80
L_06c2: ldarg.0
L_06c3: ldloc.s 49 (NetNode+Flags)
L_06c5: ldc.i4.s 32
L_06c7: or
L_06c8: stfld NetNode+Flags m_flags
L_06cd: br Label 81
L_06d2: Label 75
L_06d2: ldloc.s 4 (System.Boolean)
L_06d4: brfalse Label 82
L_06d9: ldarg.0
L_06da: ldfld NetNode+Flags m_flags
L_06df: ldc.i4 512
L_06e4: and
L_06e5: brtrue Label 83
L_06ea: ldloc.s 13 (System.Boolean)
L_06ec: brfalse Label 84
L_06f1: ldloc.s 14 (System.Boolean)
L_06f3: brfalse Label 85
L_06f8: ldloc.s 19 (NetInfo)
L_06fa: ldfld System.Boolean m_enableMiddleNodes
L_06ff: brfalse Label 86
L_0704: ldloc.s 49 (NetNode+Flags)
L_0706: ldc.i4 256
L_070b: or
L_070c: stloc.s 49 (NetNode+Flags)
L_070e: Label 83
L_070e: Label 84
L_070e: Label 85
L_070e: Label 86
L_070e: ldarg.0
L_070f: ldloc.s 49 (NetNode+Flags)
L_0711: ldc.i4.s 16
L_0713: or
L_0714: stfld NetNode+Flags m_flags
L_0719: Label 68
L_0719: Label 70
L_0719: Label 74
L_0719: Label 81
L_0719: Label 82
L_0719: ldarg.0
L_071a: ldloc.s 15 (System.Boolean)
L_071c: brtrue Label 87
L_0721: ldloc.s 19 (NetInfo)
L_0723: ldfld System.Boolean m_requireSurfaceMaps
L_0728: brtrue Label 88
L_072d: Label 87
L_072d: ldc.i4.0
L_072e: br Label 89
L_0733: Label 88
L_0733: ldc.i4.s 64
L_0735: Label 89
L_0735: conv.u1
L_0736: stfld System.Byte m_heightOffset
L_073b: ldarg.0
L_073c: ldloc.3
L_073d: conv.u1
L_073e: stfld System.Byte m_connectCount
L_0743: ldloc.s 19 (NetInfo)
L_0745: ldfld NetAI m_netAI
L_074a: ldarg.1
L_074b: ldarg.0
L_074c: ldloca.s 50 (BuildingInfo)
L_074e: ldloca.s 51 (System.Single)
L_0750: callvirt Void GetNodeBuilding(UInt16, NetNode ByRef, BuildingInfo ByRef, Single ByRef)
L_0755: ldarg.0
L_0756: ldarg.1
L_0757: ldloc.s 50 (BuildingInfo)
L_0759: ldloc.s 51 (System.Single)
L_075b: call Void UpdateBuilding(UInt16, BuildingInfo, Single)
L_0760: br Label 0
L_0765: Label 0
L_0765: ret

DONE

Problem with ref arguments

The following happened in rimworld.. so .net 3.5, mono.. you know it

This is the start of the original method

private static Pawn DoGenerateNewNakedPawn(ref PawnGenerationRequest request, out string error, bool ignoreScenarioRequirements)
{
    error = null;
    Pawn pawn = (Pawn)ThingMaker.MakeThing(request.KindDef.race, null);

The patcher:

harmony.Patch(AccessTools.Method(typeof(PawnGenerator), "DoGenerateNewNakedPawn"), null, new HarmonyMethod(typeof(HarmonyPatches), "TestPostFix"));

Postfix:

public static void TestPostFix(ref PawnGenerationRequest request)
{
    Log.Message("race: " + (request.KindDef.race != null).ToString());
}

Even tough race has a valid value in every call of the original method, in the postfix it never has

If I remove the ref from the post fix parameter, it causes the following exception on startup:

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for AlienRace.HarmonyPatches ---> System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) Verse.PawnGenerator:DoGenerateNewNakedPawn_Patch2 (Verse.PawnGenerationRequest&,string&,bool): IL_03c0: call      0x000000e7

  at (wrapper managed-to-native) System.RuntimeMethodHandle:GetFunctionPointer (intptr)
  at System.RuntimeMethodHandle.GetFunctionPointer () [0x00000] in <filename unknown>:0 
  at Harmony.ILCopying.MonoInternals.GetCodeInfo (System.Reflection.MethodBase method, System.Int64& start) [0x00000] in <filename unknown>:0 
  at Harmony.ILCopying.Memory.GetMethodStart (System.Reflection.MethodBase method) [0x00000] in <filename unknown>:0 
  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo) [0x00000] in <filename unknown>:0 
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0 
  at Harmony.HarmonyInstance.Patch (System.Reflection.MethodBase original, Harmony.HarmonyMethod prefix, Harmony.HarmonyMethod postfix, Harmony.HarmonyProcessor infix) [0x00000] in <filename unknown>:0 
  at AlienRace.HarmonyPatches..cctor () [0x00000] in <filename unknown>:0 
  --- End of inner exception stack trace ---
  at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
  at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0 
  at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0 
  at Verse.PlayDataLoader.<DoPlayLoad>m__6F8 () [0x00000] in <filename unknown>:0 
  at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0 

System.AccessViolationException

After I manually patch a type, I can't instantiate a new object of that type. Getting a System.AccessViolationException.

var a = new Test();

var harmony = HarmonyInstance.Create("com.company.project.product");
var original = typeof(Test).GetMethod("Call");
var prefix = typeof(Patch).GetMethod("Before");
var postfix = typeof(Patch).GetMethod("After");
harmony.Patch(original, new HarmonyMethod(prefix), new HarmonyMethod(postfix));

var b = new Test();

var y = a.Call();
var z = b.Call();

having the var b = new Test(); initialization right after var a works.

Is this expected behaviour, or am I missing something?

Thanks

Patch unity editor itself

Trying to patch Frame method change value 2.2f to 102f, its prints Patched, but value stays 2.2f

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Harmony;
using UnityEditor;
using UnityEngine;

public class Tools : Editor
{

    [MenuItem("Tools/Patch")]
    public static void Patch()
    {
        
        
        //var assembly = Assembly.Load(Assembly.GetExecutingAssembly().GetReferencedAssemblies()[4]);
        var assembly = typeof(Editor).Assembly;
        var bf = BindingFlags.NonPublic | BindingFlags.Instance;
        var e = (MethodInfo)assembly.GetType("UnityEditor.LookDevView").GetMember("Frame", bf)[0];
        MethodInfo transpiler = typeof(Tools).GetMethod("Transpiler", BindingFlags.Static | BindingFlags.Public);
        var harmonyInstance = HarmonyInstance.Create("1");
        harmonyInstance.Patch(e, null, null, new HarmonyMethod(transpiler));
    }
    public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instr)
    {
        foreach (CodeInstruction a in instr)
        {
            if (a.operand is float && (float)a.operand == 2.2f)
            {
                a.operand = 102f;
                Debug.Log("Patched");
            }
            yield return a;
        }
    }
}

(Mac/Linux) RimWorld crashes with Postfix on Window.PreClose

In one of my mods I was patching Window.PreClose to catch the event when a trade window closed (just patching Dialog_Trade.PreClose was not enough as it was not triggered when the user hit "esc" key to close the window)

    [HarmonyPatch(typeof(Window), "PreClose")]
    static class Patch_Window_PreClose
    {
        // Before closing any window
        static void Postfix(Window __instance)
        {
            Type type = __instance.GetType();
            if (type == typeof(Dialog_Trade))
            {
                TradeUtil.ReclaimApparel();
            }
        }
    }

I first tried removing the body to see if that fixed anything, it did not. So the simple act of adding this patch will cause RW to crash without any notice.

To reproduce, launch RW and try to close any window (even the console window) and it will cause the game to crash.

Just for clarification: This works fine on Windows 10. It does not work on Mac or Ubuntu.

Add a "only override original method" option for prefix methods

Hi,

I've been experimenting with Harmony in a mod for Cities:Skylines as a replacement for another method swizzling/detouring/replacement technique.

It seems to work pretty well, but I have some concerns regarding multiple mods attempting to prefix the same method, since a lot of the scenarios involve skipping the original method. As the return value for prefix methods skips all remaining prefix methods from executing, this would be somewhat problematic, since it prevents other mods from running.

Would it be possible to expose a separate flag that only skips execution of the original method, but allow other prefix methods to run?

Get parameters with removed names.

Is there a way currently to get parameters from original method in prefix if their names were removed (by obfuscator for example)? Perhaps just getting an array of objects that contains all parameters in order or specific parameters by index?
I ended up just patching in some names as I only needed this for debugging but in some cases you actually might need that stuff.

MethodBodyReader.GetInstructions throws NRE

Modding RimWorld, this code throws a Null Reference Exception:

    [HarmonyPatch(typeof(PathFinder), "FindPath", new Type[] { typeof(IntVec3), typeof(LocalTargetInfo), typeof(TraverseParms), typeof(PathEndMode) })]
    static class ReversePatch
    {
        [HarmonyTranspiler]
        static IEnumerable<CodeInstruction> copyCode(MethodBase original, IEnumerable<CodeInstruction> instructions)
        {
            MethodBase mb = typeof(FunctionHolders).GetMethod("original");
			
			List<CodeInstruction> codeInstructions = new List<CodeInstruction>();
            foreach (ILInstruction Ili in MethodBodyReader.GetInstructions(method))
            {
                codeInstructions.Add(Ili.GetCodeInstruction());
            }
            return codeInstructions;
        }
	}
System.NullReferenceException: Object reference not set to an instance of an object
  at Harmony.ILCopying.MethodBodyReader.ReadOperand (Harmony.ILCopying.ILInstruction instruction) [0x00000] in <filename unknown>:0 
  at Harmony.ILCopying.MethodBodyReader.ReadInstructions () [0x00000] in <filename unknown>:0 
  at Harmony.ILCopying.MethodBodyReader.GetInstructions (System.Reflection.MethodBase method) [0x00000] in <filename unknown>:0 
  at _9thFingerThreadingMod.ReversePatch.copyCode (System.Reflection.MethodBase original, IEnumerable`1 instructions) [0x00000] in <filename unknown>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)

I don't have a debugger available atm, but I'm pretty sure it is because the "variables" variable is never set (as the DeclareVariables function is never called, and there's no generator to even use) when calling MethodBodyReader.GetInstructions, and that variable is referenced in two cases inside of ReadOperand: case OperandType.ShortInlineVar and case OperandType.InlineVar:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.