HarmonyPatchExtensions

v1.3.0·Updated 2026-08-20

Adds mixin like patching to Harmony

About this mod Author’s original text

Harmony.PatchExtensions

Attribute based extensions for Harmony that lets you define mixin style patches.

Features

  • Attribute based patches
  • Injection points: HEAD (prefix), POSTFIX, RETURN (At every return), INVOKE (insert before call), REDIRECT (replace call), AFTER (after call), FINALLY, ARG, LOOP_BEFORE/TOP/BOTTOM/AFTER, BRANCH_TRUE/FALSE, LOCAL_WRITE/READ, ARG_WRITE/READ, FIELD_WRITE/READ
  • Occurrence and start-index targeting
  • Conflict detection
  • Optional wrapper for overwriting prefixes that return the target's return type
  • Patching variable declarations in methods

Install

Use one of the following:

  • Reference the project directly in your solution
  • Install the published NuGet package: (Insert package when uploaded here)
  • If using publicizers add `` to an ItemGroup so documentations shows up in the code editor

Quick Start

using System.Reflection;

using HarmonyLib;

using HarmonyLib.PatchExtensions;

public static class Program

{

public static void Main()

{

var harmony = new Harmony("example.patching");

MixinLoader.ApplyPatches(harmony, Assembly.GetExecutingAssembly());

// Or for using a single patch class

// MixinLoader.ApplyPatches(harmony, Assembly.GetExecutingAssembly(), typeof(Patches));

}

}

Patch Examples

HEAD (prefix)

using HarmonyLib.PatchExtensions;

public static class Patches

{

[Patch(typeof(Target), "Add", AT.HEAD)]

public static void AddPrefix(int a, int b)

{

// Will run before Target.Add

a += 1;

}

}

Result:

public class Target

{

public int Add(int a, int b)

{

+ a += 1;

return a + b;

}

}

HEAD overwrite (skip original)

If overwriting: true, the patch can return bool (Harmony style) or the same return type as the target.

using HarmonyLib.PatchExtensions;

public static class OverwritePatches

{

[Patch(typeof(Target), "Add", AT.HEAD, overwriting: true)]

public static int AddOverwrite(int a, int b)

{

return 68; // the wrapper sets __result and skips original automatically

}

}

Result:

public class Target

{

public int Add(int a, int b)

{

+ return 68;

  • return a + b;

}

}

POSTFIX (Just harmony postfix)

using HarmonyLib.PatchExtensions;

public static class PostfixPatches

{

[Patch(typeof(Target), "Add", AT.POSTFIX)]

public static void AddPostfix(int a, int b, ref int __result)

{

__result += 1;

}

}

Result:

public class Target

{

public int Add(int a, int b)

{

  • return a + b;

+ __result = a + b;

+ __result += 1;

return __result;

}

}

RETURN

using HarmonyLib.PatchExtensions;

public static class ReturnPatches

{

[Patch(typeof(Target), "Foo", AT.RETURN)]

public static void AddReturn(int a, int b, bool c, ref int __result)

{

__result += 1;

}

}

Result:

public class Target

{

public int Foo(int a, int b, bool c)

{

if (c)

{

__result = a;

+ __result += 1;

return __result;

}

else

{

__result = b;

+ __result += 1;

return __result;

}

}

}

INVOKE (insert before call)

using HarmonyLib.PatchExtensions;

public static class InvokePatches

{

[Patch(typeof(TargetCalls), "Foo", AT.INVOKE, target: "Helper.DoThing", occurrence: 2)] // occurrence: 0 matches all

public static void BeforeSecondCall()

{

// Injected before the 2nd call to Helper.DoThing

}

}

Result:

public class TargetCalls

{

public void Foo()

{

Helper.DoThing();

+ InvokePatches.BeforeSecondCall();

Helper.DoThing();

}

}

REDIRECT (replace call)

using HarmonyLib.PatchExtensions;

public static class RedirectPatches

{

[Patch(typeof(TargetCalls), "Foo", AT.REDIRECT, target: "Helper.DoThing", occurrence: 0)] // occurrence can also be specified to target

public static void ReplaceCall()

{

// Replaces Helper.DoThing with this method

}

}

Result:

public class TargetCalls

{

public void Foo()

{

  • Helper.DoThing();
  • Helper.DoThing();

+ RedirectPatches.ReplaceCall();

+ RedirectPatches.ReplaceCall();

}

}

REDIRECT with occurrence

using HarmonyLib.PatchExtensions;

public static class RedirectPatches

{

[Patch(typeof(TargetCalls), "Foo", AT.REDIRECT, target: "Helper.DoThing", occurrence: 1)] // occurrence

Install & use Install: BepInEx plugin

  1. The archive from playbuff is the mod itself — usually a BepInEx plugin (a .dll or a plugin folder once unzipped).
  2. Recommended: use a community mod manager (such as r2modman) — pick this game, import the mod, and the manager sets up BepInEx and its dependencies for you.
  3. Manual route: install the BepInEx loader in the game folder, then drop the unzipped .dll or plugin folder into BepInEx/plugins/.
  4. Launch the game — the BepInEx console logs every plugin it loads; seeing this mod in the log means it is installed.

Mods with listed dependencies need those installed too, or they will not load. Options are usually changed in-game or in the plugin's config file.

Dependencies

Install these first, or the mod will not work:

BepInEx-BepInExPack

File & checksum

Filename
HarmonyPatchExtensions-1.3.0.zip
File size
81.3 KiB