• 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:
Yeah, I get the naming in the "contains" part.  I just threw that together and see if it would even work.  My issue is how do i search for "animalDoe" in ALL of the groups?  I think the * is the invalid token error I am getting, thus not letting anything work.  Because it is an error and not a warning.

thanks


Sorry, I misunderstood. Try something like this:

It'll search the contents of the text(), then apply the information to the contains. Note in this case, we don't use the text() within the contains like we have with the other formats, but rather we can use a period.  

Code:
 <csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalDoe')]">
        animalIceburgBabyDeer"
    </csv>
 
Sweet, that worked.  ha to add in the delim and op operators, but it worked.  

Final Code...

    <csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalDoe, 15')]" delim="\n" op="add" >animalIceburgBabyDeer, 15</csv>>
 

Odd part is how it added it...  that section now looks like this in the configsdump.

  <!-- *** BIOME_ANIMAL_WILD_GAME_FOREST_NIGHT_GROUP -->
  <!-- NONE must not be the first entry since first name determines if enemy group -->
  <entitygroup name="WildGameForestNight">        animalDoe, 15
        none, 85
    
animalIceburgBabyDeer, 15<!--Content CSV manipulated by: "IceBurg_Animals_Overhaul"--></entitygroup>
 

Very odd, but no errors.  This thing is gonna look terrible when I am done!!   LOL

Next question is does the total of all of these have to add up to 100?  Or alternate question, do I have to mess with the "none,85" at all?  that may make a huge difference in how I do this....

 
And so close...  tried more complex logic in it.

    <csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalBoar') and text()[not contains(., 'animalboar,']]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>

Getting an invalid token..  I know it is just a formatting error, 

one of the other ones I tried gave me the error that the expression must evaluate to a node set.

    <csv xpath="/entitygroups/entitygroup/text()[[contains(., 'animalBoar')] and [not contains(., 'animalboar,']]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>
 

 
And so close...  tried more complex logic in it.

    <csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalBoar') and text()[not contains(., 'animalboar,']]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>

Getting an invalid token..  I know it is just a formatting error, 

one of the other ones I tried gave me the error that the expression must evaluate to a node set.

    <csv xpath="/entitygroups/entitygroup/text()[[contains(., 'animalBoar')] and [not contains(., 'animalboar,']]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>
 


And so close...  tried more complex logic in it.

    <csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalBoar') and text()[not contains(., 'animalboar,']]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>

Getting an invalid token..  I know it is just a formatting error, 

one of the other ones I tried gave me the error that the expression must evaluate to a node set.

    <csv xpath="/entitygroups/entitygroup/text()[[contains(., 'animalBoar')] and [not contains(., 'animalboar,']]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>
 


What about something like this:

    <csv xpath="/entitygroups/entitygroup/text()[
[
contains(., 'animalBoar')
and not (contains(., 'animalboar,'))
]
]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>




Note, not is a function, so you need to wrap its condition inside of  ( ) 

 
Nope, that didn't work.  error I got from that command below....

2023-06-19T17:00:47 55.209 ERR XML loader: Patching 'entitygroups.xml' from mod 'IceBurg_Animals_Overhaul' failed:
2023-06-19T17:00:47 55.214 EXC XML.Patch (/Iceburg/csv, line 14 at pos 2): XPath evaluation failed: Expression must evaluate to a node-set.
 

 
Nope, that didn't work.  error I got from that command below....

2023-06-19T17:00:47 55.209 ERR XML loader: Patching 'entitygroups.xml' from mod 'IceBurg_Animals_Overhaul' failed:
2023-06-19T17:00:47 55.214 EXC XML.Patch (/Iceburg/csv, line 14 at pos 2): XPath evaluation failed: Expression must evaluate to a node-set.
 
the second part is animalboar and not animalBoar like in the first part. 

I can look closer tomorrow morning

 
I`m used some modlets and want to create my fixes for it. I know that I can determine the load order of the mods with the help of names. And created my name folder in the end list mods. But don't understand how is better overwrite recipes.xml or items.xml. 

Can I use <append> for edit existing recipes?

<append xpath="/recipes">
<recipe name="drinkJarGrainAlcohol" time="120" area="stationChemistry">
<ingredient name="foodCornMeal" count="10"/>
<ingredient name="drinkJarRiverWater"/>
</recipe>
</append>



Or should I remove the existing one first and add a replacement?

<remove xpath="/recipes/recipe[@name='drinkJarGrainAlcohol']"></remove>
<append xpath="/recipes">
<recipe name="drinkJarGrainAlcohol" time="120" area="stationChemistry">
<ingredient name="foodCornMeal" count="10"/>
<ingredient name="drinkJarRiverWater"/>
</recipe>
</append>



And same question for overwrite values for items.xml. If i need to change multiple exist properties in my <item>, can i use <append> for it?

<append xpath="/items/item[@name='drinkJarGrainAlcohol']">
<property name="Stacknumber" value="1000"/>
<property name="EconomicValue" value="60"/>
<property name="CarryWeight" value=".25"/>
</append>



Or should use <set> with xpath? (But I don't like this method. Because I have to duplicate the properties that I do not need to change.)

<set xpath="/items/item[@name='drinkJarBeer']">
<property name="Group" value="Resources,Items"/>
<property name="Meshfile" value="#Other/Items?Food/bottled_waterPrefab"/>
<property name="DropMeshfile" value="#Other/Items?Misc/sack_droppedPrefab"/>
<property name="Material" value="Mglass"/><property name="Weight" value="10"/>
<property name="Stacknumber" value="1000"/>
<property name="EconomicValue" value="60"/>
<property name="CarryWeight" value=".25"/>
</set>



And same solution with <set> with full xpath for each property. Maybe it's right solution for overwrite properties. But it is not always possible to understand how to write xpath="..."

<set xpath="/items/item[@name='drinkJarGrainAlcohol']/property[@name='Stacknumber']/@value">1000</set>
<set xpath="/items/item[@name='drinkJarGrainAlcohol']/property[@name='EconomicValue']/@value">60</set>
<set xpath="/items/item[@name='drinkJarGrainAlcohol']/property[@name='CarryWeight']/@value">0.25</set>




What is the best method to use to avoid problems in the future? I don't fully understand how it works full xpath="..." with different search attributes.
What if I need overwrite more properties in 100 different items?  Will this cause performance issues?

 
Last edited by a moderator:
OK i need help still new to this and doing my best to fix this mod for the server i admin.. old line of code was

<append xpath="/progression/perks/perk[@name='perkAdvancedEngineering']/effect_group/passive_effect[@name='RecipeTagUnlocked'][@level='5']/@tags">,DmAuger</append >

new line that i edited

<append xpath="/progression/perks/perk[@name='perkAdvancedEngineering']/effect_group/passive_effect[@name='CraftingTier'][@level='5']/@tags=DmAuger']"></append>

I can not get this recipe to unlock at all .. i can see it when i load the mod in, i got the item loading in and working pulling from Creative Menu

 
OK i need help still new to this and doing my best to fix this mod for the server i admin.. old line of code was

<append xpath="/progression/perks/perk[@name='perkAdvancedEngineering']/effect_group/passive_effect[@name='RecipeTagUnlocked'][@level='5']/@tags">,DmAuger</append >

new line that i edited

<append xpath="/progression/perks/perk[@name='perkAdvancedEngineering']/effect_group/passive_effect[@name='CraftingTier'][@level='5']/@tags=DmAuger']"></append>

I can not get this recipe to unlock at all .. i can see it when i load the mod in, i got the item loading in and working pulling from Creative Menu
Are you running A21?  If so, crafting tiers and recipe unlocks are not part of perks anymore but part of crafting skills

 
Are there advanced ways to format the new CSV xpath to better construct lists like entitygroups? For example, I posted pictures of what I have in my mod and what the result in configs dump is. Why is it moving the first entity to the top and out of the group? And why is it moving what I've added to the bottom right corner, with no tabs and a skipped line. While entities seem to still spawn fine from this, I specifically want to use "none" to cut down on total sleepers spawning... which does not work unless it is formatted correctly and directly after the last entity in the list.

How can I fix the formatting and structure with a csv path, so i don't have to use set xpath and override every single sleeper group manually with the "none" positioned correctly?

csv 1.PNG

csv 2.PNG

 
Hey, so I started fiddling with xml files recently and I'm trying to figure stuff out by looking at other files, and I currently have a problem.

So I see that TFP have this commented out on the Power Attack group for the steel knucklesimage.png

I want to be able to re-add that variable on my own xml, how would I be able to do that?

EDIT: Problem solved, just found it out.

 
Last edited by a moderator:
I am still working on the search and add for the csv editing.  I have tried multiple ways with no success.  I posted earlier and we had a little discussion going on with it.  But no resolution.  I can post the specific info again if needed....

 
Are there advanced ways to format the new CSV xpath to better construct lists like entitygroups? For example, I posted pictures of what I have in my mod and what the result in configs dump is. Why is it moving the first entity to the top and out of the group? And why is it moving what I've added to the bottom right corner, with no tabs and a skipped line. While entities seem to still spawn fine from this, I specifically want to use "none" to cut down on total sleepers spawning... which does not work unless it is formatted correctly and directly after the last entity in the list.

How can I fix the formatting and structure with a csv path, so i don't have to use set xpath and override every single sleeper group manually with the "none" positioned correctly?

View attachment 28249

View attachment 28250


I don't think the ConfigDumps formatting really matters. The game reads the entitygroups line by line from memory. The configsDump is just a helper set of files so we can verify that we've added things correctly. However, it doesn't really do it cleanly anymore for entitygroups.

If your changes aren't working, I don't think its related to formatting of the file.

I`m used some modlets and want to create my fixes for it. I know that I can determine the load order of the mods with the help of names. And created my name folder in the end list mods. But don't understand how is better overwrite recipes.xml or items.xml. 

Can I use <append> for edit existing recipes?

<append xpath="/recipes">
<recipe name="drinkJarGrainAlcohol" time="120" area="stationChemistry">
<ingredient name="foodCornMeal" count="10"/>
<ingredient name="drinkJarRiverWater"/>
</recipe>
</append>



Or should I remove the existing one first and add a replacement?

<remove xpath="/recipes/recipe[@name='drinkJarGrainAlcohol']"></remove>
<append xpath="/recipes">
<recipe name="drinkJarGrainAlcohol" time="120" area="stationChemistry">
<ingredient name="foodCornMeal" count="10"/>
<ingredient name="drinkJarRiverWater"/>
</recipe>
</append>



And same question for overwrite values for items.xml. If i need to change multiple exist properties in my <item>, can i use <append> for it?

<append xpath="/items/item[@name='drinkJarGrainAlcohol']">
<property name="Stacknumber" value="1000"/>
<property name="EconomicValue" value="60"/>
<property name="CarryWeight" value=".25"/>
</append>



Or should use <set> with xpath? (But I don't like this method. Because I have to duplicate the properties that I do not need to change.)

<set xpath="/items/item[@name='drinkJarBeer']">
<property name="Group" value="Resources,Items"/>
<property name="Meshfile" value="#Other/Items?Food/bottled_waterPrefab"/>
<property name="DropMeshfile" value="#Other/Items?Misc/sack_droppedPrefab"/>
<property name="Material" value="Mglass"/><property name="Weight" value="10"/>
<property name="Stacknumber" value="1000"/>
<property name="EconomicValue" value="60"/>
<property name="CarryWeight" value=".25"/>
</set>



And same solution with <set> with full xpath for each property. Maybe it's right solution for overwrite properties. But it is not always possible to understand how to write xpath="..."

<set xpath="/items/item[@name='drinkJarGrainAlcohol']/property[@name='Stacknumber']/@value">1000</set>
<set xpath="/items/item[@name='drinkJarGrainAlcohol']/property[@name='EconomicValue']/@value">60</set>
<set xpath="/items/item[@name='drinkJarGrainAlcohol']/property[@name='CarryWeight']/@value">0.25</set>




What is the best method to use to avoid problems in the future? I don't fully understand how it works full xpath="..." with different search attributes.
What if I need overwrite more properties in 100 different items?  Will this cause performance issues?


Sorry for the delay.

Yes, you'd append new recipes and properties to recipes, items, blocks, etc. 

If you needed to add new lines and change existing settings, you would use a combination of append to add the new properties, and then use set to change the value attribute on the ones you have.  If there's a lot of edits to a single recipe, I often times feel that doing a remove and then append a whole new recipe.

I am still working on the search and add for the csv editing.  I have tried multiple ways with no success.  I posted earlier and we had a little discussion going on with it.  But no resolution.  I can post the specific info again if needed....
What do you have now with your csv editing?

 
Here are the last 2 entries I have tried :

<!-- <csv xpath="/entitygroups/entitygroup/text()[[contains(., 'animalBoar')] and [not (contains(., 'animalBoar,'))]]" delim="\n" op="add" >animalIceburgBabyBoar</csv>> -->
<csv xpath="/entitygroups/entitygroup/text()[[contains(., 'animalBoar')] and not (contains(., 'animalBoar,'))]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>
 

I have tried many others, but I kept just changing the line.  now I am keeping a list of what I have tried and didn't work.  Error I get is the same.

2023-06-19T17:00:47 55.209 ERR XML loader: Patching 'entitygroups.xml' from mod 'IceBurg_Animals_Overhaul' failed:2023-06-19T17:00:47 55.214 EXC XML.Patch (/Iceburg/csv, line 14 at pos 2): XPath evaluation failed: Expression must evaluate to a node-set.

 
And this line does not give me an error, but just a warning saying it didn't apply.

<csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalBoar')] and not(contains(., 'animalBoar,'))" delim="\n" op="add" >animalIceburgBabyBoar</csv>>
 

 
OMG!!!!  After I don't know how many tries, I got it to work!!!!!  

And the winner is.....

<csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalBoar') and not(contains(., 'animalBoar,'))]" delim="\n" op="add" >animalIceburgBabyBoar</csv>>
 

Thank you for any and all help that was provided!!!!!!!

 
I don't think the ConfigDumps formatting really matters. The game reads the entitygroups line by line from memory. The configsDump is just a helper set of files so we can verify that we've added things correctly. However, it doesn't really do it cleanly anymore for entitygroups.

If your changes aren't working, I don't think its related to formatting of the file.
I ran into the same problem and I do believe it has something to do with the formatting. More specifically it is caused by the last entity being on the same line as the closing entitygroup tag. That makes it ignore it and causes the mod fail. I ran into the same problem while trying to edit certain entitygroups to only contain one entity. The error I got said the groups were empty whereas the dump showed it had one entity in it, on the same line as the closing tag. You can also try this with just editing an existing group. As an example, group
 

<entitygroup name="scoutHordeStageGS1">
zombieDarlene</entitygroup>


gives an error for being an empty group. This is also the way csv seems to edit these groups, without a line break at the end of the last line.

 
Ok, just ran into another issue.  

Here is the command I am Running:

    <csv xpath="/entitygroups/entitygroup/text()[contains(., 'animalZombieDog') and not(contains(., 'animalZombieDog,'))]" delim="\n" op="add" >animalIceburgBabyZombieDog</csv>>

the result output I am getting with an Error is:

  <entitygroup name="ZombieDogGroup">animalZombieDog
animalIceburgBabyZombieDog<!--Content CSV manipulated by: "IceBurg_Animals_Overhaul"--></entitygroup>

Error I am getting is:
2023-07-09T14:26:31 57.267 ERR XML loader: Loading and parsing 'spawning.xml' failed
2023-07-09T14:26:31 57.267 EXC Entity spawner 'Dog_Sm_Territorial' contains invalid group ZombieDogGroup
  at EntitySpawnerClass.Init () [0x00099] in <0266d593c0f442fa83ca9e728741ee90>:0 
  at EntitySpawnerClassesFromXml.LoadEntitySpawnerClasses (System.Xml.Linq.XDocument _spawnXml) [0x00214] in <0266d593c0f442fa83ca9e728741ee90>:0 
  at WorldStaticData+<LoadSpawning>d__37.MoveNext () [0x00017] in <0266d593c0f442fa83ca9e728741ee90>:0 
  at ThreadManager+<CoroutineWrapperWithExceptionCallback>d__51.MoveNext () [0x00044] in <0266d593c0f442fa83ca9e728741ee90>:0 
 

Question is why????  I have the same type of entries in many other animals and I am not getting this error

I don't get this error  on the Dog_Med_Territorial Group.  Did i get this error because it tried to spawn in a small zombie dog group and it couldn't for some reason becasue of my mod???

Any thoughts appreciated.

Edit:  I see that Gako posted while I was writing this up.  seems I am hitting the same issue as him, maybe, probably.  With his work, I am guessing this may be a bug with mods applying to this type on entry?????

 
Last edited by a moderator:
My personal experience with that dog territory group usually led to something not being edited correctly in one or more of the group files. Because I to had never touched the spawning.xml either. So double check all the animal stuff you edited so far and make sure there isn't an extra comma or bracket anywhere. Double check quotes and caps etc.

 
Back
Top