Files
ShrinkModFramework/Runtime/Loading/ShrinkHarmonyPatchService.cs
T
cneicy 78ccae3e07
Publish UPM package / publish (push) Failing after 1s
chore: initialize standalone UPM package
2026-08-26 02:50:31 +08:00

132 lines
5.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
namespace ShrinkModFramework
{
internal static class ShrinkHarmonyPatchService
{
private sealed class EmptyLease : IDisposable
{
public static readonly EmptyLease Instance = new();
public void Dispose() { }
}
private sealed class HarmonyLease : IDisposable
{
private readonly Type _harmonyType;
private readonly object _harmony;
private readonly string _harmonyId;
private readonly bool _verboseLogging;
private bool _disposed;
public HarmonyLease(Type harmonyType, object harmony, string harmonyId, bool verboseLogging)
{
_harmonyType = harmonyType;
_harmony = harmony;
_harmonyId = harmonyId;
_verboseLogging = verboseLogging;
}
public void Dispose()
{
if (_disposed)
return;
_disposed = true;
try
{
var unpatchSelf = _harmonyType.GetMethod("UnpatchSelf",
BindingFlags.Public | BindingFlags.Instance,
null,
Type.EmptyTypes,
null);
if (unpatchSelf != null)
{
unpatchSelf.Invoke(_harmony, null);
}
else
{
var unpatchAll = _harmonyType.GetMethod("UnpatchAll",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(string) },
null);
if (unpatchAll == null)
throw new MissingMethodException("Harmony.UnpatchSelf()/UnpatchAll(string) 不存在。");
unpatchAll.Invoke(null, new object[] { _harmonyId });
}
if (_verboseLogging)
Debug.Log($"[ShrinkModFramework] 已撤回 Harmony 补丁:{_harmonyId}");
}
catch (Exception ex)
{
Debug.LogError($"[ShrinkModFramework] 撤回 Harmony 补丁失败:{_harmonyId}\n{ex.Message}");
}
finally
{
AppliedHarmonyIds.Remove(_harmonyId);
}
}
}
private static readonly HashSet<string> AppliedHarmonyIds = new(StringComparer.Ordinal);
public static void ApplyPatchesIfNeeded(ShrinkModInfo modInfo, bool enableHarmonyPatching, bool verboseLogging)
{
_ = ApplyPatchesCore(modInfo, enableHarmonyPatching, verboseLogging, reversible: false);
}
public static IDisposable AcquirePatchesIfNeeded(ShrinkModInfo modInfo, bool enableHarmonyPatching,
bool verboseLogging)
{
return ApplyPatchesCore(modInfo, enableHarmonyPatching, verboseLogging, reversible: true);
}
private static IDisposable ApplyPatchesCore(ShrinkModInfo modInfo, bool enableHarmonyPatching,
bool verboseLogging, bool reversible)
{
if (!enableHarmonyPatching || !modInfo.AutoApplyHarmonyPatches)
return EmptyLease.Instance;
var harmonyType = Type.GetType("HarmonyLib.Harmony, 0Harmony");
if (harmonyType == null)
{
if (verboseLogging)
Debug.Log($"[ShrinkModFramework] 未检测到 Harmony,跳过模组 {modInfo.ModId} 的补丁自动应用。");
return EmptyLease.Instance;
}
var harmonyId = $"shrink.mod.{modInfo.ModId}";
if (!AppliedHarmonyIds.Add(harmonyId))
return EmptyLease.Instance;
try
{
var harmony = Activator.CreateInstance(harmonyType, harmonyId);
var patchAll = harmonyType.GetMethod("PatchAll", new[] { typeof(Assembly) });
if (patchAll == null)
throw new MissingMethodException("Harmony.PatchAll(Assembly) 不存在。");
patchAll.Invoke(harmony, new object[] { modInfo.EntryType.Assembly });
if (verboseLogging)
Debug.Log($"[ShrinkModFramework] 已应用 Harmony 补丁:{modInfo.ModId}");
return reversible
? new HarmonyLease(harmonyType, harmony, harmonyId, verboseLogging)
: EmptyLease.Instance;
}
catch (Exception ex)
{
AppliedHarmonyIds.Remove(harmonyId);
throw new InvalidOperationException($"模组 {modInfo.ModId} 应用 Harmony 补丁失败:{ex.Message}", ex);
}
}
internal static void ResetForTesting() => AppliedHarmonyIds.Clear();
}
}