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); }
/// <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)); }
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 } }
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); } }