diff --git a/CringeLauncher/Patches/PluginTypePatch.cs b/CringeLauncher/Patches/PluginTypePatch.cs new file mode 100644 index 0000000..dacf7a3 --- /dev/null +++ b/CringeLauncher/Patches/PluginTypePatch.cs @@ -0,0 +1,51 @@ +using CringePlugins.Loader; +using HarmonyLib; +using Sandbox.Game.World; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Loader; +using VRage.Game.ObjectBuilder; +using VRage.Plugins; + +namespace CringeLauncher.Patches; + +[HarmonyPatch] +internal static class PluginTypePatch +{ + [HarmonyTargetMethods] + public static IEnumerable TargetMethods() + { + yield return AccessTools.Method(typeof(MySession), "RegisterComponentsFromAssemblies"); + yield return AccessTools.Method(typeof(MyGlobalTypeMetadata), nameof(MyGlobalTypeMetadata.Init)); + } + + [HarmonyTranspiler] + public static IEnumerable Transpiler(IEnumerable instructions, MethodBase original) + { + //replaces plugin.GetType() with GetPluginType(plugin) + var pve = AccessTools.Method(typeof(Assembly), nameof(Assembly.Load), [typeof(AssemblyName)]); + var method = AccessTools.Method(typeof(object), nameof(GetType)); + var getter = AccessTools.PropertyGetter(typeof(MyPlugins), nameof(MyPlugins.Plugins)); + + + var matcher = new CodeMatcher(instructions); + + if (original.DeclaringType == typeof(MySession)) + { + matcher = matcher + .SearchForward(b => b.Calls(pve)) + .Set(OpCodes.Call, AccessTools.Method(typeof(PluginTypePatch), nameof(LoadAssembly))); + } + + return matcher + .SearchForward(b => b.Calls(getter)) + .SearchForward(b => b.Calls(method)) + .Set(OpCodes.Call, AccessTools.Method(typeof(PluginTypePatch), nameof(GetPluginType))) + .InstructionEnumeration(); + } + + //Assembly.Load is called in MySession.RegisterComponentsFromAssemblies. When patching, it uses the wrong context + //todo: maybe there's a better way to do this? + private static Assembly LoadAssembly(AssemblyName name) => AssemblyLoadContext.GetLoadContext(typeof(Launcher).Assembly)!.LoadFromAssemblyName(name); + private static Type GetPluginType(IPlugin plugin) => plugin is PluginWrapper wrapper ? wrapper.InstanceType : plugin.GetType(); +} diff --git a/CringePlugins/Loader/PluginWrapper.cs b/CringePlugins/Loader/PluginWrapper.cs index e8f13ba..d7b883b 100644 --- a/CringePlugins/Loader/PluginWrapper.cs +++ b/CringePlugins/Loader/PluginWrapper.cs @@ -11,6 +11,8 @@ internal sealed class PluginWrapper(PluginMetadata metadata, IPlugin plugin) : I public bool HasError => LastException != null; public Exception? LastException { get; private set; } //todo: show exception when hovered in plugin menu? + public Type InstanceType => plugin.GetType(); + private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); private readonly IHandleInputPlugin? _handleInputPlugin = plugin as IHandleInputPlugin;