Harmony Patching Guide

Harmony is a library that allows developers to alter the functionality of C# applications at runtime.

This is a simplified overview. For the full reference, see the official documentation.

What is Patching?

Patching is the act of altering an existing function at runtime.

As opposed to recompilation, patching allows developers to apply changes and fixes to a program that is already released. This can be done for small bug fixes, security updates or modding the program.

How to Create Patches?

Now that patching is familiar territory, we can move on to writing our first patches.

Note The following methods reflect a preferred style. Other valid approaches exist.

Patches can be located anywhere in the project. For convenience, patches will often be stored inside the Patches/ folder.

Next, create a class to hold the patches for each type:

[HarmonyPatch(typeof(SomeClass))]
class SomeClass_Patches
{
    // ...
}

Grouping patches this way keeps related code together, improving readability and maintainability.

Finally, add patch methods inside the class as needed:

[HarmonyPatch(typeof(SomeClass))]
class SomeClass_Patches
{
    [HarmonyPatch(nameof(SomeClass.MethodA)), HarmonyPrefix]
    private static void MethodA_Prefix()
    {
        // Custom prefix logic
    }

    [HarmonyPatch(nameof(SomeClass.MethodA)), HarmonyPostfix]
    private static void MethodA_Postfix()
    {
        // Custom postfix logic
    }
}

How to Apply Patches?

For patches to work, they need to be manually applied first. Harmony offers ways to apply patches automatically.

Note The following methods reflect a preferred style. Other valid approaches exist.

The first step is to create a new instance of Harmony:

static void ApplyPatches()
{
    var harmony = new HarmonyLib.Harmony("com.company.project.product");
}
Note The identifier passed to the instance must be unique to your project. It allows other developers to apply their patches before or after yours.

The simplest way is to call PatchAll():

static void ApplyPatches()
{
    var harmony = new HarmonyLib.Harmony("com.company.project.product");
    harmony.PatchAll();
}

This will automatically detect and apply all patches found in the project.

Although this method is useful, it can lead to issues when dealing with soft dependencies. Some types might not exist at runtime, which could lead to crashes.

A solution is to call PatchAll() while passing the class's type:

static void ApplyPatches()
{
    var harmony = new HarmonyLib.Harmony("com.company.project.product");

    harmony.PatchAll(typeof(Patches.SomeClass_Patches));
    harmony.PatchAll(typeof(Patches.OtherClass_Patches));

    if (Dependency.IsEnabled)
    {
        harmony.PatchAll(typeof(Patches.ModdedClass_Patches));
        harmony.PatchAll(typeof(Patches.ModdedClass2_Patches));
    }
}

Call this method to apply all patches. It is recommended to apply them during Awake().

Types of Patch

There are 3 types of patches that are available to developers:

Each offers a different use case, and should be used in their given cases.

Prefix

Prefixes are methods executed before the original method. They allow to run logic before the original method, or replace the method entirely.

As an example, we will simulate a survival game. We desire to add a "x5 damage weather", but the game doesn't offer a built-in solution.

To achieve this result, we will need to add the logic ourselves. After digging in the code, we find this method:

void DamageEntity(Entity entity, int damage) { /* ... */ }

It is called when the game wants to apply damage to an entity.

To add our logic, we will need to create the prefix patch:

[HarmonyPatch(nameof(DamageEntity)), HarmonyPrefix]
private static void DamageEntity_Prefix()
{
    Debug.Log("Custom Logic");
}

Each time an entity will be damaged, the game will print our message to the console. However, this doesn't solve our issue. The entities still take their normal amount of damage.

We will need to access and modify the arguments:

[HarmonyPatch(nameof(DamageEntity)), HarmonyPrefix]
private static void DamageEntity_Prefix(Entity entity, ref int damage)
{
    damage = damage * 5;
}

And we did it! Each time the game will damage an entity, our code will multiply the incoming damage by 5.

Alternatively, we might desire to ignore any attack weaker than 5.

We will need to tell Harmony to skip the original method:

[HarmonyPatch(nameof(DamageEntity)), HarmonyPrefix]
private static bool DamageEntity_Prefix(Entity entity, int damage)
{
    // Skip the original method
    if (damage < 5)
        return false;
    
    // Continue the call as normal
    return true;
}

Postfix

Postfixes are methods executed after the original method. They allow to run logic after the original method, or modify the method's output.

As an example, we will simulate a factory game. We want to add a random nickname before our machines' name, but the game doesn't offer a built-in solution.

To achieve this result, we will need to add the logic ourselves. After digging in the code, we find this method:

string GetName(Machine machine) { /* ... */ }

It is called when the game wants to display the name of the machine.

To add our logic, we will need to create the postfix patch:

[HarmonyPatch(nameof(GetName)), HarmonyPostfix]
private static void GetName_Postfix()
{
    Debug.Log("Custom Logic");
}

Each time the game displays the name of a machine, our message will be printed to the console. However, this doesn't solve our issue. The machines still display their generic name.

We will need to access and modify the arguments:

[HarmonyPatch(nameof(GetName)), HarmonyPostfix]
private static void GetName_Postfix(ref string __result)
{
    string[] names = new string[] { "Bob", "Alice", "Pablo", "Claude" };
    string randomName = names[Random.Range(0, names.Count)];

    __result = randomName + " (" + __result + ")";
}

And we did it! Each time the game will display the name of a machine, our code will add a random nickname to it.

Transpiler

Transpilers are methods that modifies the code of the original method. They allow to run complex logic within a method.

Warning Transpilers should only be used when simpler patching methods are insufficient. This method requires an understanding of Common Intermediate Language.
Coming soon