Skip to content

Using MonoMod

Runtime hooks and offline patches use the same API. The only difference is where the method comes from.

ILContext

using MonoMod.Cil;
using MonoWeaver.Cecil;
using MonoWeaver.CFG;
using MonoWeaver.Patterns;
public static void Patch(ILContext il)
{
    var pattern = Cil.Value((int baseDamage, int bonus) =>
        baseDamage + bonus);

    il.Method.Match(pattern)
      .Single()
      .Transform(Hooks.ClampDamage)
      .Apply(VerifyOptions.Mod);
}

il.Method is an ordinary MethodDefinition:

MethodDefinition method = il.Method;
// Matching and rewriting from here on target `method`.

Branch labels

MonoMod represents branch targets as ILLabel; Cecil uses Instruction. Apply converts labels to instruction targets and converts them back when it is done, so the normal path needs nothing from you.

The precondition is that every label already points at a valid position when you call Apply. If you also make manual Cecil edits inside the same ILContext, convert them yourself:

CecilHelper.BranchLabelsToTarget(il);
try
{
    // Your own Cecil analysis or edits.
}
finally
{
    CecilHelper.BranchTargetsToLabels(il);
}

What a callback can be

Source Good for
A strongly typed delegate such as Func<int, int> Runtime mods; the most direct form
CilMethodSpec Describing a static callback by signature, without referencing its declaring type
A Cecil MethodReference Offline patchers that already hold a method reference

A static delegate is lowered to a direct call to that static method — no runtime delegate overhead.

Instance delegates are runtime-only

Instance delegates, closures, and multicast delegates capture objects in the current process. They work fine in a runtime hook, but never write them into a patch that gets saved to disk and loaded in another process.

Runtime trade-offs

  • Runtime methods are usually small. Use Apply(VerifyOptions.Mod). Verifying one method costs far less than one failed game load.
  • When several mods patch the same method, order affects what matches. Adding pattern context is more reliable than depending on load order.
  • After each rewrite, previously obtained match positions may be stale. To keep editing the same method, Match again.