Having a problem with using Harmony

Started by Keltas, September 16, 2019, 05:37:45 AM

Previous topic - Next topic

Keltas

Hello. Yesterday I've decided to try my hand in modding Rimworld by removing the need to rest at night (ideally I wanted to replace it with using rest need of your colonists to manage rest manually but I don't think I have patience to make it). I've decided to patch the NightResting Boolean in Caravan class in Rimworld.Planet to always return false thus allowing the caravan to move at night. However no matter how I format it, I keep on getting an error saying "no target method found". At this point I am pretty sure that I am setting my target method properly, as I've tried every way of formatting it I've seen on Github to no avail. Right now I feel like my mistake is something different but really obvious that I need someone to point out for me, so without further ado here is the class in question:

using Harmony;
using System.Reflection;
using Verse;
using RimWorld.Planet;

namespace NoNightRest
{
    [StaticConstructorOnStartup]
    public static class Main
    {
        static Main()
        {
            HarmonyInstance harmony = HarmonyInstance.Create("rimworld.keltas.nonightrest");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
            Log.Message("After Patching!");
        }
    }

    [HarmonyPatch(typeof(Caravan), nameof(Caravan.NightResting))]
    public static class Caravan_NightResting_Patch
    {
        [HarmonyPrefix]
        public static bool Prefix(ref bool __result)
        {
            __result = false;
            Log.Message("Bool changed!");
            return false;
        }
    }
}

The error I am getting is:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for NoNightRest.Main ---> System.ArgumentException: No target method specified for class NoNightRest.Caravan_NightResting_Patch (declaringType=RimWorld.Planet.Caravan, methodName =NightResting, methodType=, argumentTypes=NULL)
  at Harmony.PatchProcessor.PrepareType () [0x00000] in <filename unknown>:0
  at Harmony.PatchProcessor..ctor (Harmony.HarmonyInstance instance, System.Type type, Harmony.HarmonyMethod attributes) [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.<PatchAll>b__9_0 (System.Type type) [0x00000] in <filename unknown>:0
  at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0
  at NoNightRest.Main..cctor () [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
  at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0
  at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0
  at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0
  at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0
Verse.Log:Error(String, Boolean)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update()
Verse.Root_Entry:Update()

K

Since NightResting is a property, you need a slightly different Harmony header:

[HarmonyPatch(typeof(Caravan), "NightResting", MethodType.Getter)]

That declaration should do the trick.

Keltas

I've changed the header as follows:

    [HarmonyPatch(typeof(Caravan), "NightResting", MethodType.Getter)]
    public static class Caravan_NightResting_Patch
    {
        [HarmonyPrefix]
        public static bool Prefix(ref bool __result)
        {
            __result = false;
            Log.Message("Bool changed!");
            return false;
        }
    }

However I still get the same error:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for NoNightRest.Main ---> System.ArgumentException: No target method specified for class NoNightRest.Caravan_NightResting_Patch (declaringType=RimWorld.Planet.Caravan, methodName =NightResting, methodType=, argumentTypes=NULL)
  at Harmony.PatchProcessor.PrepareType () [0x00000] in <filename unknown>:0
  at Harmony.PatchProcessor..ctor (Harmony.HarmonyInstance instance, System.Type type, Harmony.HarmonyMethod attributes) [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.<PatchAll>b__9_0 (System.Type type) [0x00000] in <filename unknown>:0
  at Harmony.CollectionExtensions.Do[Type] (IEnumerable`1 sequence, System.Action`1 action) [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.PatchAll (System.Reflection.Assembly assembly) [0x00000] in <filename unknown>:0
  at NoNightRest.Main..cctor () [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at (wrapper managed-to-native) System.Runtime.CompilerServices.RuntimeHelpers:RunClassConstructor (intptr)
  at System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) [0x00000] in <filename unknown>:0
  at Verse.StaticConstructorOnStartupUtility.CallAll () [0x00000] in <filename unknown>:0
  at Verse.PlayDataLoader.<DoPlayLoad>m__2 () [0x00000] in <filename unknown>:0
  at Verse.LongEventHandler.ExecuteToExecuteWhenFinished () [0x00000] in <filename unknown>:0
Verse.Log:Error(String, Boolean)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update()
Verse.Root_Entry:Update()

K

The problem is that you're using a Prefix incorrectly. For example, this simple patch does work:

    [HarmonyPatch(typeof(Caravan), "NightResting", MethodType.Getter)]
    public class CaravanPatch {

        public static bool Postfix(bool __result) {
            __result = false;

            return __result;
        }
    }


I'm using the value passthrough mode but you don't have to. The main issue with your code is that you're trying to get the result of the property in a prefix, which isn't allowed since it hasn't been calculated yet. In general, you should also be trying to keep as many of your patches as prefixes as possible, since they have they greatest compatibility with other mods.

Keltas

Thanks for telling me the difference between Prefix and Postfix however I still get the same error saying I have no target method specified for my class. Is there something else I could be doing wrong?

   
using Harmony;
using System.Reflection;
using Verse;
using RimWorld.Planet;

namespace NoNightRest
{
    [StaticConstructorOnStartup]
    public static class Main
    {
        static Main()
        {
            HarmonyInstance harmony = HarmonyInstance.Create("rimworld.keltas.nonightrest");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
            Log.Message("After Patching!");
        }
    }

    [HarmonyPatch(typeof(Caravan), "NightResting", MethodType.Getter)]
    public class CaravanPatch
    {

        public static bool Postfix(bool __result)
        {
            __result = false;

            return __result;
        }
    }
}

LWM

Try "get_NightResting"

If that doesn't work, try

[HarmonyPatch(typeof(Caravan), "get_NightResting")]

--LWM

Keltas

Thank you very much for your help! I've finally located what was the source of my problem. The "0Harmony.dll" I was using was some outdated version I've copied off of another mod instead of downloading a fresh one off of Github. With the newest version the mod is working like a charm without any errors. Thank you again for your time!