Improve Harmony Hook
1. Hook.HookMethod can patch overloaded methods based on parameter types 2. Hook.HookMethod can add more patches to the method 3. Find all hook methods by the address of the function (origin method), not by the method path (string). 4. Fixed unable to add postfix patch to method (__params changed to __args) 5. Fix patching method will cause duplicate patches to be added
This commit is contained in:
@@ -1,36 +1,85 @@
|
||||
|
||||
Hook.HookMethod("Barotrauma.Item", "TryInteract", function (instance, p)
|
||||
if Hook.Call("itemInteract", instance, p.picker, p.ignoreRequiredItems, p.forceSelectKey, p.forceActionKey) == true then
|
||||
return false
|
||||
end
|
||||
end, Hook.HookMethodType.Before)
|
||||
Hook.HookMethod(
|
||||
"Barotrauma.Item", "TryInteract",
|
||||
{
|
||||
"Barotrauma.Character",
|
||||
"System.Boolean",
|
||||
"System.Boolean",
|
||||
"System.Boolean"
|
||||
},
|
||||
function (instance, p)
|
||||
if Hook.Call("itemInteract", instance, p.picker, p.ignoreRequiredItems, p.forceSelectKey, p.forceActionKey) == true then
|
||||
return false
|
||||
end
|
||||
end,
|
||||
Hook.HookMethodType.Before
|
||||
)
|
||||
|
||||
Hook.HookMethod("Barotrauma.Item", "ApplyTreatment", function (instance, p)
|
||||
if Hook.Call("itemApplyTreatment", instance, p.user, p.character, p.targetLimb) then
|
||||
return false
|
||||
end
|
||||
end, Hook.HookMethodType.Before)
|
||||
Hook.HookMethod(
|
||||
"Barotrauma.Item", "ApplyTreatment",
|
||||
{
|
||||
"Barotrauma.Character",
|
||||
"Barotrauma.Character",
|
||||
"Barotrauma.Limb"
|
||||
},
|
||||
function (instance, p)
|
||||
if Hook.Call("itemApplyTreatment", instance, p.user, p.character, p.targetLimb) then
|
||||
return false
|
||||
end
|
||||
end,
|
||||
Hook.HookMethodType.Before
|
||||
)
|
||||
|
||||
Hook.HookMethod("Barotrauma.Item", "Combine", function (instance, p)
|
||||
if Hook.Call("itemCombine", instance, p.item, p.user) == true then
|
||||
return false
|
||||
end
|
||||
end, Hook.HookMethodType.Before)
|
||||
Hook.HookMethod(
|
||||
"Barotrauma.Item", "Combine",
|
||||
{
|
||||
"Barotrauma.Item",
|
||||
"Barotrauma.Character"
|
||||
},
|
||||
function (instance, p)
|
||||
if Hook.Call("itemCombine", instance, p.item, p.user) == true then
|
||||
return false
|
||||
end
|
||||
end,
|
||||
Hook.HookMethodType.Before
|
||||
)
|
||||
|
||||
Hook.HookMethod("Barotrauma.Item", "Drop", function (instance, p)
|
||||
if Hook.Call("itemDrop", instance, p.dropper) == true then
|
||||
return false
|
||||
end
|
||||
end, Hook.HookMethodType.Before)
|
||||
Hook.HookMethod(
|
||||
"Barotrauma.Item", "Drop",
|
||||
{
|
||||
"Barotrauma.Character",
|
||||
"System.Boolean"
|
||||
},
|
||||
function (instance, p)
|
||||
if Hook.Call("itemDrop", instance, p.dropper) == true then
|
||||
return false
|
||||
end
|
||||
end,
|
||||
Hook.HookMethodType.Before
|
||||
)
|
||||
|
||||
Hook.HookMethod("Barotrauma.Item", "Equip", function (instance, p)
|
||||
if Hook.Call("itemEquip", instance, p.character) == true then
|
||||
return false
|
||||
end
|
||||
end, Hook.HookMethodType.Before)
|
||||
Hook.HookMethod(
|
||||
"Barotrauma.Item", "Equip",
|
||||
{
|
||||
"Barotrauma.Character"
|
||||
},
|
||||
function (instance, p)
|
||||
if Hook.Call("itemEquip", instance, p.character) == true then
|
||||
return false
|
||||
end
|
||||
end,
|
||||
Hook.HookMethodType.Before
|
||||
)
|
||||
|
||||
Hook.HookMethod("Barotrauma.Item", "Unequip", function (instance, p)
|
||||
if Hook.Call("itemUnequip", instance, p.character) == true then
|
||||
return false
|
||||
end
|
||||
end, Hook.HookMethodType.Before)
|
||||
Hook.HookMethod(
|
||||
"Barotrauma.Item", "Unequip",
|
||||
{
|
||||
"Barotrauma.Character"
|
||||
},
|
||||
function (instance, p)
|
||||
if Hook.Call("itemUnequip", instance, p.character) == true then
|
||||
return false
|
||||
end
|
||||
end,
|
||||
Hook.HookMethodType.Before
|
||||
)
|
||||
|
||||
@@ -799,7 +799,8 @@ namespace Barotrauma
|
||||
public LuaHook(LuaSetup e)
|
||||
{
|
||||
env = e;
|
||||
_hookMethods = new Dictionary<string, object>();
|
||||
_hookPrefixMethods = new Dictionary<long, HashSet<object>>();
|
||||
_hookPostfixMethods = new Dictionary<long, HashSet<object>>();
|
||||
}
|
||||
|
||||
public class HookFunction
|
||||
@@ -818,7 +819,8 @@ namespace Barotrauma
|
||||
|
||||
private Dictionary<string, Dictionary<string, HookFunction>> hookFunctions = new Dictionary<string, Dictionary<string, HookFunction>>();
|
||||
|
||||
private static Dictionary<string, object> _hookMethods;
|
||||
private static Dictionary<long, HashSet<object>> _hookPrefixMethods;
|
||||
private static Dictionary<long, HashSet<object>> _hookPostfixMethods;
|
||||
|
||||
private Queue<Tuple<object, object[]>> queuedFunctionCalls = new Queue<Tuple<object, object[]>>();
|
||||
|
||||
@@ -829,29 +831,36 @@ namespace Barotrauma
|
||||
|
||||
static void _hookLuaPatch(MethodBase __originalMethod, object[] __args, object __instance, out LuaResult result, HookMethodType hookMethodType)
|
||||
{
|
||||
// Although it works correctly, the performance is low
|
||||
result = new LuaResult(null);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var classType = __originalMethod.DeclaringType;
|
||||
var methodPath = $"{hookMethodType}:{classType.Namespace}.{classType.Name}.{__originalMethod.Name}";
|
||||
|
||||
var @params = __originalMethod.GetParameters();
|
||||
var ptable = new Dictionary<string, object>();
|
||||
for (int i = 0; i < @params.Length; i++)
|
||||
{
|
||||
ptable.Add(@params[i].Name, __args[i]);
|
||||
|
||||
}
|
||||
|
||||
if (_hookMethods.TryGetValue(methodPath, out object hookMethod))
|
||||
{
|
||||
result = new LuaResult(luaSetup.hook.env.lua.Call(hookMethod, __instance, ptable));
|
||||
}
|
||||
else
|
||||
var funcAddr = ((long)__originalMethod.MethodHandle.GetFunctionPointer());
|
||||
HashSet<object> methodSet = null;
|
||||
switch (hookMethodType)
|
||||
{
|
||||
luaSetup.PrintError($"No hook method found in _hookMethods[{methodPath}]");
|
||||
case HookMethodType.Before:
|
||||
methodSet = _hookPrefixMethods[funcAddr];
|
||||
break;
|
||||
case HookMethodType.After:
|
||||
methodSet = _hookPostfixMethods[funcAddr];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (methodSet != null)
|
||||
{
|
||||
foreach (var hookMethod in methodSet)
|
||||
{
|
||||
result = new LuaResult(luaSetup.lua.Call(hookMethod, __instance, ptable));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -881,14 +890,14 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __params, object __instance)
|
||||
static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __args, object __instance)
|
||||
{
|
||||
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
|
||||
_hookLuaPatch(__originalMethod, __args, __instance, out LuaResult result, HookMethodType.After);
|
||||
}
|
||||
|
||||
static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
|
||||
static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __args, ref object __result, object __instance)
|
||||
{
|
||||
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
|
||||
_hookLuaPatch(__originalMethod, __args, __instance, out LuaResult result, HookMethodType.After);
|
||||
|
||||
if (!result.IsNull())
|
||||
__result = result.Object();
|
||||
@@ -898,61 +907,99 @@ namespace Barotrauma
|
||||
private static MethodInfo _miHookLuaPatchRetPrefix = typeof(LuaHook).GetMethod("HookLuaPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
private static MethodInfo _miHookLuaPatchPostfix = typeof(LuaHook).GetMethod("HookLuaPatchPostfix", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
private static MethodInfo _miHookLuaPatchRetPostfix = typeof(LuaHook).GetMethod("HookLuaPatchRetPostfix", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
public void HookMethod(string className, string methodName, object hookMethod, HookMethodType hookMethodType = HookMethodType.Before)
|
||||
public void HookMethod(string className, string methodName, string[] parameterNames, object hookMethod, HookMethodType hookMethodType = HookMethodType.Before)
|
||||
{
|
||||
if (hookMethod == null)
|
||||
{
|
||||
env.PrintError("hookMethod cannot be null");
|
||||
return;
|
||||
}
|
||||
|
||||
var classType = Type.GetType(className);
|
||||
var methodInfos = classType.GetMethods();
|
||||
HarmonyMethod harmonyMethod = new HarmonyMethod();
|
||||
HarmonyMethod harmonyMethodRet = new HarmonyMethod();
|
||||
MethodInfo methodInfo = null;
|
||||
|
||||
if (parameterNames.Length > 0)
|
||||
{
|
||||
Type[] parameterTypes = parameterNames.Select(x => AccessTools.TypeByName(x)).ToArray();
|
||||
methodInfo = classType.GetMethod(methodName, parameterTypes);
|
||||
}
|
||||
else
|
||||
{
|
||||
methodInfo = classType.GetMethod(methodName);
|
||||
}
|
||||
|
||||
if (methodInfo == null)
|
||||
{
|
||||
env.PrintError($"can't find method({className}.{methodName}) with these parameters' types({string.Join(", ", parameterNames)})");
|
||||
return;
|
||||
}
|
||||
|
||||
var funcAddr = ((long)methodInfo.MethodHandle.GetFunctionPointer());
|
||||
var patches = Harmony.GetPatchInfo(methodInfo);
|
||||
|
||||
if (hookMethodType == HookMethodType.Before)
|
||||
{
|
||||
harmonyMethod = new HarmonyMethod(_miHookLuaPatchPrefix);
|
||||
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPrefix);
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
{
|
||||
if (patches == null || patches.Prefixes == null || patches.Prefixes.Find(patch => patch.PatchMethod == _miHookLuaPatchPrefix) == null)
|
||||
{
|
||||
env.harmony.Patch(methodInfo, prefix: new HarmonyMethod(_miHookLuaPatchPrefix));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (patches == null || patches.Prefixes == null || patches.Prefixes.Find(patch => patch.PatchMethod == _miHookLuaPatchRetPrefix) == null)
|
||||
{
|
||||
env.harmony.Patch(methodInfo, prefix: new HarmonyMethod(_miHookLuaPatchRetPrefix));
|
||||
}
|
||||
}
|
||||
|
||||
if (_hookPrefixMethods.TryGetValue(funcAddr, out HashSet<object> methodSet))
|
||||
{
|
||||
methodSet.Add(hookMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
_hookPrefixMethods.Add(funcAddr, new HashSet<object>() { hookMethod });
|
||||
}
|
||||
|
||||
}
|
||||
else if (hookMethodType == HookMethodType.After)
|
||||
{
|
||||
harmonyMethod = new HarmonyMethod(_miHookLuaPatchPostfix);
|
||||
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPostfix);
|
||||
}
|
||||
|
||||
foreach (var methodInfo in methodInfos)
|
||||
{
|
||||
if (methodInfo.Name == methodName)
|
||||
{
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
{
|
||||
if (hookMethodType == HookMethodType.Before)
|
||||
{
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
env.harmony.Patch(methodInfo, prefix: harmonyMethod);
|
||||
else
|
||||
env.harmony.Patch(methodInfo, prefix: harmonyMethodRet);
|
||||
}
|
||||
else if (hookMethodType == HookMethodType.After)
|
||||
{
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
env.harmony.Patch(methodInfo, postfix: harmonyMethod);
|
||||
else
|
||||
env.harmony.Patch(methodInfo, postfix: harmonyMethodRet);
|
||||
}
|
||||
|
||||
// build an unique method path by patch type, class, method self
|
||||
var methodPath = $"{hookMethodType}:{classType.Namespace}.{classType.Name}.{methodInfo.Name}";
|
||||
|
||||
if (hookMethod != null)
|
||||
if (patches == null || patches.Postfixes == null || patches.Postfixes.Find(patch => patch.PatchMethod == _miHookLuaPatchPostfix) == null)
|
||||
{
|
||||
if (!_hookMethods.TryAdd(methodPath, hookMethod))
|
||||
env.PrintError($"Failed to add key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]");
|
||||
#if DEBUG
|
||||
else
|
||||
env.PrintMessage($"Sucessfully added key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]");
|
||||
#endif
|
||||
env.harmony.Patch(methodInfo, postfix: new HarmonyMethod(_miHookLuaPatchPostfix));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (patches == null || patches.Postfixes == null || patches.Postfixes.Find(patch => patch.PatchMethod == _miHookLuaPatchRetPostfix) == null)
|
||||
{
|
||||
env.harmony.Patch(methodInfo, postfix: new HarmonyMethod(_miHookLuaPatchRetPostfix));
|
||||
}
|
||||
}
|
||||
|
||||
if (_hookPostfixMethods.TryGetValue(funcAddr, out HashSet<object> methodSet))
|
||||
{
|
||||
methodSet.Add(hookMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
_hookPostfixMethods.Add(funcAddr, new HashSet<object>() { hookMethod });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void HookMethod(string className, string methodName, object hookMethod, HookMethodType hookMethodType = HookMethodType.Before)
|
||||
{
|
||||
HookMethod(className, methodName, new string[] {}, hookMethod, hookMethodType);
|
||||
}
|
||||
|
||||
|
||||
public void EnqueueFunction(object function, params object[] args)
|
||||
{
|
||||
queuedFunctionCalls.Enqueue(new Tuple<object, object[]>(function, args));
|
||||
|
||||
Reference in New Issue
Block a user