Exemplo n.º 1
0
 static Hooks()
 {
     _layoutOptionConstructor = AccessTools.FirstConstructor(typeof(GUILayoutOption), info => info.GetParameters().Length == 2);
     _widthTrue   = CreateNewGuiLayoutOption(6, 1);
     _widthFalse  = CreateNewGuiLayoutOption(6, 0);
     _heightTrue  = CreateNewGuiLayoutOption(7, 1);
     _heightFalse = CreateNewGuiLayoutOption(7, 0);
 }
Exemplo n.º 2
0
        /// <summary>
        /// Returns an instruction to call the specified delegate.
        /// </summary>
        /// <typeparam name="T">The delegate type to emit.</typeparam>
        /// <param name="action">The delegate to emit.</param>
        /// <returns>The instruction to </returns>
        public static CodeInstruction EmitDelegate <T>(T action) where T : Delegate
        {
            if (action.Method.IsStatic && action.Target == null)
            {
                return(new CodeInstruction(OpCodes.Call, action.Method));
            }

            Type[] paramTypes = action.Method.GetParameters().Select(x => x.ParameterType).ToArray();

            DynamicMethod dynamicMethod = new DynamicMethod(action.Method.Name,
                                                            action.Method.ReturnType,
                                                            paramTypes,
                                                            typeof(HarmonyWrapper).Module,
                                                            true);

            ILGenerator il = dynamicMethod.GetILGenerator();

            Type targetType = action.Target.GetType();

            bool preserveContext = action.Target != null && targetType.GetFields().Any(x => !x.IsStatic);

            if (preserveContext)
            {
                int currentDelegateCounter = DelegateCounter++;

                DelegateCache[currentDelegateCounter] = action;

                System.Reflection.FieldInfo cacheField = AccessTools.Field(typeof(HarmonyWrapper), nameof(DelegateCache));

                System.Reflection.MethodInfo getMethod = AccessTools.Method(typeof(Dictionary <int, Delegate>), "get_Item");

                il.Emit(OpCodes.Ldsfld, cacheField);
                il.Emit(OpCodes.Ldc_I4, currentDelegateCounter);
                il.Emit(OpCodes.Callvirt, getMethod);
            }
            else
            {
                if (action.Target == null)
                {
                    il.Emit(OpCodes.Ldnull);
                }
                else
                {
                    il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.GetParameters().Length == 0 && !x.IsStatic));
                }

                il.Emit(OpCodes.Ldftn, action.Method);
                il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) }));
            }


            for (int i = 0; i < paramTypes.Length; i++)
            {
                il.Emit(OpCodes.Ldarg_S, (short)i);
            }

            il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("Invoke"));

            il.Emit(OpCodes.Ret);

            return(new CodeInstruction(OpCodes.Call, dynamicMethod));
        }
Exemplo n.º 3
0
        private static MethodInfo GetPatchMethod <T>(T action) where T : Delegate
        {
            if (action.Method.IsStatic && action.Target == null)
            {
                return(action.Method);
            }
            if (MethodCache.TryGetValue(action, out var method))
            {
                return(method);
            }

            var actionParams = action.Method.GetParameters();

            var dmd = new DynamicMethodDefinition($"ActionWrapper_{action.Method.Name}", action.Method.ReturnType,
                                                  actionParams.Select(p => p.ParameterType).ToArray());

            for (var i = 0; i < dmd.Definition.Parameters.Count; i++)
            {
                var paramInfo = actionParams[i];
                var paramDef  = dmd.Definition.Parameters[i];
                paramDef.Name = paramInfo.Name;
            }

            var il          = dmd.GetILGenerator();
            var preserveCtx = action.Target != null && action.Target.GetType().GetFields().Any(x => !x.IsStatic);

            if (preserveCtx)
            {
                var currentCounter = delegateCounter++;
                DelegateCache[currentCounter] = action;

                var cacheField = AccessTools.Field(typeof(HarmonyDelegatePatchingExtensions), nameof(DelegateCache));
                var getMethod  = AccessTools.Method(typeof(Dictionary <int, Delegate>), "get_Item");

                il.Emit(OpCodes.Ldsfld, cacheField);
                il.Emit(OpCodes.Ldc_I4, currentCounter);
                il.Emit(OpCodes.Callvirt, getMethod);
            }
            else
            {
                if (action.Target == null)
                {
                    il.Emit(OpCodes.Ldnull);
                }
                else
                {
                    il.Emit(OpCodes.Newobj,
                            AccessTools.FirstConstructor(action.Target.GetType(),
                                                         x => x.GetParameters().Length == 0 && !x.IsStatic));
                }

                il.Emit(OpCodes.Ldftn, action.Method);
                il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) }));
            }

            for (var i = 0; i < actionParams.Length; i++)
            {
                il.Emit(OpCodes.Ldarg_S, (short)i);
            }

            il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(T), "Invoke"));
            il.Emit(OpCodes.Ret);

            // Generate with cecil to ensure it's usable with Harmony (Harmony blocks DynamicMethods by default)
            var result = DMDGenerator <DMDCecilGenerator> .Generate(dmd);

            MethodCache[action] = result;
            return(result);
        }
        public VanillaExpandedFramework(ModContentPack mod)
        {
            // ItemProcessor
            {
                var type = AccessTools.TypeByName("ItemProcessor.Building_ItemProcessor");
                // _1, _5 and _7 are used to check if gizmo should be enabled, so we don't sync them
                MpCompat.RegisterSyncMethodsByIndex(type, "<GetGizmos>", 0, 2, 3, 4, 6, 8, 9, 10);

                type = AccessTools.TypeByName("ItemProcessor.Command_SetQualityList");
                MP.RegisterSyncWorker <Command>(SyncCommandWithBuilding, type, shouldConstruct: true);
                MpCompat.RegisterSyncMethodsByIndex(type, "<ProcessInput>", Enumerable.Range(0, 8).ToArray());

                type = AccessTools.TypeByName("ItemProcessor.Command_SetOutputList");
                MP.RegisterSyncWorker <Command>(SyncCommandWithBuilding, type, shouldConstruct: true);
                MP.RegisterSyncMethod(type, "TryConfigureIngredientsByOutput");

                // Keep an eye on this in the future, seems like something the devs could combine into a single class at some point
                foreach (var ingredientNumber in new[] { "First", "Second", "Third", "Fourth" })
                {
                    type = AccessTools.TypeByName($"ItemProcessor.Command_Set{ingredientNumber}ItemList");
                    MP.RegisterSyncWorker <Command>(SyncSetIngredientCommand, type, shouldConstruct: true);
                    MP.RegisterSyncMethod(type, $"TryInsert{ingredientNumber}Thing");
                    MpCompat.RegisterSyncMethodsByIndex(type, "<ProcessInput>", 0);
                }
            }

            // Vanilla Cooking Expanded
            {
                // AddHediff desyncs with Arbiter, but seems fine without it
                PatchingUtilities.PatchPushPopRand("VanillaCookingExpanded.Thought_Hediff:MoodOffset");
            }

            // VFE Core
            {
                var type = AccessTools.TypeByName("VFECore.CompPawnDependsOn");
                MpCompat.RegisterSyncMethodByIndex(type, "<CompGetGizmosExtra>", 0).SetDebugOnly();
            }

            // Vanilla Furniture Expanded
            {
                var type = AccessTools.TypeByName("VanillaFurnitureExpanded.CompConfigurableSpawner");
                MpCompat.RegisterSyncMethodByIndex(type, "<CompGetGizmosExtra>", 0).SetDebugOnly();

                type = AccessTools.TypeByName("VanillaFurnitureExpanded.Command_SetItemsToSpawn");
                MP.RegisterSyncDelegate(type, "<>c__DisplayClass2_0", "<ProcessInput>b__1");

                type = AccessTools.TypeByName("VanillaFurnitureExpanded.CompRockSpawner");
                MpCompat.RegisterSyncMethodByIndex(type, "<CompGetGizmosExtra>", 0);

                type = AccessTools.TypeByName("VanillaFurnitureExpanded.Command_SetStoneType");
                setStoneBuildingField = AccessTools.Field(type, "building");
                MpCompat.RegisterSyncMethodByIndex(type, "<ProcessInput>", 0);
                MP.RegisterSyncWorker <Command>(SyncSetStoneTypeCommand, type, shouldConstruct: true);
                MP.RegisterSyncDelegate(type, "<>c__DisplayClass2_0", "<ProcessInput>b__1");
            }

            // Vanilla Faction Mechanoids
            {
                var type = AccessTools.TypeByName("VFE.Mechanoids.CompMachineChargingStation");
                MP.RegisterSyncDelegate(type, "<>c", "<CompGetGizmosExtra>b__21_1", Array.Empty <string>()).SetContext(SyncContext.MapSelected);
                MP.RegisterSyncDelegate(type, "<>c", "<CompGetGizmosExtra>b__21_6", Array.Empty <string>()).SetContext(SyncContext.MapSelected);
                MP.RegisterSyncDelegate(type, "<>c__DisplayClass21_0", "<CompGetGizmosExtra>b__4");
            }

            // AnimalBehaviours
            {
                // RNG
                PatchingUtilities.PatchSystemRand("AnimalBehaviours.DamageWorker_ExtraInfecter:ApplySpecialEffectsToPart", false);
                PatchingUtilities.PatchSystemRandCtor(new[] { "AnimalBehaviours.CompAnimalProduct", "AnimalBehaviours.CompGasProducer" }, false);

                // Gizmos
                // Might not work, as I could not find a mod that uses this to test this
                var type = AccessTools.TypeByName("AnimalBehaviours.CompDestroyThisItem");
                MP.RegisterSyncMethod(type, "SetObjectForDestruction");
                MP.RegisterSyncMethod(type, "CancelObjectForDestruction");
            }

            // MVCF (Multi Verb Combat Framework)
            {
                var type = AccessTools.TypeByName("MVCF.WorldComponent_MVCF");
                mvcfGetWorldCompMethod   = AccessTools.Method(type, "GetComp");
                mvcfAllManagersListField = AccessTools.Field(type, "allManagers");
                mvcfManagersTableField   = AccessTools.Field(type, "managers");
                MP.RegisterSyncMethod(typeof(VanillaExpandedFramework), nameof(SyncedInitVerbManager));
                MpCompat.harmony.Patch(AccessTools.Method(type, "GetManagerFor"),
                                       prefix: new HarmonyMethod(typeof(VanillaExpandedFramework), nameof(GetManagerForPrefix)));

                type = AccessTools.TypeByName("MVCF.VerbManager");
                MP.RegisterSyncWorker <object>(SyncVerbManager, type, isImplicit: true);
                mvcfVerbManagerCtor         = AccessTools.Constructor(type);
                mvcfInitializeManagerMethod = AccessTools.Method(type, "Initialize");
                mvcfPawnGetter = AccessTools.PropertyGetter(type, "Pawn");
                mvcfVerbsField = AccessTools.Field(type, "verbs");

                var weakReferenceType = typeof(System.WeakReference <>).MakeGenericType(new[] { type });
                weakReferenceCtor = AccessTools.FirstConstructor(weakReferenceType, ctor => ctor.GetParameters().Count() == 1);

                var conditionalWeakTableType = typeof(System.Runtime.CompilerServices.ConditionalWeakTable <,>).MakeGenericType(new[] { typeof(Pawn), type });
                conditionalWeakTableAddMethod         = AccessTools.Method(conditionalWeakTableType, "Add");
                conditionalWeakTableTryGetValueMethod = AccessTools.Method(conditionalWeakTableType, "TryGetValue");

                type = AccessTools.TypeByName("MVCF.ManagedVerb");
                mvcfManagerVerbManagerField = AccessTools.Field(type, "man");
                MP.RegisterSyncWorker <object>(SyncManagedVerb, type, isImplicit: true);
                // Seems like selecting the Thing that holds the verb inits some stuff, so we need to set the context
                MP.RegisterSyncMethod(type, "Toggle");

                type = AccessTools.TypeByName("MVCF.Harmony.Gizmos");
                MP.RegisterSyncDelegate(type, "<>c__DisplayClass5_0", "<GetGizmos_Postfix>b__1");       // Fire at will
                MP.RegisterSyncDelegate(type, "<>c__DisplayClass6_0", "<GetAttackGizmos_Postfix>b__4"); // Interrupt Attack
                MP.RegisterSyncDelegate(type, "<>c__DisplayClass7_0", "<Pawn_GetGizmos_Postfix>b__0");  // Also interrupt Attack
            }
        }
Exemplo n.º 5
0
        public VanillaExpandedFramework(ModContentPack mod)
        {
            // ItemProcessor
            {
                var type = AccessTools.TypeByName("ItemProcessor.Building_ItemProcessor");
                // _1, _5 and _7 are used to check if gizmo should be enabled, so we don't sync them
                MpCompat.RegisterLambdaMethod(type, "GetGizmos", 0, 2, 3, 4, 6, 8, 9, 10);

                type = AccessTools.TypeByName("ItemProcessor.Command_SetQualityList");
                MP.RegisterSyncWorker <Command>(SyncCommandWithBuilding, type, shouldConstruct: true);
                MpCompat.RegisterLambdaMethod(type, "ProcessInput", Enumerable.Range(0, 8).ToArray());

                type = AccessTools.TypeByName("ItemProcessor.Command_SetOutputList");
                MP.RegisterSyncWorker <Command>(SyncCommandWithBuilding, type, shouldConstruct: true);
                MP.RegisterSyncMethod(type, "TryConfigureIngredientsByOutput");

                // Keep an eye on this in the future, seems like something the devs could combine into a single class at some point
                foreach (var ingredientNumber in new[] { "First", "Second", "Third", "Fourth" })
                {
                    type = AccessTools.TypeByName($"ItemProcessor.Command_Set{ingredientNumber}ItemList");
                    MP.RegisterSyncWorker <Command>(SyncSetIngredientCommand, type, shouldConstruct: true);
                    MP.RegisterSyncMethod(type, $"TryInsert{ingredientNumber}Thing");
                    MpCompat.RegisterLambdaMethod(type, "ProcessInput", 0);
                }
            }

            // Vanilla Cooking Expanded
            {
                // AddHediff desyncs with Arbiter, but seems fine without it
                PatchingUtilities.PatchPushPopRand("VanillaCookingExpanded.Thought_Hediff:MoodOffset");
            }

            // VFE Core
            {
                MpCompat.RegisterLambdaMethod("VFECore.CompPawnDependsOn", "CompGetGizmosExtra", 0).SetDebugOnly();

                learnedAbilitiesField = AccessTools.Field(AccessTools.TypeByName("VFECore.Abilities.CompAbilities"), "learnedAbilities");
                MP.RegisterSyncWorker <ITargetingSource>(SyncVEFAbility, AccessTools.TypeByName("VFECore.Abilities.Ability"));
            }

            // Vanilla Furniture Expanded
            {
                MpCompat.RegisterLambdaMethod("VanillaFurnitureExpanded.CompConfigurableSpawner", "CompGetGizmosExtra", 0).SetDebugOnly();

                var type = AccessTools.TypeByName("VanillaFurnitureExpanded.Command_SetItemsToSpawn");
                MpCompat.RegisterLambdaDelegate(type, "ProcessInput", 1);
                MP.RegisterSyncWorker <Command>(SyncCommandWithBuilding, type, shouldConstruct: true);

                MpCompat.RegisterLambdaMethod("VanillaFurnitureExpanded.CompRockSpawner", "CompGetGizmosExtra", 0);

                type = AccessTools.TypeByName("VanillaFurnitureExpanded.Command_SetStoneType");
                setStoneBuildingField = AccessTools.Field(type, "building");
                MpCompat.RegisterLambdaMethod(type, "ProcessInput", 0);
                MP.RegisterSyncWorker <Command>(SyncSetStoneTypeCommand, type, shouldConstruct: true);
                MpCompat.RegisterLambdaDelegate(type, "ProcessInput", 1);

                type = AccessTools.TypeByName("VanillaFurnitureExpanded.CompRandomBuildingGraphic");
                MpCompat.RegisterLambdaMethod(type, "CompGetGizmosExtra", 0);
            }

            // Vanilla Faction Mechanoids
            {
                var type = AccessTools.TypeByName("VFE.Mechanoids.CompMachineChargingStation");
                MpCompat.RegisterLambdaDelegate(type, "CompGetGizmosExtra", 1, 6).SetContext(SyncContext.MapSelected);
                MpCompat.RegisterLambdaDelegate(type, "CompGetGizmosExtra", 4);
            }

            // AnimalBehaviours
            {
                // RNG
                PatchingUtilities.PatchSystemRand("AnimalBehaviours.DamageWorker_ExtraInfecter:ApplySpecialEffectsToPart", false);
                var rngFixConstructors = new[]
                {
                    "AnimalBehaviours.CompAnimalProduct",
                    "AnimalBehaviours.CompFilthProducer",
                    "AnimalBehaviours.CompGasProducer",
                    "AnimalBehaviours.CompInitialHediff",
                    "AnimalBehaviours.DeathActionWorker_DropOnDeath",
                };
                PatchingUtilities.PatchSystemRandCtor(rngFixConstructors, false);

                // Gizmos
                var type = AccessTools.TypeByName("AnimalBehaviours.CompDestroyThisItem");
                MP.RegisterSyncMethod(type, "SetObjectForDestruction");
                MP.RegisterSyncMethod(type, "CancelObjectForDestruction");
            }

            // MVCF (Multi Verb Combat Framework)
            {
                var type = AccessTools.TypeByName("MVCF.WorldComponent_MVCF");
                mvcfGetWorldCompMethod   = AccessTools.Method(type, "GetComp");
                mvcfAllManagersListField = AccessTools.Field(type, "allManagers");
                mvcfManagersTableField   = AccessTools.Field(type, "managers");
                MP.RegisterSyncMethod(typeof(VanillaExpandedFramework), nameof(SyncedInitVerbManager));
                MpCompat.harmony.Patch(AccessTools.Method(type, "GetManagerFor"),
                                       prefix: new HarmonyMethod(typeof(VanillaExpandedFramework), nameof(GetManagerForPrefix)));

                type = AccessTools.TypeByName("MVCF.VerbManager");
                MP.RegisterSyncWorker <object>(SyncVerbManager, type, isImplicit: true);
                mvcfVerbManagerCtor         = AccessTools.Constructor(type);
                mvcfInitializeManagerMethod = AccessTools.Method(type, "Initialize");
                mvcfPawnGetter = AccessTools.PropertyGetter(type, "Pawn");
                mvcfVerbsField = AccessTools.Field(type, "verbs");

                var weakReferenceType = typeof(System.WeakReference <>).MakeGenericType(new[] { type });
                weakReferenceCtor = AccessTools.FirstConstructor(weakReferenceType, ctor => ctor.GetParameters().Count() == 1);

                var conditionalWeakTableType = typeof(System.Runtime.CompilerServices.ConditionalWeakTable <,>).MakeGenericType(new[] { typeof(Pawn), type });
                conditionalWeakTableAddMethod         = AccessTools.Method(conditionalWeakTableType, "Add");
                conditionalWeakTableTryGetValueMethod = AccessTools.Method(conditionalWeakTableType, "TryGetValue");

                type = AccessTools.TypeByName("MVCF.ManagedVerb");
                mvcfManagerVerbManagerField = AccessTools.Field(type, "man");
                MP.RegisterSyncWorker <object>(SyncManagedVerb, type, isImplicit: true);
                // Seems like selecting the Thing that holds the verb inits some stuff, so we need to set the context
                MP.RegisterSyncMethod(type, "Toggle");

                type = AccessTools.TypeByName("MVCF.Harmony.Gizmos");
                MpCompat.RegisterLambdaDelegate(type, "GetGizmos_Postfix", 1);       // Fire at will
                MpCompat.RegisterLambdaDelegate(type, "GetAttackGizmos_Postfix", 4); // Interrupt Attack
                MpCompat.RegisterLambdaDelegate(type, "Pawn_GetGizmos_Postfix", 0);  // Also interrupt Attack
            }

            // Explosive Trails Effect
            {
                // RNG
                PatchingUtilities.PatchPushPopRand("ExplosiveTrailsEffect.SmokeThrowher:ThrowSmokeTrail");
            }

            // KCSG (Custom Structure Generation)
            {
                // RNG
                var methods = new[]
                {
                    "KCSG.SymbolResolver_AddFields:Resolve",
                    "KCSG.SymbolResolver_Settlement:GenerateRooms",
                };

                PatchingUtilities.PatchSystemRand(methods, false);
            }
        }