public static void Init() { Type origType = _HBSAssembly.GetType(TARGET_TYPE_NAME); try { _ownTaskField = HarmonyUtils.CreateInstanceFieldRef <bool>(origType, I_SortMoveCandidatesByInfMapNode.OwnTaskTypeName); _thinkTaskField = HarmonyUtils.CreateStaticFieldRef <Task <bool> >(origType, I_SortMoveCandidatesByInfMapNode.ThinkTaskTypeName); _comparer = origType.GetNestedType("AccumulatorComparer", AccessTools.all); _drawDebugLines = origType.GetMethod("drawDebugLines", AccessTools.all); _targetMethod = origType.GetMethod("Tick", AccessTools.all); MethodInfo prefix = typeof(H_SortMoveCandidatesByInfMapNode_Tick) .GetMethod(nameof(Prefix), AccessTools.all); HarmonyUtils.Harmony.Patch(_targetMethod, new HarmonyMethod(prefix)); } catch (Exception e) { Utils.Logger.LogError($"{Utils.LOG_HEADER} Failed to patch {TARGET_TYPE_NAME}", e); } }
/// <summary> /// Our detour should execute ONLY if the caller is explicitly allowed. /// This prevents unexpected behaviors, but could require some changes in this method to allow compatibility with other /// mods. /// HACK - [ISSUE-10] [ISSUE-18] /// </summary> /// <param name="st"></param> /// <returns></returns> private bool IsAllowedCaller(StackTrace st) { // Extract both type and method var callerType = st.GetFrame(2)?.GetMethod()?.DeclaringType; var callerMethod = st.GetFrame(1)?.GetMethod(); // They should never be null because stack traces are usually longer than 3 lines, but let's add this check because you'll never know if (callerType == null || callerMethod == null) { Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(IsAllowedCaller)}] {nameof(callerType)} or {nameof(callerMethod)} is null."); Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(IsAllowedCaller)}] Stacktrace is\n{st}"); return(false); } Log._Debug($"[{nameof(NetManagerDetour)}.{nameof(IsAllowedCaller)}] Caller is {callerType.Name}.{callerMethod.Name}"); // ReSharper disable once ConvertIfStatementToReturnStatement if (callerType == typeof(NetTool)) { // We must allow only CreateNode (and eventually patched Harmony methods) // This is the compatibility patch for Network Skins 2 return(HarmonyUtils.IsNameMatching(callerMethod.Name, "CreateNode")); } return(false); }
private static IEnumerable <CodeInstruction> PatchRequirementAmountIndicator( IEnumerable <CodeInstruction> instructions) { HarmonyUtils.LogDebugBuildOnly("Transpiler patch: SetupRequirement"); var searchTemplate = new List <Tuple <int, CodeInstruction> > { new Tuple <int, CodeInstruction>(0, new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(InventoryGui), "HideRequirement", new[] { typeof(Transform) }))), new Tuple <int, CodeInstruction>(3, new CodeInstruction(OpCodes.Ldloc_2)), new Tuple <int, CodeInstruction>(5, new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(int), "ToString"))), new Tuple <int, CodeInstruction>(6, new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertySetter(typeof(Text), "text"))) }; var patchCount = 0; var instructionsList = instructions.ToList(); for (var i = 0; i < instructionsList.Count; i++) { if (!instructionsList.DoInstructionsMatchTemplate(i, searchTemplate)) { continue; } HarmonyUtils.LogDebugBuildOnly("Template matched!"); // ok, we sure we are kinda sure we are patching the right thing var numOperandReference = instructionsList[i + 7].operand; var amountOperandReference = instructionsList[i + 8].operand; instructionsList.RemoveRange(i + 4, 2); instructionsList.InsertRange(i + 4, new List <CodeInstruction> { new CodeInstruction(OpCodes.Ldstr, "{0}/{1}"), new CodeInstruction(OpCodes.Ldloc_S, numOperandReference), new CodeInstruction(OpCodes.Box, typeof(int)), new CodeInstruction(OpCodes.Ldloc_S, amountOperandReference), new CodeInstruction(OpCodes.Box, typeof(int)), new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(string), "Format", new[] { typeof(string), typeof(object), typeof(object) })) }); HarmonyUtils.LogDebugBuildOnly("Patch OK"); patchCount = 1; } HarmonyUtils.LogDebugBuildOnly($"Patch count {patchCount}"); if (patchCount == 0) { Plugin.Log.LogError( "Crafting amount indicator won't work properly"); } return(instructionsList); }
private static IEnumerable <CodeInstruction> CountItemsHaveItemsReferencesPatch(MethodBase method, IEnumerable <CodeInstruction> instructions) { HarmonyUtils.LogDebugBuildOnly($"Transpiler patching {method.DeclaringType.Name}::{method}"); var instructionsList = instructions.ToList(); var patchCount = 0; for (var i = instructionsList.Count - 1; i >= 0; i--) { var ins = instructionsList[i]; if (ins.opcode != OpCodes.Callvirt) { continue; } var patchPair = methodPatchMap .SingleOrDefault(pair => ins.Is(pair.Item1)); if (patchPair == null) { continue; } HarmonyUtils.LogDebugBuildOnly("Patching:"); instructionsList[i] = patchPair.Item2; HarmonyUtils.LogDebugBuildOnly("Post patch instruction is:"); HarmonyUtils.LogDebugBuildOnly($"{i} - {instructionsList[i]}"); patchCount += 1; } HarmonyUtils.LogDebugBuildOnly($"Patch count {patchCount}"); if (patchCount == 0) { Plugin.Log.LogError( "Counting/deducting patching was not successful. CratingWithContainers will not work properly if at all.\n" + $"Dumping initial instruction list:\n{string.Join("\n", instructionsList.Select((instruction, i) => $" {i} - {instruction}"))}"); } return(instructionsList); }