EtG Modding Guide
Search…
⌃K

How to create a hook

Difficulty: 3/10 - 4/10
-

Good video by Kyle the Scientist: https://www.youtube.com/watch?v=f9KdczInvDM
Adding using System.Reflections; and using MonoMod.RuntimeDetour; to the top of your mod
Find dnSpy, and locate the class and method you want to hook
Keep note of the visibility settings and wherever it is static or not.
If the method is an IEnumerator you will have to read the Hooking Coroutines section.
In this case, we will Hook AcquirePassiveItem in the PlayerController class. Add using MonoMod.RuntimeDetour; using System.Reflection;at the top of your class. Now to create the hook. create a public Hook ExampleHook; variable and rename it however you want. (not required unless you plan on disposing of this hook later.)
To create the hook you write
ExampleHook = new Hook(typeof(PlayerController).GetMethod("AcquirePassiveItem", BindingFlags.Instance | BindingFlags.Public), typeof(Module).GetMethod("MethodThatIsHook", BindingFlags.Static | BindingFlags.NonPublic));
PlayerController is replaced with whatever class the method you are hooking is in, and the string in the GetMethod is replaced with the name of whatever method you are hooking in that class.
Binding flags specify whether or not the method is public, private, static, etc. so if it's private we use BindingFlags.NonPublic If the method was static we would use BindingFlags.Static. Next, we need to tell the hook where our custom method is that has the code we want to run. Inside the typeof we put our class that contains the method, then we get the method name and the binding flags. PLEASE NOTE the method must be static. It cannot be an instance. (there is technically a way to make it so that your method can be an instance, but we can't really figure it out lol, and there isn't any real advantage to it)
Now we create our hook method (As you can see in my screenshots, this method is not static, i am lazy and i forgot to fix, this wont work.
Inside the Action<> we need all the parameters the original method had, + the class it was in, name that orig. For the rest of the parameters, you just need whatever you put inside the action, but outside of it now. Now, let's say our method returned something, like a string, if we tried to return orig(self, item) it would give us an error right now, because that doesn't return a string.
So in this case, we need to use Func<> instead of Action.
It has everything the same as before, but the last thing inside the little <> brackets is what it returns. Now we just create whatever our code is.
Right now, the original code will never run. So if we want that then we need to call orig. calling orig will cause the original code to run, so if we want our code before the original then we call orig after our code, but if we want our code after the original code then we call orig before.

Hooking a Coroutine

Hooking a coroutine is very similar to a regular hook, with one key difference. How we call orig. When calling orig in an IEnumerator you must do it like this.
public static IEnumerator HandleCoopDeath(Func<PlayerController, bool, IEnumerator> orig, PlayerController self, bool ignoreCorpse)
{
IEnumerator origEnum = orig(self, ignoreCorpse);
while (origEnum.MoveNext()) yield return origEnum.Current;
}
This is due to the one frame delay that is present when switching between IEnumerators in a Coroutine.