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 elseif CLIENT then
defaultLib["Sprite"] = CreateStatic("Sprite") 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") defaultLib["PlayerInput"] = CreateStatic("PlayerInput")
end end
@@ -11,6 +11,8 @@ for key, value in pairs(defaultLib) do
_G[key] = value _G[key] = value
end end
require("DefaultHook")
-- Execute Mods -- Execute Mods
if SERVER and Game.IsDedicated then 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) 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; } if (CampaignInteractionType != CampaignMode.InteractionType.None) { return false; }
bool picked = false, selected = false; bool picked = false, selected = false;
@@ -2352,7 +2347,6 @@ namespace Barotrauma
if (should.Bool()) if (should.Bool())
return; return;
bool remove = false; bool remove = false;
foreach (ItemComponent ic in components) foreach (ItemComponent ic in components)
@@ -2384,11 +2378,6 @@ namespace Barotrauma
public void ApplyTreatment(Character user, Character character, Limb targetLimb) 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 //can't apply treatment to dead characters
if (character.IsDead) { return; } if (character.IsDead) { return; }
if (!UseInHealthInterface) { return; } if (!UseInHealthInterface) { return; }
@@ -2454,11 +2443,6 @@ namespace Barotrauma
public bool Combine(Item item, Character user) 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; } if (item == this) { return false; }
bool isCombined = false; bool isCombined = false;
foreach (ItemComponent ic in components) foreach (ItemComponent ic in components)
@@ -2473,11 +2457,6 @@ namespace Barotrauma
public void Drop(Character dropper, bool createNetworkEvent = true) 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 (createNetworkEvent)
{ {
if (parentInventory != null && !parentInventory.Owner.Removed && !Removed && if (parentInventory != null && !parentInventory.Owner.Removed && !Removed &&
@@ -2530,11 +2509,6 @@ namespace Barotrauma
public void Equip(Character character) public void Equip(Character character)
{ {
var should = new LuaResult(GameMain.Lua.hook.Call("itemEquip", new object[] { this, character}));
if (should.Bool())
return;
if (Removed) if (Removed)
{ {
DebugConsole.ThrowError($"Tried to equip a removed item ({Name}).\n{Environment.StackTrace.CleanupStackTrace()}"); DebugConsole.ThrowError($"Tried to equip a removed item ({Name}).\n{Environment.StackTrace.CleanupStackTrace()}");
@@ -2546,11 +2520,6 @@ namespace Barotrauma
public void Unequip(Character character) 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); } foreach (ItemComponent ic in components) { ic.Unequip(character); }
} }
@@ -62,12 +62,12 @@ namespace Barotrauma
descriptor.AddMetaMember("__call", new ObjectCallbackMemberDescriptor("__call", LuaSetup.luaSetup.HandleCall)); 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 descriptor = (StandardUserDataDescriptor)IUUD;
var field = IUUD.Type.GetField(methodName, BindingFlags.NonPublic | BindingFlags.Instance); var field = IUUD.Type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
descriptor.RemoveMember(methodName); descriptor.RemoveMember(fieldName);
descriptor.AddMember(methodName, new FieldMemberDescriptor(field, InteropAccessMode.Default)); descriptor.AddMember(fieldName, new FieldMemberDescriptor(field, InteropAccessMode.Default));
} }
public static void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName) public static void MakeMethodAccessible(IUserDataDescriptor IUUD, string methodName)
@@ -692,7 +692,7 @@ namespace Barotrauma
public LuaHook(LuaSetup e) public LuaHook(LuaSetup e)
{ {
env = e; env = e;
methodNameToHookName = new Dictionary<string, string>(); _hookMethods = new Dictionary<string, object>();
} }
public class HookFunction public class HookFunction
@@ -711,7 +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> methodNameToHookName; private static Dictionary<string, object> _hookMethods;
private Queue<Tuple<object, object[]>> queuedFunctionCalls = new Queue<Tuple<object, object[]>>(); private Queue<Tuple<object, object[]>> queuedFunctionCalls = new Queue<Tuple<object, object[]>>();
@@ -720,47 +720,50 @@ namespace Barotrauma
Before, After 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) static bool HookLuaPatchPrefix(MethodBase __originalMethod, object[] __params, object __instance)
{ {
object[] parameters; _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
if (__instance == null) return result.IsNull();
{
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;
} }
static bool HookLuaPatchRetPrefix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance) static bool HookLuaPatchRetPrefix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{ {
object[] parameters; _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.Before);
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()) if (!result.IsNull())
{ {
@@ -773,34 +776,22 @@ namespace Barotrauma
static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __params, object __instance) static void HookLuaPatchPostfix(MethodBase __originalMethod, object[] __params, object __instance)
{ {
if (__instance == null) _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
luaSetup.hook.Call(methodNameToHookName[__originalMethod.Name], __params);
else
luaSetup.hook.Call(methodNameToHookName[__originalMethod.Name], __instance, __params);
} }
static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance) static void HookLuaPatchRetPostfix(MethodBase __originalMethod, object[] __params, ref object __result, object __instance)
{ {
object[] parameters; _hookLuaPatch(__originalMethod, __params, __instance, out LuaResult result, HookMethodType.After);
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()) if (!result.IsNull())
__result = result.Object(); __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 classType = Type.GetType(className);
var methodInfos = classType.GetMethods(); var methodInfos = classType.GetMethods();
@@ -809,42 +800,50 @@ namespace Barotrauma
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));
} }
var foundAny = false;
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);
}
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) public void EnqueueFunction(object function, params object[] args)
@@ -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>();