Patch Lifecycle
Patches don't "stay forever once written" — they are applied and removed as the Mod is enabled / disabled.
PatchAll: Auto-Scan and Apply
Call PatchAll in OnToggle(true) to scan the current assembly for all classes with [HarmonyPatch] and apply them:
private static bool OnToggle(UnityModManager.ModEntry modEntry, bool value)
{
if (value) // value=true: Mod is being enabled
{
Harmony?.PatchAll(Assembly.GetExecutingAssembly()); // Scan assembly, apply all [HarmonyPatch]
}
else // value=false: Mod is being disabled
{
Harmony?.UnpatchAll(modEntry.Info.Id); // Only remove this Mod's patches
}
return true; // Tell UMM the toggle operation succeeded
}UnpatchAll: Remove by Mod
UnpatchAll(string) only removes patches belonging to a specific HarmonyId. Our Harmony instance is created with modEntry.Info.Id, so UnpatchAll(modEntry.Info.Id) only removes this Mod's patches without affecting other Mods.
Harmony = new Harmony(modEntry.Info.Id); // HarmonyId = Mod's IdNever use the parameterless
UnpatchAll()— that removes ALL patches from ALL Mods!
Execution Order of Multiple Patches
When the same method is modified by multiple Mods / patches, ordering follows rules:
- PatchAll applies in metadata token order within the assembly (roughly class declaration order)
- Patches follow a "first declared, first executed" rule: later Prefixes run before earlier ones (LIFO), Postfixes are the reverse
- Harmony allows specifying priority via
[HarmonyPriority]on[HarmonyPatch]
[HarmonyPriority(Priority.First)]
[HarmonyPatch(typeof(SomeGameClass), nameof(SomeGameClass.SomeMethod))]
public static class First_Prefix { /* ... */ }Common priority constants: Priority.High / Normal / Low / First / Last.
Modifying Target Method Signature
Harmony locates targets by "class + method name + parameter types". If the game has multiple same-name overloads, you need to specify parameter types:
// Only patch the (int) overload
[HarmonyPatch(typeof(SomeGameClass), nameof(SomeGameClass.SomeMethod), new Type[] { typeof(int) })]
public static class SomeMethodInt_Prefix { /* ... */ }Checking if Patches Are Active at Runtime
// Get patch info for the target method (use reflection to get MethodInfo)
var info = Harmony.GetPatchInfo(typeof(SomeGameClass).GetMethod(nameof(SomeGameClass.SomeMethod)));
if (info != null) // Patches were indeed applied to the target method
{
MyMod.Logger.Log($"Prefix count: {info.Prefixes.Count}");
}Debugging Patches Not Taking Effect
When patches "don't work", check in order:
- Was it scanned by PatchAll: the class must be
public static classwith[HarmonyPatch] - Is the target correct: method name, class name, parameter types (overloads) must match
- Was it called before OnToggle: if game code called the target method before the Mod was enabled, the patch "missed" it
- Was it unpatched by another Mod: check with
GetPatchInfo
What You Learned
PatchAll/UnpatchAllapplication and removal- Execution order and priority of multiple patches
- Using
GetPatchInfoto check patch status
Next Step
Enter the world of IL and learn Transpiler → Transpiler Introduction