Harmony basics
This tutorial is intended to guide you through using Harmony to patch the game methods and more.
Adding to Code
To make your code work with existing mechaincs many times you will need to add your own code to a built in object. This can also let your mod act differently depending on if the player has specific other mods installed as well such as modifying recipes to use mod items if available or use vanilla items otherwise.
Harmony lets you do this by creating Patches. There are a few valid ways to set up your patches but a reliable one is to use attributes.
Each Patch will be its own class like so:
With one or more static methods that add or act on the target code.
Remember, your methods need to be static!
The class attribute lets Harmony know "what" to use the code on. typeof(BlockCreator) means it is trying to change how one of the methods of the BlockCreator works. That method is CanBuildBlock() and Harmony searches for it by string so make sure it matches the method name!
The method attribute lets Harmony know "when" to use the code in the patch. There are [HarmonyPrepare], [HarmonyPrefix], [HarmonyPostfix], [HarmonyTranspiler], and [HarmonyTargetMethod] each executing at a different time or in a different way.
[HarmonyPrepare]
This lets you check some things before the patch takes place to see if you even want to change something.
This method expects you to return false
to indicate Harmony should skip this patch.
[HarmonyPrefix]
This code takes place just before the original method happens and can be used to skip it all together.
This means you can replace a method entirely by having a Prefix do stuff then skip the original method by returning false
If the original method needed to return some value the patched method can accept the ref "Type" __result
arguement by reference. This will make it look like the original method is returning whatever __result
is set to
You can also use this method to store values that can be accessed in the Postfix if you have one via __state
. You can use ref
or out
to set the __state
to whatever value or type you want. If you need to store more data then you need to make your own object to pass on to the Postfix
[HarmonyPostfix]
This code takes place right after the original method. It can take in the __result
from the original method and use it or even change it.
One benefit of Postfixs is that they always run. No matter where the original method escaped its code from it will go to the Postfix right after.
One quirk of Postfixs is that they can either change __result
by ref or can return a value that would work the same way, but if they return a result it has to be the same Type as whatever the first arguement in the method is!
[HarmonyTranspiler]
Transpilers are a much more advanced way to get your own code to run. It is a bit out of the scope of a tutorial here so I will link to this article
To give a simplified overview: Instead of writing code to be executed, you are looking at all of the IL instructions that a chunk of code represents and doing some of your own instructions whenever you think the right time is.
There are also wonderful tools out there that help show you what your IL code will look like from C# code snippets. Try checking out LINQPad
[HarmonyTargetMethod]
TargetMethod is another powerful tool that lets you reuse your code and apply changes to multiple different methods. It can also be used to conditionally apply changes based on what the code finds.
TargetMethod must return a MethodBase type which points at which method your patch will apply to. Alternatively [HarmonyTargetMethods] can be used to apply the same patch logic to multiple methods as well though in this case it expects some IEnumerable collection of MethodBases.
Two different ways of doing this would be to iterate through, patching all methods:
or, affect a group of methods that you collect:
Valid Patch Rules
Your patch methods can accept a variety of arguements that let you peek into the code that is running or is about to run.
Each prefix and postfix can get all the parameters of the original method as well as the instance (if original method is not static) and the return value. In order to patch a method your patches need to follow the following principles when defining them:
A patch must be a static method
A prefix patch has a return type of void or bool
A postfix patch has a return type of void or the return signature must match the type of the first parameter (passthrough mode)
Patches can use a parameter named
__instance
to access the instance value if original method is not staticPatches can use a parameter named
__result
to access the returned value (prefixes get default value)Patches can use a parameter named
__state
to store information in the prefix method that can be accessed again in the postfix method. Think of it as a local variable. It can be any type and you are responsible to initialize its value in the prefixParameter names starting with three underscores, for example
___someField
, can be used to read and write (with 'ref') private fields on the instance that has the same name (minus the underscores)Patches can define only those parameters they want to access (no need to define all)
Patch parameters must use the exact same name and type as the original method (object is ok too)
Patches can either get parameters normally or by declaring any parameter ref (for manipulation)
To allow patch reusing, one can inject the original method by using a parameter named
__originalMethod
Using three underscores ___likeSo
to read variables is quite usefull
Transpilers have some other optional parameters:
A parameter of type ILGenerator that will be set to the current IL code generator
A parameter of type MethodBase that will be set to the current original method being patched
They must contain one parameter of type
IEnumerable<CodeInstruction>
that will be used to pass the IL codes to it
There are other ways to combine attributes and structure your patches. Try checking here if your mod starts getting complicated.
Applying your patches
To get your code actually working Harmony needs to be told to do all the work you have set up for it. One place to do that from is Start method of your Mod.
To do this make sure your file is already using HarmonyLib;
Then you need to create a new instance of Harmony with an id for your set of patches. A common structure for an id is "com.company.project.product" or for us "com.User.Project.Feature"
Then we tell the Harmony instance to patch everything it can find in our code. In older versions you need to tell Harmony to look for the currently running game with Assembly.GetExecutingAssembly()
to try and patch it but these days it will look there by default.
Remember, anything you do to load your mod you need to undo when you unload it to prevent strange errors and to keep the client stable. This also means you need to tell your Harmony instance to unpatch the things you changed.
In older versions you needed to Destroy()
the mod object when unloading but that is also handled gracefully these days.
Written by: Soggylithe 10/31/2020
Feel free to @Soggylithe in the RaftModding Discord if you have any questions.
Last updated