[1.5] HugsLib (11.0.3) Lightweight modding library

Started by UnlimitedHugs, December 15, 2016, 02:20:14 PM

Previous topic - Next topic

scuba156

Quote from: UnlimitedHugs on January 31, 2017, 06:14:16 PM
The automatic restart can be disabled, but the prompt dialog will still show up. If you want to get rid of that, too, you could call WindowInjectionManager.RemoveInjection().
If I remember correctly, your prompt dialog detours Page_ModsConfig PostClose method, which is fine as I am inserting a new page class by detouring the main menu.

Thank you.

UnlimitedHugs

#136
Quote from: Linq on January 31, 2017, 06:22:34 PM
That explains it. Less Arbitrary Surgery got a lot of confusing bug reports because of this.

Yep, got a couple myself. Thankfully, a persistent player helped in tracking this one down.

Quote from: scuba156 on January 31, 2017, 06:28:21 PM
If I remember correctly, your prompt dialog detours Page_ModsConfig PostClose method, which is fine as I am inserting a new page class by detouring the main menu.

No detour, it just uses the GUI injector to hook into Page_ModsConfig. It doesn't affect other windows, of course.
HugsLib - AllowTool - Remote Tech - Map Reroll - Defensive Positions: Github, Steam

System.Linq



UnlimitedHugs

HugsLib - AllowTool - Remote Tech - Map Reroll - Defensive Positions: Github, Steam

Rock5

Can I get some advice on how to do this detour thing? I want to detour the pathfinding in the game. I think the method is call FindPath but it uses private fields of it's parent class Pathfinder. How do I access the parents fields? Or should I detour the whole PathFinder? But Pathfinder is a class. Please point me in the right direction.
Rock5 [B18] Mods
- Butchers Can Count Meat
- Sun Lamp Planner
- JTZoneButtons
- RimSearch
- JTExport

UnlimitedHugs

Quote from: Rock5 on February 07, 2017, 10:25:52 PM
Can I get some advice on how to do this detour thing? I want to detour the pathfinding in the game. I think the method is call FindPath but it uses private fields of it's parent class Pathfinder. How do I access the parents fields? Or should I detour the whole PathFinder? But Pathfinder is a class. Please point me in the right direction.

Using just HugsLib, you would have to use reflection to access the private fields. This is not ideal, since the pathfinder
is a performance-critical piece, and reflection is fairly slow. You could use Expressions to compile accessor methods to access the fields in a performance-friendly manner, at the cost of added complexity.

Your best bet might be to go with the Harmony library and add a prefix/postfix for that method. Harmony even allows to replace specific calls in the method body.
HugsLib detouring will be moved over to Harmony in the next alpha update, so you would save yourself some updating work there.

Also, Zenthar has a mod that already makes modifications to the pathfinder, so it's a good idea to keep compatibility in mind.
HugsLib - AllowTool - Remote Tech - Map Reroll - Defensive Positions: Github, Steam

Brrainz

It all depends on how often the modification is called. Detouring is one overhead and accessing the private fields another. If you patch the part that only gets called at the beginning of path finding it does not matter and I would not go with premature optimization. If you on the other hand have a method that is called 100x per game tick your best bet is to use Harmony and not use any pre/post patches at all and instead add or modify the original code with an IL code processor. This requires some knowledge on CIL but is quite straight forward.

Rock5

Will the harmony library allow me to access the parents private fields?

I think Better Pathfinding uses it's own detouring, not a library. I didn't want to use my own implementation until I understood it better but I guess it's an option. I could just make a copy of Better Pathfinding and modify the algorithm. If I can figure it out.


@pardeike. How do I use Harmony to replace the code "and not use any pre/post patches"? I thought that's all it did. And I don't now any CIL.
Rock5 [B18] Mods
- Butchers Can Count Meat
- Sun Lamp Planner
- JTZoneButtons
- RimSearch
- JTExport

Brrainz

Quote from: Rock5 on February 08, 2017, 08:17:31 AM
Will the harmony library allow me to access the parents private fields?

I think Better Pathfinding uses it's own detouring, not a library. I didn't want to use my own implementation until I understood it better but I guess it's an option. I could just make a copy of Better Pathfinding and modify the algorithm. If I can figure it out.


@pardeike. How do I use Harmony to replace the code "and not use any pre/post patches"? I thought that's all it did. And I don't now any CIL.
If you are not familiar with CIL which is an intermediate token language designed to produce assembler, I don't recommend diving right into it. But if you feel for a challenge I would recommend to start with an ILProcessor implementation that prepends a few tokens that read the private field. Write a method that accesses a private field and decompile it into IL code and see how the code looks like.

Most likely you will see something like

LDFLD fieldinfo_here

which is the code for "load content of field onto the stack".

Zhentar

The HugsLib equivalent to Better Pathfinding's archaic standalone detours functionality would be [DetourMethod(typeof(PathFinder),"FindPath")] on the PathFinderDetour.FindPath method. In that method, it uses a compiled accessor function to get the PathFinder instance's private map field (reflection would work too) and then calls the NewPathFinder instance associated with that map (using a MapComponent to tie the NewPathFinder instance to the map's lifecycle).

Even if it were possible to easily/efficiently get & set all of the pathfinder's fields, you still wouldn't want to go that route because it wouldn't let you change their type definitions; I've changed the PathFinderNodeFast fields up quite a bit.

re: figuring out the Better Pathfinding algorithm, I've started going through it and adding some comments in an attempt to make it at least slightly comprehensible to other people. I've still got quite a bit left to go on that, but please let me know which parts particularly opaque, especially if there already are comments attempting to explain it.

Rock5

ARGH!!! Just lost my post.

@Takes a moment to do a search on how to permanently disable backspace going back a page.@

Ok, that's done. Now let me see if I can remember everything I wrote.
Rock5 [B18] Mods
- Butchers Can Count Meat
- Sun Lamp Planner
- JTZoneButtons
- RimSearch
- JTExport

Rock5

I understand a little better but it's still too complicated for me. I don't know why I'm having so much time figuring out C#. It's the hardest language I've ever learned. I think it's the IDE. I'm not used to so much happening behind the scenes. I'd love it if there was a bare bones course for C# that didn't use VS and you had to do everything manually.

Anyway, I think the best chance I have of succeeding is if I make a copy of Better Pathfinding and change the parts I'm interested in. That way i wouldn't need to know how the rest of it works. It would help to know, basically, how all the classes tie together.

This is what I can make out so far:
- DetourInjector "runs" first. It uses the methods in Detours to detour FindPath and DebugCell. Why do you detour DebugCell? Has it something to do with drawing the path?
- So now when an instance of FindPath (or is it PathFinder) is created it runes PathFinderDetour.PathFind.
- This in turn gets the map. Hm... I'm not quite sure what it does with it but I'm assuming it makes it available to NewPathFinder.FindPath which it then runs.
- RegionPathCostHeuristics is used to calculate the heuristics and it looks like it uses RegionLinkDijkstra.
- I assume BpmxFastPriortyQueue is the queue implementation you use to manage the nodes.

Is that right?

BTW, am i hijacking this post? Should we continue this elsewhere?
Rock5 [B18] Mods
- Butchers Can Count Meat
- Sun Lamp Planner
- JTZoneButtons
- RimSearch
- JTExport

UnlimitedHugs

Quote from: Rock5 on February 08, 2017, 03:53:05 PM
I understand a little better but it's still too complicated for me. I don't know why I'm having so much time figuring out C#. It's the hardest language I've ever learned. I think it's the IDE. I'm not used to so much happening behind the scenes. I'd love it if there was a bare bones course for C# that didn't use VS and you had to do everything manually.

If you're just starting out, you might have a more enjoyable time working on something simpler- the pathfinder is one of the more complicated parts of Rimworld, and detouring increases the complexity significantly.
As for VS, it absolutely is your friend when working with C#, because it will point out errors without the need to compile. You could use a plain text editor, but you would miss out on many features that are designed to make our work easier.

Quote from: Rock5 on February 08, 2017, 03:53:05 PM
BTW, am i hijacking this post? Should we continue this elsewhere?

It's fine- this is somewhat related to the library, and others might benefit, as well.
HugsLib - AllowTool - Remote Tech - Map Reroll - Defensive Positions: Github, Steam

Zhentar

Quote from: Rock5 on February 08, 2017, 03:53:05 PM
This is what I can make out so far:
- DetourInjector "runs" first. It uses the methods in Detours to detour FindPath and DebugCell. Why do you detour DebugCell? Has it something to do with drawing the path?
- So now when an instance of FindPath (or is it PathFinder) is created it runes PathFinderDetour.PathFind.
- This in turn gets the map. Hm... I'm not quite sure what it does with it but I'm assuming it makes it available to NewPathFinder.FindPath which it then runs.
- RegionPathCostHeuristics is used to calculate the heuristics and it looks like it uses RegionLinkDijkstra.
- I assume BpmxFastPriortyQueue is the queue implementation you use to manage the nodes.

Is that right?

BTW, am i hijacking this post? Should we continue this elsewhere?

That's about right. Note that DetourInjector gets run first because of the [StaticConstructorOnStartup] attribute on the class - this is a RimWorld modding feature that will run your static constructor during mod initialization.

I detour DebugCell to make the label area larger than it is in Vanilla, so that I can print out more lines of debug code. Since I managed to get ison to implement off screen clipping in A16, it doesn't help anything in release builds so I should wrap that in a #if DEBUG now.