Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Mehni

#16
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.
#17
As a modder: part laziness.

It takes about an hour to update the various channels (Steam/GitHub/Ludeon/Nexus/Discord/Patreon). All that to fix an annoyance. I have about a dozen mods, so that's a day wasted.

As a cynic: because 1.0 claims backwards compatibility but it's not.

It's a breaking change that came in the middle of a stable release (1.0 pre 2150 can't parse the supportedVersions tag!) so modders got screwed by it. You either keep using <targetVersion> and support all builds of 1.0 (including those after 2150) or you use <supportedVersions> and only support the subset of 1.0 versions that were released after build 2150.

It causes an error for some if I change to <supportedVersions>, and it causes an error for others if I don't change to <supportedVersions>.

I'll update the version when there's a real reason to update the mod.
#18
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.
#19
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 are part of the 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.
#20
Alright. So, let's look at the error.

QuoteCould 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
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.
#21
For someone requesting "invest a few hours into making a mod for my very specific desire" you sound awfully hostile.

Do it yourself: JobGiver_PackFood. Divert your anger into learning.

Please do not respond with anything that isn't "I learned X and Y. I think I need Z. How do I get from X and Y to Z?"  as nothing else will specifically advance or support the stated goal of the title.
#22
Bugs / Re: All Mods Missing
June 03, 2019, 03:44:00 PM
Check your dev console/output log. Do you have a message like this?
Quote[Steamworks.NET] SteamAPI.Init() failed. Possible causes: Steam client not running, launched from outside Steam without steam_appid.txt in place, running with different privileges than Steam client (e.g. "as administrator")

If so, try to launch RimWorld through Steam. Launching it through a shortcut doesn't work for me either anymore.
#23
Mods / Re: Mod Request - Pawn reports
June 03, 2019, 03:41:15 PM
Numbers?
QuoteAdds a customizable general overview tab, allowing you to see any stats on all your colonists or prisoners in a single window. Quickly compare colonists to see who your best doctor is, or to assign gear optimally.

https://steamcommunity.com/sharedfiles/filedetails/?id=1414302321
#24
I can't think of any immediate causes for concern regarding compatibility. XML mods will continue to work just fine. XPath mods aren't effected either by your change. All common C# operations will continue to work just fine as well.

The type of mod that might error is the type of mod that has explicit support for Alien Races. Mods like Prepare Carefully, that Alien Facial Stuff thing, Alien Pregnancy. I'm not familiar enough with any of them to know for sure, but if there's any edge-case regarding compatibility it's probably there. Ask, test, or dig through the source.

If those C# mods run into issues, mod load order will not help. C# happens separately from XML/XPath, no amount of load order will change that.
#25
Help / Re: Damage Def Help
June 02, 2019, 07:06:21 AM
Quote
<capacities>
        <li>Tase</li>
        <li>Poke</li>
</capacities>

Here you tell the game: "Use my Tase ToolCapacityDef".

QuoteCould not resolve cross-reference to Verse.ToolCapacityDef named Tase (wanter=capacities)

Here the game says to you: "You don't have a ToolCapacityDef named Tase".

To make a fresh new melee attack you need about 4 different moving parts, and you're missing at least one of them. The ManeuverDef is also one of them: see the <requiredCapacity> tag? That is another link to the ToolCapacityDef.

It feels like an over-engineered system, but it is what it is.
#26
https://rimworldwiki.com/wiki/Modding_Tutorials/Def_classes#Further_Example

combined with

https://rimworldwiki.com/wiki/Modding_Tutorials/PatchOperations#PatchOperationAttributeSet

That oughta do it. If that doesn't make sense, tell us what parts you don't understand.

If it does make sense but doesn't work, tell us what you did and what doesn't work. Preferably with the exact code/xml of what you did.
#27
Very close. You have an extra [ before li.

*/DesignationCategoryDef[defName="Structure"]/specialDesignatorClasses/li[text() = "Designator_RemoveBridge"]

Is correct.
#28
Purpose
See what an XPath PatchOperation did. Default RimWorld gives no feedback on what happened. You can have a syntactically correct XPath doing completely the wrong thing and won't get feedback. With this tool, you can see what actually happened.

Usage
Put the Assemblies folder with its PatchOperationWhatHappened.dll inside your mod folder.

Example
How to get a before and after:

<Operation Class="PatchOperationSequence">
  <success>Always</success>
  <operations>
    <li Class="PatchOperationWhatHappened.Log">
      <xpath>Defs/WorldObjectDef[defName="Caravan"]</xpath>
      </li>
      <li Class="PatchOperationAdd">
        <xpath>/Defs/WorldObjectDef[defName="Caravan"]/comps</xpath>
          <value>
            <li>
              <compClass>MyNameSpace.MyClass</compClass>
            </li>
          </value>
      </li>
      <li Class="PatchOperationWhatHappened.Log">
        <xpath>Defs/WorldObjectDef[defName="Caravan"]</xpath>
      </li>
  </operations>
</Operation>


Output
https://gist.github.com/HugsLibRecordKeeper/6654e57cc1b1af50a8bb462d2522e2ff#file-output_log-txt-L55-L57

Tips

  • It's wise to PatchOperationWhatHappened.Log one level higher for a better overview.
  • Patching multiple things with Defs/ThingDef[defName="FOO" or defName="BAR"] in one operation may result in confusing (but correct) output. The different XML nodes each get logged individually.

DOWNLOAD HERE
#29
Help / Re: Problems with placeworkers
May 26, 2019, 06:41:07 AM
Quote            try
            {
                thisMirror = (Building_Mirror_TTFM)GenClosest.ClosestThingReachable(p.Position, p.Map, ThingRequest.ForGroup(ThingRequestGroup.BuildingArtificial), PathEndMode.None, TraverseParms.For(p), 3f);
            }
            catch
            {
                return false;
            }

You have a try/catch block which looks like it'll trigger the catch a lot of the time. This is effectively an unhandled error, and you're going in blind.
#30
Quote from: LWM on May 23, 2019, 12:07:15 PM
Is there any way to tell that sort of thing?

No, not ahead of time. You don't know it until you test it. In-lining happens at run-time, as that is when the JIT (Just-In-Time) compiler does its work. Different configurations may even have different results regarding inlining.

Here's what erdelf dug up regarding it. Keep in mind this is not an exhaustive list:
Quote
regarding inlining, I gathered this general baseline at some point
Methods that are greater than 32 bytes of IL will not be inlined.
Virtual functions are not inlined.
Methods that have complex flow control will not be in-lined. Complex flow control is any flow control other than if/then/else; in this case, switch or while.
Methods that contain exception-handling blocks are not inlined, though methods that throw exceptions are still candidates for inlining.
Methods that have a larger struct as a formal argument, will not be inlined

Harmony 2.0 promises improvements regarding "don't mark this for in-lining" which looks good but that won't help you now.

From where I'm sitting, you've got the following options:
- Abandon mod.
- patch every method which uses the IsWildMan extension method to use IsWildManBySickBoyWi. You can use Harmony's MethodReplacer for that.
- Find some other way to achieve what you wish to achieve.

FWIW I don't think Harmony's Prepare() will help: the MethodInfo is there, it just never gets called. Read up on in-lining for a more in-depth technical analysis but the good enough explanation is this: To avoid overhead, method calls to short methods get replaced with the body of the method. That effectively means that instead of if (pawn.IsWildMan()) the JIT compiler optimises it to if (pawn.kindDef == PawnKindDefOf.WildMan) : the body of the method replaces the method call.