摘要
Harmony 是一個用於 .NET 的庫,它允許開發者在運行時創建、應用或修改程序集的補丁,即所謂的 "熱補丁" (hot patching)。這使得開發者能夠動態地改變已編譯代碼的行為,而無需修改原始的源代碼。Harmony 在遊戲Mod開發、軟件插件系統和復雜的應用程序中非常流行,因為它可以在不改變原始程序集的情況下,註入或改變代碼的功能。
正文
應用場景
Harmony 的常見應用場景包括:
- 遊戲Mod開發:在不修改遊戲原始代碼的情況下添加新功能或修改現有功能。
- 軟件擴展:在現有應用程序中註入代碼以增加新功能或修改現有邏輯。
- 行為修正:修復第三方庫中的錯誤或不希望的行為,而不需要等待官方更新。
- 測試和Mocking:動態替換方法以進行單元測試或集成測試。
Harmony 的基本概念
在深入例子之前,讓我們快速了解一下 Harmony 的一些基本概念:
- 原始方法 (Original Method):需要被修改或補丁的方法。
- 前置補丁 (Prefix):在原始方法執行之前運行的代碼。
- 後置補丁 (Postfix):在原始方法執行之後運行的代碼。
- 替換方法 (Transpiler):修改原始方法的IL代碼的方法。
- 反射補丁 (Reverse Patch):允許調用私有方法作為公共的靜態方法。
例子 1: 在遊戲中添加Mod功能
假設我們正在為一個遊戲添加Mod,我們想要修改玩傢的移動速度。原始的遊戲代碼可能是這樣的:
public class Player
{
public float MoveSpeed { get; set; } = 10f;
public void Move(Vector3 direction)
{
// 移動玩傢的邏輯
Console.WriteLine("MOVE:" direction.X * MoveSpeed);
Console.WriteLine("MOVE:" direction.Y * MoveSpeed);
Console.WriteLine("MOVE:" direction.Z * MoveSpeed);
}
}
我們想要在不修改原始遊戲代碼的情況下增加玩傢的移動速度。我們可以使用 Harmony 創建一個前置補丁:
using C20240114C;
using HarmonyLib;
[HarmonyPatch(typeof(Player))]
[HarmonyPatch("Move")]
public static class PlayerMoveSpeedPatch
{
static void Prefix(Player __instance)
{
__instance.MoveSpeed *= 2; // 將移動速度翻倍
}
}
class Program
{
static void Main()
{
var harmony = new Harmony("com.mod.yourmod");
harmony.PatchAll();
Player player= new Player();
player.Move(new System.Numerics.Vector3(1, 2, 3));
}
}
![](https://news.xinpengboligang.com/upload/keji/d7694b0656055ca5ec359bf2e3656343.jpeg)
在這個例子中,我們創建了一個名為 PlayerMoveSpeedPatch 的類,並使用 HarmonyPatch 屬性指定要補丁的類和方法。我們定義了一個 Prefix 方法,它會在玩傢移動之前執行,並將移動速度翻倍。
例子 2: 修改第三方庫的行為
假設我們使用了一個第三方庫,該庫有一個方法 Calculate,但它的行為不是我們想要的。我們想要修改它的返回值。
public class ThirdPartyCalculator
{
public int Calculate(int a, int b)
{
return a b; // 原始行為是相加
}
}
using C20240114C;
using HarmonyLib;
[HarmonyPatch(typeof(ThirdPartyCalculator))]
[HarmonyPatch("Calculate")]
public static class CalculatorPatch
{
static void Postfix(ref int __result, int a, int b)
{
__result = a * b; // 修改返回值為乘積
}
}
class Program
{
static void Main()
{
var harmony = new Harmony("com.mod.yourmod");
harmony.PatchAll();
ThirdPartyCalculator calculator=new ThirdPartyCalculator();
var ret= calculator.Calculate(10,20);
Console.WriteLine(ret);
}
}
![](https://news.xinpengboligang.com/upload/keji/375eaf5268751376f02a49974b87b65b.jpeg)
在這個例子中,我們使用了 Postfix 方法來修改 Calculate 方法的返回值。我們引用了 __result 參數來改變原始方法的輸出。
例子 3: 動態日志記錄
假設你有一個生產環境中的應用程序,你想要為特定的方法添加額外的日志記錄,但你不能重新編譯和部署整個應用程序。使用Harmony,你可以創建一個前置補丁來動態添加日志記錄。
// 假設這是你想要添加日志的方法
public class PaymentProcessor
{
public bool ProcessPayment(decimal amount, string accountNumber)
{
// 支付處理邏輯
return true;
}
}
using C20240114C;
using HarmonyLib;
// Harmony補丁類
[HarmonyPatch(typeof(PaymentProcessor))]
[HarmonyPatch("ProcessPayment")]
public static class LogPaymentPatch
{
static void Prefix(decimal amount, string accountNumber)
{
Console.WriteLine($"Processing payment of {amount:C} for account {accountNumber}.");
}
}
class Program
{
static void Main()
{
var harmony = new Harmony("com.mod.yourmod");
harmony.PatchAll();
PaymentProcessor payment=new PaymentProcessor();
payment.ProcessPayment(200, "10001");
}
}
![](https://news.xinpengboligang.com/upload/keji/5528488681b0b4c55a2835e4c7517cbc.jpeg)
在這個例子中,LogPaymentPatch 類在 PaymentProcessor 的 ProcessPayment 方法之前添加了日志記錄,記錄了每次支付處理的詳細信息。
結論
Harmony 是一個功能強大的庫,它為C#開發者提供了前所未有的靈活性,使他們能夠動態地修改和擴展代碼的行為。無論是在遊戲開發、軟件插件還是單元測試中,Harmony 都能提供強大的支持。通過合理使用 Harmony,開發者可以跨越原有代碼的限制,實現更加豐富和定制化的功能。