1

Here's my code. Question is at the bottom:

namespace DifficultyModNS
{
    [HarmonyPatch(typeof(Boosterpack),nameof(Boosterpack.Clicked))]
    public class OneVillagerChecks
    {
        public static Int32 frequency = 5;
        public static Int32 startChecking = 10;

        static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            foreach (var instruction in instructions)
            {
                if (instruction.opcode == OpCodes.Ldc_I4_5)
                {
                    DifficultyMod.Log("Transpiling const 5 to variable");
                    yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::frequency"));
                }
                else if (instruction.opcode == OpCodes.Ldc_I4_S && Convert.ToInt32(instruction.operand) == 10)
                {
                    DifficultyMod.Log("Transpiling const 10 to variable");
                    yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::startChecking"));
                }
                else
                {
                    yield return instruction;
                }
            }
        }
    }
}

The first log message prints. Then just after the second log message there's an exception:

[17:18:44] [Log : difficulty_mod] Transpiling const 5 to variable
[17:18:44] [Log : difficulty_mod] Transpiling const 10 to variable
ArgumentNullException: Invalid argument for ldfld NULL
Parameter name: operand
  at HarmonyLib.Internal.Patching.ILManipulator.WriteTo (Mono.Cecil.Cil.MethodBody body, System.Reflection.MethodBase original) [0x001b4] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteTranspilers () [0x00084] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteImpl () [0x00031] in <4a2409e6eb214388b8d6ab81d407350d>:0 
Rethrow as HarmonyException: IL Compile Error (unknown location)
  at HarmonyLib.Public.Patching.HarmonyManipulator.WriteImpl () [0x00382] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.Process (MonoMod.Cil.ILContext ilContext, System.Reflection.MethodBase originalMethod) [0x00042] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.Manipulate (System.Reflection.MethodBase original, HarmonyLib.PatchInfo patchInfo, MonoMod.Cil.ILContext ctx) [0x00006] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.HarmonyManipulator.Manipulate (System.Reflection.MethodBase original, MonoMod.Cil.ILContext ctx) [0x00007] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.Manipulator (MonoMod.Cil.ILContext ctx) [0x00012] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at MonoMod.Cil.ILContext.Invoke (MonoMod.Cil.ILContext+Manipulator manip) [0x00087] in <92bbbc5a09fc417ca7426fbdd31824c2>:0 
  at MonoMod.RuntimeDetour.ILHook+Context.InvokeManipulator (Mono.Cecil.MethodDefinition def, MonoMod.Cil.ILContext+Manipulator cb) [0x00012] in <925d74c1e4e3467a888e7df55b8370c5>:0 
  at (wrapper dynamic-method) MonoMod.RuntimeDetour.ILHook+Context.DMD<MonoMod.RuntimeDetour.ILHook+Context::Refresh>(MonoMod.RuntimeDetour.ILHook/Context)
  at (wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.Trampoline<MonoMod.RuntimeDetour.ILHook+Context::Refresh>?1194592624(object)
  at HarmonyLib.Internal.RuntimeFixes.StackTraceFixes.OnILChainRefresh (System.Object self) [0x00000] in <4a2409e6eb214388b8d6ab81d407350d>:0 
  at MonoMod.RuntimeDetour.ILHook.Apply () [0x00059] in <925d74c1e4e3467a888e7df55b8370c5>:0 
  at HarmonyLib.Public.Patching.ManagedMethodPatcher.DetourTo (System.Reflection.MethodBase replacement) [0x00047] in <4a2409e6eb214388b8d6ab81d407350d>:0 
// there's more but it's just additional subsystems rethrowing

Why does one of these work but the other one doesn't when the two fields are sitting next to each other in the same class:

                    yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::frequency"));
                    yield return new CodeInstruction(OpCodes.Ldfld, operand: AccessTools.Field(typeof(Int32), name: "DifficultyModNS.OneVillagerChecks::startChecking"));

EDIT: Okay so I removed the else if for the one that "doesn't work" and I've discovered the exception still happens, after this function exits. So now I guess the question is Why does this return NULL for the operand to Ldfld when the name should exist?

EDIT2: I was using the wrong IL instruction. And the Harmony AccessTools lib is garbage for getting FieldInfo. That was replaced with typeof(OneVillagerChecks).GetField("frequency").

jmucchiello
  • 18,754
  • 7
  • 41
  • 61
  • 1. Do less in a single line of code. 2. Provide us with some details on the `AccessTools.Field` method. Did you write it? Is it some library? If it's a library function, give the name of the library and the complete name, including namespace, so we know what you're talking about. – Ben Voigt Aug 31 '23 at 21:57
  • It looks like you are passing completely wrong values for both arguments of `AccessTools.Field`. [The documentation](https://harmony.pardeike.net/api/HarmonyLib.AccessTools.html#HarmonyLib_AccessTools_Field_System_Type_System_String_) wants the declaring type, not the property type. – Ben Voigt Aug 31 '23 at 22:00
  • I was using the wrong IL instruction. AccessTools is part of Harmony. – jmucchiello Aug 31 '23 at 23:23

0 Answers0