132 lines
5.0 KiB
C#
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();
|
|
}
|
|
}
|