private static IEnumerable <CodeInstruction> UpdateExplore_Transpiler(IEnumerable <CodeInstruction> instructions) { TranspilerState state = TranspilerState.Searching; CodeInstruction previous = null; foreach (CodeInstruction instruction in instructions) { switch (state) { case TranspilerState.Searching: if (instruction.opcode == OpCodes.Ldarg_0) { previous = instruction; state = TranspilerState.Checking; } else { yield return(instruction); } break; case TranspilerState.Checking: if (instruction.opcode == OpCodes.Ldfld && ((FieldInfo)instruction.operand).Name == nameof(Minimap.m_exploreRadius)) { yield return(new CodeInstruction(OpCodes.Ldarg_2)); // player yield return(new CodeInstruction(OpCodes.Call, typeof(Minimap_Patches).GetMethod(nameof(GetExploreRadius), BindingFlags.Static | BindingFlags.NonPublic))); state = TranspilerState.Finishing; } else { yield return(previous); yield return(instruction); state = TranspilerState.Searching; } previous = null; break; case TranspilerState.Finishing: yield return(instruction); break; } } }
private static IEnumerable <CodeInstruction> ApplyToHeightmap_Transpiler(IEnumerable <CodeInstruction> instructions) { TranspilerState state = TranspilerState.Searching; CodeInstruction valueInstruction = null; foreach (CodeInstruction instruction in instructions) { switch (state) { case TranspilerState.Searching: if (instruction.opcode == OpCodes.Ldc_R4 && (float)instruction.operand == 8.0f) { valueInstruction = instruction; state = TranspilerState.Replacing; } else { yield return(instruction); } break; case TranspilerState.Replacing: if (instruction.opcode == OpCodes.Sub) { valueInstruction.operand = MaximumDepth.Value; } else if (instruction.opcode == OpCodes.Add) { valueInstruction.operand = MaximumHeight.Value; } yield return(valueInstruction); yield return(instruction); valueInstruction = null; state = TranspilerState.Searching; break; } } }
private static IEnumerable <CodeInstruction> Update_Transpiler(IEnumerable <CodeInstruction> instructions, ILGenerator generator) { LocalBuilder isSlashPressed = generator.DeclareLocal(typeof(bool)); isSlashPressed.SetLocalSymInfo(nameof(isSlashPressed)); Label label1 = generator.DefineLabel(); Label label2 = generator.DefineLabel(); yield return(new CodeInstruction(OpCodes.Ldc_I4_0)); yield return(new CodeInstruction(OpCodes.Stloc, isSlashPressed.LocalIndex)); TranspilerState state = TranspilerState.Searching; foreach (CodeInstruction instruction in instructions) { if (state == TranspilerState.Inserting) { if (instruction.opcode != OpCodes.Brfalse) { throw new InvalidOperationException($"[BetterChat] {nameof(Chat_Slash_Patches)} encountered unexpected IL code. Unable to patch. This is most likely due to a game update changing the target code. Disable {nameof(SlashOpensChat)} in the config as a workaround until the mod can be fixed."); } // Previous instruction was checking if enter is pressed. If so, skip the slash key check (boolean OR). yield return(new CodeInstruction(OpCodes.Brtrue, label1)); // Check for slash key if enter is not pressed. Also store the result of the check. yield return(new CodeInstruction(OpCodes.Ldc_I4, (int)KeyCode.Slash)); yield return(new CodeInstruction(OpCodes.Call, typeof(Input).GetMethod(nameof(Input.GetKeyDown), new[] { typeof(KeyCode) }))); yield return(new CodeInstruction(OpCodes.Stloc, isSlashPressed.LocalIndex)); yield return(new CodeInstruction(OpCodes.Ldloc, isSlashPressed.LocalIndex)); state = TranspilerState.Labeling; } else if (state == TranspilerState.Labeling) { // Label this instruction as the one to jump to if the enter key is pressed (to skip the slash check). instruction.labels.Add(label1); state = TranspilerState.Searching2; } else if (state == TranspilerState.Labeling2) { // Label this instruction as the one to jump to when skipping the slash insertion code. instruction.labels.Add(label2); state = TranspilerState.Finishing; } yield return(instruction); if (state == TranspilerState.Searching && instruction.opcode == OpCodes.Call) { MethodBase method = (MethodBase)instruction.operand; if (method.Name == nameof(Input.GetKeyDown)) { state = TranspilerState.Inserting; } } else if (state == TranspilerState.Searching2 && instruction.opcode == OpCodes.Callvirt) { MethodBase method = (MethodBase)instruction.operand; if (method.Name == nameof(InputField.ActivateInputField)) { // If slash was not pressed (meaning enter was), then skip the block below yield return(new CodeInstruction(OpCodes.Ldloc, isSlashPressed.LocalIndex)); yield return(new CodeInstruction(OpCodes.Brfalse, label2)); // If slash was pressed, replace current chat input string with a / character yield return(new CodeInstruction(OpCodes.Ldarg_0)); yield return(new CodeInstruction(OpCodes.Ldfld, typeof(Chat).GetField(nameof(Chat.m_input)))); yield return(new CodeInstruction(OpCodes.Ldstr, "/")); yield return(new CodeInstruction(OpCodes.Callvirt, typeof(InputField).GetMethod("set_text"))); // Move caret to end (after slash) in LateUpdate yield return(new CodeInstruction(OpCodes.Ldc_I4_1)); yield return(new CodeInstruction(OpCodes.Stsfld, typeof(BetterChatPlugin).GetField(nameof(sMoveCaretToEnd), BindingFlags.Static | BindingFlags.NonPublic))); state = TranspilerState.Labeling2; } } } }