• If you have a mod, tool or prefab, please use the Resources section. Click Mods at the top of the forums.

XPath Modding Explanation Thread

This is a large amount of information in these posts. I'll be working on a full, properly formed tutorial as we get access to A17 and we can really make the xpath system shine. I've listed some examples here, and we'll be posting a ton more xpath samples over the coming weeks, as we port the SDX modlets over to use the native hooks.

By all means, begin posting your comments, questions, or requests for clarity.

Since we now know that xpath support will be built-into the vanilla base game, and discussions earlier were getting a bit confusing, I've decided to make a new thread to try to demystify xpath and what it'll mean for mods and modders going forward in A17. The information in this thread has been pieced together from forum and discord discussions. They may not be 100% accurate, but I will update this thread when we know the full implementation.

XPath is a way of adding, changing, or removing XML lines and nodes, without directly editing the XML files. It allows you to apply patches to the XML files without manually editing the vanilla XML files.

SDX has had partial support for this since its initial release, and a lot of the SDX Modlets are already written this way. We will be using some of the SDX's xpath as examples in this thread, but SDX is not required to do this in A17 and onwards.

I believe in using examples to explain concept, so in the next few posts, you'll see a mixture of explanation, along with sample xpath code.

The following Mod structure is expected for A17 and beyond:

Code:
Mods/
   <ModName>/
       Config/
       UIAtlases/ItemIconAtlas/
       ModInfo.xml

   <ModName2>/
       Config/
       ModInfo.xml
You will be able to have multiple mods loaded, and loading will occur as long as the ModInfo.xml exists. This is unchanged from what we've been doing doing with other alphas, with the exception of the Config folder.

This Config folder will support loading XML files, written using xpath, in the following folder structure:

Code:
Config/entityclasses.xml
Config/gamestages.xml
Config/items.xml
The files in the Config folder will not be a full copy of the vanilla file with your changes. Rather, it'll contain the changes you want done to the vanilla files. The files under the Config must match the vanilla file name. You cannot create an entityclasses2.xml file, and expect it to work. Any changes in the entityclasses.xml will have to be done in the same file. However, each mod can have its own entityclasses.xml.

During the game's initialization, it will perform the xpath merge in-memory only; no files will be actually be modified. This would allow us to remove mods we no longer want, without re-validating against steam, or previous copies of the xml. That's a big one. No more half merging of a mod, and not having it work, then trying to pull it back out.

What this means for us is that we'll be able to make a variety of smaller mods, which I've been calling modlets, which can add, remove and change smaller pieces of the game. They can be used together, or they could be added to an overhaul mod, in order to style your game play easier.

These modlets would also exists outside of the Data/Config folder, so if you have made direct XML changes in your Alpha 17.1 Data/Config files, and Steam updated the game to 17.2, you would have lost your changes, or would have to re-merge them in. We've all been there before. But if they existed as modlets, under the Mods folder, they would be safe. And as long as your xpath is still valid to the new XML, it should load up with no additional work on your part.

If we could use xyth's JunkItems modlet, which adds random, scrappable junk items to loot containers, and add them to Valmod Overhaul. Likewise, if Valmod Overhaul did not have the No Ammo modlet (which gives you the ability to unload a gun and get its bullets back without disassembling it), you could drop the NoAmmo modlet into your Mods folder. Headshots only? Want to increase stack sizes? Same deal.

With a properly constructed modlet, we'll be able to piece together new play styles for people to enjoy and share. A modder working on a large overhaul won't have to duplicate work. If they wanted to include the No Ammo mod, they wouldn't have to code it themselves, letting them focus on the bits that make their mod really unique.

Let's get started on your journey...

 
Last edited by a moderator:
Good tut.
I have a question. Here is a set of commands I've converted to xpath. Now, if the property "MaxDamage" is not in the xml for the block with the name @name='so-and-so', will it automatically be added or nah? If nah, is there a function that will only add the property if it's not there already? And if there is no way to automatically add it only if it's not there, what if I added the property to a block that already has it, would my addition overwrite the existing property or add it a 2nd time (which should be illegal)?

Is there, in other words, a method that will either change a property, or, if the property does not even exist, add it, so I do not have to know if it's there or not?
Can be done by using operators. TheElement[not(@attribute= 'whatevervalue')]

This will skip (or use depending on what you want) elements that do not contain the attribute or contain it but dont have expected value.

Cheers

-edit- all depending on xpath functions implemented by TFP of course.

 
Last edited by a moderator:
Can be done by using operators. TheElement[not(@attribute= 'whatevervalue')]
This will skip (or use depending on what you want) elements that do not contain the attribute or contain it but dont have expected value.

Cheers

-edit- all depending on xpath functions implemented by TFP of course.
I don't want it to skip if the property is already there. If it's already there, the value should be changed. If the property is not already there, it should be added with the value. For example:

Code:
<block id="150" name="woodFrameWedge">
<property name="Extends" value="woodFrameMaster"/>
<property name="CreativeMode" value="Player"/>
<property name="Shape" value="New"/>
<property name="Model" value="wedge60"/> <property name="Place" value="TowardsPlacerInverted"/>
<property name="CanPickup" value="true"/>
<property class="UpgradeBlock"> <property name="ToBlock" value="woodWedge"/>	</property>
</block>
This one has no "MaxDamage" property. It should be added with the value I want like so:

Code:
<block id="150" name="woodFrameWedge">
<property name="MaxDamage" value="75"/>
<property name="Extends" value="woodFrameMaster"/>
<property name="CreativeMode" value="Player"/>
<property name="Shape" value="New"/>
<property name="Model" value="wedge60"/> <property name="Place" value="TowardsPlacerInverted"/>
<property name="CanPickup" value="true"/>
<property class="UpgradeBlock"> <property name="ToBlock" value="woodWedge"/>	</property>
</block>
This one already has the property (it does not actually, but let's say):

Code:
<block id="830" name="woodHalf">
<property name="MaxDamage" value="100"/>
<property name="Extends" value="woodMaster" />
<property name="Shape" value="New" />
<property name="Model" value="cube_half" />
<property class="UpgradeBlock"> <property name="ToBlock" value="rWoodHalf" /> </property>
<property name="DowngradeBlock" value="solidWoodFrameHalf" />
</block>
And in this case, I want the command to change the value, like so:

Code:
<block id="830" name="woodHalf">
<property name="MaxDamage" value="50"/>
<property name="Extends" value="woodMaster" />
<property name="Shape" value="New" />
<property name="Model" value="cube_half" />
<property class="UpgradeBlock"> <property name="ToBlock" value="rWoodHalf" /> </property>
<property name="DowngradeBlock" value="solidWoodFrameHalf" />
</block>
And what I don't want is something like this:

Code:
<block id="830" name="woodHalf">
<property name="MaxDamage" value="50"/>
<property name="MaxDamage" value="100"/>
<property name="Extends" value="woodMaster" />
<property name="Shape" value="New" />
<property name="Model" value="cube_half" />
<property class="UpgradeBlock"> <property name="ToBlock" value="rWoodHalf" /> </property>
<property name="DowngradeBlock" value="solidWoodFrameHalf" />
</block>
Same property twice.

So I don't want to have to check wether the property exists or not.

 
I don't want it to skip if the property is already there. If it's already there, the value should be changed. If the property is not already there, it should be added with the value. For example:

Code:
<block id="150" name="woodFrameWedge">
<property name="Extends" value="woodFrameMaster"/>
<property name="CreativeMode" value="Player"/>
<property name="Shape" value="New"/>
<property name="Model" value="wedge60"/> <property name="Place" value="TowardsPlacerInverted"/>
<property name="CanPickup" value="true"/>
<property class="UpgradeBlock"> <property name="ToBlock" value="woodWedge"/>	</property>
</block>
This one has no "MaxDamage" property. It should be added with the value I want like so:

Code:
<block id="150" name="woodFrameWedge">
<property name="MaxDamage" value="75"/>
<property name="Extends" value="woodFrameMaster"/>
<property name="CreativeMode" value="Player"/>
<property name="Shape" value="New"/>
<property name="Model" value="wedge60"/> <property name="Place" value="TowardsPlacerInverted"/>
<property name="CanPickup" value="true"/>
<property class="UpgradeBlock"> <property name="ToBlock" value="woodWedge"/>	</property>
</block>
This one already has the property (it does not actually, but let's say):

Code:
<block id="830" name="woodHalf">
<property name="MaxDamage" value="100"/>
<property name="Extends" value="woodMaster" />
<property name="Shape" value="New" />
<property name="Model" value="cube_half" />
<property class="UpgradeBlock"> <property name="ToBlock" value="rWoodHalf" /> </property>
<property name="DowngradeBlock" value="solidWoodFrameHalf" />
</block>
And in this case, I want the command to change the value, like so:

Code:
<block id="830" name="woodHalf">
<property name="MaxDamage" value="50"/>
<property name="Extends" value="woodMaster" />
<property name="Shape" value="New" />
<property name="Model" value="cube_half" />
<property class="UpgradeBlock"> <property name="ToBlock" value="rWoodHalf" /> </property>
<property name="DowngradeBlock" value="solidWoodFrameHalf" />
</block>
And what I don't want is something like this:

Code:
<block id="830" name="woodHalf">
<property name="MaxDamage" value="50"/>
<property name="MaxDamage" value="100"/>
<property name="Extends" value="woodMaster" />
<property name="Shape" value="New" />
<property name="Model" value="cube_half" />
<property class="UpgradeBlock"> <property name="ToBlock" value="rWoodHalf" /> </property>
<property name="DowngradeBlock" value="solidWoodFrameHalf" />
</block>
Same property twice.

So I don't want to have to check wether the property exists or not.
Yes and thats possible in xpath using operators, queries and stringlen checks, but as said, the pimps implementation is what has to support it. Depending if they implement that sort of advanced usage, we will be able to use. We will have to wait and see.

Cheers

 
Yes and thats possible in xpath using operators, queries and stringlen checks, but as said, the pimps implementation is what has to support it. Depending if they implement that sort of advanced usage, we will be able to use. We will have to wait and see.
Cheers
If you know how exactly the command would have to look for that, might you provide an example..? I (briefly) looked into other tutorials and could not find what I'm looking for.
If the game won't support such stuff, it should be a good idea to find something that does, I find an option like this fairly essential. How does it work anyway, what turns the commands into actual xml-code? I'm not much of a programmer, so I don't know what's it called, a compiler? Interpreter? If the game can't do, are there stand alone programs available or easy to make?

Edit: I have, btw, understood that whatever the game uses will not even put out any actualy xml-files anymore. But I assume there are such programs, so... No confusion plz.

 
Last edited by a moderator:
A quick and easy solution to the issue is to use two xpath commands:

<remove xpath="//property" />

<append xpath="//property/@value>50</append>

The remove should fail silently if its not there, and remove if it its there. the append would put it back.

 
A quick and easy solution to the issue is to use two xpath commands:
<remove xpath="//property" />

<append xpath="//property/@value>50</append>

The remove should fail silently if its not there, and remove if it its there. the append would put it back.
Like so?
 
Probably something like this:

Code:
<remove xpath="/blocks/block[@name='woodFrameWedge']/property[@name='MaxDamage']" />
<append xpath="/blocks/block[@name='woodFrameWedge']">
     <property name="MaxDamage" value="75" >
</append>
If you aren't appending specifically to a value, but rather an entirely new line, it'll be the slightly longer format.

 
Probably something like this:

Code:
<remove xpath="/blocks/block[@name='woodFrameWedge']/property[@name='MaxDamage']" />
<append xpath="/blocks/block[@name='woodFrameWedge']">
     <property name="MaxDamage" value="75" >
</append>
If you aren't appending specifically to a value, but rather an entirely new line, it'll be the slightly longer format.
Ah, yes, my mistake, you had explained that already. Thanks.
 
Exellent intro.

Possible bug report:

You used append once with and once without comma. I assume only one method is correct.

Code:
<append xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']/@value">,NoAmmo</append>

<append xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']/@value">NoAmmo</append>
 
Exellent intro.

Possible bug report:

You used append once with and once without comma. I assume only one method is correct.

Code:
<append xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']/@value">,NoAmmo</append>

<append xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']/@value">NoAmmo</append>
yes, the first one is correct. I'll update the post. It appends as a string, so it doesn't know any context on what it's changing.

 
Code:
<items>
<item id="40" name="gunPistol">
	<!-- snip -->
	<property class="Action0">
		<!-- snip -->
		<property name="Magazine_items" value="9mmBullet" />
	</property>
</item>
</items>
Lets assume I want to add something OUTSIDE the <property> block for all guns with 9mm Bullets.

The following will surely not work because inbetween the append and the remove the xml is left with broken syntax with two closing </property>. And the remove surely wouldn't work this way anyway

Code:
<append xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']>
 </property>
 <...my new stuff....>
</set>

<remove xpath="/items/item//property[2]">
Is there are way? Maybe a '..' syntax to go outside a block again?

 
Last edited by a moderator:
Lets assume I want to add something OUTSIDE the <property> block for all guns with 9mm Bullets.
The following will surely not work because inbetween the append and the remove the xml is left with broken syntax with two closing </property>. And the remove surely wouldn't work this way anyway

Code:
<append xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']>
 </property>
 <...my new stuff....>
</set>

<remove xpath="/items/item//property[2]">
Is there are way? Maybe a '..' syntax to go outside a block again?
You'd probably want to use <insertAfter>, rather than append.

Code:
<insertAfter xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']>
  <...my new stuff....>
</insertAfter>
 
What's the actual full file look like for say items.xml using this system? Most examples don't include any overall structure where some include <configs> ... </configs> with changes inside. Are there tags that were left out?

 
What's the actual full file look like for say items.xml using this system? Most examples don't include any overall structure where some include <configs> ... </configs> with changes inside. Are there tags that were left out?
Simplest Example:

Mods/Buggi/Config/items.xml:

Code:
<configs>
        <!-- this will insert new stuff under the <property name="Magazine_items" value="9mmBullet" />
        <insertAfter xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']>
                 <...my new stuff....>
         </insertAfter>

          <!-- this will change the value of the CreativeMode to false, rather than the default none -->
         <set xpath="/items/item[@name='clubMaster']/property[@name='CreativeMode']/@value">False</set>

</configs>
The <configs> tags aren't really used at all; they are just there to make a valid XML file, which needs a root node. This is just as valid:

Code:
<buggi>
        <insertAfter xpath="/items/item/property[@class='Action0']/property[@name='Magazine_items'][@value='9mmBullet']>
                 <...my new stuff....>
         </insertAfter>
</buggi>
Does this help clear things up?

 
Thank you for posting this. This should help a lot. tbh i'm terrible at modding and codes, but, I got annoyed with having to edit the xml after updates to get my glass jars back after cooking with them. *cant nobody do the dishes* :p so once we get it and i can figure this out, it is going to save me so much time and frustration. appreciate ya!

 
Thank you for posting this. This should help a lot. tbh i'm terrible at modding and codes, but, I got annoyed with having to edit the xml after updates to get my glass jars back after cooking with them. *cant nobody do the dishes* :p so once we get it and i can figure this out, it is going to save me so much time and frustration. appreciate ya!
Looking forward to seeing your work :) Let us know if you have any issues with generating your xpath.

 
I have no idea on what you said. I did manage to pick out some of it....like the words and, the, to, etc. I still think you did a wonderful job though as I imagine it is on par with your other posts. Glad you liked 7 Days enough to be involved like this. We all benefit from that. Thank you.

...oh btw, can you loan me $10 til Friday...

 
I have no idea on what you said. I did manage to pick out some of it....like the words and, the, to, etc. I still think you did a wonderful job though as I imagine it is on par with your other posts. Glad you liked 7 Days enough to be involved like this. We all benefit from that. Thank you.


...oh btw, can you loan me $10 til Friday...

Code:
<append xpath="/bankAccounts/Account[@name='Gamida']/@balance">+$10</append>
 
Back
Top