A quick tutorial of xpathing and patching

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

Previous topic - Next topic

Shinzy

Quote from: benniii on May 27, 2017, 12:34:23 PM
Hi,

Is it possible to use the patching-method to change things added from other mods? I did read somewhere that this process is used before adding the mods.

Just for clarification

It is possible yes, all the defs are loaded first (including mods), then the xpath patching happens after all that

so yes you can modify modded defs too. Works really well for things like compatibility patches etc

benniii

Hm, so it is. Is there an example out there? It might be possible that i do something wrong. I tried like several hours yesterday and just came to the conclusion that this might not work, lol.

If i change the defName here to "HorseshoesPin" the description would change, but with added items from mods it does not. And i'm loading my mod at the very end after them. So where would be the secret behind that? Thanks for answering and sorry if it doesn't fit in this topic.

<Patch>
  <Operation Class="PatchOperationReplace">
    <xpath>*/ThingDef[defName = "PowerConduitInvisible"]/description</xpath>
    <value>
      <description>This is just a test.</description>
    </value>
  </Operation>
</Patch>


minimurgle

Hi Benni. While it is possible to use this process to change other mods you should have permission from the mod author first unless it's for your own personal use. If it is uploaded and you got permission from the author of the mod that your changing you should make it very clear that you've changed stuff from the other mod.

The only other things I can tell you from my experience is that your mod needs to be loaded after the other mod. You can also put <success>Always</success> to suppress errors if the other mod isn't present. (that could be wrong but it's something like that)

Just a warning it would appear that people are having trouble getting this to work all the time.
Don't mind the questions. I'm probably just confused.

benniii

#18
Hey.

Thanks for your concern but don't worry that's for personal use only. I'm more the introvert lurking guy here, i don't plan to publish anything. ^^

I just did some other testings and it does indeed work with other values. Replacing description or label doesn't work so far. I did start testing with these thats why i was questioning it. Kinda wondering why though. But im set now, so thanks for the tip and answering. :)

Edit: I figured out why i couldn't replace description or label. It is because of the provided languages files.

Vindar

#19
Lmao, I gotta be honest, I started learning it yesterday and I'm already falling in love w/ xpathing.

some of the things ive learned that might be helpful for people just starting it here w/ the intent to use in rimworld.

1) not all things start w/ ThingDefs, some items start w/ Buildings, ect

2) be very specific and check both the spelling and the capitalization, if its capitalized differently in the core xml, xpath wont read it and will spit out a fail.

3) start the document with the XML header:

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

Save as an XML

Use:

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

3 might seem simple but it was the first question i asked myself anyways

====================================================

to help people along ill post some examples of operations that i have working in my mod(still being worked on):

================Simple replace operation=============================

<!-- Change Basic Shirt Worn Graphic -->
   <Operation Class="PatchOperationReplace">
      <xpath>/ThingDefs/ThingDef[defName = "Apparel_BasicShirt"]/apparel/worngraphicPath</xpath>
      <value>
         <worngraphicPath>Things/Pawn/Humanlike/Apparel/Bodywear_Clothing/TeeShirt/TeeShirt</worngraphicPath>
      </value>
   </Operation>

=======================Simple Add Operation====================================

<!-- Change Basic Pants Worn Graphic -->
   <Operation Class="PatchOperationAdd">
      <xpath>/ThingDefs/ThingDef[defName = "Apparel_Pants"]/apparel</xpath>
      <value>
         <worngraphicPath>Things/Pawn/Humanlike/Apparel/Bodywear_Clothing/Pants/Pants</worngraphicPath>   
      </value>
   </Operation>

==================A replace Operation which starts at Buildings as opposed to ThingDefs=========

<!-- Coal to Fueled Generator -->
   <Operation Class="PatchOperationReplace">
      <xpath>/Buildings/ThingDef[defName = "FueledGenerator"]/description</xpath>
      <value>
         <description>Produces power by consuming wood fuel or coal. Must be fueled by hand.</description>   
      </value>
   </Operation>

==========An operation that had me confused for a while. (needs DefName as oppsed to defName)======
==============Note i also used: li[@Class = "CompProperties_Refuelable"] =========

<!-- Coal to Fueled Smithy -->   
   <Operation Class="PatchOperationAdd">
       <xpath>/ThingDefs/ThingDef[DefName = "FueledSmithy"]/comps/li[@Class = "CompProperties_Refuelable"]/fuelFilter/thingDefs</xpath>
       <value>
             <li>MedTimes_Resource_Coal</li>
       </value>
   </Operation>   

=================and finally one that adds alot of values to a trader:======================

<!-- Adding Goods to Neolithic Base -->
   <Operation Class="PatchOperationAdd">
       <xpath>/DefPackage-TraderKindDef/TraderKindDef[defName = "Base_Neolithic_Standard"]/stockGenerators</xpath>
       <value>
         <li Class="StockGenerator_SingleDef">
            <thingDef>MedTimes_Resource_IceBlocks</thingDef>
            <price>Expensive</price>
         </li>      
         <li Class="StockGenerator_Category">
            <categoryDef>TradeGoods</categoryDef>
            <price>Expensive</price>            
            <thingDefCountRange>
               <min>2</min>
               <max>5</max>
            </thingDefCountRange>
         </li>
         <li Class="StockGenerator_Tag">
            <tradeTag>MedTimes_Weapons</tradeTag>
            <price>Expensive</price>
            <thingDefCountRange>
               <min>2</min>
               <max>5</max>
            </thingDefCountRange>         
         </li>            
         <li Class="StockGenerator_Tag">
            <tradeTag>MedTimes_Armour</tradeTag>
            <price>Expensive</price>
            <thingDefCountRange>
               <min>2</min>
               <max>5</max>
            </thingDefCountRange>            
         </li>            
         <li Class="StockGenerator_Tag">
            <tradeTag>MedTimes_Artifact</tradeTag>
               <thingDefCountRange>
                  <min>1</min>
                  <max>3</max>
               </thingDefCountRange>               
         </li>   
         <li Class="StockGenerator_Tag">
            <tradeTag>MedTimes_Legendary</tradeTag>
         </li>         
       </value>
   </Operation>




skullywag

Ive just seen someone (i think) telling people that patching things like the min/max valules in the biomedefs for soil types isnt possible without patching the whole set, just wanted to state that sibling values are xpathable very simply using something like:

/thingdef/whatever[text()="thename"]/following-sibling::value[1]/text()

Hope that makes sense, the examples bad but it should give you the bits you need.
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

skullywag

#21
Also heres a good list of the functions available to us in xpath 1.0 (we arent using 2.0 in .net 3.5)

http://www.edankert.com/xpathfunctions.html


Shinzyedit:
And if all else fails here's Skully's Xpath hotline for help

Skullyedit:
Did you just.....well....thats just rude....but thanks. :)

Shinzyedit:
Oh don't mention it, My pleasure!
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

kaptain_kavern

If it can help, here my biggest patches files. It works and maybe can be used as helping material.

I use all basic syntax in it

https://github.com/kaptain-kavern/CK_AnimalPlant_Pack/blob/master/Patches/patches.xml

minimurgle

Thank you so much for adding in some examples Skully and Kaptain. Thanks for the hotline Shinzy?
Don't mind the questions. I'm probably just confused.

sulusdacor

you should mention you can patch abstracts ;)
with
[@Name = "AbstractName"]

kaptain_kavern

#25
Hey it's me again with a little more info I found while playing with the new method.

Believe it or not, but most of the time when patch aren't working it's because of a wrong xpath of course. Asking for a fresh pair of eyes can help. But here is a bit more help ...
Following are 2 things that made me lost some hours, so if it can help anyone ... ;)


- Pay attention to Parent nodes! There is no full consistency in uses. Sometimes you'll find Defs, sometimes ThingDefs for instance  8)  - Funpost with actual reply by Tynan



- Another one for the fun : I spotted that in 5 xml files <Defname> is used instead of <defname> (upcase D)

Concerned files :

  • \Core\Defs\Misc\Designations\DesignationCategories.xml
  • \Core\Defs\NeedDefs\Needs.xml
  • \Core\Defs\TerrainDefs\Terrain_Floors.xml
  • \Core\Defs\TerrainDefs\Terrain_Floors_StoneTile.xml
  • \Core\Defs\ThingDefs_Buildings\Buildings_Production.xml


EDIT
Bonus gift : if you are using SublimeText as a text editor, here's a link to a very useful plugin regarding xpath : https://github.com/rosshadden/sublime-xpath


copy xpath of selected node to clipboard

minimurgle

Thanks for pointing those things out Kaptain! I may be taking a looksy at that plugin too.
Don't mind the questions. I'm probably just confused.

Kilroy232

I have a question now that I have read through the original post and some of the comments. In my turrets mod I used this method to change specific values:

<researchMods>
      <li>
        <specialAction>TP.TPResearchMods.EnergyTurretOvercharge</specialAction>
      </li>
    </researchMods>


They have now removed the <researchMods> function but I would like to know if I can use patching in its place. I want to be able to have a research that will say increase a weapons range or damage. If anyone can help me out with that I would greatly appreciate it

Antaios

Reposting a discussion me and AngelWyrm are having over in mods, should be useful here.
Quote from: AngleWyrm on June 16, 2017, 09:54:05 PM
This post explains how to make an xpath statement that isn't going to generate duplicate field errors. Quick recap:

<Operation Class="PatchOperationSequence">
  <success>Always</success>
  <operations>
    <li Class="PatchOperationTest">
      <xpath>*/ThingDef[defName = "ChosenThingy"]/elementThatIWannaAdd</xpath>
      <success>Invert</success>
    </li>
    <li Class="PatchOperationAdd">
      <xpath>*/ThingDef[defName = "ChosenThingy"]</xpath>
      <value>
        <elementThatIWannaAdd> someValue </elementThatIWannaAdd>
      </value>
    </li>
  </operations>
</Operation>

Quote from: AngleWyrm on June 17, 2017, 01:09:25 AM
I've used this test to do the same thing: Add a sub-element if it doesn't exist.

The error messages are saying that two separate attempts (by two different mods) were made to add a specific element, and so it failed the second time because that element already exists.

This test checks to see if the element already exists (maybe because some other mod created it), and if it doesn't then there's a spot in there to add the element. And if it does already exist, then the check quietly bails out of it's sequence without spilling big red oops onto the user's screen.
Quote from: Antaios on June 17, 2017, 05:30:32 AM
Do take note that PatchSequenceTest operates per file, not global and not per def.

So if for example you're editing ThingDefs and looking to add RandomProperty1 if it doesn't exist in any of them, you need to be sure that no single file contains both ThingDefs which are missing RandomProperty1 and ThingDefs which include RandomProperty1 already.

It's why when I did that, I use a placeholder tag and some gymnastics with '../' in xpath, rather than PatchOperationTest.
Quote from: AngleWyrm on June 17, 2017, 08:03:37 PM
There should be no connection between xpath data searches and the underlying physical storage of that data.

The assertion that there is implies a defect in the implementation of the PatchSequenceTest function. Can you construct a test case that fails if the test draws values from different files, but succeeds if those values are drawn from the same file?
Quote from: Antaios on June 17, 2017, 10:34:16 PM
It is evident in the code, if you take a look at ModContentPack.LoadDefs(IEnumerable<PatchOperation> patches), PatchOperation, PatchOperationSequence and PatchOperationTest.
But a simple test is to take tuning fork, and in the core defs of Rimworld, add a blank abstract in Core\Defs\ThingDefs_Items.xml which has an ingestible/chairSearchRadius. You'll notice the patch no longer works on core meals.

AngleWyrm

#29
QuoteSo if for example you're editing ThingDefs and looking to add RandomProperty1 if it doesn't exist in any of them, you need to be sure that no single file contains both ThingDefs which are missing RandomProperty1 and ThingDefs which include RandomProperty1 already.

I've attached a testing scaffold mod that contains three files: A patch file, and two defs files.

The first defs file contains two entries with <label> tags, and the second defs xml contains three entries, with one of them that does not contain a <label> tag. The two defs files look about like so:

<Defs>
    <ThingDef>
        <defName>thingy-1</defName>
        <label>one</label>
   </ThingDef>
    <ThingDef>
        <defName>thingy-2</defName>
        <lable>two</lable>
   </ThingDef>
</Defs>


For feedback on a success/failure test, I suggest changing some visible GUI text within the game if the test succeeds.
So: What patch test operation will best demonstrate a reliance on file structure?

[attachment deleted by admin due to age]
My 5-point rating system: Yay, Kay, Meh, Erm, Bleh