CorePatcher
The framework changes some of the game’s methods so it can tell your mod what is happening. CorePatcher lets your mod change a game method the same way, so your code runs before it, after it, or around it. Use it instead of calling Harmony (the patching library BepInEx ships) yourself. See Patching for a longer walkthrough.
A change that runs before the game method is a prefix, one that runs after is a postfix, and one that runs even when the game method throws an error is a finalizer. A change that rewrites the code inside the game method is a transpiler. Pass null for the ones you do not need.
CorePatcher change a game method
Call one of the Patch overloads with the game method and your prefix, postfix or finalizer, passing null for any you do not need. IsDisabled tells you whether the framework has turned off your changes.
public static class CorePatcher
{
public static bool Patch(MethodBase target, MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer, bool core, string owner);
public static bool Patch(Type type, string name, Type[] args, MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer, bool core, string owner);
public static bool Patch(MethodBase target, MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer, bool core, string owner, int priority);
public static bool Patch(Type type, string name, Type[] args, MethodInfo prefix, MethodInfo postfix, MethodInfo finalizer, bool core, string owner, int priority);
public static bool Patch(MethodBase target, MethodInfo prefix, MethodInfo postfix, MethodInfo transpiler, MethodInfo finalizer, bool core, string owner);
public static bool Patch(MethodBase target, MethodInfo prefix, MethodInfo postfix, MethodInfo transpiler, MethodInfo finalizer, bool core, string owner, int priority);
public static bool Patch(Type type, string name, Type[] args, MethodInfo prefix, MethodInfo postfix, MethodInfo transpiler, MethodInfo finalizer, bool core, string owner);
public static bool Patch(Type type, string name, Type[] args, MethodInfo prefix, MethodInfo postfix, MethodInfo transpiler, MethodInfo finalizer, bool core, string owner, int priority);
public static bool IsDisabled(string owner);
}
| Method | Returns | What it does |
|---|---|---|
Patch(target, prefix, postfix, finalizer, core, owner) | bool | Changes a game method given as a method object. |
Patch(type, name, args, prefix, postfix, finalizer, core, owner) | bool | The same, but finds the game method by its type, name and parameter types. |
Patch(target, prefix, postfix, finalizer, core, owner, priority) | bool | The same as the first, with a priority that decides the order against other mods’ changes. |
Patch(type, name, args, prefix, postfix, finalizer, core, owner, priority) | bool | The same as the second, with a priority. |
Patch(target, prefix, postfix, transpiler, finalizer, core, owner) | bool | The first call with a transpiler that rewrites the game method’s code. |
Patch(type, name, args, prefix, postfix, transpiler, finalizer, core, owner) | bool | The second call with a transpiler. Both transpiler calls also take a priority at the end, which works as above. |
IsDisabled(owner) | bool | true when the framework turned off all changes made under this mod id. |
Putting it together
Runs a small method after the game counts a new day, and logs a warning if the change cannot be made.
using System;
using System.Reflection;
using BepInEx;
using BepInEx.Unity.Mono;
using ObenseuerCore;
public class MyMod : BaseUnityPlugin
{
private void Awake()
{
MethodInfo postfix = typeof(MyMod).GetMethod("AfterDayChanged", BindingFlags.Static | BindingFlags.NonPublic);
bool ok = CorePatcher.Patch(typeof(TimeOfDayAzure), "IncreaseDayCounter", Type.EmptyTypes, null, postfix, null, false, "example.mymod");
if (!ok || CorePatcher.IsDisabled("example.mymod")) Logger.LogWarning("MyMod patch unavailable");
}
private static void AfterDayChanged() { }
}
| Step | What it does |
|---|---|
CorePatcher.Patch(typeof(TimeOfDayAzure), "IncreaseDayCounter", ...) | Finds the game method and sets AfterDayChanged to run after it. |
CorePatcher.IsDisabled("example.mymod") | Checks whether the framework turned off the changes. |
Things to know
- Every
Patchcall returnstruewhen the change was made, andfalsewhen it was not. In these calls,owneris your mod’s id (see Owner in the glossary). - The short calls (without
priority) keep the priority attributes you put on your methods. That means[HarmonyPriority],[HarmonyBefore]and[HarmonyAfter]. (Checked with HarmonyX 2.9.) prioritydecides which of several mods’ changes runs first. It applies to your prefix, postfix and finalizer, and it replaces a[HarmonyPriority]attribute. Pass-1to leave the attributes alone. A higher number runs earlier for prefixes. The normal value is 400 (Priority.Normal), and the highest common value is 800 (Priority.First). (It is the HarmonyX priority,HarmonyLib.Priority.)- A transpiler rewrites the code inside the game method. Use it only when a prefix or a postfix cannot do the job: it depends on the exact code of that game method, so a game update is more likely to break it. A transpiler that throws fails the change like any other handler, and the game method is left as it was.
- A prefix that returns
falseskips the game method, but it does not stop other mods’ prefixes. Every prefix still runs, even when an earlier one returnedfalse. A higher priority makes yours run before the others, but it cannot stop them. (This is how HarmonyX works. Afalsefrom a prefix only skips the original method.) - The framework keeps track of every change, undoes them together if a game update breaks one, and keeps different mods from stepping on each other.
- Each mod id gets its own Harmony instance. Your changes are kept apart from everyone else’s.
- When a change fails, you get
falseand a line in your mod’s log. The log isBepInEx\logs\<owner>.log. core: truemeans this change is critical for your mod. If it fails, the framework removes every change it made under your mod id, andIsDisabled(owner)becomes true. Changes you made yourself withnew Harmony(owner)are not touched.- Once your mod id is disabled, it stays disabled until the game restarts. Later
Patchcalls returnfalseand write nothing to the log.
Limits
- Your mod id must not start with
obenseuer.moddingframeworkornomad.obenseuer.core, and it must not be empty. Those two starts are reserved for the framework’s own changes, in any mix of capital and small letters. If you break this rule, thePatchcalls without a transpiler throw anArgumentExceptionbefore they do anything else, and the four calls with a transpiler do not throw: they returnfalseand write one line, to the framework’s own log when the owner is the problem. - The framework refuses to change anything when
Core.PatchingAllowedis false. The call returnsfalseand logspatch skipped, unsupported game build. That only happens when the game build is not 0.5.08 and[Safety] RefuseUnknownBuildis set. By default an unknown build is changed anyway.