Hooks
Hooks (more formally known as events) are the main way to interact with Hollow Knight's code. We can use them to wait for an event to happen in game, then intercept data related to that event and trigger some code and potentially return different data to what we received.
There are 3 major type of hooks in the Modding API each of which will be explained below.
ModHooks
ModHooks are hooks that are built into the Modding API that allow us to interact will Hollow Knight code. To use a Modhook, you need to subscribe to the hook. For example:
public class MyFirstMod:Mod
{
// The method that will be called by Modding API when the game first opens
public override void Initialize()
{
// We subscribe to HeroUpdateHook which is an hook that is triggered when the 'Update' Function is called for the player (once every frame)
ModHooks.HeroUpdateHook += OnHeroUpdate;
}
//This event's method doesn't take any Parameters. normally the IDE can generate this function for you with the correct parameters
public void OnHeroUpdate()
{
//code to run
}
}
Note: Your IDE (Visual Studio Community/Jetbrains Rider) can generate this function for you with the correct parameters. To do this, see example video or type in
ModHooks.HeroUpdateHook += OnHeroUpdate;
, Then right click on the now red highlightedOnHeroUpdate
and click on the light bulb icon (called 'Quick actions and Refactoring') and choose 'Generate Method'.
There are many modhooks available to be used.
- Explanations for most common hooks can be found in ModHook Reference
- A list of all hooks can be found in the API Documentation
On Hooks
On Hooks are a type of hooks that are generated by MonoMod HookGen. This allows you to hook onto and replace any method in the games code. Any function (private or public) that the game's code has can be onHooked which makes the combination of Modhooks and OnHooks very useful while creating a mod. OnHooks allow you to do 2 main things
- Insert code before/after a method is run.
- Replace the games method with your code.
To subscribe to OnHook you'd first need to find a method. In this example we will be using HeroController.AddGeo(int amount)
to demonstrate. So to OnHook HeroController.AddGeo
we would do:
//We subscribe to the hook
On.HeroController.AddGeo += OnHCAddGeo;
private void OnHCAddGeo(On.HeroController.orig_AddGeo orig, HeroController self, int amount)
{
//code before original method is called
if (amount < 100)
{
//if amount of geo to be added is less than 100, return (and dont call original method)
return;
}
orig(self, amount); // call original method
//code after method is called
Log($"the amount of total geo is {self.geoCounter.playerData.geo}"
}
From the above example, the function generated contains an odd looking argument On.HeroController.orig_AddGeo
called orig
. This is a delegate that will allow us to call the original function if we wish.
The second argument HeroController self
is the object that the method is being called on. Since this method is not static, it is called on an instance of the class (in this case HeroController) which is then passed as an argument for us to use. Note that this argument will not be present for static methods.
The third argument int amount
is the normal argument the function takes.
To call the original method we would do: orig(self, amount)
. We call orig and pass in self and the rest of the arguments of the function (in this case int amount
). In the case the function has no other arguments we would just do orig(self)
. If the function had more arguments, we would pass those in too after self.
While using OnHooks we need to be careful of a few things
- If your goal is to insert code before/after a method has run, don't forget to call
orig(self)
or it might cause some unexpected behaviour especially for important functions like HeroController.Start and PlayerMakerFSM.Awake. - Unless necessary for the mod, dont replace the method with your own because if
orig(self)
is not called, other mods that also OnHooked this method will not be able to run their code. This is because the second mod's onHook will only be called when the first mod's onHook callsorig(self)
(i.e. if first mod never callsorig(self)
second mod's hook wont be called). If the vanilla game code is conflicting with your mod then by all means do replace the method but just make sure to keep this in mind. - If you want to replace a method, make sure to not call
orig(self)
. Also if you do this it is very likely you will encounter private fields and methods that you would like to access. To be able to do this, Reflection is the best way to do it.
For more information about OnHooks please refer to the OnHooks Page
Note: Your IDE (Visual Studio Community/Jetbrains Rider) can generate this function for you with the correct parameters. To do this, see example video or type in
On.HeroController.AddGeo += OnHCAddGeo;
, Then right click on the now red highlightedOnHCAwake
and click on the light bulb icon (called 'Quick actions and Refactoring') and choose 'Generate Method'.
Note: To be able to write OnHooks, you will need to import
MMHOOK_Assembly-CSharp.dll
andMMHOOK_PlayMaker.dll
from your managed folder.
IL Hooks
TODO IL Hooks
- simple explanation of what they are and what is IL
- when to use
- link to IL hook page
Todo General
- how use custom values from player data & language (@dandy)