Merge pull request #26 from zhu-rengong/master

modified way to patch, can realize params map for compatibility, and try to hook Item in lua.
This commit is contained in:
Evil Factory
2021-11-11 11:18:53 -03:00
committed by GitHub
6 changed files with 102 additions and 109 deletions
@@ -0,0 +1,24 @@
Hook.HookMethod("Barotrauma.Item", "TryInteract", function (instance, p)
Hook.Call("itemInteract", instance, p.picker, p.ignoreRequiredItems, p.forceSelectKey, p.forceActionKey)
end, Hook.HookMethodType.Before)
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)
@@ -49,7 +49,7 @@ if SERVER then
elseif CLIENT then
defaultLib["Sprite"] = CreateStatic("Sprite")
defaultLib["Keys"] = LuaUserData.RegisterType("Microsoft.Xna.Framework.Input.Keys")
defaultLib["Keys"] = LuaUserData.CreateStatic("Microsoft.Xna.Framework.Input.Keys")
defaultLib["PlayerInput"] = CreateStatic("PlayerInput")
end
@@ -11,6 +11,8 @@ for key, value in pairs(defaultLib) do
_G[key] = value
end
require("DefaultHook")
-- Execute Mods
if SERVER and Game.IsDedicated then
@@ -2166,11 +2166,6 @@ namespace Barotrauma
public bool TryInteract(Character picker, bool ignoreRequiredItems = false, bool forceSelectKey = false, bool forceActionKey = false)
{
var should = new LuaResult(GameMain.Lua.hook.Call("itemInteract", new object[] { this, picker, ignoreRequiredItems, forceSelectKey, forceActionKey }));
if (!should.IsNull())
return should.Bool();
if (CampaignInteractionType != CampaignMode.InteractionType.None) { return false; }
bool picked = false, selected = false;
@@ -2352,7 +2347,6 @@ namespace Barotrauma
if (should.Bool())
return;
bool remove = false;
foreach (ItemComponent ic in components)
@@ -2384,11 +2378,6 @@ namespace Barotrauma
public void ApplyTreatment(Character user, Character character, Limb targetLimb)
{
var should = new LuaResult(GameMain.Lua.hook.Call("itemApplyTreatment", new object[] { this, user, character, targetLimb }));
if (should.Bool())
return;
//can't apply treatment to dead characters
if (character.IsDead) { return; }
if (!UseInHealthInterface) { return; }
@@ -2454,11 +2443,6 @@ namespace Barotrauma
public bool Combine(Item item, Character user)
{
var should = new LuaResult(GameMain.Lua.hook.Call("itemCombine", new object[] { this, item, user }));
if (!should.IsNull())
return should.Bool();
if (item == this) { return false; }
bool isCombined = false;
foreach (ItemComponent ic in components)
@@ -2473,11 +2457,6 @@ namespace Barotrauma
public void Drop(Character dropper, bool createNetworkEvent = true)
{
var should = new LuaResult(GameMain.Lua.hook.Call("itemDrop", new object[] { this, dropper}));
if (should.Bool())
return;
if (createNetworkEvent)
{
if (parentInventory != null && !parentInventory.Owner.Removed && !Removed &&
@@ -2530,11 +2509,6 @@ namespace Barotrauma
public void Equip(Character character)
{
var should = new LuaResult(GameMain.Lua.hook.Call("itemEquip", new object[] { this, character}));
if (should.Bool())
return;
if (Removed)
{
DebugConsole.ThrowError($"Tried to equip a removed item ({Name}).\n{Environment.StackTrace.CleanupStackTrace()}");
@@ -2546,11 +2520,6 @@ namespace Barotrauma
public void Unequip(Character character)
{
var should = new LuaResult(GameMain.Lua.hook.Call("itemUnequip", new object[] { this, character }));
if (should.Bool())
return;
foreach (ItemComponent ic in components) { ic.Unequip(character); }
}
@@ -62,12 +62,12 @@ namespace Barotrauma
descriptor.AddMetaMember("__call", new ObjectCallbackMemberDescriptor("__call", LuaSetup.luaSetup.HandleCall));
}
public static void MakeFieldAccessible(IUserDataDescriptor IUUD, string methodName)
public static void MakeFieldAccessible(IUserDataDescriptor IUUD, string fieldName)
{
var descriptor = (StandardUserDataDescriptor)IUUD;
var field = IUUD.Type.GetField(methodName, BindingFlags.NonPublic | BindingFlags.Instance);
descriptor.RemoveMember(methodName);
descriptor.AddMember(methodName, new FieldMemberDescriptor(field, InteropAccessMode.Default));
var field = IUUD.Type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
descriptor.RemoveMember(fieldName);
descriptor.AddMember(fieldName, new FieldMemberDescriptor(field, InteropAccessMode.Default));
}
public static void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName)
@@ -692,7 +692,7 @@ namespace Barotrauma
public LuaHook(LuaSetup e)
{
env = e;
methodNameToHookName = new Dictionary<string, string>();
_hookMethods = new Dictionary<string, object>();
}
public class HookFunction
@@ -711,7 +711,7 @@ namespace Barotrauma
private Dictionary<string, Dictionary<string, HookFunction>> hookFunctions = new Dictionary<string, Dictionary<string, HookFunction>>();
private static Dictionary<string, string> methodNameToHookName;
private static Dictionary<string, object> _hookMethods;
private Queue<Tuple<object, object[]>> queuedFunctionCalls = new Queue<Tuple<object, object[]>>();
@@ -720,47 +720,50 @@ namespace Barotrauma
Before, After
}
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
{
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, __params[i]);
}
if (_hookMethods.TryGetValue(methodPath, out object hookMethod))
{
result = new LuaResult(luaSetup.hook.env.lua.Call(hookMethod, __instance, ptable));
}
else
{
DebugConsole.ThrowError($"No hook method found in _hookMethods[{methodPath}]");
}
}
catch (Exception ex)
{
DebugConsole.ThrowError(nameof(_hookLuaPatch), ex);
}
}
static bool HookLuaPatchPrefix(MethodBase __originalMethod, object[] __params, object __instance)
{
object[] parameters;
if (__instance == null)
{
parameters = __params;
}
else
{
parameters = new object[__params.Length + 1];
__params.CopyTo(parameters, 1);
parameters[0] = __instance;
}
var result = new LuaResult(luaSetup.hook.Call(methodNameToHookName[__originalMethod.Name], parameters));
if (!result.IsNull())
{
return false;
}
return true;
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
return result.IsNull();
}
static bool HookLuaPatchRetPrefix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{
object[] parameters;
if (__instance == null)
{
parameters = __params;
}
else
{
parameters = new object[__params.Length + 1];
__params.CopyTo(parameters, 1);
parameters[0] = __instance;
}
var result = new LuaResult(luaSetup.hook.Call(methodNameToHookName[__originalMethod.Name], parameters));
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
if (!result.IsNull())
{
@@ -773,34 +776,22 @@ namespace Barotrauma
static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __params, object __instance)
{
if (__instance == null)
luaSetup.hook.Call(methodNameToHookName[__originalMethod.Name], __params);
else
luaSetup.hook.Call(methodNameToHookName[__originalMethod.Name], __instance, __params);
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
}
static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{
object[] parameters;
if (__instance == null)
{
parameters = __params;
}
else
{
parameters = new object[__params.Length + 1];
__params.CopyTo(parameters, 1);
parameters[0] = __instance;
}
var result = new LuaResult(luaSetup.hook.Call(methodNameToHookName[__originalMethod.Name], parameters));
_hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
if (!result.IsNull())
__result = result.Object();
}
public void HookMethod(string className, string methodName, string hookName, 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 classType = Type.GetType(className);
var methodInfos = classType.GetMethods();
@@ -809,42 +800,50 @@ namespace Barotrauma
if (hookMethodType == HookMethodType.Before)
{
harmonyMethod = new HarmonyMethod(GetType().GetMethod("HookLuaPatchPrefix", BindingFlags.NonPublic | BindingFlags.Static));
harmonyMethodRet = new HarmonyMethod(GetType().GetMethod("HookLuaPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static));
harmonyMethod = new HarmonyMethod(_miHookLuaPatchPrefix);
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPrefix);
}
else if (hookMethodType == HookMethodType.After)
{
harmonyMethod = new HarmonyMethod(GetType().GetMethod("HookLuaPatchPostfix", BindingFlags.NonPublic | BindingFlags.Static));
harmonyMethodRet = new HarmonyMethod(GetType().GetMethod("HookLuaPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static));
harmonyMethod = new HarmonyMethod(_miHookLuaPatchPostfix);
harmonyMethodRet = new HarmonyMethod(_miHookLuaPatchRetPostfix);
}
var foundAny = false;
foreach (var methodInfo in methodInfos)
{
if(methodInfo.Name == methodName)
if (methodInfo.Name == methodName)
{
if (hookMethodType == HookMethodType.Before)
{
if (methodInfo.ReturnType == typeof(void))
env.harmony.Patch(methodInfo, harmonyMethod);
env.harmony.Patch(methodInfo, prefix: harmonyMethod);
else
env.harmony.Patch(methodInfo, harmonyMethodRet);
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);
}
foundAny = true;
// build an unique method path by patch type, class, method self
var methodPath = $"{hookMethodType}:{classType.Namespace}.{classType.Name}.{methodInfo.Name}";
if (hookMethod != null)
{
if (!_hookMethods.TryAdd(methodPath, hookMethod))
DebugConsole.ThrowError($"Failed to add key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]");
#if DEBUG
else
DebugConsole.NewMessage($"Sucessfully added key-value in {nameof(_hookMethods)}\n[{methodPath}, {hookMethod.ToString()}]", Color.LightSkyBlue);
#endif
}
break;
}
}
if(foundAny)
methodNameToHookName.Add(methodName, hookName);
}
public void EnqueueFunction(object function, params object[] args)
@@ -324,7 +324,6 @@ namespace Barotrauma
UserData.RegisterType<LuaFile>();
UserData.RegisterType<LuaNetworking>();
UserData.RegisterType<LuaUserData>();
UserData.RegisterType<LuaHook.HookMethodType>();
UserData.RegisterType<IUserDataDescriptor>();