mirror of
https://github.com/D1GQ/BetterAmongUs.git
synced 2026-10-20 01:46:19 +00:00
108 lines
4.3 KiB
C#
108 lines
4.3 KiB
C#
using BepInEx.Unity.IL2CPP;
|
|
using HarmonyLib;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
|
|
namespace BetterAmongUs.Items.Attributes;
|
|
|
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
|
internal abstract class MethodPatchAttribute : Attribute
|
|
{
|
|
private static readonly HashSet<Assembly> _registeredAssemblies = new();
|
|
private static readonly Dictionary<MethodInfo, MethodPatchAttribute> _methodPatchAttributes = [];
|
|
private static readonly HarmonyMethod _prefixHarmonyMethod = new(typeof(MethodPatchAttribute), nameof(PatchPrefix));
|
|
private static readonly HarmonyMethod _postfixHarmonyMethod = new(typeof(MethodPatchAttribute), nameof(PatchPostfix));
|
|
private static readonly HashSet<MethodInfo> _methodsToPatch = [];
|
|
|
|
protected MethodBase? PatchedMethod { get; private set; }
|
|
protected Delegate? DelegateMethod { get; private set; }
|
|
private bool runOriginal;
|
|
|
|
public static void Register(Assembly assembly, BasePlugin plugin)
|
|
{
|
|
if (_registeredAssemblies.Contains(assembly)) return;
|
|
_registeredAssemblies.Add(assembly);
|
|
|
|
foreach (var type in assembly.GetTypes())
|
|
{
|
|
foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
|
{
|
|
if (method.GetCustomAttribute<MethodPatchAttribute>(true) is MethodPatchAttribute attribute)
|
|
{
|
|
if (method?.DeclaringType != null && attribute.ShouldPatch(plugin, method.DeclaringType, method, method.GetParameters()))
|
|
{
|
|
attribute.PatchedMethod = method;
|
|
_methodPatchAttributes[method] = attribute;
|
|
_methodsToPatch.Add(method);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static void Finished()
|
|
{
|
|
foreach (var method in _methodsToPatch.OrderBy(method => method.Name))
|
|
{
|
|
var attribute = _methodPatchAttributes[method];
|
|
|
|
if (method is not MethodInfo methodInfo)
|
|
throw new InvalidOperationException("OriginalMethod is not a MethodInfo");
|
|
|
|
var paramTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
|
|
var delegateType = methodInfo.IsStatic
|
|
? Expression.GetDelegateType(paramTypes.Concat(new[] { methodInfo.ReturnType }).ToArray())
|
|
: Expression.GetDelegateType([methodInfo.DeclaringType!, .. paramTypes, .. new[] { methodInfo.ReturnType }]);
|
|
|
|
attribute.DelegateMethod = methodInfo.CreateDelegate(delegateType);
|
|
|
|
var patch = Main.Harmony.Patch(method, _prefixHarmonyMethod, _postfixHarmonyMethod);
|
|
}
|
|
}
|
|
|
|
private static bool PatchPrefix(object __instance, MethodBase __originalMethod, object[] __args)
|
|
{
|
|
if (_methodPatchAttributes.TryGetValue((MethodInfo)__originalMethod, out var attribute))
|
|
{
|
|
if (attribute.runOriginal)
|
|
{
|
|
attribute.runOriginal = false;
|
|
return true;
|
|
}
|
|
|
|
return attribute.RunPrefix(__instance, __args);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static void PatchPostfix(object __instance, MethodBase __originalMethod, object[] __args)
|
|
{
|
|
if (_methodPatchAttributes.TryGetValue((MethodInfo)__originalMethod, out var attribute))
|
|
{
|
|
attribute.RunPostfix(__instance, __args);
|
|
}
|
|
}
|
|
|
|
internal object? InvokeOriginal(object? instance, object[] args)
|
|
{
|
|
if (DelegateMethod == null) return null;
|
|
|
|
var allArgs = PatchedMethod?.IsStatic == false
|
|
? new[] { instance }.Concat(args).ToArray()
|
|
: args;
|
|
|
|
runOriginal = true;
|
|
return DelegateMethod.DynamicInvoke(allArgs);
|
|
}
|
|
|
|
internal virtual bool ShouldPatch(BasePlugin plugin, Type declaringType, MethodBase method, ParameterInfo[] args) => true;
|
|
internal abstract bool RunPrefix(object instance, object[] args);
|
|
internal abstract void RunPostfix(object instance, object[] args);
|
|
|
|
internal static void Initialize()
|
|
{
|
|
IL2CPPChainloader.Instance.PluginLoad += (_, assembly, plugin) => Register(assembly, plugin);
|
|
IL2CPPChainloader.Instance.Finished += Finished;
|
|
}
|
|
} |