Ludeon Forums

RimWorld => Mods => Help => Topic started by: bionicjoethehat on June 04, 2019, 11:15:49 AM

Title: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 04, 2019, 11:15:49 AM
My previous topic: https://ludeon.com/forums/index.php?topic=48889.0

Hi!

Since no one seemed to be interested in porting RandomPlus to B18, I figured I could try doing it myself since I had some coding experience. So I did.

There were some errors during the building process but I managed to fix them. The only thing I couldn't fix though was some run-time error and I had absolutely now idea what it said so, could anyone please help me with this?

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.Exception: Exception from HarmonyInstance "RandomPlus" ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
  at System.Collections.Generic.List`1[Harmony.CodeInstruction].CheckIndex (Int32 index) [0x00000] in <filename unknown>:0
  at System.Collections.Generic.List`1[Harmony.CodeInstruction].set_Item (Int32 index, Harmony.CodeInstruction value) [0x00000] in <filename unknown>:0
  at RandomPlus.Patch_RandomizeMethod.Transpiler (IEnumerable`1 instructions) [0x00000] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
  at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <filename unknown>:0
  at Harmony.CodeTranspiler+<>c__DisplayClass10_0.<GetResult>b__0 (System.Reflection.MethodInfo transpiler) [0x00000] in <filename unknown>:0
  at System.Collections.Generic.List`1[System.Reflection.MethodInfo].ForEach (System.Action`1 action) [0x00000] in <filename unknown>:0
  at Harmony.CodeTranspiler.GetResult (System.Reflection.Emit.ILGenerator generator, System.Reflection.MethodBase method) [0x00000] in <filename unknown>:0
  at Harmony.ILCopying.MethodBodyReader.FinalizeILCodes (System.Collections.Generic.List`1 transpilers, System.Collections.Generic.List`1 endLabels, System.Collections.Generic.List`1 endBlocks) [0x00000] in <filename unknown>:0
  at Harmony.ILCopying.MethodCopier.Finalize (System.Collections.Generic.List`1 endLabels, System.Collections.Generic.List`1 endBlocks) [0x00000] in <filename unknown>:0
  at Harmony.MethodPatcher.CreatePatchedMethod (System.Reflection.MethodBase original, System.String harmonyInstanceID, System.Collections.Generic.List`1 prefixes, System.Collections.Generic.List`1 postfixes, System.Collections.Generic.List`1 transpilers) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at Harmony.MethodPatcher.CreatePatchedMethod (System.Reflection.MethodBase original, System.String harmonyInstanceID, System.Collections.Generic.List`1 prefixes, System.Collections.Generic.List`1 postfixes, System.Collections.Generic.List`1 transpilers) [0x00000] in <filename unknown>:0
  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.<PatchAll>b__7_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 RandomPlus.HarmonyPatches..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)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update_Patch1(Object)
Verse.Root_Entry:Update()


Another issue is that when I build the mod, my IDE exports not only my mod's dll, but also some dll-dependenices(?) to the Assemblies folder.

Any help is appreciated, cheers!
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Mehni on June 05, 2019, 03:28:32 AM
Alright. So, let's look at the error.

QuoteCould not execute post-long-event action. Exception: System.TypeInitializationException (https://docs.microsoft.com/en-us/dotnet/api/system.typeinitializationexception?view=netframework-3.5): An exception was thrown by the type initializer (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/constructors) for RandomPlus.HarmonyPatches ---> System.Exception: Exception from HarmonyInstance "RandomPlus" ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ArgumentOutOfRangeException (https://docs.microsoft.com/en-gb/dotnet/api/system.argumentoutofrangeexception?view=netframework-3.5): Argument is out of range.
Parameter name: index
That's a lot of words, but it basically says: "Your constructor threw an ArgumentOutOfRangeException."

The rest of the stacktrace tells you where:


  at System.Collections.Generic.List`1[Harmony.CodeInstruction].CheckIndex (Int32 index) [0x00000] in <filename unknown>:0
  at System.Collections.Generic.List`1[Harmony.CodeInstruction].set_Item (Int32 index, Harmony.CodeInstruction value) [0x00000] in <filename unknown>:0
  at RandomPlus.Patch_RandomizeMethod.Transpiler (IEnumerable`1 instructions) [0x00000] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)


The RandomPlus.Patch_RandomizeMethod.Transpiler tried to grab a part of a List<T> that was out of range. In 8 out of ten cases, you tried to grab element 11 out of 10. In the other cases, you tried to grab element -1. If you want more specific help, post your code.

QuoteAnother issue is that when I build the mod, my IDE exports not only my mod's dll, but also some dll-dependenices(?) to the Assemblies folder.

https://rimworldwiki.com/wiki/Modding_Tutorials/Setting_up_a_solution

Copy Local => False.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 05, 2019, 04:53:18 AM
Thanks for replying! :)

Quotehttps://rimworldwiki.com/wiki/Modding_Tutorials/Setting_up_a_solution

Copy Local => False.

I think the tutorial needs to be more specific on setting Copy Local to False in Xamarin Studio because I couldn't find how to do that and had to switch to SharpDevelop, for which there is a more detailed instruction.

Turns out, unlike in SharpDevelop, you don't right-click a reference, but left-click it and proceed to the Properties window where you can uncheck the Copy Local parameter or, if the window is not open, you right-click the appropriate reference and click Properties. I think this (perhaps with a better wording) should be added to the Wiki page.

QuoteThe RandomPlus.Patch_RandomizeMethod.Transpiler tried to grab a part of a List<T> that was out of range. In 8 out of ten cases, you tried to grab element 11 out of 10. In the other cases, you tried to grab element -1. If you want more specific help, post your code.

Here you are:

using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;

namespace RandomPlus
{
    [HarmonyPatch(typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
    class Patch_RandomizeMethod
    {
        static void Prefix()
        {
            RandomSettings.ResetRerollCounter();
        }

        static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            int startIndex = -1;

            var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
                .GetField("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
            var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
                .GetMethod("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);

            var codes = new List<CodeInstruction>(instructions);

            for (int i = 0; i < codes.Count; i++)
            {
                if (codes[i].opcode == OpCodes.Ldfld &&
                    codes[i].operand == curPawnFieldInfo &&
                    codes[i + 1].opcode == OpCodes.Call &&
                    codes[i + 1].operand == randomizeInPlaceMethodInfo)
                {
                    startIndex = i;
                    break;
                }
            }

            if (startIndex != -1)
            {
                var randomLimitRerollMethodInfo = typeof(RandomSettings)
                    .GetMethod("RandomRerollLimit", BindingFlags.Public | BindingFlags.Static);

                var CheckPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
                    .GetMethod("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);

                codes[startIndex + 8] = new CodeInstruction(OpCodes.Call, randomLimitRerollMethodInfo);
               
                var startLoopLocation = codes[startIndex + 12].operand;

                var newCode = new List<CodeInstruction>();
                newCode.Add(new CodeInstruction(OpCodes.Nop));
                newCode.Add(new CodeInstruction(OpCodes.Ldarg_0));
                newCode.Add(new CodeInstruction(OpCodes.Ldfld, curPawnFieldInfo));
                newCode.Add(new CodeInstruction(OpCodes.Call, CheckPawnIsSatisfiedMethodInfo));
                newCode.Add(new CodeInstruction(OpCodes.Brfalse, startLoopLocation));
                codes.InsertRange(startIndex + 12, newCode);
            }

            return codes;
        }
    }
}


UPD: The error disappears when I comment the last if-clause, so, it must be it.

UPD2: Seems like this is the problem:
codes[startIndex + 8] = new CodeInstruction(OpCodes.Call, randomLimitRerollMethodInfo);

UPD3: it seems we've localized the problem but I have no idea what that piece of code does. :D

UPD4: At least the Edit window where you select skills and traits that your pawn must have is not working but the mod won't roll pawns until you get one that matches your criteria.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 05, 2019, 11:06:27 AM
At this point, there is very little I understand about the Patch_RandomizeMethod class but it seems that it is used to replace the original RandomizeCurPawn method?

I'm also wondering what this code does:
var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
                .GetField("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);

Perhaps grabs a value for the valuable from somewhere else?

UPD: figured they're grabbing "curPawn" from Page_ConfigureStartingPawns which has a private field "curPawn" but I have a question, why, when I was looking at this class through Assembly Browser in the IDE, it didn't show me the field? Because when I looked at the decompilied version, there is was such field.
Solved by setting "Visibility" to "All members". So happy now. :D

UPD2: The biggest mystery for me now is perhaps those "codes": what are they? What do they do?

UPD3: seems like Transpiler is used to modify the code of an original method, probably RandomizeCurPawn or something but I have absolutely no idea what those OpCodes stand for.

UPD4:RandomizeCurPawn in 1.0 (https://github.com/josh-m/RW-Decompile/blob/d5bbfd741a46452bbfbec3a38b11a122f766f057/RimWorld/Page_ConfigureStartingPawns.cs#L234) has more lines than the one in B18 (https://github.com/josh-m/RW-Decompile/blob/3a7cd955b574c183405cc8065b15f1c231c7da08/RimWorld/Page_ConfigureStartingPawns.cs#L233). I guess the code tries to change some command in the original method of 1.0 but it is not there in B18 so it fails.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 06, 2019, 03:40:19 AM
Seems like this piece of code is searching for the line (https://github.com/josh-m/RW-Decompile/blob/d5bbfd741a46452bbfbec3a38b11a122f766f057/RimWorld/Page_ConfigureStartingPawns.cs#L243) which calls randomizeInPlace in the original method and then, as far as I can tell, saves its instruction number or something to the startIndex variable:

            int startIndex = -1;

            var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
                .GetField("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
            var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
                .GetMethod("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);

            var codes = new List<CodeInstruction>(instructions);

            for (int i = 0; i < codes.Count; i++)
            {
                if (codes[i].opcode == OpCodes.Ldfld &&
                    codes[i].operand == curPawnFieldInfo &&
                    codes[i + 1].opcode == OpCodes.Call &&
                    codes[i + 1].operand == randomizeInPlaceMethodInfo)
                {
                    startIndex = i;
                    break;
                }
            }
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Mehni on June 06, 2019, 04:07:41 AM
Quote from: bionicjoethehat on June 05, 2019, 11:06:27 AM
At this point, there is very little I understand about the Patch_RandomizeMethod class but it seems that it is used to replace the original RandomizeCurPawn method?

UPD2: The biggest mystery for me now is perhaps those "codes": what are they? What do they do?

UPD3: seems like Transpiler is used to modify the code of an original method, probably RandomizeCurPawn or something but I have absolutely no idea what those OpCodes stand for.


Background info: There's a layer of abstraction between C# and machine code. C# isn't compiled into machine code, it's compiled into an intermediate language that is then compiled into machine code by the JIT-compiler during runtime.

Those OpCodes (https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes?view=netframework-3.5) are part of the Common Intermediate Language (https://en.wikipedia.org/wiki/Common_Intermediate_Language). They're the lowest-level human readable CPU instructions. Harmony allows you to manipulate the OpCodes prior to their use by the JIT compiler, mostly to make small modifications to methods. You're basically manipulating what goes on the stack.

If you have a good decompiler, you can select "view IL" and see exactly what happens. For the interest of education, this is what ILSpy's "IL with C#" view gives me:

.method private hidebysig
instance void RandomizeCurPawn () cil managed
{
// Method begins at RVA 0x1707a8
// Code size 83 (0x53)
.maxstack 2
.locals init (
[0] int32
)

// if (TutorSystem.AllowAction("RandomizePawn"))
IL_0000: ldstr "RandomizePawn"
IL_0005: call valuetype RimWorld.EventPack RimWorld.EventPack::op_Implicit(string)
IL_000a: call bool RimWorld.TutorSystem::AllowAction(valuetype RimWorld.EventPack)
// (no C# code)
IL_000f: brtrue IL_0015

IL_0014: ret

// int num = 0;
IL_0015: ldc.i4.0
IL_0016: stloc.0
// loop start (head: IL_0017)
// curPawn = StartingPawnUtility.RandomizeInPlace(curPawn);
IL_0017: ldarg.0
IL_0018: ldarg.0
IL_0019: ldfld class Verse.Pawn RimWorld.Page_ConfigureStartingPawns::curPawn
IL_001e: call class Verse.Pawn Verse.StartingPawnUtility::RandomizeInPlace(class Verse.Pawn)
IL_0023: stfld class Verse.Pawn RimWorld.Page_ConfigureStartingPawns::curPawn
// num++;
IL_0028: ldloc.0
IL_0029: ldc.i4.1
IL_002a: add
IL_002b: stloc.0
// while (num <= 20 && !StartingPawnUtility.WorkTypeRequirementsSatisfied());
IL_002c: ldloc.0
IL_002d: ldc.i4.s 20
IL_002f: ble IL_0039

// (no C# code)
IL_0034: br IL_0043

IL_0039: call bool Verse.StartingPawnUtility::WorkTypeRequirementsSatisfied()
IL_003e: brfalse IL_0017
// end loop

// TutorSystem.Notify_Event("RandomizePawn");
IL_0043: ldstr "RandomizePawn"
IL_0048: call valuetype RimWorld.EventPack RimWorld.EventPack::op_Implicit(string)
IL_004d: call void RimWorld.TutorSystem::Notify_Event(valuetype RimWorld.EventPack)
// (no C# code)
IL_0052: ret
} // end of method Page_ConfigureStartingPawns::RandomizeCurPawn


I'm not too familiar with RandomPlus, but it looks like you could change

while (num <= 20 && !StartingPawnUtility.WorkTypeRequirementsSatisfied());

into

while (num <= 20 && !StartingPawnUtility.WorkTypeRequirementsSatisfied() && RandomSettings.CheckPawnIsSatisfied(curPawn));

Doing && is a bit tricky (jumping to labels is annoying in IL), so you could elect to use &. No short-circuiting, but oh well. The rest of the code is already present in the transpiler: it's just a matter of inserting it at the right spot.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 06, 2019, 09:09:38 AM
codes[startIndex + 8] = new CodeInstruction(OpCodes.Call, randomLimitRerollMethodInfo);
Does this piece of code mean that it replaces certain instruction which is at "startIndex + 8" line?
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: LWM on June 06, 2019, 11:26:34 AM
If any of those loops are on the final line of code (that is, codes.Count-1), then codes[i+1] is going to throw exactly that sort of error.

Ask me how I know :p

Quote from: bionicjoethehat on June 06, 2019, 09:09:38 AM
codes[startIndex + 8] = new CodeInstruction(OpCodes.Call, randomLimitRerollMethodInfo);
Does this piece of code mean that it replaces certain instruction which is at "startIndex + 8" line?

Yes.  Hopefully startIndex+8 IS what you want to be replacing.  And is still in bounds.

As a sanity check, I have started doing multiple for(...) loops when I transpile.  One of the checks is that the current index is (i<codes.Count-X), where X is however many steps ahead I am.  Then after each loop I check to see if my index i has hit the end of the code too early.  If it has, I know I messed something up in the code block above it.

Debugging (or writing) transpilers is tedious and needs careful attention.  Fun, right?  Kudos for taking this on - v brave ;)

--LWM
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 06, 2019, 01:37:08 PM
Pardon, which loops? I only see one. :)
            for (int i = 0; i < codes.Count; i++)
            {
                if (codes[i].opcode == OpCodes.Ldfld &&
                    codes[i].operand == curPawnFieldInfo &&
                    codes[i + 1].opcode == OpCodes.Call &&
                    codes[i + 1].operand == randomizeInPlaceMethodInfo)
                {
                    startIndex = i;
                    break;
                }
            }
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 06, 2019, 01:50:07 PM
What I'm stuck with now is that I can't tell where in the code the "codes[startIndex + 8]" and "startIndex + 12" instructions are. I also don't understand what these commands do:
                var newCode = new List<CodeInstruction>();
                newCode.Add(new CodeInstruction(OpCodes.Nop));
                newCode.Add(new CodeInstruction(OpCodes.Ldarg_0));
                newCode.Add(new CodeInstruction(OpCodes.Ldfld, curPawnFieldInfo));
                newCode.Add(new CodeInstruction(OpCodes.Call, CheckPawnIsSatisfiedMethodInfo));
                newCode.Add(new CodeInstruction(OpCodes.Brfalse, startLoopLocation));
                codes.InsertRange(startIndex + 12, newCode);

But perhaps if I understood the idea behind this piece of code, I would know how to change it to so it works in B18.

UPD:
Log.Message ((startIndex + 8).ToString() + " | " + codes.Count.ToString());
shows
15 | 14
If it says 15, then the fifteenth command in the ILSpy view of the method that Mehni posted is what is replaced by
codes[startIndex + 8] = new CodeInstruction(OpCodes.Call, randomLimitRerollMethodInfo);
?

So basically, I need to know where and what is added/changed to the original code.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 06, 2019, 11:59:52 PM
This may sound like a dumb idea, but if I don't understand what the mod author's code does with instructions and can't visualize where in the code of the original method, maybe I could just replace all the instructions of the original B18 method with those from 1.0 one?
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 03:17:20 AM
Here is my current code:
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;

namespace RandomPlus
{
[HarmonyPatch (typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
class Patch_RandomizeMethod
{
static void Prefix ()
{
RandomSettings.ResetRerollCounter ();
}

static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)
{
var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
.GetField ("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
.GetMethod ("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);
var workTypeRequirementsSatisfiedMethodInfo = typeof(StartingPawnUtility)
.GetMethod ("workTypeRequirementsSatisfied", BindingFlags.Public | BindingFlags.Static);

var codes = new List<CodeInstruction> (instructions);

// I basically replace the B18 RandomizeCurPawn with 1.0's.
// The IL code is available at:
// https://ludeon.com/forums/index.php?topic=48902.msg460016#msg460016

codes.RemoveRange (5, 5);

var IL_0015 = new CodeInstruction (OpCodes.Ldc_I4_0);

var IL_0039 = new CodeInstruction (OpCodes.Call, workTypeRequirementsSatisfiedMethodInfo);

List<CodeInstruction> supportCode = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Ldc_I4_0),
new CodeInstruction (OpCodes.Stloc_0),

new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),

new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),

new CodeInstruction (OpCodes.Ldloc_0),
new CodeInstruction (OpCodes.Ldc_I4_1),
new CodeInstruction (OpCodes.Add),
new CodeInstruction (OpCodes.Stloc_0),

new CodeInstruction (OpCodes.Ldloc_0),
new CodeInstruction (OpCodes.Ldc_I4_S, 20),
new CodeInstruction (OpCodes.Ble, IL_0039),

// These IL_* strings are just placeholders for now

new CodeInstruction (OpCodes.Br, "IL_0043"),

IL_0039,
new CodeInstruction (OpCodes.Brfalse, "IL_0017"),
};

codes.InsertRange (5, supportCode);

for (var i = 0; i < codes.Count; i++) {
Log.Message (codes[i].ToString());
}

// Original code:

// int startIndex = -1;
//           
//            for (int i = 0; i < codes.Count; i++)
//            {
//                if (codes[i].opcode == OpCodes.Ldfld &&
//                    codes[i].operand == curPawnFieldInfo &&
//                    codes[i + 1].opcode == OpCodes.Call &&
//                    codes[i + 1].operand == randomizeInPlaceMethodInfo)
//                {
//                    startIndex = i;
//                    break;
//                }
//            }
//
// Log.Message (startIndex.ToString());


//            if (startIndex != -1)
//            {
//                var randomLimitRerollMethodInfo = typeof(RandomSettings)
//                    .GetMethod("RandomRerollLimit", BindingFlags.Public | BindingFlags.Static);
//
//                var CheckPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
//                    .GetMethod("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);
//
// codes[startIndex + 8] = new CodeInstruction(OpCodes.Call, randomLimitRerollMethodInfo);
//
//                var startLoopLocation = codes[startIndex + 12].operand;
//
//                var newCode = new List<CodeInstruction>();
//                newCode.Add(new CodeInstruction(OpCodes.Nop));
//                newCode.Add(new CodeInstruction(OpCodes.Ldarg_0));
//                newCode.Add(new CodeInstruction(OpCodes.Ldfld, curPawnFieldInfo));
//                newCode.Add(new CodeInstruction(OpCodes.Call, CheckPawnIsSatisfiedMethodInfo));
//                newCode.Add(new CodeInstruction(OpCodes.Brfalse, startLoopLocation));
//                codes.InsertRange(startIndex + 12, newCode);
//            }

return codes;
}
}
}

And my current problem is that some instructions depend on others. For instance:
IL_002f: ble IL_0039
I tried doing this, where IL_0039 is a variable containing the appropriate CodeInstruction:
new CodeInstruction (OpCodes.Ble, IL_0039),
But it was interpreted like this:
ble call NULL
So, how do you refer properly to other instructions?

Also, there is an instruction that I don't touch but it refers to another one that is changed:
IL_000f: brtrue IL_0015
It refers to the instruction that is changed with:
new CodeInstruction (OpCodes.Ldc_I4_0),
So I basically replace one instruction with another but will it still be accessible by IL_0015?
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Mehni on June 07, 2019, 03:30:01 AM
Now I finally think you read what I posted. If you didn't understand a lick of it, ask. Don't ignore it, because you need to understand it to update that transpiler. You cannot expect to solve this if you only look at it from a high-level perspective.

You have a list of CodeInstructions. CodeInstructions are things like:

- load the value of one on the stack.
- load the value of two on the stack.
- load the value of local variable A on the stack.
- Add together.

Think programming with BASIC.
>10 PRINT "HELLO WORLD!";
>20 GOTO 10.
That level.

What do you think "startIndex + 12" is? It's the (n + 12)th element in the list of instructions. Again, take a look at the ILCode I posted.

---

If you can't (yet) wrap your head around what the RandomizeCurPawn method should look like in ILCode, see if you can make sense of what that method should look like in C#. Then try and translate that back into ILCode.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 04:10:20 AM
Mehni, I read all posts as soon as I see them so don't worry. (:

I now think that changing the whole method is an overkill; it's rather easier to alter the existing code now that I know what the author does with it in their mod. I think all I need to do is to replace
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
in
private void RandomizeCurPawn()
{
if (!TutorSystem.AllowAction("RandomizePawn"))
{
return;
}
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
TutorSystem.Notify_Event("RandomizePawn");
}

with something along these lines
do
{
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
}
while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);

But I don't know how to write it in IL.
UDP:
Ah! I guess a loop is just piece of code that uses gotos.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 05:39:29 AM
Please, correct me if I'm wrong but it should be something like this, right?
ldarg.0

IL_0019: ldfld class Verse.Pawn RimWorld.Page_ConfigureStartingPawns::curPawn
IL_001e: call class Verse.Pawn Verse.StartingPawnUtility::RandomizeInPlace(class Verse.Pawn)
IL_0023: stfld class Verse.Pawn RimWorld.Page_ConfigureStartingPawns::curPawn

callvirt / CheckPawnIsSatisfiedMethodInfo
Brfalse => goto first line

I don't know how to refer to another instruction in Brfalse and I also don't think the rest of the code would work. :D How do I write it in proper IL?
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Mehni on June 07, 2019, 06:40:04 AM
QuoteI now think that changing the whole method is an overkill

And that's exactly what transpilers are for :D. Altering a specific and small part of a method, rather than the entire method.

Your second codeblock is B18. 1.0 already has a loop, you'll want to take that into account.

Write in C# what you envision the end-result should look like or carefully explain it.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 08:25:41 AM
1.0 does have a loop indeed, but I'm porting a mod from 1.0 to B18 which doesn't and I have already written an envision in my previous post (https://ludeon.com/forums/index.php?topic=48902.msg460079#msg460079) but to translate it to IL, I would either need to write something in C# use a decompiler which for some reason I can't install right now (but soon will) or to write IL code from scratch which I can't do. :P

So, would you please use your decompiler and post the instructions for:
do
{
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
}
while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);

?

Would you also answer my question regarding referring to other instructions in IL? Like in:
Brfalse ???
What would you write instead of the question marks?
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 10:22:39 AM
Found an online C# -> IL converter and here is what it showed:

        // loop start (head: IL_0001)
            IL_0001: nop
            IL_0002: ldarg.0
            IL_0003: ldarg.0
            IL_0004: call instance int32 C::B()
            IL_0009: stfld int32 C::curPawn
            IL_000e: nop
            IL_000f: ldarg.0
            IL_0010: call instance bool C::A()
            IL_0015: ldc.i4.0
            IL_0016: ceq
            IL_0018: stloc.0
            // sequence point: hidden
            IL_0019: ldloc.0
            IL_001a: brtrue.s IL_0001


The last thing left to do is to assign a label to an instruction IL_0001 so it can be used by IL_001a but I can't find how you can do it online.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Mehni on June 07, 2019, 11:06:49 AM
You want to turn
// Page_ConfigureStartingPawns
using Verse;

private void RandomizeCurPawn()
{
if (TutorSystem.AllowAction("RandomizePawn"))
{
int num = 0;
do
{
curPawn = StartingPawnUtility.RandomizeInPlace(curPawn);
num++;
}
while (num <= 20 && !StartingPawnUtility.WorkTypeRequirementsSatisfied());
TutorSystem.Notify_Event("RandomizePawn");
}
}


into

// Page_ConfigureStartingPawns
using Verse;

private void RandomizeCurPawn()
{
if (TutorSystem.AllowAction("RandomizePawn"))
{
do
{
curPawn = StartingPawnUtility.RandomizeInPlace(curPawn);
}
while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);
TutorSystem.Notify_Event("RandomizePawn");
}
}


Those are a few changes too many. Keep the "num". You can elect to change it to a higher value, but removing it is too invasive. Payoff vs effort. Not to mention the side-effects.

Changing !StartingPawnUtility.WorkTypeRequirementsSatisfied() to !RandomSettings.CheckPawnIsSatisfiedMethodInfo is good, but you'll need to keep labels in mind. Those IL_003e: brfalse IL_0017 can't be used easily in a transpiler. You'll need to find those labels manually -- it's much easier to avoid that entirely.

      IL_002f: ble IL_0039

      // (no C# code)
      IL_0034: br IL_0043

      IL_0039: call bool Verse.StartingPawnUtility::WorkTypeRequirementsSatisfied()
      IL_003e: brfalse IL_0017

IL_0039 is what we're after, but there's a jump label to it. We're going to recycle it.

here's some (pseudo) code

foreach codeinstruction in instructions
{
    if (codeinstruction == call bool Verse.StartingPawnUtility::WorkTypeRequirementsSatisfied())
    {
        codeinstruction.operand = null;
        codeinstruction.opcode = OpCodes.Ldarg_0;
        yield return codeinstruction;
        yield return new CodeInstruction(OpCodes.Ldfld, curPawnFieldInfo);
        yield return new CodeInstruction(OpCodes.Call, CheckPawnIsSatisfiedMethodInfo);       
    }
    else
        yield return codeinstruction;
}


Note that this removes StartingPawnUtility.WorkTypeRequirementsSatisfied(). The good news is that it's a static public method, so you can move the call to it into the CheckPawnIsSatisfiedMethodInfo method.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 11:26:12 AM
Your first code is taken from 1.0 but I am adapting a 1.0 mod for B18.

Here is the code of RandomizeCurPaw in B18:
private void RandomizeCurPawn()
{
if (!TutorSystem.AllowAction("RandomizePawn"))
{
return;
}
this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);
TutorSystem.Notify_Event("RandomizePawn");
}

As you can see, it doesn't have a loop.

And here is what I currently have:
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;

namespace RandomPlus
{
[HarmonyPatch (typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
class Patch_RandomizeMethod
{
static void Prefix ()
{
RandomSettings.ResetRerollCounter ();
}

static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)
{
var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
.GetField ("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
.GetMethod ("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);
var checkPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
.GetMethod ("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);

var codes = new List<CodeInstruction> (instructions);

var appropriatePlace = 5;

/* Removing following code in its IL form */

// this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);

codes.RemoveRange (appropriatePlace, 5);

/* Adding following code in its IL form: */

// do {
//  this.curPawn = StartingPawnUtility.RandomizeInPlace (this.curPawn);
  // } while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);

List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Nop), // I need to give this instruction a label
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
new CodeInstruction (OpCodes.Ldc_I4_0),
new CodeInstruction (OpCodes.Ceq),
new CodeInstruction (OpCodes.Stloc_0),
new CodeInstruction (OpCodes.Ldloc_0),
new CodeInstruction (OpCodes.Brfalse_S /* and use it here */),
};

codes.InsertRange (appropriatePlace, newCodes);

return codes;
}
}
}
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 11:30:39 AM
So, I need to figure out how to jump to a certain instruction, but how?
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 12:23:48 PM
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;

namespace RandomPlus
{
[HarmonyPatch (typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
class Patch_RandomizeMethod
{
static void Prefix ()
{
RandomSettings.ResetRerollCounter ();
}

static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)
{
var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
.GetField ("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
.GetMethod ("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);
var checkPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
.GetMethod ("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);

var codes = new List<CodeInstruction> (instructions);

var appropriatePlace = 6;

/* Removing the following code in its IL form */

// this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);

codes.RemoveRange (appropriatePlace, 5);

/* Adding the following code in its IL form: */

// do {
//  this.curPawn = StartingPawnUtility.RandomizeInPlace (this.curPawn);
// } while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);

List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Nop), // I need to give this instruction a label
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
new CodeInstruction (OpCodes.Ldc_I4_0),
new CodeInstruction (OpCodes.Ceq),
new CodeInstruction (OpCodes.Stloc_0),
new CodeInstruction (OpCodes.Ldloc_0),
};

newCodes [0].labels.Add (new Label ());

var nopLabel = newCodes [0].labels [0];

newCodes.Add (new CodeInstruction (OpCodes.Brfalse_S, nopLabel));

codes.InsertRange (appropriatePlace, newCodes);

// for (var i = 0; i < labels.Count; i++) {
// Log.Message (labels[i].ToString());
// }

// for (var i = 0; i < codes.Count; i++) {
// Log.Message (codes [i].ToString ());
// }

return codes;
}
}
}

Managed to assign a label but there is another error shown:

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_0036: stloc.0   


  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.<PatchAll>b__7_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 RandomPlus.HarmonyPatches..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)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update()
Verse.Root_Entry:Update()
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 07, 2019, 01:07:20 PM
This is the link to the playground I"m using.

https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgJgIwFgBQPkGYACNQgYUIG8dCbCBIAwsAO2EIGMBXAJwAUBDAO7MA3NVriaDIgCMA9nIiEAghAhzBydKgAUWgAyFm/ALYBTAJSVJtWzWQB2QsG6czY7HdoBfGzenEACyEAHJywGAAZgCeAPoAogBuZqx66IbG5lZUnl52Nra+uTT+jCxsAEr8zDByJmAAXmYAksy8EPzsZjrlhAAOQszZBbQMTgPCHnmERbalsgpKpAAWZuwA1gLCzQDOAMr8ETuRYGYwOsPFdmPOru4jNLMSV3R93GCJh2ZBhFU1dY0zKQeFtmBccHQ6DlIXQooQdABCYDLMA7AB0qnUmm0OgARH9avUmqDcRYLBDIdCYY4PDCijDatZsDCaMjUWiuHxBoQALzOFHogkApqtdqdb46Nnozmgiy0yEzQiCFEQCVIgVolZrTaDXYHI4nM4XOUUuhStFhCIxBLJVL46qEwEkk3MuhPEovNCoJnTIpFHAAegAVBS3h8vj8hUSgSDBuDXVTYZF4QiACqcYBybhaDFqDRaXT2/7R53k11Q01jeVu03mmXcvl7YD8bgRZgAc1BAFUIhAwMBomio4DRR0upKNfXhC6YenM9n0BbwlE4kkUsA8cPiYNSfKikGA0A==

And this is my current code:
using Harmony;
using RimWorld;
using Verse;
using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;
using System;
using System.Reflection;

namespace RandomPlus
{
[HarmonyPatch (typeof(Page_ConfigureStartingPawns), "RandomizeCurPawn")]
class Patch_RandomizeMethod
{
static void Prefix ()
{
RandomSettings.ResetRerollCounter ();
}

static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)
{
var curPawnFieldInfo = typeof(Page_ConfigureStartingPawns)
.GetField ("curPawn", BindingFlags.NonPublic | BindingFlags.Instance);
var randomizeInPlaceMethodInfo = typeof(StartingPawnUtility)
.GetMethod ("RandomizeInPlace", BindingFlags.Public | BindingFlags.Static);
var checkPawnIsSatisfiedMethodInfo = typeof(RandomSettings)
.GetMethod ("CheckPawnIsSatisfied", BindingFlags.Public | BindingFlags.Static);

var codes = new List<CodeInstruction> (instructions);

var appropriatePlace = 6;

/* Removing the following code in its IL form */

// this.curPawn = StartingPawnUtility.RandomizeInPlace(this.curPawn);

codes.RemoveRange (appropriatePlace, 5);

/* Adding the following code in its IL form: */

// do {
//   this.curPawn = StartingPawnUtility.RandomizeInPlace (this.curPawn);
// } while (!RandomSettings.CheckPawnIsSatisfiedMethodInfo);

//
// // loop start (head: IL_0016)
// IL_0016: nop
// IL_0017: ldarg.0
// IL_0018: ldarg.0
// IL_0019: ldarg.0
// IL_001a: ldfld int32 C::curPawn
// IL_001f: call instance int32 C::RandomizeInPlace(int32)
// IL_0024: stfld int32 C::curPawn
// IL_0029: nop
// IL_002a: ldarg.0
// IL_002b: call instance bool C::CheckPawnIsSatisfied()
// IL_0030: ldc.i4.0
// IL_0031: ceq
// IL_0033: stloc.1
// // sequence point: hidden
// IL_0034: ldloc.1
// IL_0035: brtrue.s IL_0016
// // end loop

List <CodeInstruction> newCodes = new List<CodeInstruction> {
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Ldfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Call, randomizeInPlaceMethodInfo),
new CodeInstruction (OpCodes.Stfld, curPawnFieldInfo),
new CodeInstruction (OpCodes.Nop),
new CodeInstruction (OpCodes.Ldarg_0),
new CodeInstruction (OpCodes.Call, checkPawnIsSatisfiedMethodInfo),
new CodeInstruction (OpCodes.Ldc_I4_0),
new CodeInstruction (OpCodes.Ceq),
new CodeInstruction (OpCodes.Stloc_1),
new CodeInstruction (OpCodes.Ldloc_1),
};

newCodes [0].labels.Add (new Label ());

var nopLabel = newCodes [0].labels [0];

newCodes.Add (new CodeInstruction (OpCodes.Brtrue_S, nopLabel));

codes.InsertRange (appropriatePlace, newCodes);

for (var i = 0; i < codes.Count; i++) {
Log.Message (codes [i].ToString ());
}

return codes;
}
}
}


And the text of the error:
Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for RandomPlus.HarmonyPatches ---> System.FormatException: Method RimWorld.Page_ConfigureStartingPawns.RandomizeCurPawn() cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.Page_ConfigureStartingPawns:RandomizeCurPawn_Patch1 (object): IL_003c: stloc.1   


  at Harmony.PatchFunctions.UpdateWrapper (System.Reflection.MethodBase original, Harmony.PatchInfo patchInfo, System.String instanceID) [0x00000] in <filename unknown>:0
  at Harmony.PatchProcessor.Patch () [0x00000] in <filename unknown>:0
  at Harmony.HarmonyInstance.<PatchAll>b__7_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 RandomPlus.HarmonyPatches..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)
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:UpdateCurrentAsynchronousEvent()
Verse.LongEventHandler:LongEventsUpdate(Boolean&)
Verse.Root:Update()
Verse.Root_Entry:Update()
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Mehni on June 07, 2019, 01:59:55 PM
Quote from: bionicjoethehat on June 07, 2019, 11:26:12 AM
Your first code is taken from 1.0 but I am adapting a 1.0 mod for B18.

oh ffs -- I only now realise it. You're downgrading a mod? well screw the transpiler then -- who cares about compatibility within B18. Just prefix it and return false. Amount of users affected: You.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 08, 2019, 01:35:28 AM
Quoteoh ffs -- I only now realise it. You're downgrading a mod?
Yes, and I've said that both in my messages and in the title which is shown next to each message here.
Quotewell screw the transpiler then -- who cares about compatibility within B18. Just prefix it and return false. Amount of users affected: You.
At least I care and that fact is enough for me to port this mod though I think more that just me will find it interesting, but you're right, not many.

In the end of the day, I am free to spend my time and energy however I want (in a good way) and if something is useful for at least one person or even more people, I don't see why it shouldn't exist.

I appreciate your help, Mehni and I've already included you in the credits section of this mod but I still need to make it work and would appreciate any help from anyone.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 08, 2019, 05:12:31 AM
Solved the problem with "IL_003c: stloc.1"
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: LWM on June 08, 2019, 12:19:02 PM
If you run into any further problems, code posted on a site such as github might make things easier for people trying to help.  Or might not.

Congratulations on making use of the transpiler!

--LWM
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 10, 2019, 05:50:07 AM
Yay! The port is now fully working! I've been using it for two days now and I absolutely enjoy having it in B18!
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Canute on June 10, 2019, 06:10:23 AM
Gratzs !!
And now you can slowly port all your missing mod's to 1.0 and update rimworld itself to 1.0. too:-)
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 10, 2019, 06:48:52 AM
I downgraded a mod from 1.0 to to the cozy B18 which I play :P

https://ludeon.com/forums/index.php?topic=48959.msg460267#msg460267
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: Mehni on June 10, 2019, 08:21:34 AM
Good job. One addition to the source I saw on GitHub:

When you're adding Labels, you should use the ILGenerator shared through Harmony. Adding your own labels might cause your code to jump to unexpected places. With Harmony's ILGenerator, labels are in their proper place even with multiple patches.

Your method signature would then be
static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions, ILGenerator generator)
rather than
static IEnumerable<CodeInstruction> Transpiler (IEnumerable<CodeInstruction> instructions)

How you name the ILGenerator parameter has no influence, according to Harmony's wiki.
Title: Re: Beginner needs help with porting a mod 1.0 -> B18
Post by: bionicjoethehat on June 11, 2019, 02:22:50 AM
Thanks for your suggestion!

The label is now added properly.