A quick tutorial of xpathing and patching

Started by minimurgle, May 24, 2017, 08:27:11 PM

Previous topic - Next topic

Alias

I have figured out how to xpath to a given tag, but is there a way to patch multiple tags for a given defName?
So for example if I wanted to change a gun's warmupTime and Mass, can this be done in one xpath  operation?

frenchiveruti

Quote from: Alias on December 21, 2017, 12:30:35 PM
I have figured out how to xpath to a given tag, but is there a way to patch multiple tags for a given defName?
So for example if I wanted to change a gun's warmupTime and Mass, can this be done in one xpath  operation?
You have to do a Patch Sequence for that.

Nightinggale

Quote from: Jaxxa on December 17, 2017, 10:10:14 PM
Is it possible to write logic in C# to decide if an XML patch will be applied or not?
I want to conditionally execute an XML patch based on a value in mod settings.
Looks like GetSettings<Settings>() is available when Verse.Mod is allocated, which is before running the patches. In other words it can be used. The approach to do so would be:

public class CheckSetting : PatchOperation
{
    public string someInfo;
    protected override bool ApplyWorker(XmlDocument xml)
    {
        try
        {
           // some code with GetSettings<Settings>(), which might return true
        }
        catch
        {
        }
        return false;
    }
}

If you return true, then the next operation in a sequence will be reached. If you return false, the rest of the operations will be ignored. Using try-catch prevents any possible issues with settings not existing and such. Better be safe than sorry.

The properties, like in this case someInfo is the tags in xml and they will be null if not filled out. You can skip those if you have nothing to use them for.

The PatchOperation can then be called from xml by using the class namespace.classname.

All files are patched by all patches, meaning you will get a performance hit if your code is slow. Putting your operation after ModCheck.FindFile will avoid that issue.
ModCheck - boost your patch loading times and include patchmods in your main mod.

Shurp

Quote from: Vindar on May 27, 2017, 06:31:19 PM

Use:

<Patch>   and </Patch> to open and close the document (placed after the header)


OK, is any advice in this thread still useable?  Because when I start a document with <Patch> Rimworld immediately complains "root element named Patch; should be named Defs".  And if I use <Defs><Patch> it simply complains "Found no useable data when trying to get defs".  So I have no idea what I'm doing wrong here.

Here's my entire file; what do I need to do so that this actually works?

<?xml version="1.0" encoding="utf-8" ?>

<Defs>
<Patch>
   <Operation Class="PathOperationReplace">
      <xpath>/Defs/ThingDef[defName = "Apparel_Pants"]/description</xpath>
      <value>
         <description>Super Pants</description>
      </value>
   </Operation>
</Patch>
</Defs>
If you give an annoying colonist a parka before banishing him to the ice sheet you'll only get a -3 penalty instead of -5.

And don't forget that the pirates chasing a refugee are often better recruits than the refugee is.

Nightinggale

Quote from: Shurp on February 25, 2018, 12:25:40 PM
OK, is any advice in this thread still useable?  Because when I start a document with <Patch> Rimworld immediately complains "root element named Patch; should be named Defs".
Yes it still applies, but you seem to have mixed up Defs and Patches.

There are two different directories to place xml files in. They are called Defs and Patches. All files in Defs needs to have Defs as root. All files in Patches needs to have Patch as root. On load, the game will load the Defs one by one and each time a Def is read, all the patches are applied to that one.
ModCheck - boost your patch loading times and include patchmods in your main mod.

Shurp

"Patches go in xml files in a subfolder named "Patches" in your mod folder (So "MyMod\Patches", just like you might have "MyMod\Defs" or "MyMod\Textures"). "

Ahhh, so it's not that this thread is inaccurate... it's just seriously incomplete.  Reading https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782 is a necessary prerequisite.

Thanks for clarifying Nightinggale, I will give that a try!
If you give an annoying colonist a parka before banishing him to the ice sheet you'll only get a -3 penalty instead of -5.

And don't forget that the pirates chasing a refugee are often better recruits than the refugee is.

Shurp

#66
Woo hoo!  It worked!

So, for anyone else still reading this thread, here's a list of all the corrections I needed to make it work:
1) Move the file to ./Mods/[MyMod]/Patches where it belongs
2) Get rid of the "<Defs>" silliness
2) Spell "PatchOperationReplace" correctly

And now all my colonists are wearing Super Pants.  Yay!

Now I just have to figure out how to use sequences...

--------------------------[edit]----------------------------

And here is the complete code for a working patch mod:

<Patch>
   <Operation Class="PatchOperationSequence">
      <success>Always</success>
      <operations>
      <li Class="PatchOperationReplace">
         <xpath>/Defs/ThingDef[defName = "Apparel_Pants"]/description</xpath>
         <value>
            <description>Super Pants</description>
         </value>
      </li>
      <li Class="PatchOperationReplace">
         <xpath>/Defs/ThingDef[defName = "Apparel_Pants"]/statBases/ArmorRating_Blunt</xpath>
         <value>
            <ArmorRating_Blunt>1.00</ArmorRating_Blunt>
         </value>
      </li>
      <li Class="PatchOperationReplace">
         <xpath>/Defs/ThingDef[defName = "Apparel_Pants"]/statBases/ArmorRating_Sharp</xpath>
         <value>
            <ArmorRating_Sharp>0.90</ArmorRating_Sharp>
         </value>
      </li>
      </operations>
   </Operation>
</Patch>

But I have a question: what does the "<success>" flag do?
If you give an annoying colonist a parka before banishing him to the ice sheet you'll only get a -3 penalty instead of -5.

And don't forget that the pirates chasing a refugee are often better recruits than the refugee is.

Takashi Yonezawa

Thanks for writing this. This is awesomely useful!
May I translate this document into Japanese? There are many Japanese players and modders who need to know this.

Nightinggale

Quote from: Takashi Yonezawa on March 27, 2018, 03:44:05 PM
May I translate this document into Japanese? There are many Japanese players and modders who need to know this.
Go ahead and translate. My only concern is about keeping it up to date with future releases. You should use a wiki to allow other people to update in the future, if needed. It would also be nice if you post a link here.

I have a half written update for ModCheck, which I will finish when I get time (whenever that is). You should include ModCheck in the translation because it adds some rather powerful modding options. It also adds much faster startup times, but only for mods actually using ModCheck.FindFile.
ModCheck - boost your patch loading times and include patchmods in your main mod.

Takashi Yonezawa


Nightinggale

Quote from: Takashi Yonezawa on March 29, 2018, 01:38:33 AM
Dear Nightinggale.
I've finished the translation. Thanks to your post and permission.
My translation is here.
https://github.com/oramu7524/Mods-for-RIMWORLD/blob/master/A_quick_tutorial_of_xpathing_and_patching_Japanese_translation.txt
A minor, yet important correction: you credited me for writing the first post while it was actually written by minimurgle.
It could also be worth to translate the performance thread or at least mention that // should not be used.
ModCheck - boost your patch loading times and include patchmods in your main mod.

Takashi Yonezawa

Oh... I've misunderstood that you are the author of this thread. But thank you for your suggestion. And I've subscribed your ModCheck.FindFile. That's Powerful!

czpetr


HalfdeadKiller

#73
For anyone who may find this useful. If you need to edit a specific <li> you can do so with this sort of format.


<Patch>
<Operation Class="PatchOperationSequence">
<operations>
<li Class="PatchOperationTest">
<xpath>/StorytellerDefs/StorytellerDef[defName = "Phoebe-DoublePop"]</xpath>
</li>
<li Class="PatchOperationReplace">
<xpath>/StorytellerDefs/StorytellerDef[defName = "Phoebe-DoublePop"]/comps/li["StorytellerCompProperties_CategoryMTB"]/allowedTargetTypes</xpath>
<value>
<allowedTargetTypes>MapPlayerHome, MapMisc</allowedTargetTypes>
</value>
</li>
</operations>
</Operation>
</Patch>


This particular patch was for a mod for an older version of the game, but I think the format still works.

LWM

There are a lot of scattered "tutorials" people have written, but they all seem to be missing bits and pieces.  I have found a reference to PatchOperationFindMod on the wiki.  Not many people seem to be using the wiki.  Is it accurate?

No one else seems to mention PatchOperationFindMod.  Does it actually exist?  If so, can anyone explain/show how it's used?

Does anyone want to update the wiki, or is that not a place people use?

--LWM