diff --git a/CringeLauncher/CringeLauncher.csproj b/CringeLauncher/CringeLauncher.csproj index 886fcaf..2e39316 100644 --- a/CringeLauncher/CringeLauncher.csproj +++ b/CringeLauncher/CringeLauncher.csproj @@ -18,7 +18,7 @@ - + diff --git a/CringeLauncher/Patches/PluginTypePatch.cs b/CringeLauncher/Patches/PluginTypePatch.cs index dacf7a3..85fd092 100644 --- a/CringeLauncher/Patches/PluginTypePatch.cs +++ b/CringeLauncher/Patches/PluginTypePatch.cs @@ -3,7 +3,7 @@ using HarmonyLib; using Sandbox.Game.World; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.Loader; +using VRage.Game; using VRage.Game.ObjectBuilder; using VRage.Plugins; @@ -12,40 +12,67 @@ 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)); - } - + [HarmonyPatch(typeof(MyGlobalTypeMetadata), nameof(MyGlobalTypeMetadata.Init))] [HarmonyTranspiler] - public static IEnumerable Transpiler(IEnumerable instructions, MethodBase original) + private static IEnumerable MetadataTranspiler(IEnumerable instructions, ILGenerator generator) { - //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))) + return new CodeMatcher(instructions, generator) + .SearchForward(b => b.opcode == OpCodes.Ldloc_2) + .Advance(-1) + .CreateLabel(out var regularPluginLabel) + .DeclareLocal(typeof(PluginWrapper), out var wrapper) + .DefineLabel(out var continueLabel) + .Insert(new(OpCodes.Ldloc_2), + new(OpCodes.Isinst, typeof(PluginWrapper)), new(OpCodes.Stloc, wrapper), + new(OpCodes.Ldloc, wrapper), + new(OpCodes.Brfalse, regularPluginLabel), + new(OpCodes.Ldloc_0), + new(OpCodes.Ldloc, wrapper), + new(OpCodes.Call, + AccessTools.PropertyGetter(typeof(PluginWrapper), nameof(PluginWrapper.InstanceType))), + new(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Type), nameof(Type.Assembly))), + CodeInstruction.Call(typeof(MyGlobalTypeMetadata), nameof(MyGlobalTypeMetadata.RegisterAssembly), + [typeof(Assembly)]), + new(OpCodes.Br, continueLabel)) + .SearchForward(b => b.opcode == OpCodes.Ldloca_S) + .AddLabels([continueLabel]) .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(); + [HarmonyPatch(typeof(MySession), "RegisterComponentsFromAssemblies", typeof(Assembly), typeof(bool), typeof(MyModContext))] + [HarmonyPrefix] + private static bool RegisterComponentsPrefix(MySession __instance) + { + __instance.m_componentsToLoad = [..__instance.GameDefinition.SessionComponents.Keys]; + __instance.m_componentsToLoad.ExceptWith(__instance.SessionComponentDisabled); + __instance.m_componentsToLoad.UnionWith(__instance.SessionComponentEnabled); + + __instance.RegisterComponentsFromAssembly(MyPlugins.SandboxAssembly); + __instance.RegisterComponentsFromAssembly(MyPlugins.SandboxGameAssembly); + __instance.RegisterComponentsFromAssembly(MyPlugins.GameAssembly); + + foreach (var (context, ids) in __instance.ScriptManager.ScriptsPerMod) + { + foreach (var id in ids) + { + __instance.RegisterComponentsFromAssembly(__instance.ScriptManager.Scripts[id], true, context); + } + } + + foreach (var plugin in MyPlugins.Plugins) + { + var type = plugin is PluginWrapper wrapper ? wrapper.InstanceType : plugin.GetType(); + + __instance.RegisterComponentsFromAssembly(type.Assembly, true); + } + + foreach (var component in __instance.m_sessionComponents.Values) + { + if (component.ModContext is null or { IsBaseGame: true }) + __instance.m_sessionComponentForDrawAsync.Add(component); + else + __instance.m_sessionComponentForDraw.Add(component); + } + return false; + } }