Ludeon Forums

RimWorld => Mods => Help => Topic started by: skullywag on June 02, 2017, 04:26:23 AM

Title: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 02, 2017, 04:26:23 AM
Hey all,

XPATH is kind of my thing and ive been slowly pumping out info about best practice when using it and some others have posted some info on my behalf, like the performance hit of using // over / (if the tree is known there is no need to use //). I could put down a tonne of info on using xpath 1.0 but thought it better to get real world examples, so with that in mind, if you have a RW xpath issue, post it here and ill try and get back to everyone with any answers or relevant knowledge on any xpath subjects. We can then collate these examples into the relevant stickied help threads etc. (This is my way of helping out "modding" while I cannot actually mod right now)

Fire away. :)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Haplo on June 02, 2017, 11:11:19 AM
*Dialing Hotline*...ring, ring... ring, ring... "Hello, how may I help you?"

Hello XPath hotline.
I'd like to know from a professional side if my code is ok as it is.
It does work, but I'm just not sure if it's really the best way to do this?

So, here is my code:

<!-- Check if <comps /> exists. If not, add it -->
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[statBases/AccuracyLong > 0.05]/comps</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationAdd">
<xpath>*/ThingDef[statBases/AccuracyLong > 0.05]</xpath>
<value>
<comps />
</value>
</li>
</operations>
</Operation>

<!-- Find entries where AccuracyLong > 0.5 and add the new comp -->
<Operation Class="PatchOperationAdd">
<xpath>*/ThingDef[statBases/AccuracyLong > 0.05]/comps</xpath>
<value>
<li Class="WeaponRepair.CompProperties_WeaponRepairTwo2One">
<worktableDefs>
<li>TableMachining</li>
</worktableDefs>
<allowedDefSubName>Gun_</allowedDefSubName>
<compareQuality>true</compareQuality>
<jobDef>WeaponRepairTwo2One</jobDef>
<workTypeDef>Smithing</workTypeDef>
<maxRepair>0.95</maxRepair>
</li>
</value>
</Operation>


My questions: Is this useful or is there a better way to handle all available ranged weapons (only defs: Gun_*)?
Previously I tried ThingDef[defName="Gun_*"] but that didn't work and I didn't find an alternate call for that..

Thank you for your time ;D
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on June 02, 2017, 11:42:06 AM

(https://s-media-cache-ak0.pinimg.com/736x/12/ae/b2/12aeb2aa618295ab904e5b1d2aaabe14.jpg)I recently came across a situation where I wanted to specify more than one selection criteria, as the item I was looking for had a composite key. What I was attempting to do looked like this (but it generated errors):

someTraitDefType[ defName="traitgroup" and subType="2" ]/recordSubThingy

Is there another approach, or some expected grammatical pattern to this approach?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 03, 2017, 07:19:37 AM
Haplo, would this help?

/ThingDef/defName[starts-with(., 'Gun_')]

To extend this with some further info starts-with is in xpath 1.0 but there is no ends-with, to do that you can use a mix of substring and string length:

substring(., string-length(.)- string-length('wordtofind') +1)

Hope i understood the issue properly. Let me know if i didnt.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 03, 2017, 07:25:32 AM
AngleWyrm:

//ThingDef[defName='whatever' and /ThingDef/someotherthing/text()='sometext']/whatever

Should work fine, let me know if that doesnt make sense. I think the issue here might have been that you werent supplying the second AND a path to thing you wanted it to find, it wuld have been expecting subtype to be at the same level as defname, i could be wrong but thats how it seems to me. If you can give the exact xml you have and what youre trying to target i can help further.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: milon on June 03, 2017, 02:33:53 PM
@Skully & Haplo, is there any reason that neither of you stickied this thread?

FYI, I un-stickied some old A12/A13 modding threads that haven't seen any action for a long time.  That should decrease our sticky-clutter.  :)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on June 03, 2017, 09:24:23 PM

(http://pm1.narvii.com/5834/6ceda525cc83c273c1e272ff26308a06c76c1ce1_hq.jpg)Sometimes duplicate Add operation errors come up, and the situation in which they grow looks like this:

someDef[defName="aChosenThingy"]/entryToModify

The problem is that entryToModify may or may not exist in the record, and I want to add that entry if none currently exists.

Is there a best practice for this?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 04, 2017, 02:32:54 AM
AngleWyrm:

You need to test for its existence and add it if it doesnt exist, you can then add values to it in a further path if thats needed as well. as per:


<Operation Class="PatchOperationSequence">
  <success>Always</success>
  <operations>
    <li Class="PatchOperationTest">
      <xpath>//ThingDef[defName = "DiningChair"]/costList</xpath>
      <success>Invert</success>
    </li>
    <li Class="PatchOperationAdd">
      <xpath>//ThingDef[defName = "DiningChair"]</xpath>
      <value>
        <costList />
      </value>
    </li>
  </operations>
</Operation>

<Operation Class="PatchOperationAdd">
  <xpath>//ThingDef[defName = "DiningChair"]/costList</xpath>
  <value>
    <Cloth>5</Cloth>
  </value>
</Operation>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 04, 2017, 02:35:58 AM
Milon:

Stickied threads dont get seen much tbh, plus i was hoping the relevant people or myself would take from this thread and add to the tutorial threads that are about, 1 of which is stickied.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 04, 2017, 02:43:45 PM
Is there a way that I can make a sequence test not give an error when it fails to find the xpath? This is what I'm working with:
<Operation Class="PatchOperationSequence">
<success>Always</success>
<Operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[defName="VG_Biofuel"]</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationInsert">
<xpath>*/ThingDef[defName="FueledGenerator"]/comps/li[@Class="CompProperties_Refuelable"]/fuelFilter/thingDefs</xpath>
<value>
<li>VG_Biofuel</li>
</value>
</li>
<li Class="PatchOperationInsert">
<xpath>*/ThingDef[DefName="FueledSmithy"]/comps/li[@Class="CompProperties_Refuelable"]/fuelFilter/thingDefs</xpath>
<value>
<li>VG_Biofuel</li>
</value>
</li>
<li Class="PatchOperationInsert">
<xpath>*/ThingDef[defName="Forge"]/comps/li[@Class="CompProperties_Refuelable"]/fuelFilter/thingDefs</xpath>
<value>
<li>VG_Biofuel</li>
</value>
</li>
</Operations>
</Operation>


This works with Vegetable Garden installed but I'm trying to make it work like an optional dependency, so if VG isn't installed it just ignores this operation.

EDIT: Nevermind, I commented the success element from the test operation and now it does the test without giving an error if it fails. Yay!
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: delheit on June 09, 2017, 12:20:42 AM
I hope you can help I can't figure out what's wrong.

This works fine
<Operation Class="PatchOperationReplace">
    <xpath>/ThingDefs/ThingDef[defName = "MineableSteel"]/building/mineableYield</xpath>
    <value>
                <mineableYield>40</mineableYield>
    </value>
</Operation>


But this does not
<Operation Class="PatchOperationReplace">
    <xpath>/ThingDefs/ThingDef[defName = "Wall"]/costStuffCount</xpath>
    <value>
                <costStuffCount>10</costStuffCount>
    </value>
</Operation>


I get this error on the second one.

QuotePatch operation Verse.PatchOperationReplace(/ThingDefs/ThingDef[defName = "Wall"]/costStuffCount) failed

I have tried changing some things around like ThingDefs to Buildings or ThingDefs_Buildings but honestly both examples look so similar I am surprised it's not working the way it is.

Thank You

Update: I got it to work but I think I'm not suppose to do it this way.

<xpath>//ThingDef[defName = "Wall"]/costStuffCount</xpath>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 09, 2017, 05:02:46 AM
Yeah don't use the // instead try this

<xpath>*/ThingDef[defName = "Wall"]/costStuffCount</xpath>

The * at the beginning tells it to check all root tags where as the // tells it to check EVERY tag, even non root tags.

EDIT: To elaborate more, anywhere you place a * it will match "anything"
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 09, 2017, 05:52:48 AM
Yep as sixdd has rightly stated, we as people that know the structure of the xml should never have a need for //

however in terms of your second patch costStuffCount doesnt exist on mineableSteel does it? thats what you are adding.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: dburgdorf on June 09, 2017, 11:16:10 AM
I am virtually certain the answer to this question is "no," but I'll ask just for the sake of asking. Is there any way to use patching to create an entirely new def? Specifically, I'm wondering if there's any way to have a mod that adds plants also create seed defs if (and only if) "Seeds Please!" is in use, to avoid the need to have a secondary mod add them.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 09, 2017, 12:01:17 PM
That is actually possible. Basically (in my understanding) as long as you have a root node to build off of you can add anything you like using an Add or Insert operation.

Also you can use a reference to a ThingDef in the mod you want to check for like this:

<Operation Class="PatchOperationSequence">
<success>Always</success>
<Operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[defName="EXP_Copper"]</xpath>
</li>
etc...


This returns true if the ThingDef with defName "EXP_Copper" exists. From there you just put more <li Class="someclass"> elements in for each operation you want to do.

Also I believe that Cupro has some kind of thing that checks for a mod name rather than checking for a defName.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: dburgdorf on June 09, 2017, 12:18:44 PM
Quote from: Sixdd on June 09, 2017, 12:01:17 PMThat is actually possible.

Thanks, but what you're describing is using patches to modify existing defs, which I already do in several of my mods. I'm asking if there's any way to use a patch to create a *new* def. I know I can use patches to *modify* ThingDef[defname="Whatever"], but can I use a patch to *create* that def in the first place? That's what I'd like to do, but I don't believe it's actually possible.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 09, 2017, 12:43:01 PM
You can only check for a mods existence really in c#. The xpath is running over the defs 1 by 1, so adding 1 in there is not really going to work....i dont think....theres probably some crazy thing you can do but i wouldnt suggest trying it. I think this sits in the realms of c# (and people have done stuff similar already) for now, who knows what that crazy Zorba has planned for the future. :)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: dburgdorf on June 09, 2017, 12:52:24 PM
Quote from: skullywag on June 09, 2017, 12:43:01 PMThe xpath is running over the defs 1 by 1, so adding 1 in there is not really going to work....

Yeah, that's what I figured. And unfortunately, since the seed defs in "Seeds Please!" use a custom def type -- they're SeedsPlease.SeedDefs, not ThingDefs -- I can't just create the new defs in my code, either, as even though I could easily see if "Seeds Please!" is installed, my mod wouldn't, on its own, know what the hell a "SeedDef" was supposed to be. :)

So much for my attempt to be clever. I guess I'll just release a "Seeds Please!" patch mod for "Wild Cultivation," just like everybody else does for their own mods that add new plants. :D
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 09, 2017, 01:29:53 PM
This is what I was talking about when I referenced Cupros code:


<li Class="CuprosAlloys.PatchOperationFindMod">
<modName>Powerless!</modName>


it's from the Cupro's Alloys mod in one of the patches.

And this is the rest of that code which adds an entirely new ThingDef:


<li Class="PatchOperationInsert">
<!-- Inserting code before an attribute places it as a separate def -->
<xpath>/ThingDefs/ThingDef[@Name="CampTableBase"]</xpath>
<Order>Prepend</Order>
<value>
<ThingDef ParentName="BuildingBase">
<DefName>POW_Crucible</DefName>
<label>crucible</label>
<ThingClass>Building_WorkTable</ThingClass>
<Description>A structure dating back to chalcolithic times used for melting metal and creating alloys.</Description>
<graphicData>
<texPath>Cupro/Object/Station/Crucible/Crucible</texPath>
<graphicClass>Graphic_Multi</graphicClass>
<shaderType>CutoutComplex</shaderType>
</graphicData>
<fillPercent>0.25</fillPercent>
<stuffCategories>
<li>Stony</li>
</stuffCategories>
<costStuffCount>60</costStuffCount>
<costList>
<POW_Charcoal>25</POW_Charcoal>
</costList>
<altitudeLayer>Building</altitudeLayer>
<UseHitPoints>true</UseHitPoints>
<destroyable>true</destroyable>
<rotatable>true</rotatable>
<statBases>
<MaxHitPoints>2000</MaxHitPoints>
<WorkToBuild>1500</WorkToBuild>
<Flammability>0</Flammability>
<Cleanliness>-4</Cleanliness>
<Mass>20</Mass>
</statBases>
<Size>(1,1)</Size>
<DesignationCategory>Production</DesignationCategory>
<Passability>PassThroughOnly</Passability>
<canOverlapZones>false</canOverlapZones>
<pathCost>60</pathCost>
<hasInteractionCell>True</hasInteractionCell>
<interactionCellOffset>(0,0,1)</interactionCellOffset>
<surfaceType>Item</surfaceType>
<tickerType>Normal</tickerType>
<comps>
<li>
<compClass>CompQuality</compClass>
</li>
<li Class="CompProperties_AffectedByFacilities">
<linkableFacilities>
<li>POW_Bellows</li>
</linkableFacilities>
</li>
<li Class="CompProperties_Refuelable">
<fuelConsumptionRate>64.0</fuelConsumptionRate>
<fuelCapacity>40.0</fuelCapacity>
<fuelFilter>
<thingDefs>
<li>POW_Charcoal</li>
</thingDefs>
</fuelFilter>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
</li>
<li Class="Powerless.CompProperties_Smoker">
<smokeStyle>Single</smokeStyle>
<frequencyMin>800</frequencyMin>
<frequencyMax>1200</frequencyMax>
<produceSmokeOnlyWhenUsed>true</produceSmokeOnlyWhenUsed>
<size>0.5</size>
</li>
</comps>
<inspectorTabs>
<li>ITab_Bills</li>
</inspectorTabs>
<building>
<spawnedConceptLearnOpportunity>BillsTab</spawnedConceptLearnOpportunity>
<heatPerTickWhileWorking>1.0</heatPerTickWhileWorking>
</building>
<placeWorkers>
<li>PlaceWorker_ShowFacilitiesConnections</li>
</placeWorkers>
<researchPrerequisites>
<li>POW_Forge</li>
</researchPrerequisites>
</ThingDef>
</value>
</li>


Maybe this still isn't what you were aiming for but I thought it would be relevant.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: dburgdorf on June 09, 2017, 02:15:37 PM
Quote from: Sixdd on June 09, 2017, 01:29:53 PMMaybe this still isn't what you were aiming for but I thought it would be relevant.

Well, I'll be damned. I can't try it until I get home tonight, but that just might be exactly what I need.

I apologize for assuming earlier that you weren't addressing my actual request. ;)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: delheit on June 09, 2017, 02:21:02 PM
Quote from: Sixdd on June 09, 2017, 05:02:46 AM
Yeah don't use the // instead try this

<xpath>*/ThingDef[defName = "Wall"]/costStuffCount</xpath>

The * at the beginning tells it to check all root tags where as the // tells it to check EVERY tag, even non root tags.

EDIT: To elaborate more, anywhere you place a * it will match "anything"

Thank You, */ThingDef is also working, so I guess it should save on performance compared to //ThingDef I can't help but wonder what the correct path is though. :)

Quote from: skullywag on June 09, 2017, 05:52:48 AM
Yep as sixdd has rightly stated, we as people that know the structure of the xml should never have a need for //

however in terms of your second patch costStuffCount doesnt exist on mineableSteel does it? thats what you are adding.

Yea <costStuffCount>5</costStuffCount> is in the Buildings_Structure file under Wall and it later has the stuff categories Woody, Metallic and Stony. All I did was change the 5 to a 10. And it is working in game now with // or */ as above. I was even able to build the walls. (I hope I don't sound like I am arguing, I know your way more experienced then me.)


Update: Oh wait I think I see where the confusion was there. The first example is in fact mineable steel and I change the yield but in the second example I am only changing the cost of building a regular wood wall.
Quote[defName = "Wall"]
Thanks but no that's not what the issue is, The cost to build em is working. :) I just don't know the exactly correct xpath to standard walls.

I guess I will use */ for now until I can find the exact path.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on June 09, 2017, 02:28:41 PM
Quote from: dburgdorf on June 09, 2017, 12:52:24 PM
...unfortunately, since the seed defs in "Seeds Please!" use a custom def type -- they're SeedsPlease.SeedDefs, not ThingDefs -- I can't just create the new defs in my code, either, as even though I could easily see if "Seeds Please!" is installed, my mod wouldn't, on its own, know what the hell a "SeedDef" was supposed to be.

Instead of an xpath that looks like this: */ThingDef[defName = "DiningChair"]/costList
The search can be changed to look for something like this: */SeedsPlease.SeedDefs
Which should return a set of all SeedDefs, or nothing if it can't find SeedsPlease records.

The sequence patch operation then looks something like so:
[patch sequence]
   [test for existence of a SeedsPlease record, or bail if none are found]
   [do a series of seed-related patches]
[/end of patch sequence]

Attached is a mini-mod called testing which is just an easy drop-in for mucking about with xmls and patching.

<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid XML Studio 2009 - the LAST FREE Community Edition 7.0.2.746

    Patch and xpath testing

        Tests
        1). File name that has spaces for legibility
        2). Replacement of known-to-exist core element
        3). Creation of a new Def record to the Defs collection
        4). Conditionally add a cost list to dining chair if it doesn't already have one
    -->
<Patch>
    <!--
        Testing replacement of a known-to-exist element
        Replace default "Allow Rotten" behavior
    -->
    <Operation Class="PatchOperationReplace">
        <xpath>*/SpecialThingFilterDef[defName = "AllowRotten"]/allowedByDefault</xpath>
        <value>
            <allowedByDefault>false</allowedByDefault>
        </value>
    </Operation>
    <!--
        Testing the addition of a new def record to the Defs collection
        Add a new Def with the defName of 'someThingy'
    -->
    <Operation Class="PatchOperationAdd">
        <xpath>/Defs</xpath>
        <value>
            <!-- new def record to add to Defs collection -->
            <Def>
                <defName>someThingy</defName>
                <!-- other elements of the def -->
            </Def>
        </value>
    </Operation>
    <!--
        Conditional testing
        Check if Dining Chair already has a cost list, add one if it doesn't
        then work with the cost list
    -->
    <Operation Class="PatchOperationSequence">
        <success>Always</success>
        <operations>
            <li Class="PatchOperationTest">
                <xpath>//ThingDef[defName = "DiningChair"]/costList</xpath>
                <success>Invert</success>
            </li>
            <li Class="PatchOperationAdd">
                <xpath>//ThingDef[defName = "DiningChair"]</xpath>
                <value>
                    <costList />
                </value>
            </li>
        </operations>
    </Operation>
    <!-- at this point the dining chair has a cost list -->
    <Operation Class="PatchOperationAdd">
        <xpath>//ThingDef[defName = "DiningChair"]/costList</xpath>
        <value>
            <Cloth>5</Cloth>
        </value>
    </Operation>
</Patch>


[attachment deleted by admin due to age]
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: dburgdorf on June 09, 2017, 03:49:04 PM
Quote from: AngleWyrm on June 09, 2017, 02:28:41 PM....

First, as I've already noted, I am familiar with the use of XML patches to *modify* defs. I had inquired about the possibility of using them to *create* defs. Second, the portion of my comment that you quoted wasn't even discussing XML at all, but was specifically referring to whether something could be done in my mod's *code* (that is, C#).

Fortunately, I already got the information I needed from folk who actually understood what I was asking about.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on June 09, 2017, 05:21:33 PM
Have you considered investing in some reading glasses?

Quote from: AngleWyrm on June 09, 2017, 02:28:41 PM

        3). Creation of a new Def record to the Defs collection

    <!--
        Testing the addition of a new def record to the Defs collection
        Add a new Def with the defName of 'someThingy'
    -->
    <Operation Class="PatchOperationAdd">
        <xpath>/Defs</xpath>
        <value>
            <!-- new def record to add to Defs collection -->
            <Def>
                <defName>someThingy</defName>
                <!-- other elements of the def -->
            </Def>
        </value>
    </Operation>

Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: delheit on June 09, 2017, 05:22:06 PM
Quote from: AngleWyrm on June 09, 2017, 02:28:41 PM

Attached is a mini-mod called testing which is just an easy drop-in for mucking about with xmls and patching.


Cool code, helped me learn more about something else I was attempting.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on June 15, 2017, 02:03:35 PM

(https://s-media-cache-ak0.pinimg.com/736x/97/57/5d/97575d355e4fb8f58ef9c404dd80738e.jpg)Wondering about optimization


The subject records are the set of backstories contained in the Defs collection that have WorkDisables tag(s)
/Defs/REB_Code.BackstoryDef/WorkDisables

The goal is to perform two patch operations based on content
  • Insert (or Add) a ForcedTrait/li element according to an arbitrary mapping of some workDisable -> some forcedTrait.
  • Remove the WorkDisables element after processing the additions/insertions
My question is this: Is there a mechanism that will help to avoid making the full search for each of the workdisables? Can I perform a main search once that returns sub-groups?


Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: craus on June 16, 2017, 07:49:06 AM
Hi guys, Hope you are fine.
This is an example of what i want to do but idk why it does not work.
<Patch>
   <Operation Class="PatchOperationAdd">
     <xpath>/ThingDefs/ThingDef[defName = "SleepingSpot"]</xpath>
     <value>
    <researchPrerequisites><li>HospitalBed</li></researchPrerequisites>
     </value>
   </Operation>
</Patch>

I want to add research prerequisites to some things but i cant make it work.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 16, 2017, 07:38:03 PM
Not sure why that isn't working but you could always try this:

<Patch>
   <Operation Class="PatchOperationInsert">
     <xpath>/ThingDefs/ThingDef[defName = "SleepingSpot"]/defName</xpath>
     <value>
       <researchPrerequisites><li>HospitalBed</li></researchPrerequisites>
     </value>
   </Operation>
</Patch>


I'm not on my PC or I'd test it but that should work I think.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: kaptain_kavern on June 16, 2017, 08:22:22 PM
Maybe because it's the wrong root node ;-)

Try with Buildings\ThingDef\...
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: craus on June 16, 2017, 11:49:03 PM
Quote from: kaptain_kavern on June 16, 2017, 08:22:22 PM
Maybe because it's the wrong root node ;-)

Try with Buildings\ThingDef\...

OMG it works. Thanks so much! I was gonna start rewriting every def I wanted to change. Now with this I can finally do the changes I want without touching vanilla def and files.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: kaptain_kavern on June 17, 2017, 12:51:34 AM
Hey, you're welcome. More importantly, have you understand why it wasn't working?

It's dumb as hell but when I'm writing a xpath patch I always copy paste tag names from the actual vanilla file I intend to patch/modify to be sure of my path. In order to avoid head-scratching sessions (https://ludeon.com/forums/index.php?topic=32785.msg342256#msg342256) ... And even that way I always end up missing the obvious ^^

Have fun patching everything now :p
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 22, 2017, 02:07:06 PM
I have another probably obvious problem. I've been trying to figure this one out for a day now and no luck no matter what I try.
<Patch>

  <Operation Class="PatchOperationSequence">
    <!-- <success>Always</success> -->
    <Operations>
      <li Class="PatchOperationTest">
        <xpath>*/ThingDef[defName="Cinnabar"]</xpath>
      </li>
      <li Class="PatchOperationAdd">
        <xpath>RecipeDefs</xpath>
        <value>
          <RecipeDef ParentName="DrillMetalRecipeBase">
          <defName>DrillCinnabar</defName>
          <label>Extract Cinnabar</label>
          <workAmount>7200</workAmount>
            <products>
              <IndustrialPDA>1</IndustrialPDA>
              <Cinnabar>10</Cinnabar>
            </products>
          </RecipeDef>
        </value>
      </li>
    </Operations>
  </Operation>

</Patch>



When I try this I get an error but I feel like I've tried a dozen different ways to go about it and nothing has worked so far. The Parent is in the same mod so I thought I could reference it without issue. Any help is appreciated.

~Six
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 22, 2017, 05:59:20 PM
im not sure what that xpath is supposed to be doing? that will add everything in value to all RecipeDefs, or fail miserably. Im sure thats not what youre trying to do. Xpath has to be the fullpath each time if that helps.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 22, 2017, 07:57:34 PM
Basically what I was trying to do is add a recipe if the condition is met. But yeah failed miserably is more like it.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 23, 2017, 03:34:35 AM
I think its just you second xpath on the add. It needs to be a fully qualified xpath. Ill have a proper look later on (not at my pc) but check the xpath help thread here. Zorba put a sequence example up. May hold a clue.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sixdd on June 23, 2017, 03:48:06 AM
Yeah I think I can fix it by defining empty recipes in the mods main recipe file and just replace them, that should work. I'm taking a break right now, eyes were bugging out, but I'll let you all know the outcome.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: JT on June 26, 2017, 08:37:23 AM
[Never mind.  There was a stray E.  Sigh. =P]
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: JT on June 26, 2017, 10:13:04 AM
For reasons which I assume are similar to the stuff in Antaios' (https://ludeon.com/forums/index.php?topic=32785.msg344721#msg344721) post, I'm having trouble getting a check-for-other-mod-and-then-edit-my-own-mod patch to work.

Here goes:


<Operation Class="PatchOperationSequence">
<!-- If Combat Extended is installed, add Combat Extended items to the Security Contractor -->
<operations>
<li Class="PatchOperationTest">
<xpath>/Defs/ThingDef[@Class="CombatExtended.AmmoDef"]</xpath>
</li>
<li Class="PatchOperationAdd">
<xpath>/Defs/TraderKindDef[defName="JTTraderKindSecurityContractor"]/stockGenerators</xpath>
<value>
<li Class="StockGenerator_Tag">
<tradeTag>CE_Ammo</tradeTag>
<thingDefCountRange>
<min>2</min>
<max>5</max>
</thingDefCountRange>
<price>Cheap</price>
<totalPriceRange>
<min>200</min>
<max>700</max>
</totalPriceRange>
</li>
<li Class="StockGenerator_SingleDef">
<thingDef>Ammo_303British_FMJ</thingDef>
<totalPriceRange>
<min>110</min>
<max>170</max>
</totalPriceRange>
</li>
<li Class="StockGenerator_SingleDef">
<thingDef>Ammo_45ACP_FMJ</thingDef>
<totalPriceRange>
<min>70</min>
<max>130</max>
</totalPriceRange>
</li>
</value>
</li>
</operations>
</Operation>


This purportedly searches for any XML item that derives from a Combat Extended class and if it finds one, we know that Combat Extended is installed and can proceed to add appropriate items to our trader.

However, it fails.  Without debugging messages to be able to identify where specifically it is failing, I wasn't able to rule out what was happening.

Until I tried separating the operations out of the sequence entirely...


<Operation Class="PatchOperationTest">
<xpath>/Defs/ThingDef[@Class="CombatExtended.AmmoDef"]</xpath>
</Operation>
<Operation Class="PatchOperationAdd">
<xpath>/Defs/TraderKindDef[defName="JTTraderKindSecurityContractor"]/stockGenerators</xpath>
<value>
<li Class="StockGenerator_Tag">
<tradeTag>CE_Ammo</tradeTag>
<thingDefCountRange>
<min>2</min>
<max>5</max>
</thingDefCountRange>
<price>Cheap</price>
<totalPriceRange>
<min>200</min>
<max>700</max>
</totalPriceRange>
</li>
<li Class="StockGenerator_SingleDef">
<thingDef>Ammo_303British_FMJ</thingDef>
<totalPriceRange>
<min>110</min>
<max>170</max>
</totalPriceRange>
</li>
<li Class="StockGenerator_SingleDef">
<thingDef>Ammo_45ACP_FMJ</thingDef>
<totalPriceRange>
<min>70</min>
<max>130</max>
</totalPriceRange>
</li>
</value>
</Operation>


...which succeeds with absolutely no errors, but obviously means that the Security Contractor receives the items even when Combat Extended is not installed (which of course would produce def errors in a game running without it once the defs are instantiated).

This has me completely perplexed: how is it possible that the sequence can fail when performing the two operations, when those exact same operations will succeed independently?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 26, 2017, 01:04:21 PM
Does it require a <success>Always</success> inside the PatchOperationSequence

NIA spotted the issue, you are testing per MOD the patching system works per xml FILE. You cannot test per file for the ammodef and then patch the stockgenerator as no file contains both.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: JT on June 26, 2017, 03:30:42 PM
Hm.  Well, then I'm proper screwed. =P

Combat Extended, ironically, includes an extension PatchOperation which checks to see if another mod is loaded.  This, of course, requires Combat Extended to use. ;-)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 26, 2017, 03:31:45 PM
Copy it into yours, that method didnt originate there, others are using it.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: JT on June 26, 2017, 08:52:36 PM
I was actually hoping to keep this mod XML-only.  After all, if I had an assembly I wouldn't even need the patching operations, since I'd have direct access to the definitions at runtime when I could guarantee their mutability.

I'll throw up a feature request if there isn't one already.  If others are using it, it's clearly a popular enough requirement that it should be there in the spec.  I don't hold anything against it, the patching feature is young. =)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 27, 2017, 01:04:52 PM
I think (Not 100%) that Zorba will be adding a "does mod exist" patch operation for next release. Dont quote me on that but hes been talking to us modders about it all on the modding discord so he is very aware of the issue.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Tamias on June 28, 2017, 09:02:50 AM
Hey guys, modding noob here with a couple of xpath questions that may or may not be related.

1. I know that standard procedure now is to start off an xpath patch with either "/" or "*/", and to avoid using "//" altogether. However, are there any valid instances to use "//" ever in Rimworld modding applications? I know that it searches every possible node, but wouldn't there be some limited situations where that function is useful?

2. Is it possible, through xpath patching, to simply replace all instances of "X" with "Y" (like a "replace all" function)? Or even remove all instances of "X" altogether, no matter where it is? For example, lets say you wanted to create a compatability patch between two mods, and you need to either remove or change all instances of a particular weapon or apparel tag for all factions. Instead of searching for every single instance of tag X throughout the two mods, and writing an individual "replace" or "remove" patch for each one, would it be possible to write a single patch that searches for all instances of that tag no matter where it is, and performs the desired operation? This is just an example, but there have been several situations where this thought has occured to me.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 28, 2017, 09:51:02 AM
The only time to use // is if you DO NOT KNOW THE STRUCTURE OF THE XML, which we always do in Rimworld land. So no never use that. :)

xpath is run PER FILE, thats the key here, ive seen people trying to run tests looking in 1 file to change something in another, that will not work. You can literally say "if that thing exists in this file, than change it to this other thing" but its getting the selector in the xpath robust enough to not grab something it shouldnt thats the issue. It really depends on what you are trying to do here.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Tamias on June 28, 2017, 11:45:58 AM
Thanks for the reply Skully, I really appreciate it. I'm still really new to the concept of xpath, so I'm sorta trying to fill out my xpath "toolbox" by trying to understand what the possibilities are with xpath in the context of Rimworld. I've read quite a bit online about it as a primer, but nearly all the info I've come across is general purpose, so I've been having trouble figuring out exactly what I can and cannot do in certain situations with RimWorld modding specifically.

As far as my second question goes, I think it would help if I used an expanded example with some code for clarity.

Let's say I'm using two mods: mod A and mod B. Both mods add weapons, as well as their own factions that use the weapons from their respective mods. However, I don't like the weapons from mod B, so I want to patch all the faction pawns from mod B to use weapons from mod A instead. My method so far would be a patch that might look something like this:

  <Operation Class="PatchOperationReplace">
    <xpath>/PawnKindDefs/PawnKindDef[defName="ModB_Soldier"]/weaponTags/li[.="ModB_Weapons"]</xpath>
    <value>
      <li>ModA_Weapons</li>
    </value>
  </Operation>

Then, I would repeat the process for every pawn that mod B adds, creating an individual patch for every instance where the weapon tag "ModB_Weapons" appears, and replacing it with "ModA_Weapons". However, the issue is that the tag is peppered throughout all the various PawnKindDef files, and it's a pain in the ass to have to track down and path every single one, when what I'm doing here basicly amounts to a "replace all" operation. I know there has to be a more efficient way to accomplish something like this with xpath patching, I'm just not fluent enough with it to know what that solution is. For this example, would there be a way to write a single "PatchOperationReplace" patch that would simply replace every instance of "ModB_Weapons" with "ModA_Weapons" throughout all the various PawnKindDef files?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on June 28, 2017, 01:22:10 PM
well, per file if you can make the xpath match what you want itll patch each thing it finds. The issue above is that it will obviously continue to run on ALL files therefore you have to define the pawns name to ensure you are ONLY hitting those pawns from that mod. If thats the only identifier you have theres not much you can do with the vanilla patching system bar what youve already stated.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Granitecosmos on July 01, 2017, 08:31:39 AM
If
<Defs>
<ThingDef Name="ExampleAbstract" Abstract="True">
</ThingDef>
</Defs>

and
<Defs>
<ThingDef>
<defName>ExampleDef</defName>
</ThingDef>
</Defs>

exists, is it possible to patch ExampleDef's
<ThingDef>
to
<ThingDef ParentName="ExampleAbstract">
and if it is, how do I do it?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on July 01, 2017, 09:14:37 AM
For people that care:

namespace Verse
{
public class PatchOperationAttributeAdd : PatchOperationAttribute
{
protected string value;

protected override bool ApplyWorker(XmlDocument xml)
{
bool result = false;
foreach (object current in xml.SelectNodes(this.xpath))
{
XmlNode xmlNode = current as XmlNode;
if (xmlNode.Attributes[this.attribute] == null)
{
XmlAttribute xmlAttribute = xmlNode.OwnerDocument.CreateAttribute(this.attribute);
xmlAttribute.Value = this.value;
xmlNode.Attributes.Append(xmlAttribute);
result = true;
}
}
return result;
}
}
}


simple answer:

<li Class="PatchOperationAttributeAdd">
  <xpath>/Defs/ThingDef[defName = "ExampleDef"]</xpath>
  <attribute>ParentName</attribute>
  <value>ExampleAbstract</value>
</li>


Think thats right. :)

There also attributeSet for changing ones that already exist.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on July 02, 2017, 01:06:04 AM
Is there a way to throw an error box log text if an xpath fails?
I'd like my add-on mod to issue a message to the user:
"Ancient Magic: failed to find dependency Infused mod; is Infused mod installed?"

<!-- remove two records if they exist -->
<Operation Class="PatchOperationSequence">
    <success>Always</success>
    <operations>
        <li Class="PatchOperationTest">
            <xpath>/Defs/Infused.ChanceDef</xpath> <!-- is there a record in defs called Infused.ChanceDef? -->
            </li>
            <li Class="PatchOperationRemove">
                <xpath>/Defs/Infused.ChanceDef[defName = "Default"]</xpath>
            </li>
            <li Class="PatchOperationRemove">
                <xpath>/Defs/Infused.ChanceDef[defName = "IndustrialAndBelow"]</xpath>
            </li>
        </operations>
    </Operation>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on July 02, 2017, 05:36:55 AM
Not really an xpath question, this is one for the modding community in general, i dont know of any way within the core system, but others may have done funky stuff with custom operations. Chuck a thread on mods and see if anyone knows of a way.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on July 05, 2017, 11:10:42 PM
There's been some talk about using wildcards and how they generally increase game load time through unnecessarily long searches. There hasn't been any objective tools or tests measuring that difference, but that's another story.

There has also been some demonstrations of how the in-game version of xpath is a partial implementation with some unexpected behaviors.

The following expression works as intended in my patch operation:
<xpath>//ThingDef[DefName="Mineshaft"]/recipes</xpath>

This works as a replacement:
<xpath>*/ThingDef[DefName="Mineshaft"]/recipes</xpath>

Can I replace the wildcard ("//" or "*/") with something specific?
(further info on patch file) (https://github.com/AngleWyrm/Mines/blob/master/Patches/Additional%20recipes.xml#L45)

Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on July 06, 2017, 03:11:44 AM
It depends if you are targetting one def then yes use the wrapper the file your targetted def is in is using.

/Defs/ThingDef
/BuildableDef/ThingDef

Etc

If you are hunting every file and they have different starting wrappers then you can use the wildcard,  // should never be used.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on July 07, 2017, 12:08:50 PM

(http://img11.deviantart.net/24e0/i/2016/120/3/b/lady_death_by_drakon_art-da0s7nf.jpg)On the subject of using a PatchOperationFindMod function in patch sequences as a test: I made that into a mod (attached, with source), which works just like any other PatchOperationXXX.

The impetus behind making it was that it would enable modders to write patches without the need for C# coding, and in as far as that was the goal it has been met. But there's significant problems with this approach that make it inadvisable: The active mods list in the config file is made of folder names.

Many mods attach version numbers to their mod folder, or go through modder fidgeting to get a better name. They also get renamed by users where those folder names fail to clearly convey their contents. Thus any patch mod that relies on folder names is apt to break, early and often. It's a maintenance nightmare waiting to happen.

BTW, is (mods)/*/About/About.xml/ModMetaData/name visible in xpath space?

[attachment deleted by admin due to age]
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on July 11, 2017, 12:54:44 AM
The xpath union operator '|' that combines multiple searches seems to work (doesn't throw a red error log message), so that it's theoretically possible to generate a node-set result like this:

/somewhere/deeper[Characteristic="whatever"]/recipe | /somewhereElse/depth/is/irrelevant[defName="stuff"]

My understanding of this implementation of xpath (please correct me if I'm wrong) is that it handles it's work on a per-file basis that maintains a concept of current file. If that is a reasonable approximation of the code, then what happens when an xpath with multiple searches ("|") is applied?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: kaptain_kavern on July 11, 2017, 12:22:34 PM
From my personal experimentations, using "or" instead of "|", the patch is applied in only on file, the other "or" options were simply not applied (without throwing any errors - Took me time to even noticed it wasn't working for all my "or"). I guess (but have not tested to confirm) it is applied in the file where the first occurrence is found.

Sorry if it's not that clear, I'm struggling with finding the right English words to express myself better.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Sp0nge on July 13, 2017, 02:14:45 PM
Im trying to change the movementspeed of cats here: https://github.com/XenEmpireAdmin/AnimalCollabProjectA17/blob/master/Defs/ThingDefs_Races/Races_Animal_Cats.xml

However im not shure what to write in order to direct my xpath to the correct location. I tried something like:


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

<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="CombatExtended.PatchOperationFindMod">
<modName>AnimalCollabProj</modName>
</li>
<li Class="PatchOperationAddModExtension">
<xpath>*/ThingDef[defName="ACPLion"]</xpath>
<value>
<li Class="CombatExtended.RacePropertiesExtensionCE">
<bodyShape>QuadrupedLow</bodyShape>
</li>
</value>
</li>
<li Class="PatchOperationReplace">
<xpath>*/ThingDef[defName="ACPLion"]/verbs</xpath>
<value>
<verbs>
<li Class="CombatExtended.VerbPropertiesCE">
<verbClass>CombatExtended.Verb_MeleeAttackCE</verbClass>
</li>
</verbs>
</value>
</li>
<li Class="PatchOperationReplace">
<xpath>*/ThingDef[defName="BigCatThingBase"]/statBases/MoveSpeed</xpath>
<value>
<MoveSpeed>40.3</MoveSpeed>
</value>
</li>
</operations>
</Operation>
</Patch>


I know that my bodyShape and verbClass wont execute, as they are targeting wrongly (I used some copy paste since there is alot of animals to patch so i made a crude xml pr animal to begin with). Now im trying to sort out my code and finish it up.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: shadowstitch on August 21, 2017, 05:03:14 PM
Okay, I'm stumped. Which isn't hard, admittedly, but I've tried doing my homework here, reading documentation and forum posts and I cannot figure out what I'm doing wrong. I'm hoping someone with better eyes and a bigger brain can take pity on me and help.

I'm trying to make a very simple change, (for personal preference usage only, not trying to steal anyone's work.)
In Latta/Skullywag's "Better Vents," I discovered that you could change the costlist to a stuffcategories/stufflist and make the vents out of any stuff you want.
In a16 this was easy enough, since all you had to do was make the change in the Buildings_Temperature.xml thingdef. Worked like a charm.
In a17, it's considerably more complicated, because patches. Better Vents now works all its magic on the core xml with a patch, and even though the existing patch is right there in front of me as an example, I can't get my little changes to work. I get red errors in log on load. If I change the core def directly, it appears to work perfectly with no errors, but I'm trying to play by the rules and use patches as they are intended.

What I'm trying to change is this:

I want to take the core Buildings.Temperature.xml, and mess with this thingdef:


<ThingDef ParentName="BuildingBase">
<defName>Vent</defName>
<label>vent</label>
<thingClass>Building_Vent</thingClass>
                ~~~snip~~~


down in its defs, I want to  change:


<costList>
<Steel>30</Steel>
</costList>

to:

    <stuffCategories>
          <li>Metallic</li>
          <li>Woody</li>
  <li>Stony</li>
    </stuffCategories>
    <costStuffCount>30</costStuffCount>


This is what I have in my patch, which doesn't work:


<Operation Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[defName = "Vent"]/stuffCategories</xpath>
<value>
<li>Metallic</li>
         <li>Woody</li>
     <li>Stony</li>
</value>
</Operation>

<Operation Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[defName = "Vent"]/costStuffCount</xpath>
<value> 30 </value>
</Operation>

<Operation Class="PatchOperationRemove">
<xpath>/Defs/ThingDef[defName = "Vent"]/costList</xpath>
</Operation>


I get this error in the log when I try to run it, so I ASSUME my code to snip the costlist is working, but the other 2 add-ons are wrong somehow. I tried a PatchOperationInsert as well, attempting to follow the syntax guidelines, but that didn't work either -- if that's what I need to use, I'm certainly not doing it right.


[BetterVents] Patch operation Verse.PatchOperationAdd(/Defs/ThingDef[defName = "Vent"]/stuffCategories) failed
Verse.Log:Error(String)
Verse.PatchOperation:Complete(String)
Verse.LoadedModManager:LoadAllActiveMods()
Verse.PlayDataLoader:DoPlayLoad()
Verse.PlayDataLoader:LoadAllPlayData(Boolean)
Verse.Root:<Start>m__853()
Verse.LongEventHandler:RunEventFromAnotherThread(Action)
Verse.LongEventHandler:<UpdateCurrentAsynchronousEvent>m__851()

[BetterVents] Patch operation Verse.PatchOperationAdd(/Defs/ThingDef[defName = "Vent"]/costStuffCount) failed
Verse.Log:Error(String)
Verse.PatchOperation:Complete(String)
Verse.LoadedModManager:LoadAllActiveMods()
Verse.PlayDataLoader:DoPlayLoad()
Verse.PlayDataLoader:LoadAllPlayData(Boolean)
Verse.Root:<Start>m__853()
Verse.LongEventHandler:RunEventFromAnotherThread(Action)
Verse.LongEventHandler:<UpdateCurrentAsynchronousEvent>m__851()


I'm following the same procedure as the existing operations in the patch, and to the best of my understanding, I'm performing the same sort of operations, but mine are not accepted for some reason. Can someone please teach me the error of my ways?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on August 21, 2017, 05:26:08 PM
You cannot add a value to something that doesnt exist, you need to add stuffCategories and costStuffCount first before you add values to them.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AngleWyrm on August 21, 2017, 09:15:34 PM
Quote from: shadowstitch on August 21, 2017, 05:03:14 PM
This is what I have in my patch, which doesn't work:

<Operation Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[defName = "Vent"]/stuffCategories</xpath>


Reference/tutorial wiki page (https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782)

PatchOperationAdd adds a new sub-part to a record, so the xpath search for those operations should only select the whole 'vent' record, and then add parts into it.

<xpath>/Defs/ThingDef[ defName="Vent" ]</xpath>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: shadowstitch on August 22, 2017, 12:10:28 AM
AngleWyrm, skullywag, thank you.

I had read through the documentation so many times my eyes were bleeding, and in all the examples, creating an entirely new parent node under a thingdef was never explicitly addressed; I assumed if you added a record with values that it would automatically add the declared path, not just look for the path to only insert values under it. It took me a minute to really wrap my head around the fact that this operation just adds a dumb string -- I was apparently giving the operations too much credit and overthinking it. I changed the add code to this:


<Operation Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[defName = "Vent"]</xpath>
<value>
    <stuffCategories>
      <li>Metallic</li>
      <li>Woody</li>
      <li>Stony</li>
    </stuffCategories>
    <costStuffCount>30</costStuffCount>
</value>
</Operation>


This may not be the INTENDED syntax, like, I don't know if I should be adding the subnode in one operation to declare it properly, then adding the values in another operation, but this seems to be working. Thanks for the guidance!
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: skullywag on August 22, 2017, 03:28:22 AM
Thatll work for now. You can do things like test for the existence of a node and add if not found using the patch system. It looks more complicated than it actually is.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: The-Eroks on August 27, 2017, 11:52:18 AM
Is there any way to target an abstract without a defName? If so, what is the most elegant/efficient method?

For example, I'm looking to patch FurnitureBase and change the <DesignationCategory> to something else (for example, "Medieval").

Thanks!
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: kaptain_kavern on August 27, 2017, 02:31:42 PM
Have a look in there : https://ludeon.com/forums/index.php?topic=32785.msg341693#msg341693

You can target the @Name of any abstracts
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: CannibarRechter on August 27, 2017, 04:23:59 PM
Indeed, you can do that, like this:

   <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef[@Name = "ChunkRockBase"]/graphicData/texPath</xpath>
      <value><texPath>Items/StoneChunk</texPath></value>
   </Operation>

You can also combine queries, like this:

   <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef[defName = "GlowPod"]/comps/li[@Class = "CompProperties_Glower"]/glowColor</xpath>
      <value><glowColor>(113,0,117,0)</glowColor></value>
   </Operation>

Note how this one looks for thing thingDef named "GlowPod," and then finds the <li> item with a Class name as shown. Find the GlowPod definition to see what I did there. This method of fetching a named <li> is better than the li[1], etc, method you often see here, as it is order independent. I.e., if the dev moves its position, or adds an item, causing it to not be in the same place, the named query still works.

Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: The-Eroks on August 30, 2017, 08:35:03 PM
Awesome! Thank you and thank you.

Follow up question, what is the most efficient way of patching the same single entry multiple times? For example, if one were to change the label, description, and graphics path for the double bed? Three separate operations (which requires three separate searches) doesn't seem as efficient as searching for a target once and applying three changes.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: CannibarRechter on August 30, 2017, 09:00:24 PM
Yeah; I don't think there is such a thing, sadly. If you think about it, it kinda can't exist, because while label and description may be roughly in the same area, graphics path isn't quite. Anyway, if you can select the right region, what you CAN do is include an entire paragraph of XML. For example, here is a (mostly pointless) patch that replaces the entire comps section of the WindTurbine:

   <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef[defName = "WindTurbine"]/comps</xpath>
      <value>
      <comps>
         <li Class="CompProperties_Power">
            <compClass>CompPowerPlantWind</compClass>
            <basePowerConsumption>-3000</basePowerConsumption>
            <transmitsPower>true</transmitsPower>
      </li>
      <li Class="CompProperties_Breakdownable"/>
      </comps>    
      </value>
   </Operation>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: The-Eroks on September 04, 2017, 05:33:39 PM
Thanks for the reply. I do use a technique similar to what you posted for larger blocks that include nested lists and such.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: XeoNovaDan on November 26, 2017, 06:12:37 AM
Anything on the new PatchOperationFindMod?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on November 26, 2017, 02:07:48 PM
I can tell you how to use PatchOperationFindMod since I've been experimenting with it today.

Syntax is as follows where "node" is a context dependent name (such as Operation, or li, or match):

<node Class="PatchOperationFindMod">
  <mods>
    <li>Mod Name 1</li>
    <li>Mod Name 2</li>
  </mods>
  <match Class="PatchOperationClassName">
  </match>
  <nomatch Class="PatchOperationClassName">
  </nomatch>
</node>

You'll find a working example of this in the repo of my fork of Extended Woodworking.

https://github.com/indented-automation/ExtendedWoodworking/blob/master/generated/ExtendedWoodworking/Patches/EW-Building_CompProperties_Refuelable_fuelFilter.xml

HTH

Chris
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: XeoNovaDan on November 26, 2017, 02:58:35 PM
Quote from: TeflonJim on November 26, 2017, 02:07:48 PM
...

Thanks!
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on November 26, 2017, 11:33:45 PM
Quote from: XeoNovaDan on November 26, 2017, 06:12:37 AM
Anything on the new PatchOperationFindMod?
PatchOperationFindMod works on folder names. Be aware that steam actually renames the folder into some number (the ID in the steam URL), which in turn mean steam and non-steam users will not use the same folder name for the same mod.

ModCheck.isModLoaded has the same goal, but it works on Name tag from About.xml. Also ModCheck caches the output, ensuring that it will only execute once. PatchOperationFindMod will execute once for every def xml file in every mod, including core.

Writing ModCheck has given me some insights in how patching works and how it affects performance. It turns out that poorly written patches can slow down the game startup quite a bit and I have decided to write some guidelines for cases where the same patching can be written in multiple ways and the time spend on each differs greatly. I will post a link here when I have actually written it. It could take a while to write though as I want to get it right and may have to conduct research and measurements.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: XeoNovaDan on November 27, 2017, 05:18:44 AM
I guess the multiple mod names is to take the folder name fiasco into account then.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on November 27, 2017, 04:29:09 PM
The biggest problem with PatchOperationFindMod for me at this point is not the need to list by ID and name. Rather that it gives no way of differentiating between mods that are present on the system and mods that are active in a load order. If a mod is present, match will execute.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on November 27, 2017, 05:00:28 PM
Quote from: TeflonJim on November 27, 2017, 04:29:09 PM
The biggest problem with PatchOperationFindMod for me at this point is not the need to list by ID and name. Rather that it gives no way of differentiating between mods that are present on the system and mods that are active in a load order. If a mod is present, match will execute.
:o
Checking if a mod is on the HD, but not loaded is so useless while patching that it did not cross my mind, not even testing if it works like that.

ModCheck.isModLoaded will test to see if the mod is in the list from ModsConfig.ActiveModsInLoadOrder, meaning it will not care what is on the HD, but rather it checks for enabled mods. ModCheck.loadOrder can tell the load order of two mods while ModCheck.isVersion tells if a mod is of at least a specific version. Each can be used with success Invert, meaning it should pretty much cover everything I can think of anybody would need when making conditional patching.

Each of those can print errors to the log on failure, or just messages on success or failure. Default is not writing to the log, but it allows the patch file to write an error if a required mod is not present, an incompatible mod loaded, incorrect order etc. This too should cover any case I could think of being useful when writing patch files.

If you think need anything else, let me know and I will consider adding it.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on November 28, 2017, 02:18:04 PM
Trying to figure out if what I'm trying to do is even possible or not. I'm attempting to merge my fork of Extended Woodworking with its Vegetable Garden patch. To achieve that I (think I) either need to:

1. Only create a set of ThingDefs if a particular mod is present (and loaded)

Or:

2. Remove a small set of ThingDefs if a particular mod is not present

The second approach is working for a bunch of floors the mod injects, it appears to strip out the TerrainDefs well enough. The linked sequence attempts to do this:

https://github.com/indented-automation/ExtendedWoodworking/blob/master/generated/ExtendedWoodworking/Patches/EW-Cleanup-NoVGP.xml

The link uses PathOperationFindMod which has since been substituted for a PatchOperationConditional based on a Def exposed by the dependent mod. I'm going to replace that with ModCheck because it's just really, really disgusting and I've been starring at PatchOperation classes in ilspy for too long now.

Doing this in C# is likely easier, but my fork was only ever intended to maintain the life of a mod I've always used (and certainly didn't originally create). I'm reluctant to move it out of just-a-bit-of-xml at the moment.

If I can figure this bit out I can do (at least) one more "is this sane" pass on the XML / XPath before making it live.

Otherwise, is there a better way of doing this that I don't know about yet?

Thanks for any / all comments in advance :)

All the best!

Chris

Edit: "May" be related to the way PatchOperationSquence works, where all steps in a sequence are applied to a single XmlDocument (first failure aborts patch).
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on November 28, 2017, 02:38:44 PM
Quote from: TeflonJim on November 28, 2017, 02:18:04 PM1. Only create a set of ThingDefs if a particular mod is present (and loaded)
I wrote a patch for that. I added it here with comments.

The idea is to use PatchOperationSequence. It's important to realize that a patch will be applied to each xml file in Defs in each mod. This mean you need to apply some xpath, which ensures the add new item will only apply once and not once for each xml file.
In this specific case PatchOperationAdd takes up that job because that operation is needed anyway, but there are multiple ways to do it.
<Patch>
<!-- a sequence works by taking each PatchOperation in operations and execute them one by one -->
<!-- if a PatchOperation in operations fail, the following will not be executed -->
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<!-- only work if the needed mod is loaded -->
<li Class="ModCheck.isModLoaded">
<modName>A Dog Said...</modName>
<yourMod>MoreMonstergirls B18 Vanilla</yourMod>
</li>
<!-- make sure the mods are loaded in the needed order -->
<!-- delete this one if order doesn't matter -->
<li Class="ModCheck.loadOrder">
<modName>A Dog Said...</modName>
<yourMod>MoreMonstergirls B18 Vanilla</yourMod>
<errorOnFail>true</errorOnFail>
</li>
<!-- test if an xpath exist -->
<!-- PatchOperationAdd, PatchOperationInsert and PatchOperationTest can be used -->
<!-- which one you pick depends on your specific needs -->
<li Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[defName = "BlackHarpy"]/recipes</xpath>
<value>
<li>InstallPegLegAnimal</li>
<li>InstallSimpleProstheticLegAnimal</li>
<li>InstallBionicLegAnimal</li>
<li>InstallSpineAnimalSimple</li>
<li>InstallSpineAnimalBionic</li>
<li>InstallBionicEyeAnimal</li>
<li>InstallAnimalBrainStimulator</li>
<li>InstallBionicAnimalHeart</li>
<li>InstallBionicAnimalLung</li>
<li>InstallBionicAnimalLiver</li>
<li>InstallBionicAnimalKidney</li>
<li>InstallBionicAnimalStomach</li>
<li>InstallNoseAnimalSimple</li>
<li>InstallJawAnimalSimple</li>
<li>InstallJawAnimalBionic</li>
<li>CureInjuryBurnAnimal</li>
<li>CureInjuryCrushAnimal</li>
<li>CureInjuryCrackAnimal</li>
<li>CureInjuryCutAnimal</li>
<li>CureInjurySurgicalCutAnimal</li>
<li>CureInjuryScratchAnimal</li>
<li>CureInjuryBiteAnimal</li>
<li>CureInjuryStabAnimal</li>
<li>CureInjuryGunshotAnimal</li>
<li>CureInjuryShreddedAnimal</li>
<li>CureInjuryBruiseAnimal</li>
</value>
</li>
<!-- insert the new item into Defs. In this case it's a recipe, but it can be anything -->
<li Class="PatchOperationAdd">
<xpath>/Defs</xpath>
<value>
<RecipeDef ParentName="SurgeryFleshAnimal">
<defName>InstallSimpleProstheticWingHarpy</defName>
<label>install simple prosthetic harpy wing</label>
<description>Installs simple prosthetic harpy wing. Requires a min skill of 4 in Medicine.</description>
<workerClass>Recipe_InstallArtificialBodyPart</workerClass>
<jobString>Installing simple prosthetic harpy wing</jobString>
<workAmount>2000</workAmount>
<skillRequirements>
<Medicine>4</Medicine>
</skillRequirements>
<ingredients>
<li>
<filter>
<categories>
<li>Medicine</li>
</categories>
</filter>
</li>
<li>
<filter>
<thingDefs>
<li>SimpleHarpyWing</li>
</thingDefs>
</filter>
</li>
</ingredients>
<fixedIngredientFilter>
<categories>
<li>Medicine</li>
</categories>
<thingDefs>
<li>SimpleHarpyWing</li>
</thingDefs>
</fixedIngredientFilter>
<appliedOnFixedBodyParts>
<li>LeftShoulder</li>
<li>RightShoulder</li>
</appliedOnFixedBodyParts>
<addsHediff>SimpleHarpyWing</addsHediff>
<recipeUsers>
<li>BlackHarpy</li>
<li>Harpy</li>
</recipeUsers>
</RecipeDef>
</value>
</li>
</operations>
</Operation>
</Patch>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on November 28, 2017, 03:29:14 PM
Thank you kindly, I'll follow the same scheme.

Chris
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: QuantumX on November 30, 2017, 03:07:42 PM
Hello, could i ask for help on this XPATH please.. I cannot seem to get a insert operation to work on the BodyParts_General.xml file

I want to go from this


<BodyPartDef>
<defName>Brain</defName>
<label>brain</label>
<hitPoints>10</hitPoints>
<oldInjuryBaseChance>1</oldInjuryBaseChance>
<skinCovered>false</skinCovered>
<tags>
<li>ConsciousnessSource</li>
</tags>
<dontSuggestAmputation>true</dontSuggestAmputation>
</BodyPartDef>


To this:


<BodyPartDef>
<defName>Brain</defName>
<label>brain</label>
<hitPoints>10</hitPoints>
<oldInjuryBaseChance>1</oldInjuryBaseChance>
<skinCovered>false</skinCovered>
<tags>
<li>ConsciousnessSource</li>
</tags>
<dontSuggestAmputation>true</dontSuggestAmputation>
<spawnThingOnRemoved>IGBrain</spawnThingOnRemoved>
</BodyPartDef>


My Patch Operation Looks Like this;


<Operation Class="PatchOperationInsert">
<xpath>/Defs/BodyPartDef[defName = "Brain"]/specialDesignatorClasses</xpath>
<value>
<spawnThingOnRemoved>IGBrain</spawnThingOnRemoved>
</value>
</Operation>


But i'm getting operation failed, i think its something to do with the /Defs/BodyPartDef part... but not sure, this is only the second time i've ever done xpath, and the first i had alot of help from here.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on November 30, 2017, 03:13:36 PM
Quote from: QuantumX on November 30, 2017, 03:07:42 PM
<xpath>/Defs/BodyPartDef[defName = "Brain"]/specialDesignatorClasses</xpath>
You only mentioned specialDesignatorClasses here, not in the original or the wanted codes. I think you need to remove it and insert directly into BodyPartDef.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: QuantumX on November 30, 2017, 03:17:16 PM
So so i have changed to the following;


<Operation Class="PatchOperationInsert">
<xpath>/Defs/BodyPartDef[defName = "Brain"]/</xpath>
<value>
<spawnThingOnRemoved>IGBrain</spawnThingOnRemoved>
</value>
</Operation>


And does not work
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on November 30, 2017, 03:31:20 PM
PatchOperationAdd: adds a new child to the element found in xpath
PatchOperationInsert: adds a new sibling to the element found in xpath

You picked the wrong one and now it tries to read <spawnThingOnRemoved> as a Def.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: QuantumX on November 30, 2017, 03:33:10 PM
So next change..


So so i have changed to the following;

[code]
<Operation Class="PatchOperationInsert">
<xpath>/Defs/BodyPartDef[defName = "Brain"]/defName</xpath>
<value>
<spawnThingOnRemoved>IGBrain</spawnThingOnRemoved>
</value>
</Operation>


Now upon loading it now WORKS!!!  WhooHoooo....
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: larSyn on December 01, 2017, 11:57:09 AM
Quick question...Is it better to use /Defs/whateverDef than */whateverDef now?  I've seen a lot of this since the update and wasn't sure which is better.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on December 01, 2017, 01:20:13 PM
Quote from: larSyn on December 01, 2017, 11:57:09 AM
Quick question...Is it better to use /Defs/whateverDef than */whateverDef now?  I've seen a lot of this since the update and wasn't sure which is better.
Good question. I have seen multiple different versions in use as well and assuming they all work as intended, the difference would presumably be speed. In other words the answer would come from benchmarking each one, so that's what I did.





Def/baseline
/Def/+1.4%
*/Def/+6.4%
//+749%
I ran 500 of each and measured several times. The results are fairly consistent and as such trustworthy and I used time for patching core only. I think the numbers speak for themselves.

Maybe it was a quick question, but providing the answer wasn't  :P
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: kaptain_kavern on December 01, 2017, 02:59:19 PM
Told ya ;-)

As we're explicitly knowing the whole path upfront, we need to use the most precise "query".

But, duh! I never thought about just removing the first "/". But it's logical... Once someone pointed you to it ^^

Thanks for doing the test.

Much appreciate ;-) this precise information need to be spread fast!
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on December 01, 2017, 03:23:17 PM
Quote from: kaptain_kavern on December 01, 2017, 02:59:19 PMBut, duh! I never thought about just removing the first "/". But it's logical... Once someone pointed you to it ^^
Same here. I saw such a patch, then I was like "it's different. Will it work?" and indeed it works.

Quote from: kaptain_kavern on December 01, 2017, 02:59:19 PMthis precise information need to be spread fast!
Added to the sticky about xpath performance. I recommend everybody reading it the entire thread, particularly first post and (at the time of writing) the last two posts, both written by me.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: larSyn on December 01, 2017, 04:05:51 PM
Thanks, Nightinggale.  Now I know what I'll be doing later. 
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: o-o-o on December 11, 2017, 10:58:47 PM
I am working on faction mods, and have made good use of some very basic xpath stuff.  Specifically, I have been adding tags to weapons so that custom pawntypes would wield specific vanilla weapons.  All good there so far; all my dudes spawn with their proper weapons. 

While trying to tweak the spawn apparel for the new pawns, I was bothered that my mostly non-violent industrial-tech and Outlander apparel tag pawns wanted to take a lot of their apparel money and buy armor vests.  Instead of forcing a lot of specific apparel on the pawns and leaving no money left over for the armor vests, I decided to use xpath to just modify the armor vests.  Specifically, I wanted to replace one of the list items under Apparel_Various/Apparel_VestPlate/apparel/tags.  I would keep the Military list item, but change the Outlander list item to a custom new tag ("RaidMonkey" in this case).  I can't seem to get the path properly selected, or don't understand the subtleties of nodes vs list items or something.  Using the following code I get an error that my list items do not correspond to any field type in type ApparelProperties.



Quote<Operation Class="PatchOperationReplace">
    <xpath>*/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags</xpath>
    <value>
      <li>Military</li>
      <li>RaidMonkey</li>
      </value>
    </Operation>

I have not found any good examples of replacing list items in patches in the other faction mods I have been looking at, nor did I find an example in this thread that was easy enough for me to translate to my specific case.  The more I see of xpath, the more I like it for modding, so I expect to get a lot of mileage out of replacing or removing list items found in tags or weaponTags.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on December 11, 2017, 11:11:09 PM
You aren't doing it right. There is no list in the source code, hence you should not use a list in xml. The question is how to do it correctly.
  <Operation Class="PatchOperationReplace">
    <xpath>Defs/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/Military</xpath>
    <value>RaidMonkey</value>
    </Operation>

I think that will work. At least it matches the source code better. The idea is that whatever is in xpath is replaced with value and for replacement it only considers what is after the last "/". This mean since xpath will find an element with tags as parent, the replacement will have tags as parent and as such moving to a different location is not supported, but I can't really think of any case where that would be useful.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: o-o-o on December 11, 2017, 11:51:16 PM
I think there is a list in the source code.  Still kind of new at this, but anywhere I see <li>stuff</li> i read that as a list, even if it has only a single entry.  The Apparel_VestPlate/apparel/tags section has two entries:
Quote<tags>
        <li>Outlander</li>
        <li>Military</li>
      </tags>

I want to replace "Outlander" with "RaidMonkey".   I don't know how to correctly select the list item "Outlander" in order to get xpath to replace it.  I am fine with leaving "Military" as-is.  I am also fine with Outlanders spawning without the benefit of an armored vest.  Why would city-folk need body armor anyway?  City guards could always use the Military apparel tag.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Nightinggale on December 12, 2017, 12:16:18 AM
Quote from: o-o-o on December 11, 2017, 11:51:16 PMI think there is a list in the source code.
Gahhh, you are right.... there is one. I really hate the vanilla PatchOperations. They are written so confusing, at least the way they appear in ILSpy. The complete lack of comments doesn't help.

The issue is that it doesn't have a list in the code. Instead it has a single node. However that node is value and then later on it use the xml child nodes and not value itself. It looks like this is the approach in some PatchOperation methods while others have a list in C#.

  <Operation Class="PatchOperationReplace">
    <xpath>Defs/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/Outlander</xpath>
    <value>
      <li>RaidMonkey</li>
    </value>
  </Operation>

It goes to the Outlander node, locates the parent, inserts all the child nodes from value before Outlander and then it deletes Outlander. The fact that Military is also there doesn't matter.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: CannibarRechter on December 12, 2017, 06:07:15 AM
Method 1 is to replace all tags with a complete new set. Method 2 is to replace the first tag with another tag. I don't believe there is any other alternative, unless the li has attributes (which yours don't).

Method 1 (finds and replaces all the tags and all their list items):


  <Operation Class="PatchOperationReplace">
    <xpath>Defs/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags</xpath>
    <value>
      <tags>
          <li>RaidMonkey</li>
          <li>Military</li>
      <tags>
    </value>
  </Operation>


Method 2 (finds the first list item; note xpath indexes begin at '1'):


  <Operation Class="PatchOperationReplace">
    <xpath>Defs/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/li[1]</xpath>
    <value>
      <tags>
          <li>RaidMonkey</li>
      <tags>
    </value>
  </Operation>


There is probably an advanced xpath query mechanism to find an li with specific text body, but I don't know it.

EDIT:

Online documentation (https://stackoverflow.com/questions/3655549/xpath-containstext-some-string-doesnt-work-when-used-with-node-with-more) for xpath would suggest to me that the advanced query method would be this:


<xpath>Defs/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/li[text()[contains(.,"Outlander")]]</xpath>


It didn't test it, but if that actually works, let us know. It could be really useful. ;-P
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on December 12, 2017, 06:16:05 AM
You can use . to refer to the current node and therefore filter to a single instance in the list. For example:


<xpath>Defs/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/li[.="Outlander"]</xpath>


This will let you target it for patch operations.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: o-o-o on December 12, 2017, 11:27:53 AM
A bunch of stuff worked.

Method 1 worked with slight modification (forgot slash at end of tags)
Quote<Operation Class="PatchOperationReplace">
    <xpath>Defs/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags</xpath>
    <value>
      <tags>
          <li>RaidMonkey</li>
          <li>Military</li>
      </tags>
    </value>
  </Operation>

I didn't want to try Method 2 - as I understand it, it looks for a specific location (first item in list) and replaces whatever it finds, as opposed to searching for the specific target (Outlander) and just replacing that.  Seems like a potential nightmare when things change in the vanilla code in chasing down those misapplied operations.

Both of the advanced querry methods worked:

Quote<Operation Class="PatchOperationReplace">
    <xpath>*/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/li[.="Outlander"]</xpath>
    <value>
      <li>RaidMonkey</li>
      </value>
    </Operation>

and

Quote<Operation Class="PatchOperationReplace">
    <xpath>*/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/li[text()[contains(.,"Outlander")]]</xpath>
    <value>
      <li>RaidMonkey</li>
      </value>
    </Operation>

My rich pawns with the Outlander apparel tag never spawned armored vests, and my rich pawns with both Outlander and RaidMonkey tags spawn standard apparel plus the armored vests.

Can you comment on the differences between the two advanced querry operations?  Is one more efficient than the other? Are there more complex cases where one would be preferable to the other?



Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on December 12, 2017, 12:06:07 PM
This expression is a very simple one and I would recommend it over nested function calls. I doubt you'll notice a speed difference, but there's certainly a complexity difference.


[.="Outlander"]


In theory all it needs you to know is that "." is the current XML node (be that an element as it is here, or an attribute), and that we can test a simple value using "=".

It's perhaps worth mentioning you can drop the "text()" function entirely, making the other expression into this:


*/ThingDef[defName = "Apparel_VestPlate"]/apparel/tags/li[contains(.,"Outlander")]


About text()

text() gets text elements (as opposed to node elements) from a given path. In the example below, "item" is a text element, "child" is a node element.


<document>
  <item>ItemValue</item>
  <child>
    <otherItem>ItemValue</otherItem>
  </child>
</document>


The expression below will nodes of text type only, therefore you would only find "item", and not "child".


document/*[text()]


About contains(this, containsthat)

The xpath expression below uses contains:


document/stuff/li[contains(., "Wood")]


When used to search the following document it will find both Wood_Green and Wood_Orange. Both contain the specified value.


<document>
  <stuff>
    <li>Wood_Green</li>
    <li>Wood_Orange</li>
    <li>Metal</li>
    <li>Plastic</li>
  </stuff>
</document>


Contains is most useful where you might have wanted a wildcard, a partial match. In the case where you want an exact value equality (using "=") is much more appropriate.

In all cases, XML (and xpath expressions) are case-sensitive. The changes in B18 should make it easier to develop patches as documents will be more predictable in the future.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: CannibarRechter on December 12, 2017, 02:15:52 PM
> I didn't want to try Method 2 - as I understand it, it looks for a specific location (first item in list) and replaces whatever it finds, as opposed to searching for the specific target (Outlander) and just replacing that.

I agree with your decision there. I see it used in a lot of mods. It's probably because one of the well-known online xpath query generators prefers that method, and a lot of people copy others. But yeah, order-dependency is just a recipe for future headache.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: kaptain_kavern on December 13, 2017, 03:31:11 AM
Regarding this precisely,

I'm currently updating my mods patches, trying to reflect all the things I've learned here. And I would need help for one, please.


                        <li Class="PatchOperationAdd"> <!-- Gazelle drop horn  -->
<xpath>Defs/PawnKindDef[defName = "Gazelle"]/lifeStages/li[3]</xpath>
<value>
<butcherBodyPart>
<bodyPartGroup>HornAttackTool</bodyPartGroup>
<thing>KKAO_GazelleHorn</thing>
<allowFemale>true</allowFemale>
</butcherBodyPart>
</value>
</li>


Here the patch I currently using. Noticed the "li[3]" in the xpath.

This is what I wanted to change for something more precise, but vanilla corresponding code is looking like:

    <lifeStages>
      <li>
            ......
      </li>
      <li>
            ......
      </li>
      <li>
        <bodyGraphicData>
          <texPath>Things/Pawn/Animal/Gazelle/Gazelle</texPath>
          <drawSize>1.65</drawSize>
          <shadowData>
            <volume>(0.4, 0.4, 0.4)</volume>
            <offset>(0,0,-0.2)</offset>
          </shadowData>
        </bodyGraphicData>
        <dessicatedBodyGraphicData>
          <texPath>Things/Pawn/Animal/Dessicated/CritterDessicatedMedium</texPath>
          <drawSize>1.65</drawSize>
        </dessicatedBodyGraphicData>
      </li>
    </lifeStages>


Thank you in advance for your help
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on December 13, 2017, 04:24:33 AM
Quote from: kaptain_kavern on December 13, 2017, 03:31:11 AM
Here the patch I currently using. Noticed the "li[3]" in the xpath.

You ideally want something that uniquely identifies the last item in the list. Is dessicatedBodyGraphicData absent from the remaining items in the list?

If so, you might use this, which find li based on the presence of the dessicatedBodyGraphicData element:


Defs/PawnKindDef[defName = "Gazelle"]/lifeStages/li[dessicatedBodyGraphicData]


Otherwise I'll pull the def out from core tonight and have a better look.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Boartato on July 20, 2018, 10:30:45 AM
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationReplace">
<xpath>*/RangedIndustrial[defName="Gun_Revolver"]/description</xpath>
<value>
<description>Why doesn't anything work?</description>
</value>
</Operation>
</Patch>


I've been struggling with Xpath for a solid 8 hours. Finally decided to stop trying to do anything meaningful and really see if I could get a basic Xpath down. This code above fails. Every tutorial I've read, if I'm understanding things correctly, says this should work. This tutorial in particular would be the one closest to what I'm trying to do: https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on July 21, 2018, 03:29:51 AM
Quote from: Boartato on July 20, 2018, 10:30:45 AM
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationReplace">
<xpath>*/RangedIndustrial[defName="Gun_Revolver"]/description</xpath>
<value>
<description>Why doesn't anything work?</description>
</value>
</Operation>
</Patch>


I've been struggling with Xpath for a solid 8 hours. Finally decided to stop trying to do anything meaningful and really see if I could get a basic Xpath down. This code above fails. Every tutorial I've read, if I'm understanding things correctly, says this should work. This tutorial in particular would be the one closest to what I'm trying to do: https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782
I assume you're trying to edit Gun_Revolver in Core\Defs\ThingDefs_Misc\Weapons\RangedIndustrial.xml?

If so, RangedIndustrial is the file name, not the name of the second element. I recommend you try:

<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationReplace">
<xpath>Defs/ThingDef[defName="Gun_Revolver"]/description</xpath>
<value>
<description>Why doesn't anything work?</description>
</value>
</Operation>
</Patch>

For anyone else visiting, I pulled the def from core, here it is for reference:

<ThingDef ParentName="BaseHumanMakeableGun">
  <defName>Gun_Revolver</defName>
  <label>revolver</label>
  <description>An ancient pattern double-action revolver. It's not very powerful, but has a decent range for a pistol and is quick on the draw.</description>
  <graphicData>
    <texPath>Things/Item/Equipment/WeaponRanged/Revolver</texPath>
    <graphicClass>Graphic_Single</graphicClass>
  </graphicData>
  <soundInteract>Interact_Revolver</soundInteract>
  <statBases>
    <WorkToMake>4000</WorkToMake>
    <Mass>1.4</Mass>
    <AccuracyTouch>0.80</AccuracyTouch>
    <AccuracyShort>0.75</AccuracyShort>
    <AccuracyMedium>0.45</AccuracyMedium>
    <AccuracyLong>0.35</AccuracyLong>
    <RangedWeapon_Cooldown>1.6</RangedWeapon_Cooldown>
  </statBases>
  <weaponTags>
    <li>SimpleGun</li>
  </weaponTags>
  <costList>
    <Steel>30</Steel>
    <ComponentIndustrial>2</ComponentIndustrial>
  </costList>
  <recipeMaker>
    <skillRequirements>
      <Crafting>3</Crafting>
    </skillRequirements>
  </recipeMaker>
  <verbs>
    <li>
      <verbClass>Verb_Shoot</verbClass>
      <hasStandardCommand>true</hasStandardCommand>
      <defaultProjectile>Bullet_Revolver</defaultProjectile>
      <warmupTime>0.3</warmupTime>
      <minRange>1.5</minRange>
      <range>25.9</range>
      <soundCast>Shot_Revolver</soundCast>
      <soundCastTail>GunTail_Light</soundCastTail>
      <muzzleFlashScale>9</muzzleFlashScale>
    </li>
  </verbs>
  <tools>
    <li>
      <label>grip</label>
      <capacities>
        <li>Blunt</li>
      </capacities>
      <power>9</power>
      <cooldownTime>2</cooldownTime>
    </li>
    <li>
      <label>barrel</label>
      <capacities>
        <li>Blunt</li>
        <li>Poke</li>
      </capacities>
      <power>9</power>
      <cooldownTime>2</cooldownTime>
    </li>
  </tools>
</ThingDef>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Boartato on July 21, 2018, 11:21:52 AM
Thank you very much! I was definitely including filenames in the path, I didn't realize they don't count as nodes. That said: Holy crap it works! Taking what you told me, I also got my original experiment to work as well!!!

<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationAdd">
<xpath>Defs/ThingDef[defName="Human"]/recipes</xpath>
<value>
<li>Boart_InstallPogLeg</li>
</value>
</Operation>
</Patch>


You're the best :)

One thing that confuses me: Why is it /Defs/ThingDef when it's stored in Defs/ThingDefs_Misc/Weapons ? Every tutorial I've read says "where the file is stored" and yet there isn't even a folder called ThingDef. Would recipes like my little surgery mock up be stored in Defs/RecipeDef then?

Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on July 21, 2018, 11:42:06 AM
The file path is completely irrelevant to the expression you're writing. XPath is used to search XML content, not files themselves. Defs is the root node (inside the XML document), and ThingDef is often the node you're most interested in editing in this case.

There was a twist with the previous versions, the game would patch a file at a time. At that point you might attempt to optimize a little, pushing more complex expressions behind simpler ones which targetted the right file first. In version 1.0 this changes. Everything is loaded into memory first, then patches are applied to the composite in memory.

In short, completely ignore the file name, and the directories it resides in. Concentrate only on the content within the files.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Boartato on July 21, 2018, 03:54:50 PM
Thanks again! This was a huge help.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: tcjaime on July 23, 2018, 09:17:32 AM
Help!  I'm new to patching and I'm stuck. Can someone tell me why this is changing all the power consumptions (ex wind gen) to 5 instead of just the lamp? Thank you!

<Patch>
   <Operation Class="PatchOperationReplace">
   <xpath>/Defs/ThingDef["StandingLampBase"]/comps/li["CompProperties_Power"]/basePowerConsumption</xpath>
      <value>
         <basePowerConsumption>5</basePowerConsumption>
      </value>
   </Operation>
</Patch>[/size][/size]
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: TeflonJim on July 23, 2018, 09:23:54 AM
Quote from: tcjaime on July 23, 2018, 09:17:32 AM
Help!  I'm new to patching and I'm stuck. Can someone tell me why this is changing all the power consumptions (ex wind gen) to 5 instead of just the lamp? Thank you!
You need to make a comparison inside your expression or it'll always evaluate to true.
<xpath>/Defs/ThingDef[defName="StandingLampBase"]/comps/li["CompProperties_Power"]/basePowerConsumption</xpath>
You'll need something similar for CompProperties_Power, but I don't have rimworld on this computer to check exactly what that should be.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: tcjaime on July 23, 2018, 09:55:36 AM
Fixed!  Thank you all. 

Here's the correct version:

<?xml version="1.0" encoding="utf-8" ?>
<Patch>
   <Operation Class="PatchOperationReplace">
   <xpath>*/ThingDef[@Name="StandingLampBase"]/comps/li[@Class="CompProperties_Power"]/basePowerConsumption</xpath>
      <value><basePowerConsumption>5</basePowerConsumption></value>
   </Operation>
</Patch>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Boartato on July 23, 2018, 07:45:43 PM
<?xml version="1.0" encoding="utf-8" ?>

<Patch>
<Operation Class="PatchOperationReplace">
<xpath>Defs/RecipeDef[defName="CookMealBase"]/workSkill</xpath>
<value>
<workSkill>Intellectual</workSkill>
</value>
</Operation>
</Patch>


Is there some semantics related to abstract classes that's needed for xpath? Changing only CookMealBase to CookMealSimple I can edit things like the labels, but if I try to do it on CookMealBase it errors continually.

https://rimworldwiki.com/wiki/Modding_Tutorials/XML_file_structure

This tutorial seems to suggest that abstract classes cease to exist after loading, and I seem to remember relatively recently that Xpathing happens after loading now instead of before. Would that mean that instead of being able to Xpath an abstract, I will have to xpath each file that inherits the abstract instead?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Haplo on July 24, 2018, 01:12:25 PM
Ok, I have a strange problem here.
Can somebody tell me why this patch works for 'Bow_' but not for 'Gun_'?


<!-- Check if <comps /> exists. If not, add it -->
<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationTest">
<xpath>*/ThingDef[starts-with(defName, 'Gun_')]/comps</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationAdd">
<xpath>*/ThingDef[starts-with(defName, 'Gun_')]</xpath>
<value>
<comps />
</value>
</li>
</operations>
</Operation>

<!-- Find entries and add the new comp -->
<Operation Class="PatchOperationAdd">
<xpath>*/ThingDef[starts-with(defName, 'Gun_')]/comps</xpath>
<value>
<li Class="WeaponRepair.CompProperties_WeaponRepairTwo2One">
<worktableDefs>
<li>TableMachining</li>
</worktableDefs>
<allowedDefSubName>Gun_</allowedDefSubName>
<compareQuality>false</compareQuality>
<jobDef>WeaponRepairTwo2One</jobDef>
<workTypeDef>Smithing</workTypeDef>
<maxRepair>0.95</maxRepair>
</li>
</value>
</Operation>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Boartato on July 24, 2018, 04:13:08 PM
After hours of racking my brains, googling and testing:

I can modify workSkill in CookMealSimple
I cannot modify workSkill in CookMealBase (abstract limitation maybe...?)
I cannot modify defaultIngredientFilter in CookMealSimple.

Edit: Found my answer. My tutorials were wrong or I misread them, xpath is not applied after inheritance at all. I still can't modify abstract classes, but the reason why trying to add to the defaultIngredientFilter was failing is because it didn't exist yet (not yet inherited from parent). However if I create one in the CookMealSimple, it adds anything in its filter to the one it inherits from base, so I'll take it. Still feels hacky needing to do it to every meal individually, but eh.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mehni on July 25, 2018, 02:45:14 AM
@Haplo -- Don't know how it is in 1.0 but at least in B18 PatchOperationSequences were limited to one XML-file, for some reason unknown to me. All Defs starting with Bow_ reside in one file, whereas the Gun_ is spread out over four. Maybe target the parent instead? Read on:

@Boartato -- tcjaime had the exact same issue one page ago and posted the solution on this page. If you want to target by attribute, such as Name="StandingLampBase", you use @Name="StandingLampBase". The @ is one of the many special symbols in xpath; in this case it targets attributes. * would be a wildcard. See also https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782#patching-abstracts
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Haplo on July 25, 2018, 06:52:25 AM
Thanks for the info. That might be the reason. Strange as it is...
I've now added it to the weapon base for the Gun_ part and there it works :)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Boartato on July 25, 2018, 08:12:48 AM
Quote from: Mehni on July 25, 2018, 02:45:14 AM
@Haplo -- Don't know how it is in 1.0 but at least in B18 PatchOperationSequences were limited to one XML-file, for some reason unknown to me. All Defs starting with Bow_ reside in one file, whereas the Gun_ is spread out over four. Maybe target the parent instead? Read on:

@Boartato -- tcjaime had the exact same issue one page ago and posted the solution on this page. If you want to target by attribute, such as Name="StandingLampBase", you use @Name="StandingLampBase". The @ is one of the many special symbols in xpath; in this case it targets attributes. * would be a wildcard. See also https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782#patching-abstracts

Thanks! I actually ran across that naming convention, but didn't realize why it was being used. That link in particular is awesome, I don't know how I haven't found it but that'll be very useful! And I'll remember haplo's lesson as well :)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Chehalden on October 27, 2018, 09:24:26 PM
I am trying to make a small change to the standing lamp but the section I need to change doesn't appear to have any easy unique identifiers (no defname)

This is the section I am trying to make a change to, the <basePowerConsumption> specifically.
  <ThingDef Abstract="True" Name="StandingLampBase" ParentName="LampBase">
    <graphicData>
      <texPath>Things/Building/Furniture/LampStanding</texPath>
    </graphicData>
    <costList>
      <Steel>20</Steel>
    </costList>
    <statBases>
      <Mass>4</Mass>
    </statBases>
    <comps>
      <li Class="CompProperties_Power">
        <compClass>CompPowerTrader</compClass>
        <basePowerConsumption>75</basePowerConsumption>
      </li>
    </comps>
  </ThingDef>


I cannot for the life of me figure out how to specifically target this section based on the <ThingDef> for this. There is <defName> in this section, it is in another child section. This section is also a child of another.

This has been my latest attempt hoping i could get into the right section

<Operation Class="PatchOperationReplace">
   <xpath>/Defs/ThingDefs_Buildings[texPath = "Things/Building/Furniture/LampStanding"]/comps</xpath>
     <value>
      <li Class="CompProperties_Power">
        <compClass>CompPowerTrader</compClass>
        <basePowerConsumption>30</basePowerConsumption>
      </li>
     </value>
   </Operation>
   


I have a feeling there is something simple I am missing as I am new to XPATH stuff. I was able to use the patch method before when I was working on an animal, but that one had a clear easy defName to work off of.

Any help would be greatly appreciated
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AileTheAlien on October 28, 2018, 08:51:23 AM
You can target other stuff besides just what tag to look for, like attributes on the tag, child tags, siblings, etc. (You'll probably want to read some tutorials on this stuff; It helped me out a lot!) For your specific case, I believe this would work (unless I made a typo :P ):

<?xml version="1.0" encoding="utf-8"?>
<Patch>
    <Operation Class="PatchOperationReplace">
        <xpath>/Defs/ThingDef[@Name="StandingLampBase"]/comps/li[@Class="CompProperties_Power"]/basePowerConsumption</xpath>
        <value>
            <basePowerConsumption>30</basePowerConsumption>
        </value>
    </Operation>
</Patch>


Note that your method of looking for a thing that has a graphicData tag (you missed this part), which has a texPath tag, which has a specific value, could also work; It's just a bit more complicated than you need in this case. (Although, that type of patch is definitely useful; I had to do something weird like that, plus or minus child / grandparent / whatever tags, although I cannot for the life of me, remember which of my mods it was in...)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Chehalden on October 28, 2018, 01:32:04 PM
@AileTheAlien -- Awesome it is working perfectly! Thank you for showing me how to use @attributes correctly. That really simplifies what I was trying to do.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: dninemfive on October 28, 2018, 06:34:06 PM
How can I target specific list items by the value they hold? I'm trying to remove the tag <li>Western</li> from other tags. I know I could just overwrite the tags, but I want to avoid potential mod compatibility issues.

the relevant code is as follows:


<tags>
    <li>Industrial</li>
    <li>Western</li>
    <li/> <!-- &c. -->
</tags>


And the patch I've tried is as follows:


<Operation Class="PatchOperationRemove">
   <xpath>*/<correct path>/tags/li[*="Western"]</xpath>
</Operation>


(Note that any syntax errors in this example code are not present in mine; the file loads properly and I've confirmed that the path is correct.)

I've also tried replacing the '*' in my code with a '.', something I saw somewhere in some xpath tutorial, but that doesn't work either.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AileTheAlien on October 30, 2018, 08:29:20 PM
You want to use text() (https://stackoverflow.com/a/2994336), so the last part would be li[text()='Western']
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: dninemfive on October 31, 2018, 12:52:00 AM
Thanks! That worked great.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Dr_Zhivago on November 14, 2018, 05:29:01 AM
Edit: Got help from the Discord
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Distman on January 30, 2019, 01:28:48 PM
So, i'm just a dabbler in the mystical arts of xpath.
Simple operations i have no problem with, but when messing with defs with a lot of lists and children my mind explodes.

What i want to do is patch a bodypart in a body with an added <Groups> node.
path is (i think): BodyDef/Human/corePart/parts/li/Neck/parts/li/Head/parts/li/Nose/groups
Defined in Bodies_Humanlike.xml

Should look something like this? Havn't tried it yet...
<Operation Class="PatchOperationAdd">
<xpath>*/BodyDef[defName="Human"]/corePart/parts/li[.="Neck"]/parts/li[.="Head"]/parts/li[.="Nose"]/groups</xpath>
<value>
<li>Face</li>
</value>
</Operation>


I guess my question is; can i use wildcards to reach the node easier?
I see a lot of talk about *text(), but not fully understand how it works. (Just shooting from the hip here...)
<Operation Class="PatchOperationAdd">
<xpath>*/BodyDef[defName="Human"]/*[text("Nose")]/groups</xpath>
<value>
<li>Face</li>
</value>
</Operation>


Thanks in advance!
/Distman
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mehni on January 31, 2019, 04:53:18 AM
Defs/BodyDef[defName="Human"]/corePart/parts/li[def="Neck"]/parts/li[def="Head"]/parts/li[def="Nose"]/groups

Defs/BodyDef[defName="Human"]/corePart/parts//li[def="Nose"]/groups

* is a wildcard that only skips a single node. // is a wildcard that looks in all nodes. Note that // is a LOT slower for general searches. In this case, since you're already a few nodes deep, the performance cost doesn't have that much impact.

For using text, the syntax is text() = "sometext". See also https://gist.github.com/Lanilor/e36af29ce5ce725b9b29768c63b00ef2#some-more-selectors and whatever other documentation you can find.

Xpath isn't a mystical art; it's a well documented standard that's been around since 1999. The more you read and learn about it, the more the mysticism disappears.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Distman on January 31, 2019, 01:45:59 PM
Thanks for the insight and link.
I had to do some more detective work since the parts are not children of def, rather the lists themselves.

Might be a quicker and easier way to solve this, but here is what i got if anybody else wanna patch bodies:
Defs/BodyDef[defName="Human"]/corePart/parts//li[def="Neck"]/ancestor:parts//li[def="Head"]/ancestor:parts//li[def="Nose"]/ancestor:groups

/Distman

Quote from: Mehni on January 31, 2019, 04:53:18 AM
Defs/BodyDef[defName="Human"]/corePart/parts/li[def="Neck"]/parts/li[def="Head"]/parts/li[def="Nose"]/groups

Defs/BodyDef[defName="Human"]/corePart/parts//li[def="Nose"]/groups

* is a wildcard that only skips a single node. // is a wildcard that looks in all nodes. Note that // is a LOT slower for general searches. In this case, since you're already a few nodes deep, the performance cost doesn't have that much impact.

For using text, the syntax is text() = "sometext". See also https://gist.github.com/Lanilor/e36af29ce5ce725b9b29768c63b00ef2#some-more-selectors and whatever other documentation you can find.

Xpath isn't a mystical art; it's a well documented standard that's been around since 1999. The more you read and learn about it, the more the mysticism disappears.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: blohod on February 01, 2019, 08:37:36 PM
I'm trying to patch factiondefs so that they use my animal, donkeys, for their caravans as a pack animal. The patch is failing and I can't work out why.


<Operation Class="PatchOperationAdd">
  <xpath>*/FactionDef[defName="TribeBase"]/pawnGroupMakers/li[5]/carriers</xpath>
  <value>
<Donkey>6</Donkey>
  </value>
</Operation>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Pelador on February 02, 2019, 09:55:19 AM
Quote from: blohod on February 01, 2019, 08:37:36 PM
I'm trying to patch factiondefs so that they use my animal, donkeys, for their caravans as a pack animal. The patch is failing and I can't work out why.


<Operation Class="PatchOperationAdd">
  <xpath>*/FactionDef[defName="TribeBase"]/pawnGroupMakers/li[5]/carriers</xpath>
  <value>
<Donkey>6</Donkey>
  </value>
</Operation>


You are patching a parent abstract so will need to use [@Name="TribeBase"].

To potentially search for all carriers despite their list order and also in case other mods add other trader groups you could also use the open-ended pathing of "//" to select any occurrence below the current node rather than targeting a specific node numerically. Shouldn't effect performance too drastically at the level you intend to patch. Though it makes an assumption that donkeys should be added to all carriers definitions that occur. Otherwise, using the literal numercial reference it would be sensible to check that the carrier node exists for the listing you're referencing and apply a conditional test as part of a patch sequence to ensure that the addition doesn't fall over due to the absence of the carrier node not being in it, should the listing order change due to other modding additions/changes.

"A" solution in simplified form could then be:


<Operation Class="PatchOperationAdd">
<xpath>*/FactionDef[@Name="TribeBase"]/pawnGroupMakers//carriers</xpath>
<value>
<Donkey>6</Donkey>
</value>
        </Operation>


otherwise with the specific node:


    <Operation Class="PatchOperationSequence">
    <success>Always</success>
    <operations>
      <li Class="PatchOperationTest">
            <xpath>*/FactionDef[@Name = "Tribebase"]/pawnGroupMakers/li[5]/carriers</xpath>
      </li>
<li Class="PatchOperationAdd">
<xpath>*/FactionDef[@Name="TribeBase"]/pawnGroupMakers/li[5]/carriers</xpath>
<value>
<Donkey>6</Donkey>
</value>
         </li>
         </operations>
        </Operation>


This won't show you an error with the success is always true set, but then it won't upset players either in the case it fails, but the sequence won't attempt to incorrectly patch the carriers node with your addition if it does not exist, then causing an error without the test. (You can then toggle/comment out the success part for your own debugging purposes of course). This can remedially remove some compatibility issues with other mods just in case someone does attempt to mod and change the ordering for the definitions. (Which would be naughty of course. ;) )
It'll be self-evident to the player if he doesn't see donkeys on tribal traders to then know there's a potential issue with the patching and perhaps his definitions and use of others mods. This might be over-engineering for purposes, but it does at least add one catch to a potential issue. I'll let you decide the merits/relevance of its use.

(Also ensure you are putting the patch file into a mod "Patches" folder with the XML tags of <Patch></Patch>)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mat1az on February 05, 2019, 03:20:41 PM
Hello, guys. May you help me with my problem?

I'm try to modify a Gun_Autopistol stats after it's been patched by Combat Extended, and catch a problem here. This code works as intended:


<li Class="PatchOperationReplace">
<xpath>/Defs/ThingDef[defName = "Gun_Autopistol"]/verbs/li[@Class = "CombatExtended.VerbPropertiesCE"]</xpath>
<value>
<defaultProjectile>Bullet_9mmPistol_FMJ</defaultProjectile>
</value>
</li>

<li Class="PatchOperationReplace">
<xpath>/Defs/ThingDef[defName = "Gun_Autopistol"]/verbs/li[@Class = "CombatExtended.VerbPropertiesCE"]/warmupTime</xpath>
<value>
<warmupTime>0.5</warmupTime>
</value>
</li>

<li Class="PatchOperationReplace">
<xpath>/Defs/ThingDef[defName = "Gun_Autopistol"]/verbs/li[@Class = "CombatExtended.VerbPropertiesCE"]/range</xpath>
<value>
<range>16</range>
</value>
</li>


And with following code game itself and savegame load without problems, but then I trying to spawn Autopistol throught devconsole, I'm catch System.MissingMethodException.

Problematic code:

<li Class="PatchOperationReplace">
<xpath>/Defs/ThingDef[defName = "Gun_Autopistol"]/verbs/li[@Class = "CombatExtended.VerbPropertiesCE"]</xpath>
<value>
<li>
<defaultProjectile>Bullet_9mmPistol_FMJ</defaultProjectile>
<warmupTime>0.5</warmupTime>
<range>16</range>
</li>
</value>
</li>


Error log:
Could not instantiate Verb (directOwner=CompEquippable(parent=Gun_Autopistol109808 at=(-1000, -1000, -1000))): System.MissingMethodException: Cannot create an abstract class 'Verse.Verb'.
  at System.Activator.CheckAbstractType (System.Type type) [0x00000] in <filename unknown>:0
  at System.Activator.CreateInstance (System.Type type, Boolean nonPublic) [0x00000] in <filename unknown>:0
  at System.Activator.CreateInstance (System.Type type) [0x00000] in <filename unknown>:0
  at Verse.VerbTracker.<InitVerbsFromZero>m__1 (System.Type type, System.String id) [0x00000] in <filename unknown>:0
  at Verse.VerbTracker.InitVerbs (System.Func`3 creator) [0x00000] in <filename unknown>:0
Verse.Log:Error(String, Boolean)
Verse.VerbTracker:InitVerbs(Func`3)
Verse.VerbTracker:InitVerbsFromZero()
Verse.VerbTracker:get_PrimaryVerb()
Verse.CompEquippable:get_PrimaryVerb()
CombatExtended.CompFireModes:get_Verb()
CombatExtended.CompFireModes:InitAvailableFireModes()
Verse.LongEventHandler:ExecuteToExecuteWhenFinished()
Verse.LongEventHandler:ExecuteWhenFinished(Action)
CombatExtended.CompFireModes:Initialize(CompProperties)
Verse.ThingWithComps:InitializeComps()
Verse.ThingWithComps:PostMake()
Verse.ThingMaker:MakeThing(ThingDef, ThingDef)
Verse.DebugThingPlaceHelper:DebugSpawn(ThingDef, IntVec3, Int32, Boolean)
Verse.<DoListingItems_MapTools>c__AnonStorey38:<>m__0()
Verse.DebugTool:DebugToolOnGUI()
Verse.DebugTools:DebugToolsOnGUI()
RimWorld.UIRoot_Play:UIRootOnGUI()
Verse.Root:OnGUI()


Besides, this code successfully changes at least a "warmupTime" - I can see this ingame in Autopistol description:
https://imgur.com/a/5Vgi4YJ (https://imgur.com/a/5Vgi4YJ)

Principally, I may use first code as well, but I'd would like to use as most short code as it possible, of course.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on February 06, 2019, 10:54:11 AM
Quote from: Pelador on February 02, 2019, 09:55:19 AM
[/code]
    <Operation Class="PatchOperationSequence">
    <success>Always</success>
    <operations>
      <li Class="PatchOperationTest">
            <xpath>*/FactionDef[@Name = "Tribebase"]/pawnGroupMakers/li[5]/carriers</xpath>
      </li>
   <li Class="PatchOperationAdd">
      <xpath>*/FactionDef[@Name="TribeBase"]/pawnGroupMakers/li[5]/carriers</xpath>
      <value>
          <Donkey>6</Donkey>
      </value>
         </li>
         </operations>
        </Operation>
[/code]

I believe you could also do this?  Which should run faster (starts with /Defs, instead of searching all nodes for "FactionDef"), be more robust (if something in the xml changes ever ><), and do it all in one step:


<Operation Class="PatchOperationAdd">
  <success>Always</success>
  <xpath>/Defs/FactionDef[@Name="TribeBase"]/pawnGroupMakers/li/carriers</xpath>
  <value>
    <Donkey>6</Donkey>
  </value>
</Operation>


Heck...why can't non-tribals use donkeys too?  We use them now in Grand Canyon tours in the US!  Just take out the entire [@Name="..."] identifier in the <xpath> and it will patch everyone in one go!

As I wrote it, if - for some reason - someone wiped out tribals, because success is always, your mod would silently fail.  I might tend to leave it out, just so there's warning of an incompatibility, but in this case might leave it in.

Hope that helps,

--LWM

PS - industrial factions too, plz?  ^.^
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on February 06, 2019, 11:25:49 AM
Quote from: Mat1az on February 05, 2019, 03:20:41 PM
Hello, guys. May you help me with my problem?
Your first code works, your second code doesn't?

Does this work?


<li Class="PatchOperationReplace">
<xpath>/Defs/ThingDef[defName = "Gun_Autopistol"]/verbs/li[@Class = "CombatExtended.VerbPropertiesCE"]</xpath>
<value>
<li Class="CombatExtended.VerbPropertiesCE">
<defaultProjectile>Bullet_9mmPistol_FMJ</defaultProjectile>
<warmupTime>0.5</warmupTime>
<range>16</range>
</li>
</value>
</li>


--LWM
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mat1az on February 06, 2019, 04:13:30 PM
Quote from: LWM on February 06, 2019, 11:25:49 AM
Does this work?

Nope. I've tried this already - game loaded without errors, but stats ingame didn't altered.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on February 06, 2019, 06:15:02 PM
Quote from: Mat1az on February 06, 2019, 04:13:30 PM
Nope. I've tried this already - game loaded without errors, but stats ingame didn't altered.
Ah, apologies.  The code you posted didn't have the Class="..." in the <li>, so I thought that might be the problem.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Pelador on February 06, 2019, 07:40:08 PM
Verbs are a fickle characteristic of a thingdef, you cannot change them on an item in a dynamic way IG. Once an item is made it will retain the verbs it has at the time of its creation. You will probably find that the xpath to change the CE has worked, but you may not see these change on an existing item. If you created a new one however you may then see these new values.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: mcduff on February 15, 2019, 02:05:57 PM
Hi, I'm slowly losing my mind over something which feels like it should be incredibly simple and yet does not work.

It's just an experiment to see if I can get pawns to be able to wear parkas over armor, for obvious reasons. All I'm doing is taking out "shell" from the layers so they appear on the middle layer only.

If I edit the code directly it works, but I can't seem to make a patch that does it.

  <Operation Class="PatchOperationReplace">
    <xpath>Defs/ThingDef[defname = "Apparel_PlateArmor"]/apparel/layers</xpath>
    <value>
      <layers>
        <li>Middle</li>
      </layers>
    </value>
  </Operation>


This errors out - what obvious, simple thing am I missing??
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Pelador on February 16, 2019, 01:36:17 AM
Quote from: mcduff on February 15, 2019, 02:05:57 PM
Hi, I'm slowly losing my mind over something which feels like it should be incredibly simple and yet does not work.

It's just an experiment to see if I can get pawns to be able to wear parkas over armor, for obvious reasons. All I'm doing is taking out "shell" from the layers so they appear on the middle layer only.

If I edit the code directly it works, but I can't seem to make a patch that does it.

  <Operation Class="PatchOperationReplace">
    <xpath>Defs/ThingDef[defname = "Apparel_PlateArmor"]/apparel/layers</xpath>
    <value>
      <layers>
        <li>Middle</li>
      </layers>
    </value>
  </Operation>


This errors out - what obvious, simple thing am I missing??

You have your patch file in a mod "Patches" folder with the following XML tags?

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


Otherwise, it might be a really simple thing like not having the root path in front of the Defs in your xpath definition:

So  "<xpath>/Defs/ThingDef[.........."

I tend to use the more open-ended method of "<xpath>*/ThingDef[......."

The rest of the xpath syntax looks ok to me. If not any of the above things, be interested to know then what the error message is.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: mcduff on February 16, 2019, 04:42:50 AM
I've tried Def, /Def, */, and anything else I can think of.

This is what shows up in the error log. It doesn't seem helpful.

[One-Layer Armour] Patch operation Verse.PatchOperationReplace(*/ThingDef[defname = "Apparel_PlateArmor"]/apparel/layers) failed
file: /Users/xxxx/Library/Application Support/Steam/steamapps/common/RimWorld/RimWorldMac.app/Mods/Armourpatch/Patches/patch.xml
Verse.Log:Error(String, Boolean)
Verse.PatchOperation:Complete(String)
Verse.LoadedModManager:ClearCachedPatches()
Verse.LoadedModManager:LoadAllActiveMods()
Verse.PlayDataLoader:DoPlayLoad()
Verse.PlayDataLoader:LoadAllPlayData(Boolean)
Verse.Root:<Start>m__1()
Verse.LongEventHandler:RunEventFromAnotherThread(Action)
Verse.LongEventHandler:<UpdateCurrentAsynchronousEvent>m__1()

Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: mcduff on February 16, 2019, 04:51:11 AM
This is my full "patch.xml" file

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

<Patch>

  <Operation Class="PatchOperationReplace">
    <xpath>*/ThingDef[defname = "Apparel_PlateArmor"]/apparel/layers</xpath>
    <value>
      <layers>
        <li>Middle</li>
      </layers>
    </value>
  </Operation>
 
    <Operation Class="PatchOperationReplace">
    <xpath>*/ThingDef[defname = "Apparel_PowerArmor"]/apparel/layers</xpath>
    <value>
      <layers>
        <li>Middle</li>
      </layers>
    </value>
  </Operation>
 

</Patch>
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mehni on February 16, 2019, 05:43:05 AM
defName

Capital N. XPath is case sensitive.

In regards to */ vs Defs/: Defs/ is a few percent quicker, and thus preferable. RimWorld forces all rootnodes to be Defs anyway, so the * wildcard just slows things down needlessly.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: mcduff on February 16, 2019, 04:33:30 PM
Thank you! I knew it would be something ridiculous like that but I couldn't see it for looking.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: WereCat88 on February 17, 2019, 09:07:00 AM
I am patching a mod that uses thingDef instead of ThingDef in certain places and i need it to use the other one, i tried this:
<li Class="PatchOperationSetName">
<xpath>Defs/thingDef[defName="ExampleDef"]</xpath>
<name>ThingDef</name>
</li>


But it failed
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on February 17, 2019, 09:44:12 AM
Quote from: Mehni on February 16, 2019, 05:43:05 AM
In regards to */ vs Defs/: Defs/ is a few percent quicker, and thus preferable. RimWorld forces all rootnodes to be Defs anyway, so the * wildcard just slows things down needlessly.

Wouldn't Defs/ be just about as slow?  /Defs/ should be much quicker, no?  As it starts at the root, instead of searching for a field named Defs anywhere in the xml tree?

--LWM
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: AileTheAlien on February 17, 2019, 02:41:49 PM
Always use fully-qualified paths if you have them, and put any type of wild-card or searching behaviour as close to the end as possible, to limit the size of tree to search through.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mehni on February 17, 2019, 02:49:13 PM
Quote from: LWM on February 17, 2019, 09:44:12 AM
Wouldn't Defs/ be just about as slow?  /Defs/ should be much quicker, no?  As it starts at the root, instead of searching for a field named Defs anywhere in the xml tree?

--LWM

apparently not: https://ludeon.com/forums/index.php?topic=32874.msg382089#msg382089

Quote from: WereCat88 on February 17, 2019, 09:07:00 AM
I am patching a mod that uses thingDef instead of ThingDef in certain places and i need it to use the other one, i tried this:
<li Class="PatchOperationSetName">
<xpath>Defs/thingDef[defName="ExampleDef"]</xpath>
<name>ThingDef</name>
</li>


But it failed


I know some people are going to hate me for being lazy, but.. <xpath>Defs/*[defName="ExampleDef"]</xpath>

Only do that if you're certain there are no other types of Defs with that defName. As an example: Pemmican is the defName for both the research project and the ThingDef. Note that will also overwrite any attributes.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on February 18, 2019, 12:08:20 AM
Well THAT's some methodology I'd like to see ^.^

What did they do, restart RimWorld 1000 times each way?

--LWM
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mehni on February 18, 2019, 03:26:54 AM
Verbose logging, probably.

RimWorld loading has changed (quite substantially) since then so I'd like to see a repeat of that benchmark.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: WereCat88 on February 18, 2019, 07:52:35 AM
Does xpathing using set name delete the attribute of the node being modified?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on February 20, 2019, 08:18:05 AM
Quote from: WereCat88 on February 18, 2019, 07:52:35 AM
Does xpathing using set name delete the attribute of the node being modified?

Depends on how you're modifying it?

If I knew exactly what you were planning, I'd have better idea.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on February 20, 2019, 08:19:00 AM
Quote from: Mehni on February 18, 2019, 03:26:54 AM
Verbose logging, probably.

RimWorld loading has changed (quite substantially) since then so I'd like to see a repeat of that benchmark.

Well, I know what my next mod is going to be!  Excuse me while I read up on best practices for measuring execution time in C#.

--LWM
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: EagleCall on March 21, 2019, 11:14:02 PM
Hi, I've been quietly lurking a bit but still pretty new and haven't posted before, so I hope this considered within the scope of this thread.

While I think I'm getting a decent handle on how XML modding works, I'm confused about PatchOperationTest and <success>Invert</success>.  I've forked an existing XML mod on Github to fix/tweak/mess with (well, several actually, but there's a specific one in question here), and it uses this method to patch things from other mods (that may or may not be there):

<Operation Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationTest">
<xpath>*/Defs/ThingDef[defName = "name of thing to patch"]</xpath>
<success>Invert</success>
</li>
<li Class="PatchOperationReplace">
<xpath>/Defs/ThingDef[defName = "name of thing to patch"]/path/to/node</xpath>
<value>
<node>patched stuff</node>
</value>
</li>
</operations>
</Operation>


But, from what I understand, that <success>Invert</success> actually cancels the patch if the thing to patch is found, rather than the intended cancel if not found?  And yet no one seems to have had problems with it, so how?  Is it wrong and just not noticed/reported?  I feel sure that must be the case, but to further complicate things, some of the stuff I've found while searching for answers makes the results of going without that line seem broken/erratic.  So how is it actually supposed to work when you are checking that there is a thing there rather than that there isn't?

Basically, I am now very confused and doubting myself here.  And almost feeling like it would be simpler to just switch to PatchOperationConditional match to be sure of things but...  not really.  Plus I don't know if I can nest a conditional within a conditional or how that would work, while I know I can nest a conditional within a sequence, and some situations require that sort of thing.  Ugg...



I'm also very interested in this whole */Defs/ vs. /Defs/ vs. Defs/ question, but that is a minor issue in comparison.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Mehni on March 22, 2019, 05:15:52 AM
Welcome to the forums.

The operation you posted is kinda weird.

There's a Sequence with a <success>Always</success> (https://rimworldwiki.com/wiki/Modding_Tutorials/PatchOperations#.22Success.22_option) tag. Nothing surprising so far: a sequence is often expected to "fail" at some point and setting <success>Always</success> will prevent "xpath failed" errors.

The "Test" has an invalid xpath, so this would always fail[1]. Inverting it's success means it'll always be considered successful, which in turn means the "Replace" does get executed. That one may or may not fail, but we'll never know. The <success>Always</success> in the sequence means the error is never reported.

It looks like you found a bug in the sequence. Congratulations, you have a better understanding of XPath than the one who wrote the patch sequence.

___
[1]: What you posted as a side question is actually central to the XPath failure: */Defs isn't a valid XPath for RimWorld's XML structure. * is a wildcard for "all nodes at this point". The way RimWorlds XML structure is set out is that the root (i.e. first) node of all Defs is <Defs>, and the first childnode is <SomeTypeOfDef> (e.g. <ThingDef>).

This structure (and enforcing) of "all rootnodes start with <Defs>" is relatively recent: before A17 or B18, rootnodes could be anything. There where things like a <Drugs> rootnode, a <ThingDefs> rootnode and even a <Beer> rootnode. That's why the * was practical: you didn't need to specify the exact rootnode. Now that's changed to a uniform <Defs> rootnode, the * wildcard isn't required anymore: the exact XML structure is known in advance.

TL;DR: */Defs/ThingDef points effectively points to Defs/Defs/ThingDef, which doesn't exist in RimWorld's XML.

*/ vs Defs/ vs /Defs/ practically only differs in speed.


More on-topic, or more to the point:

I'd use PatchOperationFindMod (https://rimworldwiki.com/wiki/Modding_Tutorials/PatchOperations#PatchOperationFindMod) to support other mods.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on March 22, 2019, 10:16:40 AM
Speed tests for Defs vs /Defs

Ok, I put together a little test that does an xpath patch operation a large number of times, and I found a few interesting results.

I would run one xpath operation, either Defs/ThingDef/etc or /Defs/ThingDef/etc a large number of times, then run the other one, and time them.  I consistently found that whichever I put 2nd in my tests was slower than whatever ran first.  I consistently found that (as I expected from reading the specification) /Defs/... - which is more specific than Defs/... - is slightly faster.

But it's only very very slightly, like <1%.

Using */... was slower, by about 10% on my system.  Which is still not huge, unless you have a slow computer and a LOT of mods.

A few caveats:
I was only running 10000 xpath operations.
I was running the same type of patch over and over - it's possible some internal library caching was speeding up the process?

You can see what I was using here:  https://github.com/lilwhitemouse/RimWorld-PatchSpeedTest

tldr; Use /Defs/...   If you have previously written mods using Defs/... don't go back and change things unless bored.  Prefer not to use */...

Alternate amusing tldr:  Disk space is cheap.  Use the extra "/"
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on March 22, 2019, 10:26:03 AM
Re: EagleCall's patch:

You can often just attempt the patch operation directly, without using a sequence.  If the xpath is valid, the patch is done.  If not (e.g., if the node doesn't exist because a specific mod hasn't been loaded), the patch isn't done, and no harm done.

So:

<Operation Class="PatchOperationReplace">
<success>Always</success><!-- Avoid unnecessary errors, don't scare users -->
<xpath>/Defs/ThingDef[defName = "name of thing to patch"]/path/to/node</xpath>
<value>
<node>patched stuff</node>
</value>
</Operation>


This may or may not be applicable to what you are trying, of course, but it's good to remember.

--LWM
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: EagleCall on March 22, 2019, 09:56:58 PM
:o  (blinks)  ...Wow, ok.  Lot to process, there.  Many thanks for both the welcome and the help!


That direct patch method is so much easier!  I think for most of these patches that would indeed be all it needs.  And that principle should also apply to other operations like Add, right?  If it can't find the thing or node you are adding to because the mod isn't there, it just fails (silently because of that success line)-- no magically "adding" the full xpath worth of empty stuff or whatever nonsense like that?  :P  Just checking, lol.

I did not catch or at all expect that bit about */Defs being outdated, but it makes so much sense now.  And I was already trying to watch out for outdated information, but stuff like that is so tricky and easily overlooked!  So, that xpath failure combined with the Invert problem just so to make it run anyway, like a direct patch?  Except... hmm... some of the actual Replace xpaths use */Defs, though only two or three and... I think those are actually unnecessary patches?  As in what they are patching in with Replace is what was already there, at least in the current version of the mod it's patching.  ???  So anyway wow this mod has some problems, more even than I originally thought.  I think it was probably an older mod or based on one, or copying old coding examples or something, and was "updated" (cough cough) for the game release.

Hmm, PatchOperationFindMod probably is better for most cases that need the check, although I think in some cases it may actually make sense to test for the things themselves?  IDK, really.  But in such a case, I wonder, would you use <success>Normal</success> on the Test or remove that line entirely?


(Pretends to crack knuckles)  Welp, anyway, looks like I have some work to do.  Thanks again!  ;D
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Trunken on March 30, 2019, 03:33:48 PM
Hey Doc,
i'm using patch files to change the labels from traits. This works.
Now here comes my problem, for some reason the description part changes too from german to english, even though i dont touch it at all :o

Whats going on? Is there any possible reason for this? I dont know to proceed and need your help

here is the code:

<Patch>
  <Operation Class="PatchOperationReplace">
    <xpath>/Defs/TraitDef[defName="TRAITNAME"]/degreeDatas/li/label</xpath>
    <value>
        <label>TEXT</label>
    </value>
  </Operation>
</Patch>

for a workaround, i added this lines to change description too,
but its not working...

Example for Slowpoke:

  <Operation Class="PatchOperationReplace">
    <xpath>/Defs/TraitDef[defName="SpeedOffset"]/degreeDatas/li[label="slowpoke"]/description</xpath>
    <value>
    <description>TEXTTEXTTEXT</description>
    </value>
  </Operation>

Where is my mistake?

Error Log:

[New_Mod] Patch operation Verse.PatchOperationReplace(/Defs/TraitDef[defName="SpeedOffset"]/degreeDatas/li[label="slowpoke"]/description) failed
file: C:\Steam\steamapps\common\RimWorld\Mods\New_Mod\Patches\Core_SpectrumPatch.xml
Verse.Log:Error(String, Boolean)
Verse.PatchOperation:Complete(String)
Verse.LoadedModManager:ClearCachedPatches()

please help me
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on April 29, 2019, 01:15:26 PM
Another xpath question:

Are "Parent" xml nodes added to defs before patching happens, or after?

That is, can I patch a thingDef's thingCategories if the thingCategories are defined in a different def with Abstract="true"?

If I can't, is there an easy way to ensure compatibility with other mods?

For example, if I want it to have thingCategory A, and someone else wants it to also have thingCategory B, but we neither of us want it to have its original category, can we both just add

  <!-- Patch Operation Add, correct xpath, etc-->
  <value>
    <thingCategories Inherit="false">
      <li>A</li>
    </thingCategories>
  </value>

Or will we step on each others' feet?  Has anyone done this?  EDIT: They step on each others' feet.

EDIT2: The example on the wiki here https://rimworldwiki.com/wiki/Modding_Tutorials/PatchOperations#PatchOperationConditional looks like EXACTLY this case.

So, original question: do Abstracts get loaded into defs before patching happens?

--LWM
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: jptrrs on August 08, 2019, 07:33:39 PM
Ok, so here's a though nut to crack.
I'm trying to use xml patch to remove some defs added by Fluffy's Stuffed Floors. He uses custom defs, like this one:

    <StuffedFloors.FloorTypeDef ParentName="StuffedFloorsWoodBasicBase">
    <defName>StuffedWoodFloorRoughHorizontal</defName>
    <label>rough horizontal planks</label>
    <description>A rough plank flooring.</description>
        <texturePath>Floors/Wood/WoodFloor2</texturePath>
        <obsoletes>
            <li>FloorWood1</li>
        </obsoletes>
    </StuffedFloors.FloorTypeDef>


I want to track and remove just some of them, but for the sake of clarity let's say I need all them gone. So I tried this, and it didn't work:

    <Operation Class="PatchOperationRemove">
        <xpath>/Defs/StuffedFloors.FloorTypeDef</xpath>
    </Operation>


also this:
    <Operation Class="PatchOperationRemove">
        <xpath>//StuffedFloors.FloorTypeDef</xpath>
    </Operation>


I figured RW could be hiding this custom defs somewhere, so I've been researching alternate methods to look for a xml node. I tried this as well, and it floped too:

<Operation Class="PatchOperationRemove">
<xpath>//[contains(local-name(),'StuffedFloors.FloorTypeDef')]</xpath>
</Operation>


Any thoughts?
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: LWM on August 08, 2019, 11:57:43 PM
Well, I would think it would work.

Are you getting any errors?

Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: jptrrs on August 09, 2019, 02:56:18 PM
That's what's wierd, I get SOME errors. Allow me explain the way his code works:
Those custom defs are used to generate some regular TerrainDefs based on what materials are in use. Say there's mod-created stone def, basalt for instance. It would be recognized as a new building material for a tiled stone floor, so Stuffed Floors would use his custom def as a base to generate a basalt stone floor. So, with those patches I wrote, the errors I get are saying things like: "basalt stone floor has no fertility rating", showing that info was really suppressed somehow. But the button to build the basalt stone floor remains active, and it can be built. Whereas if instead of trying to patch that def I simply delete it from the mod's xml files, that type of floor does vanish from the game. My conclusion is that there must be something wrong with my patch!
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: NinjaSiren on August 17, 2019, 07:29:09 AM
An xpath question,
Can PatchOperationFindMod be ok and not show errors, when the mod to find is disabled or not-installed, and without using C#/Assembly codes?

I am the current modder for Yes, Vehicles and there's a guy that keeps pushing me to try running the game where there will be automated mod compatibility using xpath and XML xpath only with or without the mod installed or enabled.

Link of the Combat Extended patch (https://github.com/NinjaSiren/Yes-Vehicles-finally/blob/master/Patches/CE_Patches.xml)
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: jptrrs on August 27, 2019, 10:29:32 AM
Quote from: NinjaSiren on August 17, 2019, 07:29:09 AM
An xpath question,
Can PatchOperationFindMod be ok and not show errors, when the mod to find is disabled or not-installed, and without using C#/Assembly codes?

I am the current modder for Yes, Vehicles and there's a guy that keeps pushing me to try running the game where there will be automated mod compatibility using xpath and XML xpath only with or without the mod installed or enabled.

Link of the Combat Extended patch (https://github.com/NinjaSiren/Yes-Vehicles-finally/blob/master/Patches/CE_Patches.xml)

Yes. Lots of mods do that.
Title: Re: XPATH surgery - Need help with xpath? post here.
Post by: Vakari on September 22, 2019, 06:27:16 PM
I'm leaving this post in case anyone else ever runs into this error.  Maybe it can help with debugging.  In addition, if anyone knows a more elegant way to combine both "<graphicData>" changes into one element, please let me know.  Anyway, I was able to figure this out for myself.  Here is what worked:

<?xml version="1.0" encoding="utf-8" ?>
<Patch>
   <Operation Class="PatchOperationReplace">
      <xpath>/Defs/ThingDef[defName="MineableUranium"]/graphicData/color</xpath>
      <value>
         <color>(255,249,105)</color>
      </value>
   </Operation>
   <Operation Class="PatchOperationReplace">
      <xpath>/Defs/ThingDef[defName="MineableUranium"]/graphicData/colorTwo</xpath>
      <value>
         <colorTwo>(240,178,17)</colorTwo>
      </value>
   </Operation>
</Patch>



First of all:  Thank you to the author of this post and everyone helping.  For a noob like myself, it's invaluable.

So, I'm trying to learn xpathing, and so I've read tons of articles, other posts, wikis, and looked at actual downloaded mods.  I think I've actually confused myself by information overload.

So, I thought I'd start small and just try a simple replace operation.  I want to change "color" and "colorTwo" values for the mineable uranium ore.

Everything I've tried causes errors.  I am sure there is a way to list both values in the same operation, but I can't make it work.  I've even tried separating each (color vs colorTwo) into separate operations.  I still get errors.  Would some generous soul please help me out?

EDIT:  The file with the change is:  Defs\ThingDefs_Buildings\Buildings_Natural.xml.

Again, thank you for any help!

Here is what I've tried last:


<?xml version="1.0" encoding="utf-8" ?>
<Patch>
   <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef/[defName = "MineableUranium"]/graphicData/color</xpath>
      <value>
            <color>(255,255,64)</color>
      </value>
   </Operation>
   
      <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef/[defName = "MineableUranium"]/graphicData/colorTwo</xpath>
      <value>
            <colorTwo>(240,178,17)</colorTwo>   
      </value>
   </Operation>
</Patch>


Here is the error log.  I've tried googling this but couldn't figure out what is wrong:

Error in patch.Apply(): System.Xml.XPath.XPathException: Error during parse of */ThingDef/[defName = "MineableUranium"]/graphicData/color ---> Mono.Xml.XPath.yyParser.yyException: irrecoverable syntax error
  at Mono.Xml.XPath.XPathParser.yyparse (yyInput yyLex) [0x00000] in <filename unknown>:0
  at Mono.Xml.XPath.XPathParser.Compile (System.String xpath) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at Mono.Xml.XPath.XPathParser.Compile (System.String xpath) [0x00000] in <filename unknown>:0
  at System.Xml.XPath.XPathExpression.Compile (System.String xpath, IXmlNamespaceResolver nsmgr, IStaticXsltContext ctx) [0x00000] in <filename unknown>:0
  at System.Xml.XPath.XPathExpression.Compile (System.String xpath) [0x00000] in <filename unknown>:0
  at System.Xml.XPath.XPathNavigator.Compile (System.String xpath) [0x00000] in <filename unknown>:0
  at System.Xml.XmlNode.SelectNodes (System.String xpath, System.Xml.XmlNamespaceManager nsmgr) [0x00000] in <filename unknown>:0
  at System.Xml.XmlNode.SelectNodes (System.String xpath) [0x00000] in <filename unknown>:0
  at Verse.PatchOperationReplace.ApplyWorker (System.Xml.XmlDocument xml) [0x00000] in <filename unknown>:0
  at Verse.PatchOperation.Apply (System.Xml.XmlDocument xml) [0x00000] in <filename unknown>:0
  at Verse.LoadedModManager.ApplyPatches (System.Xml.XmlDocument xmlDoc, System.Collections.Generic.Dictionary`2 assetlookup) [0x00000] in <filename unknown>:0
Verse.Log:Error(String, Boolean)
Verse.LoadedModManager:ApplyPatches(XmlDocument, Dictionary`2)
Verse.LoadedModManager:LoadAllActiveMods()
Verse.PlayDataLoader:DoPlayLoad()
Verse.PlayDataLoader:LoadAllPlayData(Boolean)
Verse.Root:<Start>m__1()
Verse.LongEventHandler:RunEventFromAnotherThread(Action)
Verse.LongEventHandler:<UpdateCurrentAsynchronousEvent>m__1()


Thank you in advance for any assistance with my learning this process!