披露:Harmony 是一个由我(本文的作者)编写和维护的库。
和谐2 https://github.com/pardeike/Harmony是一个开源库(MIT 许可证),旨在在运行时替换、装饰或修改任何类型的现有 C# 方法。它的主要重点是用 Mono 或 .NET 编写的游戏和插件。它负责对同一方法进行多次更改 - 它们会累积而不是相互覆盖。
它为每个原始方法创建动态替换方法,并向它们发出在开始和结束时调用自定义方法的代码。它还允许您编写过滤器来处理原始 IL 代码和自定义异常处理程序,从而允许对原始方法进行更详细的操作。
为了完成该过程,它在原始方法的蹦床上编写了一个简单的汇编程序跳转,该跳转指向通过编译动态方法生成的汇编程序。这适用于 Windows、macOS 和 Mono 支持的任何 Linux 上的 32/64 位。
文档可以找到here https://harmony.pardeike.net.
Example
(Source https://harmony.pardeike.net/articles/intro.html)
原始代码
public class SomeGameClass
{
private bool isRunning;
private int counter;
private int DoSomething()
{
if (isRunning)
{
counter++;
return counter * 10;
}
}
}
使用 Harmony 注释进行修补
using SomeGame;
using HarmonyLib;
public class MyPatcher
{
// make sure DoPatching() is called at start either by
// the mod loader or by your injector
public static void DoPatching()
{
var harmony = new Harmony("com.example.patch");
harmony.PatchAll();
}
}
[HarmonyPatch(typeof(SomeGameClass))]
[HarmonyPatch("DoSomething")]
class Patch01
{
static FieldRef<SomeGameClass,bool> isRunningRef =
AccessTools.FieldRefAccess<SomeGameClass, bool>("isRunning");
static bool Prefix(SomeGameClass __instance, ref int ___counter)
{
isRunningRef(__instance) = true;
if (___counter > 100)
return false;
___counter = 0;
return true;
}
static void Postfix(ref int __result)
{
__result *= 2;
}
}
或者,使用反射手动修补
using SomeGame;
using System.Reflection;
using HarmonyLib;
public class MyPatcher
{
// make sure DoPatching() is called at start either by
// the mod loader or by your injector
public static void DoPatching()
{
var harmony = new Harmony("com.example.patch");
var mOriginal = typeof(SomeGameClass).GetMethod("DoSomething", BindingFlags.Instance | BindingFlags.NonPublic);
var mPrefix = typeof(MyPatcher).GetMethod("MyPrefix", BindingFlags.Static | BindingFlags.Public);
var mPostfix = typeof(MyPatcher).GetMethod("MyPostfix", BindingFlags.Static | BindingFlags.Public);
// add null checks here
harmony.Patch(mOriginal, new HarmonyMethod(mPrefix), new HarmonyMethod(mPostfix));
}
public static void MyPrefix()
{
// ...
}
public static void MyPostfix()
{
// ...
}
}