[LIB] Harmony v1.2.0.1

Started by Brrainz, January 13, 2017, 04:59:21 PM

Previous topic - Next topic

Kiame

#270
Quote from: dburgdorf on December 09, 2018, 08:47:17 AM
Quote from: notfood on December 09, 2018, 04:37:37 AMbe careful, the "?" syntax after  is required in the situation that WorkTab is null so you don't get a null exception

When I add the question mark to the line, it won't even compile; I get a syntax error.

Edit: c# 3.5 should have the ? syntax support as least in VS

You can re-write the code to be explicit about types and null checking

var assembly = mod.assemblies.loadedAssemblies.FirstOrDefault(a => a.GetName().Name == "WorkTab");
Type type = null;
if (assembly != null)
    type = assembly.GetType("WorkTab.Pawn_Extensions");
if (type != null)
{
    // patch WorkTab method
}


Or close to that at least

notfood

#271
If you are using monodevelop, make sure you are using MSBuild and C# Language version is set to default or latest.

Try updating your VS or Monodevelop

dburgdorf

Quote from: notfood on December 09, 2018, 02:52:36 PMIf you are using monodevelop....

I'm using SharpDevelop, and it's already set to compile with C# version 5.0, so I have no idea why it doesn't like the syntax in your example. But as Kiame pointed out, it can be worked around, so I don't think it's too big a concern, and I'd really rather not derail this thread any more than I already have.  ;)
- Rainbeau Flambe (aka Darryl Burgdorf) -
Old. Short. Grumpy. Bearded. "Yeah, I'm a dorf."



Buy me a Dr Pepper?

Bendigeidfran

Hello guys, I'm wondering if someone can talk me through using Harmony to change a variable in vanilla. I've got Harmony in my mod already and I've used it before to use parts of vanilla code in an incident but I don't have a very good grasp of it. Essentially all I want to do is change a variable in PowerNetGraphics to point at a different texture. Specifically it's "WireMat" which currently points to the vanilla wire texture and the usual way of handling this would be to overwrite the texture by putting it in the same file structure inside my mod's textures folder but that doesn't work for this particular texture.

Kiame

#274
Quote from: Bendigeidfran on January 14, 2019, 03:19:19 PM
Hello guys, I'm wondering if someone can talk me through using Harmony to change a variable in vanilla. I've got Harmony in my mod already and I've used it before to use parts of vanilla code in an incident but I don't have a very good grasp of it. Essentially all I want to do is change a variable in PowerNetGraphics to point at a different texture. Specifically it's "WireMat" which currently points to the vanilla wire texture and the usual way of handling this would be to overwrite the texture by putting it in the same file structure inside my mod's textures folder but that doesn't work for this particular texture.

Since WireMat is a `static readonly` value you won't be able to override it. The only option i can think of is patching wherever it's used. The only place i see it being referenced is in PowerNetGraphics.PrintWirePieceConnecting. In this case use a Transpiler. A simpler but less compatible route would be a prefix which re-writes the entire method just replacing the first call that sets the mat then prevents further execution.

Bendigeidfran

Quote from: Kiame on January 15, 2019, 02:18:51 AM
Since WireMat is a `static readonly` value you won't be able to override it. The only option i can think of is patching wherever it's used. The only place i see it being referenced is in PowerNetGraphics.PrintWirePieceConnecting. In this case use a Transpiler. A simpler but less compatible route would be a prefix which re-writes the entire method just replacing the first call that sets the mat then prevents further execution.
Thanks for your reply, I figured that would be the case. Compatibility isn't a major issue with my mod since it's a total overhaul of the game, plus I doubt all that many mods rely on that particular function anyway. Is there a simple tutorial or example somewhere I can look at showing me how to Prefix it and prevent further execution? I'm having trouble just finding something I can look at to learn how to use this stuff. I'd love to learn to transpile but unfortunately it's a bit beyond my skill, or at least it's not worth the effort to learn for such a tiny change.

LWM

Ah, no.  You'll have to *Transpile* it.  Much more complicated.  Still doable.

Actually, I finally got the IL view in Mono to work for me, so I can say it's likely to be very easy.  For transpiling ;)

You now how to set up the Harmony Patch?  You can select the correct function, etc?

If you look at some of my Harmony Transpilers, you'll get one way to approach the situation:  https://github.com/lilwhitemouse/RimWorld-LWM.DeepStorage/blob/master/DeepStorage/Deep_Storage_PutInto.cs

For you, it should be easier:

Look for the code with the opcode OpCodes.Ldsfld (it should be the very very first operation, but no promises) and then replace it with the appropriate OpCode to load your texture instead - something like opcode=OpCodes.Ldfld and operand=Harmony.AccessTools.?...Field?("your field?").  Then just return everything else untouched!

Hope that helps!  Hope that works.

LWM




LWM

I seem to be stuck with one particular line of IL Code:


yield return new CodeInstruction(OpCodes.Callvirt, typeof(Thing).GetMethod("CanStackWith"));


This fails as soon as Harmony attempts to patch.  If the one line is taken out, it patches fine (it would probably crash if it ran, of course, the stack would be messed up, but it patches) Can anyone point out what I'm doing wrong here?  Thanks a lot!

--LWM

Error I'm getting:

[HugsLib][ERR] Failed to apply Harmony patches for HugsLib.LWM_DeepStorage. Exception was: System.FormatException: Method RimWorld.WorkGiver_Merge.JobOnThing(Verse.Pawn, Verse.Thing, System.Boolean) cannot be patched. Reason: Invalid IL code in (wrapper dynamic-method) RimWorld.WorkGiver_Merge:JobOnThing_Patch0 (object,Verse.Pawn,Verse.Thing,bool): IL_009c: callvirt  0x0000001b



Mehni

One particular line of IL is not a lot of context to debug with.

How about you post a gist with your transpiler and the harmony log?

LWM

<grumble grumble>New technology<grumble>

Hey, they make that really easy, actually!  This should be code one can drop in:

https://gist.github.com/lilwhitemouse/03bccaa66c4ba64fa13701427456842a

--LWM

Roolo

#280
Quote from: LWM on March 12, 2019, 08:34:17 AM
<grumble grumble>New technology<grumble>

Hey, they make that really easy, actually!  This should be code one can drop in:

https://gist.github.com/lilwhitemouse/03bccaa66c4ba64fa13701427456842a

--LWM

It's a bit hard to confirm this without testing, but I think I see what's going wrong. It seems that instead of putting two Things on the stack, your code results in putting two ThingDefs on the stack before CanStackWith is called.

At line 13, you do:
yield return code[i];

AFTER that, you check whether the current codeinstruction is getting the value of the field Thing.def, and whether the code instruction following the next one is also a call to the field Thing.def:


            if (code[i].opcode == OpCodes.Ldfld &&
                code[i].operand == typeof(Verse.Thing).GetField("def")) {
                if (code[i+2]?.opcode == OpCodes.Ldfld &&
                    code[i+2].operand == typeof(Verse.Thing).GetField("def")) {


When this check passes, you already put Thing.def on the stack (during line 13). You then skip the next code instruction, return the other code instruction that was calling Thing.def:

i=i+2;
yield return code[i];


By doing this, you actually put another ThingDef on the stack.
You then call the CanStackWith method, with two ThingDefs instead of Things on the stack, resulting in errors.

The fix should be straightforward. You could for instance put an else condition after line 52, and move line 13 inside of that. (I didn't test this, so I'm not 100% sure if this fix is correct). Or you could change the index offsets so that you're actually putting Things on the stack.

Let me know if this helps, or if I made any mistakes in my reasoning :)

LWM

#281
OFFS.  That was a stupid error on my part - thanks v much for the catch.

Ok.  Now I am baffled:

In SOME cases, I can put whatever I like on the stack and then add various calls, and it will still patch.  I corrected my counting, it works, but then I was curious and in place of the CanStackWith() call, I tried several others:


    // Either one of these patch without Harmony errors:
    yield return new CodeInstruction(OpCodes.Callvirt, typeof(Pawn).GetProperty("Map").GetGetMethod());
    yield return new CodeInstruction(OpCodes.Callvirt,typeof(Hediff).GetProperty("Part").GetGetMethod());


So.... Harmony returns no errors for these, and the functions are never actually called during patching, no?  Heck, the 2nd one doesn't even crash the game (didn't try the 1st).  So...why didn't my attempt to call CanStackWith() pass muster?  And why does the Hediff.Part pass?

Anyway, thank you v much!  I am confused, but I have working code now, so that's a solid win ^.^

--LWM

Roolo

Quote from: LWM on March 12, 2019, 11:06:26 AM
OFFS.  That was a stupid error on my part - thanks v much for the catch.

Ok.  Now I am baffled:

In SOME cases, I can put whatever I like on the stack and then add various calls, and it will still patch.  I corrected my counting, it works, but then I was curious and in place of the CanStackWith() call, I tried several others:


    // Either one of these patch without Harmony errors:
    yield return new CodeInstruction(OpCodes.Callvirt, typeof(Pawn).GetProperty("Map").GetGetMethod());
    yield return new CodeInstruction(OpCodes.Callvirt,typeof(Hediff).GetProperty("Part").GetGetMethod());


So.... Harmony returns no errors for these, and the functions are never actually called during patching, no?  Heck, the 2nd one doesn't even crash the game (didn't try the 1st).  So...why didn't my attempt to call CanStackWith() pass muster?  And why does the Hediff.Part pass?

Anyway, thank you v much!  I am confused, but I have working code now, so that's a solid win ^.^

--LWM

You're welcome. I wouldn't know why it won't throw errors for the other examples you gave. I'd certainly expect it. We might never know, but at least it's working now for you.

LWM

Harmony Transpiler question:

Do the transpilations (transpiles?) happen serially?  I.e., does one get the IL code that has already been ...tinkered with by the earlier mods?

--LWM

LegendaryDrop

If I'm just trying to add a new public float to an existing .dll (verse.RaceProperties) is Harmony the way to go? It's not patching a specific method per se... Played around with it a little but can't get it to work, and there seems to be a lack of example code out there to use for such a thing.

What I'm trying to do is make a custom property akin to 'petness' that I can add via XML tags to any farm animals I choose. From what I gather it's drawing things like petness and packAnimal from this RaceProperties.cs file (I THINK, I could be way off...) and I would like to augment it at runtime instead of replacing the whole thing.

Or if there is a much simpler alternative to give an animal pawn a custom property that I'm overlooking, that works too :)