It works. Except (HookMethod->Harmony: L189) is throwing NRE.

This commit is contained in:
MapleWheels
2026-02-02 20:45:09 -05:00
committed by Maplewheels
parent 2eb593f461
commit 06348d3ba5
7 changed files with 69 additions and 66 deletions

View File

@@ -17,12 +17,12 @@ public interface ILuaCsHook : ILuaCsShim
T Call<T>(string eventName, params object[] args);
// Hook/Method Patching
string Patch(string identifier, string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, EventService.HookMethodType hookType = EventService.HookMethodType.Before);
string Patch(string identifier, string className, string methodName, LuaCsPatchFunc patch, EventService.HookMethodType hookType = EventService.HookMethodType.Before);
string Patch(string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, EventService.HookMethodType hookType = EventService.HookMethodType.Before);
string Patch(string className, string methodName, LuaCsPatchFunc patch, EventService.HookMethodType hookType = EventService.HookMethodType.Before);
bool RemovePatch(string identifier, string className, string methodName, string[] parameterTypes, EventService.HookMethodType hookType);
bool RemovePatch(string identifier, string className, string methodName, EventService.HookMethodType hookType);
string Patch(string identifier, string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before);
string Patch(string identifier, string className, string methodName, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before);
string Patch(string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before);
string Patch(string className, string methodName, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before);
bool RemovePatch(string identifier, string className, string methodName, string[] parameterTypes, HookMethodType hookType);
bool RemovePatch(string identifier, string className, string methodName, HookMethodType hookType);
void HookMethod(string identifier, MethodBase method, LuaCsCompatPatchFunc patch, HookMethodType hookType = HookMethodType.Before, IAssemblyPlugin owner = null);

View File

@@ -477,7 +477,7 @@ public class PluginManagementService : IAssemblyManagementService
IEnumerable<MetadataReference> GetMetadataReferences()
{
#if !DEBUG
throw new NotImplementedException($"Needs to use publicized barotrauma assemblies.");
throw new NotImplementedException($"Needs to use publicized barotrauma assemblies and cache metadata.");
#endif
return Basic.Reference.Assemblies.Net80.References.All
.Union(AppDomain.CurrentDomain.GetAssemblies()

View File

@@ -303,6 +303,7 @@ public sealed class ModConfigService : IModConfigService
FriendlyName = IAssemblyLoaderService.InternalsAwareAssemblyName,
IncompatiblePackages = ImmutableArray<Identifier>.Empty,
RequiredPackages = ImmutableArray<Identifier>.Empty,
UseInternalAccessName = true,
IsScript = true
});
}

View File

@@ -416,11 +416,6 @@ namespace Barotrauma.LuaCs.Services
partial class EventService
{
public enum HookMethodType
{
Before, After
}
private class LuaCsHookCallback
{
public string name;
@@ -805,7 +800,7 @@ namespace Barotrauma.LuaCs.Services
// If you need to debug this:
// - use https://sharplab.io ; it's a very useful for resource for writing IL by hand.
// - use il.NewMessage("") or il.WriteLine("") to see where the IL crashes at runtime.
private MethodInfo CreateDynamicHarmonyPatch(string identifier, MethodBase original, HookMethodType hookType)
private MethodInfo CreateDynamicHarmonyPatch(string identifier, MethodBase original, LuaCsHook.HookMethodType hookType)
{
var parameters = new List<DynamicParameterMapping>
{
@@ -840,9 +835,9 @@ namespace Barotrauma.LuaCs.Services
var luaCsField = typeBuilder.DefineField(FIELD_LUACS, typeof(LuaCsSetup), FieldAttributes.Public | FieldAttributes.Static);
var methodName = hookType == HookMethodType.Before ? "HarmonyPrefix" : "HarmonyPostfix";
var methodName = hookType == LuaCsHook.HookMethodType.Before ? "HarmonyPrefix" : "HarmonyPostfix";
var il = Emit.BuildMethod(
returnType: hookType == HookMethodType.Before ? typeof(bool) : typeof(void),
returnType: hookType == LuaCsHook.HookMethodType.Before ? typeof(bool) : typeof(void),
parameterTypes: parameters.Select(x => x.HarmonyPatchParamType).ToArray(),
type: typeBuilder,
name: methodName,
@@ -911,7 +906,7 @@ namespace Barotrauma.LuaCs.Services
il.NewObject(typeof(ParameterTable), typeof(Dictionary<string, object>));
il.StoreLocal(ptable);
if (hasReturnType && hookType == HookMethodType.After)
if (hasReturnType && hookType == LuaCsHook.HookMethodType.After)
{
// IL: ptable.OriginalReturnValue = __result;
il.LoadLocal(ptable);
@@ -924,7 +919,7 @@ namespace Barotrauma.LuaCs.Services
var enumerator = il.DeclareLocal<IEnumerator<LuaCsPatch>>("enumerator");
il.LoadLocal(patches);
il.CallVirtual(typeof(PatchedMethod).GetMethod(
name: hookType == HookMethodType.Before
name: hookType == LuaCsHook.HookMethodType.Before
? nameof(PatchedMethod.GetPrefixEnumerator)
: nameof(PatchedMethod.GetPostfixEnumerator),
bindingAttr: BindingFlags.Public | BindingFlags.Instance));
@@ -1073,7 +1068,7 @@ namespace Barotrauma.LuaCs.Services
il.EndExceptionBlock(exceptionBlock);
// Only prefixes return a bool
if (hookType == HookMethodType.Before)
if (hookType == LuaCsHook.HookMethodType.Before)
{
il.LoadLocal(harmonyReturnValue);
}
@@ -1090,7 +1085,7 @@ namespace Barotrauma.LuaCs.Services
return type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
}
private string Patch(string identifier, MethodBase method, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before)
private string Patch(string identifier, MethodBase method, LuaCsPatchFunc patch, LuaCsHook.HookMethodType hookType = LuaCsHook.HookMethodType.Before)
{
if (method == null) throw new ArgumentNullException(nameof(method));
if (patch == null) throw new ArgumentNullException(nameof(patch));
@@ -1102,13 +1097,13 @@ namespace Barotrauma.LuaCs.Services
var patchKey = MethodKey.Create(method);
if (!registeredPatches.TryGetValue(patchKey, out var methodPatches))
{
var harmonyPrefix = CreateDynamicHarmonyPatch(identifier, method, HookMethodType.Before);
var harmonyPostfix = CreateDynamicHarmonyPatch(identifier, method, HookMethodType.After);
var harmonyPrefix = CreateDynamicHarmonyPatch(identifier, method, LuaCsHook.HookMethodType.Before);
var harmonyPostfix = CreateDynamicHarmonyPatch(identifier, method, LuaCsHook.HookMethodType.After);
harmony.Patch(method, prefix: new HarmonyMethod(harmonyPrefix), postfix: new HarmonyMethod(harmonyPostfix));
methodPatches = registeredPatches[patchKey] = new PatchedMethod(harmonyPrefix, harmonyPostfix);
}
if (hookType == HookMethodType.Before)
if (hookType == LuaCsHook.HookMethodType.Before)
{
if (methodPatches.Prefixes.Remove(identifier))
{
@@ -1121,7 +1116,7 @@ namespace Barotrauma.LuaCs.Services
PatchFunc = patch,
});
}
else if (hookType == HookMethodType.After)
else if (hookType == LuaCsHook.HookMethodType.After)
{
if (methodPatches.Postfixes.Remove(identifier))
{
@@ -1138,31 +1133,31 @@ namespace Barotrauma.LuaCs.Services
return identifier;
}
public string Patch(string identifier, string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before)
public string Patch(string identifier, string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, LuaCsHook.HookMethodType hookType = LuaCsHook.HookMethodType.Before)
{
var method = ResolveMethod(className, methodName, parameterTypes);
return Patch(identifier, method, patch, hookType);
}
public string Patch(string identifier, string className, string methodName, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before)
public string Patch(string identifier, string className, string methodName, LuaCsPatchFunc patch, LuaCsHook.HookMethodType hookType = LuaCsHook.HookMethodType.Before)
{
var method = ResolveMethod(className, methodName, null);
return Patch(identifier, method, patch, hookType);
}
public string Patch(string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before)
public string Patch(string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, LuaCsHook.HookMethodType hookType = LuaCsHook.HookMethodType.Before)
{
var method = ResolveMethod(className, methodName, parameterTypes);
return Patch(null, method, patch, hookType);
}
public string Patch(string className, string methodName, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before)
public string Patch(string className, string methodName, LuaCsPatchFunc patch, LuaCsHook.HookMethodType hookType = LuaCsHook.HookMethodType.Before)
{
var method = ResolveMethod(className, methodName, null);
return Patch(null, method, patch, hookType);
}
private bool RemovePatch(string identifier, MethodBase method, HookMethodType hookType)
private bool RemovePatch(string identifier, MethodBase method, LuaCsHook.HookMethodType hookType)
{
if (identifier == null) throw new ArgumentNullException(nameof(identifier));
identifier = NormalizeIdentifier(identifier);
@@ -1175,19 +1170,19 @@ namespace Barotrauma.LuaCs.Services
return hookType switch
{
HookMethodType.Before => methodPatches.Prefixes.Remove(identifier),
HookMethodType.After => methodPatches.Postfixes.Remove(identifier),
_ => throw new ArgumentException($"Invalid {nameof(HookMethodType)} enum value.", nameof(hookType)),
LuaCsHook.HookMethodType.Before => methodPatches.Prefixes.Remove(identifier),
LuaCsHook.HookMethodType.After => methodPatches.Postfixes.Remove(identifier),
_ => throw new ArgumentException($"Invalid {nameof(LuaCsHook.HookMethodType)} enum value.", nameof(hookType)),
};
}
public bool RemovePatch(string identifier, string className, string methodName, string[] parameterTypes, HookMethodType hookType)
public bool RemovePatch(string identifier, string className, string methodName, string[] parameterTypes, LuaCsHook.HookMethodType hookType)
{
var method = ResolveMethod(className, methodName, parameterTypes);
return RemovePatch(identifier, method, hookType);
}
public bool RemovePatch(string identifier, string className, string methodName, HookMethodType hookType)
public bool RemovePatch(string identifier, string className, string methodName, LuaCsHook.HookMethodType hookType)
{
var method = ResolveMethod(className, methodName, null);
return RemovePatch(identifier, method, hookType);

View File

@@ -23,7 +23,7 @@ namespace Barotrauma.LuaCs.Services
private Dictionary<long, HashSet<(string, LuaCsCompatPatchFunc, IAssemblyPlugin)>> compatHookPrefixMethods = new Dictionary<long, HashSet<(string, LuaCsCompatPatchFunc, IAssemblyPlugin)>>();
private Dictionary<long, HashSet<(string, LuaCsCompatPatchFunc, IAssemblyPlugin)>> compatHookPostfixMethods = new Dictionary<long, HashSet<(string, LuaCsCompatPatchFunc, IAssemblyPlugin)>>();
private static void _hookLuaCsPatch(MethodBase __originalMethod, object[] __args, object __instance, out object result, HookMethodType hookType)
private static void _hookLuaCsPatch(MethodBase __originalMethod, object[] __args, object __instance, out object result, ILuaCsHook.HookMethodType hookType)
{
result = null;
@@ -33,14 +33,14 @@ namespace Barotrauma.LuaCs.Services
HashSet<(string, LuaCsCompatPatchFunc, IAssemblyPlugin)> methodSet = null;
switch (hookType)
{
case HookMethodType.Before:
case ILuaCsHook.HookMethodType.Before:
instance.compatHookPrefixMethods.TryGetValue(funcAddr, out methodSet);
break;
case HookMethodType.After:
case ILuaCsHook.HookMethodType.After:
instance.compatHookPostfixMethods.TryGetValue(funcAddr, out methodSet);
break;
default:
throw new ArgumentException($"Invalid {nameof(HookMethodType)} enum value.", nameof(hookType));
throw new ArgumentException($"Invalid {nameof(ILuaCsHook.HookMethodType)} enum value.", nameof(hookType));
}
if (methodSet != null)
@@ -98,16 +98,16 @@ namespace Barotrauma.LuaCs.Services
private static bool HookLuaCsPatchPrefix(MethodBase __originalMethod, object[] __args, object __instance)
{
_hookLuaCsPatch(__originalMethod, __args, __instance, out object result, HookMethodType.Before);
_hookLuaCsPatch(__originalMethod, __args, __instance, out object result, ILuaCsHook.HookMethodType.Before);
return result == null;
}
private static void HookLuaCsPatchPostfix(MethodBase __originalMethod, object[] __args, object __instance) =>
_hookLuaCsPatch(__originalMethod, __args, __instance, out object _, HookMethodType.After);
_hookLuaCsPatch(__originalMethod, __args, __instance, out object _, ILuaCsHook.HookMethodType.After);
private static bool HookLuaCsPatchRetPrefix(MethodBase __originalMethod, object[] __args, ref object __result, object __instance)
{
_hookLuaCsPatch(__originalMethod, __args, __instance, out object result, HookMethodType.Before);
_hookLuaCsPatch(__originalMethod, __args, __instance, out object result, ILuaCsHook.HookMethodType.Before);
if (result != null)
{
__result = result;
@@ -118,7 +118,7 @@ namespace Barotrauma.LuaCs.Services
private static void HookLuaCsPatchRetPostfix(MethodBase __originalMethod, object[] __args, ref object __result, object __instance)
{
_hookLuaCsPatch(__originalMethod, __args, __instance, out object result, HookMethodType.After);
_hookLuaCsPatch(__originalMethod, __args, __instance, out object result, ILuaCsHook.HookMethodType.After);
if (result != null) __result = result;
}
@@ -130,10 +130,6 @@ namespace Barotrauma.LuaCs.Services
// TODO: deprecate this
public void HookMethod(string identifier, MethodBase method, LuaCsCompatPatchFunc patch, ILuaCsHook.HookMethodType hookType = ILuaCsHook.HookMethodType.Before, IAssemblyPlugin owner = null)
{
throw new NotImplementedException();
}
public void HookMethod(string identifier, MethodBase method, LuaCsCompatPatchFunc patch, HookMethodType hookType = HookMethodType.Before, IAssemblyPlugin owner = null)
{
if (identifier == null || method == null || patch == null)
{
@@ -145,7 +141,7 @@ namespace Barotrauma.LuaCs.Services
var funcAddr = ((long)method.MethodHandle.GetFunctionPointer());
var patches = Harmony.GetPatchInfo(method);
if (hookType == HookMethodType.Before)
if (hookType == ILuaCsHook.HookMethodType.Before)
{
if (method is MethodInfo mi && mi.ReturnType != typeof(void))
{
@@ -177,7 +173,7 @@ namespace Barotrauma.LuaCs.Services
}
}
else if (hookType == HookMethodType.After)
else if (hookType == ILuaCsHook.HookMethodType.After)
{
if (method is MethodInfo mi && mi.ReturnType != typeof(void))
{
@@ -209,7 +205,7 @@ namespace Barotrauma.LuaCs.Services
}
}
}
protected void HookMethod(string identifier, string className, string methodName, string[] parameterNames, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before)
protected void HookMethod(string identifier, string className, string methodName, string[] parameterNames, LuaCsCompatPatchFunc patch, ILuaCsHook.HookMethodType hookMethodType = ILuaCsHook.HookMethodType.Before)
{
var method = ResolveMethod(className, methodName, parameterNames);
if (method == null) return;
@@ -219,26 +215,26 @@ namespace Barotrauma.LuaCs.Services
}
HookMethod(identifier, method, patch, hookMethodType);
}
protected void HookMethod(string identifier, string className, string methodName, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before) =>
protected void HookMethod(string identifier, string className, string methodName, LuaCsCompatPatchFunc patch, ILuaCsHook.HookMethodType hookMethodType = ILuaCsHook.HookMethodType.Before) =>
HookMethod(identifier, className, methodName, null, patch, hookMethodType);
protected void HookMethod(string className, string methodName, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before) =>
protected void HookMethod(string className, string methodName, LuaCsCompatPatchFunc patch, ILuaCsHook.HookMethodType hookMethodType = ILuaCsHook.HookMethodType.Before) =>
HookMethod("", className, methodName, null, patch, hookMethodType);
protected void HookMethod(string className, string methodName, string[] parameterNames, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before) =>
protected void HookMethod(string className, string methodName, string[] parameterNames, LuaCsCompatPatchFunc patch, ILuaCsHook.HookMethodType hookMethodType = ILuaCsHook.HookMethodType.Before) =>
HookMethod("", className, methodName, parameterNames, patch, hookMethodType);
public void UnhookMethod(string identifier, MethodBase method, HookMethodType hookType = HookMethodType.Before)
public void UnhookMethod(string identifier, MethodBase method, ILuaCsHook.HookMethodType hookType = ILuaCsHook.HookMethodType.Before)
{
var funcAddr = (long)method.MethodHandle.GetFunctionPointer();
Dictionary<long, HashSet<(string, LuaCsCompatPatchFunc, IAssemblyPlugin)>> methods;
if (hookType == HookMethodType.Before) methods = compatHookPrefixMethods;
else if (hookType == HookMethodType.After) methods = compatHookPostfixMethods;
if (hookType == ILuaCsHook.HookMethodType.Before) methods = compatHookPrefixMethods;
else if (hookType == ILuaCsHook.HookMethodType.After) methods = compatHookPostfixMethods;
else throw null;
if (methods.ContainsKey(funcAddr)) methods[funcAddr]?.RemoveWhere(t => t.Item1 == identifier);
}
protected void UnhookMethod(string identifier, string className, string methodName, string[] parameterNames, HookMethodType hookType = HookMethodType.Before)
protected void UnhookMethod(string identifier, string className, string methodName, string[] parameterNames, ILuaCsHook.HookMethodType hookType = ILuaCsHook.HookMethodType.Before)
{
var method = ResolveMethod(className, methodName, parameterNames);
if (method == null) return;

View File

@@ -11,6 +11,16 @@ using Microsoft.CodeAnalysis.CSharp;
namespace Barotrauma.LuaCs;
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class IgnoresAccessChecksToAttribute : Attribute
{
public string AssemblyName { get; }
public IgnoresAccessChecksToAttribute(string assemblyName)
{
AssemblyName = assemblyName;
}
}
public interface IAssemblyLoaderService : IService
{
public interface IFactory : IService

View File

@@ -1,5 +1,5 @@
extern alias Client;
extern alias Server;
using Client::Barotrauma.LuaCs.Services;
using Client::Barotrauma;
using MoonSharp.Interpreter;
@@ -8,6 +8,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Server::Barotrauma.LuaCs.Services.Compatibility;
using Xunit;
namespace TestProject.LuaCs
@@ -64,14 +65,14 @@ namespace TestProject.LuaCs
string methodName,
string[]? parameters,
string function,
EventService.HookMethodType patchType)
ILuaCsHook.HookMethodType patchType)
{
var args = BuildHookPatchArgsList(patchId, className, methodName, parameters);
args.Add(function);
args.Add(patchType switch
{
EventService.HookMethodType.Before => "Hook.HookMethodType.Before",
EventService.HookMethodType.After => "Hook.HookMethodType.After",
ILuaCsHook.HookMethodType.Before => "Hook.HookMethodType.Before",
ILuaCsHook.HookMethodType.After => "Hook.HookMethodType.After",
_ => throw new NotImplementedException(),
});
throw new NotImplementedException();
@@ -84,13 +85,13 @@ namespace TestProject.LuaCs
string className,
string methodName,
string[]? parameters,
EventService.HookMethodType patchType)
ILuaCsHook.HookMethodType patchType)
{
var args = BuildHookPatchArgsList(patchId, className, methodName, parameters);
args.Add(patchType switch
{
EventService.HookMethodType.Before => "Hook.HookMethodType.Before",
EventService.HookMethodType.After => "Hook.HookMethodType.After",
ILuaCsHook.HookMethodType.Before => "Hook.HookMethodType.Before",
ILuaCsHook.HookMethodType.After => "Hook.HookMethodType.After",
_ => throw new NotImplementedException(),
});
throw new NotImplementedException();
@@ -104,7 +105,7 @@ namespace TestProject.LuaCs
function(instance, ptable)
{body}
end
", EventService.HookMethodType.Before);
", ILuaCsHook.HookMethodType.Before);
Assert.Equal(DataType.String, returnValue.Type);
return new(returnValue.String, () => luaCs.RemovePrefix<T>(returnValue.String, methodName, parameters));
}
@@ -116,7 +117,7 @@ namespace TestProject.LuaCs
function(instance, ptable)
{body}
end
", EventService.HookMethodType.After);
", ILuaCsHook.HookMethodType.After);
Assert.Equal(DataType.String, returnValue.Type);
return new(returnValue.String, () => luaCs.RemovePostfix<T>(returnValue.String, methodName, parameters));
}
@@ -124,7 +125,7 @@ namespace TestProject.LuaCs
public static bool RemovePrefix<T>(this LuaCsSetup luaCs, string patchId, string methodName, string[]? parameters = null)
{
var className = typeof(T).FullName!;
var returnValue = luaCs.DoHookRemovePatch(patchId, className, methodName, parameters, EventService.HookMethodType.Before);
var returnValue = luaCs.DoHookRemovePatch(patchId, className, methodName, parameters, ILuaCsHook.HookMethodType.Before);
Assert.Equal(DataType.Boolean, returnValue.Type);
return returnValue.Boolean;
}
@@ -132,7 +133,7 @@ namespace TestProject.LuaCs
public static bool RemovePostfix<T>(this LuaCsSetup luaCs, string patchId, string methodName, string[]? parameters = null)
{
var className = typeof(T).FullName!;
var returnValue = luaCs.DoHookRemovePatch(patchId, className, methodName, parameters, EventService.HookMethodType.After);
var returnValue = luaCs.DoHookRemovePatch(patchId, className, methodName, parameters, ILuaCsHook.HookMethodType.After);
Assert.Equal(DataType.Boolean, returnValue.Type);
return returnValue.Boolean;
}