do a hook call inside callback param of HookMethod, and wrapped __params

This commit is contained in:
zhurengong
2021-11-11 17:19:06 +08:00
parent 9635db22fc
commit d1188a1ea5
4 changed files with 86 additions and 65 deletions
@@ -1,10 +1,24 @@
Hook.HookMethod("Barotrauma.Item", "TryInteract", "itemInteract", nil) -- instance, picker, ignoreRequiredItems, forceSelectKey, forceActionKey Hook.HookMethod("Barotrauma.Item", "TryInteract", function (instance, p)
Hook.HookMethod("Barotrauma.Item", "Use", "itemUse", function (instance, deltaTime, character, targetLimb) return {instance, character, targetLimb} end) Hook.Call("itemInteract", instance, p.picker, p.ignoreRequiredItems, p.forceSelectKey, p.forceActionKey)
Hook.HookMethod("Barotrauma.Item", "SecondaryUse", "itemSecondaryUse", function (instance, deltaTime, character) return {instance, character} end) end, Hook.HookMethodType.Before)
Hook.HookMethod("Barotrauma.Item", "ApplyTreatment", "itemApplyTreatment", nil) -- instance, user, character, targetLimb
Hook.HookMethod("Barotrauma.Item", "Combine", "itemCombine", nil) -- instance, item, user
Hook.HookMethod("Barotrauma.Item", "Drop", "itemDrop", function (instance, dropper, createNetworkEvent) return {instance, dropper} end)
Hook.HookMethod("Barotrauma.Item", "Equip", "itemEquip", nil) -- instance, character
Hook.HookMethod("Barotrauma.Item", "Unequip", "itemUnequip", nil) -- instance, character
Hook.HookMethod("Barotrauma.Item", "ApplyTreatment", function (instance, p)
Hook.Call("itemApplyTreatment", instance, p.user, p.character, p.targetLimb)
end, Hook.HookMethodType.Before)
Hook.HookMethod("Barotrauma.Item", "Combine", function (instance, p)
Hook.Call("itemCombine", instance, p.item, p.user)
end, Hook.HookMethodType.Before)
Hook.HookMethod("Barotrauma.Item", "Drop", function (instance, p)
Hook.Call("itemDrop", instance, p.dropper)
end, Hook.HookMethodType.Before)
Hook.HookMethod("Barotrauma.Item", "Equip", function (instance, p)
Hook.Call("itemEquip", instance, p.character)
end, Hook.HookMethodType.Before)
Hook.HookMethod("Barotrauma.Item", "Unequip", function (instance, p)
Hook.Call("itemUnequip", instance, p.character)
end, Hook.HookMethodType.Before)
@@ -2304,6 +2304,11 @@ namespace Barotrauma
if (condition == 0.0f) { return; } if (condition == 0.0f) { return; }
var should = new LuaResult(GameMain.Lua.hook.Call("itemUse", new object[] { this, character, targetLimb }));
if (should.Bool())
return;
bool remove = false; bool remove = false;
foreach (ItemComponent ic in components) foreach (ItemComponent ic in components)
@@ -2337,6 +2342,11 @@ namespace Barotrauma
{ {
if (condition == 0.0f) { return; } if (condition == 0.0f) { return; }
var should = new LuaResult(GameMain.Lua.hook.Call("itemSecondaryUse", new object[] { this, character}));
if (should.Bool())
return;
bool remove = false; bool remove = false;
foreach (ItemComponent ic in components) foreach (ItemComponent ic in components)
@@ -692,8 +692,7 @@ namespace Barotrauma
public LuaHook(LuaSetup e) public LuaHook(LuaSetup e)
{ {
env = e; env = e;
_methodPathToHookName = new Dictionary<string, string>(); _hookMethods = new Dictionary<string, object>();
_luaPatchParamsMap = new Dictionary<string, object>();
} }
public class HookFunction public class HookFunction
@@ -712,8 +711,7 @@ namespace Barotrauma
private Dictionary<string, Dictionary<string, HookFunction>> hookFunctions = new Dictionary<string, Dictionary<string, HookFunction>>(); private Dictionary<string, Dictionary<string, HookFunction>> hookFunctions = new Dictionary<string, Dictionary<string, HookFunction>>();
private static Dictionary<string, string> _methodPathToHookName; private static Dictionary<string, object> _hookMethods;
private static Dictionary<string, object> _luaPatchParamsMap;
private Queue<Tuple<object, object[]>> queuedFunctionCalls = new Queue<Tuple<object, object[]>>(); private Queue<Tuple<object, object[]>> queuedFunctionCalls = new Queue<Tuple<object, object[]>>();
@@ -722,48 +720,50 @@ namespace Barotrauma
Before, After Before, After
} }
static void _hookLuaPatchPreProcess(MethodBase __originalMethod, object[] __params, object __instance, out string hookName, out object[] parameters) static void _hookLuaPatch(MethodBase __originalMethod, object[] __params, object __instance, out LuaResult result, HookMethodType hookMethodType)
{ {
// Although it works correctly, the performance is low
result = new LuaResult(null);
try try
{ {
var @class = __originalMethod.DeclaringType; var classType = __originalMethod.DeclaringType;
var methodPath = $"{@class.Namespace}.{@class.Name}.{__originalMethod.Name}"; var methodPath = $"{hookMethodType}:{classType.Namespace}.{classType.Name}.{__originalMethod.Name}";
hookName = _methodPathToHookName[methodPath]; var @params = __originalMethod.GetParameters();
var ptable = new Dictionary<string, object>();
for (int i = 0; i < @params.Length; i++)
{
ptable.Add(@params[i].Name, __params[i]);
// Regardless of whether __instance is null or not, it's necessaray to avoid the inability to distinguish parameters when adding hooks in lua. }
parameters = new object[__params.Length + 1];
__params.CopyTo(parameters, 1); if (_hookMethods.TryGetValue(methodPath, out object hookMethod))
parameters[0] = __instance;
// Map the parameters for compatibility
if (_luaPatchParamsMap.TryGetValue(methodPath, out object map))
{ {
var result = luaSetup.hook.env.lua.Call(map, parameters); result = new LuaResult(luaSetup.hook.env.lua.Call(hookMethod, __instance, ptable));
if (!result.IsNil()) parameters = result.ToObject<object[]>(); }
else
{
DebugConsole.ThrowError($"No hook method found in _hookMethods[{methodPath}]");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
parameters = null; DebugConsole.ThrowError(nameof(_hookLuaPatch), ex);
hookName = null;
DebugConsole.ThrowError(nameof(_hookLuaPatchPreProcess), ex);
} }
} }
static bool HookLuaPatchPrefix(MethodBase __originalMethod, object[] __params, object __instance) static bool HookLuaPatchPrefix(MethodBase __originalMethod, object[] __params, object __instance)
{ {
_hookLuaPatchPreProcess(__originalMethod, __params, __instance, out string hookName, out object[] parameters); _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
var result = new LuaResult(luaSetup.hook.Call(hookName, parameters));
return result.IsNull(); return result.IsNull();
} }
static bool HookLuaPatchRetPrefix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance) static bool HookLuaPatchRetPrefix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{ {
_hookLuaPatchPreProcess(__originalMethod, __params, __instance, out string hookName, out object[] parameters); _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
var result = new LuaResult(luaSetup.hook.Call(hookName, parameters));
if (!result.IsNull()) if (!result.IsNull())
{ {
@@ -776,71 +776,69 @@ namespace Barotrauma
static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __params, object __instance) static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __params, object __instance)
{ {
_hookLuaPatchPreProcess(__originalMethod, __params, __instance, out string hookName, out object[] parameters); _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
luaSetup.hook.Call(hookName, parameters);
} }
static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance) static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{ {
_hookLuaPatchPreProcess(__originalMethod, __params, __instance, out string hookName, out object[] parameters); _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
var result = new LuaResult(luaSetup.hook.Call(hookName, parameters));
if (!result.IsNull()) if (!result.IsNull())
__result = result.Object(); __result = result.Object();
} }
public void HookMethod(string className, string methodName, string hookName, object paramsMap, HookMethodType hookMethodType = HookMethodType.Before) private static MethodInfo _miHookLuaPatchPrefix = typeof(LuaHook).GetMethod("HookLuaPatchPrefix", BindingFlags.NonPublic | BindingFlags.Static);
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)
{ {
var @class = Type.GetType(className); var classType = Type.GetType(className);
var methodInfos = @class.GetMethods(); var methodInfos = classType.GetMethods();
HarmonyMethod harmonyMethod = new HarmonyMethod(); HarmonyMethod harmonyMethod = new HarmonyMethod();
HarmonyMethod harmonyMethodRet = new HarmonyMethod(); HarmonyMethod harmonyMethodRet = new HarmonyMethod();
if (hookMethodType == HookMethodType.Before) if (hookMethodType == HookMethodType.Before)
{ {
harmonyMethod = new HarmonyMethod(GetType().GetMethod("HookLuaPatchPrefix", BindingFlags.NonPublic | BindingFlags.Static)); harmonyMethod = new HarmonyMethod(_miHookLuaPatchPrefix);
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPrefix);
harmonyMethodRet = new HarmonyMethod(GetType().GetMethod("HookLuaPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static));
} }
else if (hookMethodType == HookMethodType.After) else if (hookMethodType == HookMethodType.After)
{ {
harmonyMethod = new HarmonyMethod(GetType().GetMethod("HookLuaPatchPostfix", BindingFlags.NonPublic | BindingFlags.Static)); harmonyMethod = new HarmonyMethod(_miHookLuaPatchPostfix);
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPostfix);
harmonyMethodRet = new HarmonyMethod(GetType().GetMethod("HookLuaPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static));
} }
foreach (var methodInfo in methodInfos) foreach (var methodInfo in methodInfos)
{ {
if (methodInfo.Name == methodName) if (methodInfo.Name == methodName)
{ {
if (hookMethodType == HookMethodType.Before) if (hookMethodType == HookMethodType.Before)
{
if (methodInfo.ReturnType == typeof(void)) if (methodInfo.ReturnType == typeof(void))
env.harmony.Patch(methodInfo, harmonyMethod); env.harmony.Patch(methodInfo, prefix: harmonyMethod);
else else
env.harmony.Patch(methodInfo, harmonyMethodRet); env.harmony.Patch(methodInfo, prefix: harmonyMethodRet);
}
else if (hookMethodType == HookMethodType.After) else if (hookMethodType == HookMethodType.After)
{
if (methodInfo.ReturnType == typeof(void)) if (methodInfo.ReturnType == typeof(void))
env.harmony.Patch(methodInfo, postfix: harmonyMethod); env.harmony.Patch(methodInfo, postfix: harmonyMethod);
else else
env.harmony.Patch(methodInfo, postfix: harmonyMethodRet); env.harmony.Patch(methodInfo, postfix: harmonyMethodRet);
}
var methodPath = $"{@class.Namespace}.{@class.Name}.{methodInfo.Name}"; // build an unique method path by patch type, class, method self
if (_methodPathToHookName.TryAdd(methodPath, hookName)) var methodPath = $"{hookMethodType}:{classType.Namespace}.{classType.Name}.{methodInfo.Name}";
DebugConsole.NewMessage($"Sucessfully added key-value in {nameof(_methodPathToHookName)}\n[{methodPath}, {hookName}]", Color.LightSkyBlue);
else DebugConsole.ThrowError($"Failed to add key-value in {nameof(_methodPathToHookName)}\n[{methodPath}, {hookName}]");
if (paramsMap != null) if (hookMethod != null)
{ {
if (_luaPatchParamsMap.TryAdd(methodPath, paramsMap)) if (!_hookMethods.TryAdd(methodPath, hookMethod))
DebugConsole.NewMessage($"Sucessfully added key-value in {nameof(_luaPatchParamsMap)}\n[{methodPath}, {paramsMap.ToString()}]", Color.LightSkyBlue); DebugConsole.ThrowError($"Failed to add key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]");
else DebugConsole.ThrowError($"Failed to add key-value in {nameof(_luaPatchParamsMap)}\n[{methodPath}, {paramsMap.ToString()}]"); #if DEBUG
else
DebugConsole.NewMessage($"Sucessfully added key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]", Color.LightSkyBlue);
#endif
} }
break; break;
} }
@@ -324,7 +324,6 @@ namespace Barotrauma
UserData.RegisterType<LuaFile>(); UserData.RegisterType<LuaFile>();
UserData.RegisterType<LuaNetworking>(); UserData.RegisterType<LuaNetworking>();
UserData.RegisterType<LuaUserData>(); UserData.RegisterType<LuaUserData>();
UserData.RegisterType<LuaHook.HookMethodType>();
UserData.RegisterType<IUserDataDescriptor>(); UserData.RegisterType<IUserDataDescriptor>();