[Solved] Need help with Harmony Detour

Started by CallMeDio, June 12, 2017, 03:25:11 PM

Previous topic - Next topic

CallMeDio

After not modding since A14 im trying to recreate an old mod using harmony instead of ccl.
What im trying to do for this first test is replace the pawn.MainDesc with my method that returns "abc", this very first test has taken me more than 10 hours so far and no success, so time to ask for help as im sure this must be stupidly simple.
Current attempt:
namespace Stem_Cells
{
    [StaticConstructorOnStartup]
    class Main
    {
        static Main()
        {
            HarmonyInstance.DEBUG = true;
            var harmony = HarmonyInstance.Create("Dio.test.stmod");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
        }
       
    }

    [HarmonyPatch(typeof(Pawn))]
    [HarmonyPatch("MainDesc")]
    static class PawnPatch
    {
        public static IEnumerable<CodeInstruction> Transpiler(
            MethodBase original,
            IEnumerable<CodeInstruction> instructions)
        {
            return instructions.MethodReplacer(
                AccessTools.Method(typeof(Pawn), "MainDesc"),
                AccessTools.Method(typeof(PawnPatch), "MainDescPatch"));
        }
        static string MainDescPatch(bool writeAge)
        {
            Log.Message("My method was executed");
           
            return "abc";
           
        }
    }
}



harmony log:
PATCHING Verse.Pawn System.String MainDesc(Boolean)
L_0000: Local var #0 System.String
L_0000: ldarg.0
L_0001: ldc.i4.1
L_0002: ldc.i4.1
L_0003: ldc.i4.0
L_0004: call System.String BestKindLabel(Verse.Pawn, Boolean, Boolean, Boolean)
L_0009: stloc.0
L_000a: ldarg.0
L_000b: call RimWorld.Faction get_Faction()
L_0010: brfalse Label #2
L_0015: ldarg.0
L_0016: call RimWorld.Faction get_Faction()
L_001b: ldfld RimWorld.FactionDef def
L_0020: ldfld System.Boolean hidden
L_0025: brtrue Label #3
L_002a: ldstr "PawnMainDescFactionedWrap"
L_002f: ldc.i4.2
L_0030: newarr System.Object
L_0035: dup
L_0036: ldc.i4.0
L_0037: ldloc.0
L_0038: stelem.ref
L_0039: dup
L_003a: ldc.i4.1
L_003b: ldarg.0
L_003c: call RimWorld.Faction get_Faction()
L_0041: callvirt System.String get_Name()
L_0046: stelem.ref
L_0047: call System.String Translate(System.String, System.Object[])
L_004c: stloc.0
L_004d: Label #2
L_004d: Label #3
L_004d: ldarg.1
L_004e: brfalse Label #4
L_0053: ldarg.0
L_0054: ldfld Verse.Pawn_AgeTracker ageTracker
L_0059: brfalse Label #5
L_005e: ldloc.0
L_005f: ldstr ", "
L_0064: ldstr "AgeIndicator"
L_0069: ldc.i4.1
L_006a: newarr System.Object
L_006f: dup
L_0070: ldc.i4.0
L_0071: ldarg.0
L_0072: ldfld Verse.Pawn_AgeTracker ageTracker
L_0077: callvirt System.String get_AgeNumberString()
L_007c: stelem.ref
L_007d: call System.String Translate(System.String, System.Object[])
L_0082: call System.String Concat(System.String, System.String, System.String)
L_0087: stloc.0
L_0088: Label #4
L_0088: Label #5
L_0088: ldloc.0
L_0089: call System.String CapitalizeFirst(System.String)
L_008e: br Label #0
L_0093: Label #0
L_0093: ret
DONE

PATCHING Verse.AreaManager Void ExposeData()
L_0000: ldarg.0
L_0001: call Void Prefix(Verse.AreaManager)
L_0006: ldarg.0
L_0007: ldflda System.Collections.Generic.List`1[Verse.Area] areas
L_000c: ldstr "areas"
L_0011: ldc.i4.2
L_0012: ldc.i4.0
L_0013: newarr System.Object
L_0018: call Void Look[Area](System.Collections.Generic.List`1[Verse.Area] ByRef, System.String, LookMode, System.Object[])
L_001d: ldsfld Verse.LoadSaveMode mode
L_0022: ldc.i4.2
L_0023: bne.un Label #2
L_0028: ldarg.0
L_0029: call Void UpdateAllAreasLinks()
L_002e: Label #2
L_002e: br Label #0
L_0033: Label #0
L_0033: ret
DONE

PATCHING RimWorld.PlaySettings Void DoPlaySettingsGlobalControls(Verse.WidgetRow, Boolean)
L_0000: Local var #0 System.Boolean
L_0000: Local var #1 System.Boolean
L_0000: Local var #2 System.Boolean
L_0000: Local var #3 System.Boolean
L_0000: ldarg.0
L_0001: ldfld System.Boolean showColonistBar
L_0006: stloc.0
L_0007: ldarg.2
L_0008: brfalse Label #2
L_000d: call ProgramState get_ProgramState()
L_0012: ldc.i4.2
L_0013: bne.un Label #3
L_0018: ldarg.1
L_0019: ldarg.0
L_001a: ldflda System.Boolean showColonistBar
L_001f: ldsfld UnityEngine.Texture2D ShowColonistBar
L_0024: ldstr "ShowColonistBarToggleButton"
L_0029: call System.String Translate(System.String)
L_002e: ldsfld Verse.SoundDef MouseoverToggle
L_0033: ldnull
L_0034: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0039: Label #3
L_0039: ldarg.0
L_003a: ldfld System.Boolean lockNorthUp
L_003f: stloc.1
L_0040: ldarg.1
L_0041: ldarg.0
L_0042: ldflda System.Boolean lockNorthUp
L_0047: ldsfld UnityEngine.Texture2D LockNorthUp
L_004c: ldstr "LockNorthUpToggleButton"
L_0051: call System.String Translate(System.String)
L_0056: ldsfld Verse.SoundDef MouseoverToggle
L_005b: ldnull
L_005c: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0061: ldloc.1
L_0062: ldarg.0
L_0063: ldfld System.Boolean lockNorthUp
L_0068: beq Label #4
L_006d: ldarg.0
L_006e: ldfld System.Boolean lockNorthUp
L_0073: brfalse Label #5
L_0078: call RimWorld.Planet.WorldCameraDriver get_WorldCameraDriver()
L_007d: ldc.i4.1
L_007e: callvirt Void RotateSoNorthIsUp(Boolean)
L_0083: Label #4
L_0083: Label #5
L_0083: ldarg.1
L_0084: ldarg.0
L_0085: ldflda System.Boolean usePlanetDayNightSystem
L_008a: ldsfld UnityEngine.Texture2D UsePlanetDayNightSystem
L_008f: ldstr "UsePlanetDayNightSystemToggleButton"
L_0094: call System.String Translate(System.String)
L_0099: ldsfld Verse.SoundDef MouseoverToggle
L_009e: ldnull
L_009f: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_00a4: ldarg.1
L_00a5: ldarg.0
L_00a6: ldflda System.Boolean expandingIcons
L_00ab: ldsfld UnityEngine.Texture2D ExpandingIcons
L_00b0: ldstr "ExpandingIconsToggleButton"
L_00b5: call System.String Translate(System.String)
L_00ba: ldsfld Verse.SoundDef MouseoverToggle
L_00bf: ldnull
L_00c0: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_00c5: br Label #6
L_00ca: Label #2
L_00ca: ldarg.1
L_00cb: ldarg.0
L_00cc: ldflda System.Boolean showLearningHelper
L_00d1: ldsfld UnityEngine.Texture2D ShowLearningHelper
L_00d6: ldstr "ShowLearningHelperWhenEmptyToggleButton"
L_00db: call System.String Translate(System.String)
L_00e0: ldsfld Verse.SoundDef MouseoverToggle
L_00e5: ldnull
L_00e6: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_00eb: ldarg.1
L_00ec: ldarg.0
L_00ed: ldflda System.Boolean showZones
L_00f2: ldsfld UnityEngine.Texture2D ShowZones
L_00f7: ldstr "ZoneVisibilityToggleButton"
L_00fc: call System.String Translate(System.String)
L_0101: ldsfld Verse.SoundDef MouseoverToggle
L_0106: ldnull
L_0107: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_010c: ldarg.1
L_010d: ldarg.0
L_010e: ldflda System.Boolean showEnvironment
L_0113: ldsfld UnityEngine.Texture2D ShowEnvironment
L_0118: ldstr "ShowEnvironmentToggleButton"
L_011d: call System.String Translate(System.String)
L_0122: ldsfld Verse.SoundDef MouseoverToggle
L_0127: ldstr "InspectRoomStats"
L_012c: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0131: ldarg.1
L_0132: ldarg.0
L_0133: ldflda System.Boolean showColonistBar
L_0138: ldsfld UnityEngine.Texture2D ShowColonistBar
L_013d: ldstr "ShowColonistBarToggleButton"
L_0142: call System.String Translate(System.String)
L_0147: ldsfld Verse.SoundDef MouseoverToggle
L_014c: ldnull
L_014d: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0152: ldarg.1
L_0153: ldarg.0
L_0154: ldflda System.Boolean showRoofOverlay
L_0159: ldsfld UnityEngine.Texture2D ShowRoofOverlay
L_015e: ldstr "ShowRoofOverlayToggleButton"
L_0163: call System.String Translate(System.String)
L_0168: ldsfld Verse.SoundDef MouseoverToggle
L_016d: ldnull
L_016e: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0173: ldarg.1
L_0174: ldarg.0
L_0175: ldflda System.Boolean autoHomeArea
L_017a: ldsfld UnityEngine.Texture2D AutoHomeArea
L_017f: ldstr "AutoHomeAreaToggleButton"
L_0184: call System.String Translate(System.String)
L_0189: ldsfld Verse.SoundDef MouseoverToggle
L_018e: ldnull
L_018f: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0194: call Boolean get_ResourceReadoutCategorized()
L_0199: stloc.2
L_019a: ldloc.2
L_019b: stloc.3
L_019c: ldarg.1
L_019d: ldloca.s 2 (System.Boolean)
L_019f: ldsfld UnityEngine.Texture2D CategorizedResourceReadout
L_01a4: ldstr "CategorizedResourceReadoutToggleButton"
L_01a9: call System.String Translate(System.String)
L_01ae: ldsfld Verse.SoundDef MouseoverToggle
L_01b3: ldnull
L_01b4: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_01b9: ldloc.2
L_01ba: ldloc.3
L_01bb: beq Label #7
L_01c0: ldloc.2
L_01c1: call Void set_ResourceReadoutCategorized(Boolean)
L_01c6: Label #6
L_01c6: Label #7
L_01c6: ldloc.0
L_01c7: ldarg.0
L_01c8: ldfld System.Boolean showColonistBar
L_01cd: beq Label #8
L_01d2: call RimWorld.ColonistBar get_ColonistBar()
L_01d7: callvirt Void MarkColonistsDirty()
L_01dc: Label #8
L_01dc: br Label #0
L_01e1: Label #0
L_01e1: ldarg 1
L_01e7: ldarg 2
L_01ed: call Void Postfix(Verse.WidgetRow, Boolean)
L_01f2: ret
DONE

PATCHING Verse.Pawn System.String MainDesc(Boolean)
L_0000: Local var #0 System.String
L_0000: ldarg.0
L_0001: ldc.i4.1
L_0002: ldc.i4.1
L_0003: ldc.i4.0
L_0004: call System.String BestKindLabel(Verse.Pawn, Boolean, Boolean, Boolean)
L_0009: stloc.0
L_000a: ldarg.0
L_000b: call RimWorld.Faction get_Faction()
L_0010: brfalse Label #2
L_0015: ldarg.0
L_0016: call RimWorld.Faction get_Faction()
L_001b: ldfld RimWorld.FactionDef def
L_0020: ldfld System.Boolean hidden
L_0025: brtrue Label #3
L_002a: ldstr "PawnMainDescFactionedWrap"
L_002f: ldc.i4.2
L_0030: newarr System.Object
L_0035: dup
L_0036: ldc.i4.0
L_0037: ldloc.0
L_0038: stelem.ref
L_0039: dup
L_003a: ldc.i4.1
L_003b: ldarg.0
L_003c: call RimWorld.Faction get_Faction()
L_0041: callvirt System.String get_Name()
L_0046: stelem.ref
L_0047: call System.String Translate(System.String, System.Object[])
L_004c: stloc.0
L_004d: Label #2
L_004d: Label #3
L_004d: ldarg.1
L_004e: brfalse Label #4
L_0053: ldarg.0
L_0054: ldfld Verse.Pawn_AgeTracker ageTracker
L_0059: brfalse Label #5
L_005e: ldloc.0
L_005f: ldstr ", "
L_0064: ldstr "AgeIndicator"
L_0069: ldc.i4.1
L_006a: newarr System.Object
L_006f: dup
L_0070: ldc.i4.0
L_0071: ldarg.0
L_0072: ldfld Verse.Pawn_AgeTracker ageTracker
L_0077: callvirt System.String get_AgeNumberString()
L_007c: stelem.ref
L_007d: call System.String Translate(System.String, System.Object[])
L_0082: call System.String Concat(System.String, System.String, System.String)
L_0087: stloc.0
L_0088: Label #4
L_0088: Label #5
L_0088: ldloc.0
L_0089: call System.String CapitalizeFirst(System.String)
L_008e: br Label #0
L_0093: Label #0
L_0093: ret
DONE

PATCHING Verse.AreaManager Void ExposeData()
L_0000: ldarg.0
L_0001: call Void Prefix(Verse.AreaManager)
L_0006: ldarg.0
L_0007: ldflda System.Collections.Generic.List`1[Verse.Area] areas
L_000c: ldstr "areas"
L_0011: ldc.i4.2
L_0012: ldc.i4.0
L_0013: newarr System.Object
L_0018: call Void Look[Area](System.Collections.Generic.List`1[Verse.Area] ByRef, System.String, LookMode, System.Object[])
L_001d: ldsfld Verse.LoadSaveMode mode
L_0022: ldc.i4.2
L_0023: bne.un Label #2
L_0028: ldarg.0
L_0029: call Void UpdateAllAreasLinks()
L_002e: Label #2
L_002e: br Label #0
L_0033: Label #0
L_0033: ret
DONE

PATCHING RimWorld.PlaySettings Void DoPlaySettingsGlobalControls(Verse.WidgetRow, Boolean)
L_0000: Local var #0 System.Boolean
L_0000: Local var #1 System.Boolean
L_0000: Local var #2 System.Boolean
L_0000: Local var #3 System.Boolean
L_0000: ldarg.0
L_0001: ldfld System.Boolean showColonistBar
L_0006: stloc.0
L_0007: ldarg.2
L_0008: brfalse Label #2
L_000d: call ProgramState get_ProgramState()
L_0012: ldc.i4.2
L_0013: bne.un Label #3
L_0018: ldarg.1
L_0019: ldarg.0
L_001a: ldflda System.Boolean showColonistBar
L_001f: ldsfld UnityEngine.Texture2D ShowColonistBar
L_0024: ldstr "ShowColonistBarToggleButton"
L_0029: call System.String Translate(System.String)
L_002e: ldsfld Verse.SoundDef MouseoverToggle
L_0033: ldnull
L_0034: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0039: Label #3
L_0039: ldarg.0
L_003a: ldfld System.Boolean lockNorthUp
L_003f: stloc.1
L_0040: ldarg.1
L_0041: ldarg.0
L_0042: ldflda System.Boolean lockNorthUp
L_0047: ldsfld UnityEngine.Texture2D LockNorthUp
L_004c: ldstr "LockNorthUpToggleButton"
L_0051: call System.String Translate(System.String)
L_0056: ldsfld Verse.SoundDef MouseoverToggle
L_005b: ldnull
L_005c: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0061: ldloc.1
L_0062: ldarg.0
L_0063: ldfld System.Boolean lockNorthUp
L_0068: beq Label #4
L_006d: ldarg.0
L_006e: ldfld System.Boolean lockNorthUp
L_0073: brfalse Label #5
L_0078: call RimWorld.Planet.WorldCameraDriver get_WorldCameraDriver()
L_007d: ldc.i4.1
L_007e: callvirt Void RotateSoNorthIsUp(Boolean)
L_0083: Label #4
L_0083: Label #5
L_0083: ldarg.1
L_0084: ldarg.0
L_0085: ldflda System.Boolean usePlanetDayNightSystem
L_008a: ldsfld UnityEngine.Texture2D UsePlanetDayNightSystem
L_008f: ldstr "UsePlanetDayNightSystemToggleButton"
L_0094: call System.String Translate(System.String)
L_0099: ldsfld Verse.SoundDef MouseoverToggle
L_009e: ldnull
L_009f: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_00a4: ldarg.1
L_00a5: ldarg.0
L_00a6: ldflda System.Boolean expandingIcons
L_00ab: ldsfld UnityEngine.Texture2D ExpandingIcons
L_00b0: ldstr "ExpandingIconsToggleButton"
L_00b5: call System.String Translate(System.String)
L_00ba: ldsfld Verse.SoundDef MouseoverToggle
L_00bf: ldnull
L_00c0: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_00c5: br Label #6
L_00ca: Label #2
L_00ca: ldarg.1
L_00cb: ldarg.0
L_00cc: ldflda System.Boolean showLearningHelper
L_00d1: ldsfld UnityEngine.Texture2D ShowLearningHelper
L_00d6: ldstr "ShowLearningHelperWhenEmptyToggleButton"
L_00db: call System.String Translate(System.String)
L_00e0: ldsfld Verse.SoundDef MouseoverToggle
L_00e5: ldnull
L_00e6: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_00eb: ldarg.1
L_00ec: ldarg.0
L_00ed: ldflda System.Boolean showZones
L_00f2: ldsfld UnityEngine.Texture2D ShowZones
L_00f7: ldstr "ZoneVisibilityToggleButton"
L_00fc: call System.String Translate(System.String)
L_0101: ldsfld Verse.SoundDef MouseoverToggle
L_0106: ldnull
L_0107: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_010c: ldarg.1
L_010d: ldarg.0
L_010e: ldflda System.Boolean showEnvironment
L_0113: ldsfld UnityEngine.Texture2D ShowEnvironment
L_0118: ldstr "ShowEnvironmentToggleButton"
L_011d: call System.String Translate(System.String)
L_0122: ldsfld Verse.SoundDef MouseoverToggle
L_0127: ldstr "InspectRoomStats"
L_012c: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0131: ldarg.1
L_0132: ldarg.0
L_0133: ldflda System.Boolean showColonistBar
L_0138: ldsfld UnityEngine.Texture2D ShowColonistBar
L_013d: ldstr "ShowColonistBarToggleButton"
L_0142: call System.String Translate(System.String)
L_0147: ldsfld Verse.SoundDef MouseoverToggle
L_014c: ldnull
L_014d: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0152: ldarg.1
L_0153: ldarg.0
L_0154: ldflda System.Boolean showRoofOverlay
L_0159: ldsfld UnityEngine.Texture2D ShowRoofOverlay
L_015e: ldstr "ShowRoofOverlayToggleButton"
L_0163: call System.String Translate(System.String)
L_0168: ldsfld Verse.SoundDef MouseoverToggle
L_016d: ldnull
L_016e: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0173: ldarg.1
L_0174: ldarg.0
L_0175: ldflda System.Boolean autoHomeArea
L_017a: ldsfld UnityEngine.Texture2D AutoHomeArea
L_017f: ldstr "AutoHomeAreaToggleButton"
L_0184: call System.String Translate(System.String)
L_0189: ldsfld Verse.SoundDef MouseoverToggle
L_018e: ldnull
L_018f: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_0194: call Boolean get_ResourceReadoutCategorized()
L_0199: stloc.2
L_019a: ldloc.2
L_019b: stloc.3
L_019c: ldarg.1
L_019d: ldloca.s 2 (System.Boolean)
L_019f: ldsfld UnityEngine.Texture2D CategorizedResourceReadout
L_01a4: ldstr "CategorizedResourceReadoutToggleButton"
L_01a9: call System.String Translate(System.String)
L_01ae: ldsfld Verse.SoundDef MouseoverToggle
L_01b3: ldnull
L_01b4: callvirt Void ToggleableIcon(Boolean ByRef, UnityEngine.Texture2D, System.String, Verse.SoundDef, System.String)
L_01b9: ldloc.2
L_01ba: ldloc.3
L_01bb: beq Label #7
L_01c0: ldloc.2
L_01c1: call Void set_ResourceReadoutCategorized(Boolean)
L_01c6: Label #6
L_01c6: Label #7
L_01c6: ldloc.0
L_01c7: ldarg.0
L_01c8: ldfld System.Boolean showColonistBar
L_01cd: beq Label #8
L_01d2: call RimWorld.ColonistBar get_ColonistBar()
L_01d7: callvirt Void MarkColonistsDirty()
L_01dc: Label #8
L_01dc: br Label #0
L_01e1: Label #0
L_01e1: ldarg 1
L_01e7: ldarg 2
L_01ed: call Void Postfix(Verse.WidgetRow, Boolean)
L_01f2: ret
DONE




no error, no log message,it simply doesn't replace the text with "abc"
sorry if it is a dumb question/post but I simply can't spot the problem
QuoteYou may need a rubber duck.  Also, try some caveman debugging.

Released mods: No Mood Loss on Prisoner Sold or Died

Fluffy (l2032)

#1
first off, stop using the transpiler for now, it's an advanced use case that does way more than you need.
second, think about what you actually want to do. More often than not, the reason you're detouring something will be to attach a bit of extra code. With Harmony, you don't have to replace the whole method, you can just attach your extra code before or after the original method has run.
third, you are aware Harmony has a fairly good wiki, right?

Harmony allows you to do three main things, prefix, postfix and infix.

A prefix _can_ act like the CCL detours, but you may not need it to. A prefix gets all the arguments the original function was called with, then allows you to do whatever you want, including changing the return value, and then allows you to return control to the original function - or not, by returning true or false.

A postfix has all of the same capabilities as a prefix, but runs _after_ the original method has executed. Obviously, that means you can't stop it from executing anymore like you could with a prefix.

An infix is what you've been trying with the transpiler. It gets the actual IL code (we're getting close to assembly here, so this is not going to be pretty), and allows you to change it line-by-line. Where the pre- and post-fixes are fairly foolproof, the transpiler is extremely unforgiving, relying on you to make sure the result will be valid IL instructions. If it's not valid, you'll get a black screen or a CTD.

What you're trying to do could be accomplished with;

[HarmonyPatch(typeof(Pawn), "MainDesc")]
static class MyPatch // a better name would be Patch_Pawn_MainDesc
{
    static bool Prefix( Pawn __instance, ref string __result, bool writeAge)
    {
        // __instance is a magic variable that gives you the instance this method was called on.
        // __result is a magic variable that gives you the result of the original function
        // note that it will be null in a prefix, but if the ref keyword is supplied you _can_ edit it.
        // there are more of these magic variables; refer to the wiki.
        // note that you can list all the arguments you want from the original method, but you don't have to.
        // we could have left bool writeAge off.

        Log.Message("My method was executed");
       
        __result = "abc"; // assign our text to the result.
        return false; // stop original method from executing, and return __result to the caller.
    }
}


Or, even better, you could append your string to the original method's result

[HarmonyPatch(typeof(Pawn), "MainDesc")]
static class MyPatch
{
    static void Postfix( ref string __result )
    {
        // postfix can't stop execution, so return is always void.
        // we don't need the instance or writeAge, so leave them off.

        Log.Message("My method was executed");
        __result += "\nabc"; // append our text (on a new line) to the result.
    }
}



scuba156

The problem is your confusing it with how a detour work. Harmony patches the methods, it doesn't detour them. Prefix patches before and can return false to not execute the orignal method, Postfix runs after the orignal method returns, and a transpiler is a source-to-source compiler where you can manipulate the original methods OpCodes and data.

Understanding the above, when you use a Harmony transpiler patch on a method, the codeInstructions it gets are the OPCodes INSIDE the method. In your example, your trying to replace any calls to Pawn.MainDesc() that are called within the Pawn.MainDesc() which doesnt happen (and why the harmony log shows things like 'call System.String BestKindLabel(Verse.Pawn, Boolean, Boolean, Boolean)' because that is the call to GenLabel.BestKindLabel() that is INSIDE the Pawn.MainDesc()).

If you want to use a transpiler then you would need to patch the call to Pawn.MainDesc() that you want to override at the place that method originally gets called. Otherwise, with a prefix and postfix patch then you have the right method patched.

If you just want to change to output of a method, its generally easier to do it in a Prefix or Postfix patch. Just remember that if you use a prefix to return the value, then any other mod trying to call that method will also return your value. Heres a prefix and a postfix you can mess around with to get an idea.

        public static bool Prefix(ref string __result) {
            Log.Message("Prefix");
            __result = "abc";
            return false;
        }

        public static bool Postfix(ref string __result) {
            Log.Message("Postfix"); // Wont run if Prefix returns false as the orginal method does not get called
            __result = "abc";
        }


EDIT: I got ninja'd

CallMeDio

#3
Hello, first of all thanks a lot Fluffy and scuba for putting together this awesome well explained responses, it cleared my mind on the parts I didn't understand about harmony, this is pure gold for awful self thought programmers like me.
I saw the wiki but when I did I saw that prefix was before and postfix after and I wrongly got the idea I needed the other (transpiler) to actually change the method but that was not it, I will double my attention when reading documentations next time (my bad).

So if I got it right the transpiler is for replacing a method on one of its uses inside other method? Like for example if I have a method A that uses method B, the transpiler would be to only replace that method B being used by Method A without actually replacing B for all it other uses?

btw, the posted examples worked and I finally got the "abc"!


edit: and how to deal with methods that use methods that are private?
edit²: is it using reflection?
QuoteYou may need a rubber duck.  Also, try some caveman debugging.

Released mods: No Mood Loss on Prisoner Sold or Died

Fluffy (l2032)

In theory, transpilers allow you to change anything that happens inside of a method. Be aware that what you're changing is the code instructions that are normally generated by the compiler, so this won't be the pretty code you normally type. You could literally redefine it, or - as more often happens - make it not call a specific function, or change the call target (so yes, what you said).

Handling non-public methods, properties, and values is indeed done through reflection. Harmony itself uses quite a bit of reflection under the hood, and also exposes a few helpers for reflection in the AccessTools and Traverse classes.