• 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:
In which folder do you place the mod folder and do you need to specify the mod somewhere for the game to use it? I tried to do a search for this information but I couldn't find it. I would like to try this instead of modifying the source files even though I wrote a program to do the modifying for me because this seems to be a much more elegant solution and safer.

 
In which folder do you place the mod folder and do you need to specify the mod somewhere for the game to use it? I tried to do a search for this information but I couldn't find it. I would like to try this instead of modifying the source files even though I wrote a program to do the modifying for me because this seems to be a much more elegant solution and safer.
At the top level of your game folder (C:\Program Files (x86)\Steam\steamapps\common\7 Days To Die), make a Mods folder. Then, under that, make a folder for each of the mods you want to make.

Code:
C:\Program Files (x86)\Steam\steamapps\common\7 Days To Die\Mods\BloodMoonTrickle
C:\Program Files (x86)\Steam\steamapps\common\7 Days To Die\Mods\Zicky\
Under your Zicky folder, you should have this:

Code:
Config/entityclasses.xml  ( if you want to make a change to the entityclasses.xml file )
ModInfo.xml
The game will look in the Mods folder for mods automatically; As long as it has a ModInfo.xml, it'll try to load it.

 
Do you know in what order the mods are applied? I'm assuming that it is most likely going to be trying to parse them alphabetically.

May do some testing later today to see if that seems to be the case

 
Do you know in what order the mods are applied? I'm assuming that it is most likely going to be trying to parse them alphabetically.
May do some testing later today to see if that seems to be the case
It's alphabetical :)

Changes start from the top and end with the last mod in the folder.

 
(Have not tested this yet - will attempt to do so when I get home tonight)

How about adding/changing things within the text files such as the locilization.txt?

 
(Have not tested this yet - will attempt to do so when I get home tonight)
How about adding/changing things within the text files such as the locilization.txt?
Not at this point. Maybe layer in A17 or possibly in A18.

 
Wow this is what I have wanted for months. Thank you Sphereii for explaining it.

Jittering with excitment

 
One Question beside this: Has anything changed regarding sprites transfer from server to client? This would be awesome for dedicated servers having custom UI's without the need that everybody has installed the mod them self.

 
Something worth knowing, because I spent almost an hour trying to figure it out, is that the "or" and "and" commands can be a bit finicky.

The example given has:

property[@name=Magazine_items] or [@value=9mmBullet]

I have not tested this to see if it works, but I'll assume it does.

However, if you want to use "or" for different names, the syntax is actually different. It has to be like this instead:

property[@name=Magazine_items' or @name='Another_name]

Notice that now both names are inside the same bracket, and each is labeled with "@name". This does not work if the names are in separate brackets.

As an example, I made a "mod" that returns an empty jar from boiled foods:

Code:
<configs>
<append xpath="/items/item[@name='foodBoiledMeat'
or @name='foodCornBread'
or @name='foodCornOnTheCob'
or @name='foodVegetableStew'
or @name='foodEggBoiled'
or @name='foodBlueberryPie'
or @name='foodHoboStew'
or @name='foodMeatStew']
/property[@class='Action0']">
	<property name="Create_item" value="drinkJarEmpty"/>
</append>
</configs>
 
Just wanted to confirm what @epigeios said, the syntax with operators is different than what is written in this tutorial.

For example this line:

Code:
<set xpath="/items/item/property[@name='Stacknumber'] and [@value='500']/@value">512</set>
will result in following error:

Code:
2018-11-21T10:41:53 9.789 EXC Error during parse of /items/item/property[@name='Stacknumber'] and [@value='500']/@value
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
Rethrow as XPathException: Error during parse of /items/item/property[@name='Stacknumber'] and [@value='500']/@value
 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 XmlFile.SetByXPath (System.String _xpath, System.Xml.XmlElement _xml, System.String _patchName) [0x00000] in <filename unknown>:0
 at XmlPatcher.singlePatch (.XmlFile _targetFile, System.Xml.XmlElement _patchElement, System.String _patchName) [0x00000] in <filename unknown>:0
 at XmlPatcher.PatchXml (.XmlFile _xmlFile, .XmlFile _patchXml, System.String _patchName) [0x00000] in <filename unknown>:0
 at XmlPatcher.LoadAndPatchConfig (System.String _configName) [0x00000] in <filename unknown>:0
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
Logger:masterLogException(Exception)
Logger:Exception(Exception)
Log:Exception(Exception)
XmlPatcher:LoadAndPatchConfig(String)
<loadSingleXml>c__Iterator1:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
should be this instead:

Code:
<set xpath="/items/item/property[@name='Stacknumber' and @value='500']/@value">512</set>
same with "or".

 
Just wanted to confirm what @epigeios said, the syntax with operators is different than what is written in this tutorial.
For example this line:

Code:
<set xpath="/items/item/property[@name='Stacknumber'] and [@value='500']/@value">512</set>
will result in following error:

Code:
2018-11-21T10:41:53 9.789 EXC Error during parse of /items/item/property[@name='Stacknumber'] and [@value='500']/@value
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
Rethrow as XPathException: Error during parse of /items/item/property[@name='Stacknumber'] and [@value='500']/@value
 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 XmlFile.SetByXPath (System.String _xpath, System.Xml.XmlElement _xml, System.String _patchName) [0x00000] in <filename unknown>:0
 at XmlPatcher.singlePatch (.XmlFile _targetFile, System.Xml.XmlElement _patchElement, System.String _patchName) [0x00000] in <filename unknown>:0
 at XmlPatcher.PatchXml (.XmlFile _xmlFile, .XmlFile _patchXml, System.String _patchName) [0x00000] in <filename unknown>:0
 at XmlPatcher.LoadAndPatchConfig (System.String _configName) [0x00000] in <filename unknown>:0
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
Logger:masterLogException(Exception)
Logger:Exception(Exception)
Log:Exception(Exception)
XmlPatcher:LoadAndPatchConfig(String)
<loadSingleXml>c__Iterator1:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
should be this instead:

Code:
<set xpath="/items/item/property[@name='Stacknumber' and @value='500']/@value">512</set>
same with "or".
Thank you both! I'll update the posts.

 
First i want to congratulate u for making this amazing tool/way to mod your game, but...

I get errors cause i mistype something in the vanilla files(fixing it with trial and error). Your way is another beast.

It is not very simple (ofc it needs to be complex for flexibility i know) and when i try a very simple mod i do not know if

i mistype or forgot a </rect> or i mistype the commands to do the appends etc.

Now i'll ask for more....is it possible to have a modded lets say windows.xml and a vanilla and a tool to compare and automate the xpath method or a way to append the values in the vanilla xml and see where the problem is?

I'll eventually learn how to use it but now i struggle to find out why for example why my hunger bar is not green and flashing from green to gray rapidly(solved it but it took me 1 day) ;p

I know i ask ALOT.

 
No problem and thank you for the tutorial :)

I noticed another problem, this time with your "not" example. Using the syntax you provided I tried this:

Code:
<set xpath="/items/item[not contains(@name, 'Empty'))]/property[@name='Stacknumber']/@value">64</set>
and this give following error:

Code:
2018-11-21T14:12:50 10.898 EXC invalid operator name: 'contains'
XPathException: invalid operator name: 'contains'
 at Mono.Xml.XPath.Tokenizer.ParseIdentifier () [0x00000] in <filename unknown>:0
 at Mono.Xml.XPath.Tokenizer.ParseToken () [0x00000] in <filename unknown>:0
 at Mono.Xml.XPath.Tokenizer.advance () [0x00000] in <filename unknown>:0
 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
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
Logger:masterLogException(Exception)
Logger:Exception(Exception)
Log:Exception(Exception)
XmlPatcher:LoadAndPatchConfig(String)
<loadSingleXml>c__Iterator1:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
proper example should look like this

Code:
<set xpath="/entity_classes/entity_class[not (contains(@name, 'zombie'))]/property[@name='Class']/@value">EntityZombieSDX, Mods</set>
I made a combination of two conditions, you can include it in your examples if you like. This will set stack number to 64 for all drinks in jars but will leave drinkJarEmpty at it's default stack number:

Code:
<set xpath="/items/item[starts-with(@name, 'drinkJar') and not(contains(@name, 'Empty'))]/property[@name='Stacknumber']/@value">64</set>
 
First i want to congratulate u for making this amazing tool/way to mod your game, but...
I get errors cause i mistype something in the vanilla files(fixing it with trial and error). Your way is another beast.

It is not very simple (ofc it needs to be complex for flexibility i know) and when i try a very simple mod i do not know if

i mistype or forgot a </rect> or i mistype the commands to do the appends etc.

Now i'll ask for more....is it possible to have a modded lets say windows.xml and a vanilla and a tool to compare and automate the xpath method or a way to append the values in the vanilla xml and see where the problem is?

I'll eventually learn how to use it but now i struggle to find out why for example why my hunger bar is not green and flashing from green to gray rapidly(solved it but it took me 1 day) ;p

I know i ask ALOT.
I'm sorry, but there's no tool yet that will generate an xpath file. There are online generators that you can use to help build your xpath. I haven't linked to them yet, since they sometimes do strange things, like using index values.

 
No problem and thank you for the tutorial :)
I noticed another problem, this time with your "not" example. Using the syntax you provided I tried this:

Code:
<set xpath="/items/item[not contains(@name, 'Empty'))]/property[@name='Stacknumber']/@value">64</set>
and this give following error:

Code:
2018-11-21T14:12:50 10.898 EXC invalid operator name: 'contains'
XPathException: invalid operator name: 'contains'
 at Mono.Xml.XPath.Tokenizer.ParseIdentifier () [0x00000] in <filename unknown>:0
 at Mono.Xml.XPath.Tokenizer.ParseToken () [0x00000] in <filename unknown>:0
 at Mono.Xml.XPath.Tokenizer.advance () [0x00000] in <filename unknown>:0
 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
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
Logger:masterLogException(Exception)
Logger:Exception(Exception)
Log:Exception(Exception)
XmlPatcher:LoadAndPatchConfig(String)
<loadSingleXml>c__Iterator1:MoveNext()
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)
proper example should look like this

Code:
<set xpath="/entity_classes/entity_class[not (contains(@name, 'zombie'))]/property[@name='Class']/@value">EntityZombieSDX, Mods</set>
I made a combination of two conditions, you can include it in your examples if you like. This will set stack number to 64 for all drinks in jars but will leave drinkJarEmpty at it's default stack number:

Code:
<set xpath="/items/item[starts-with(@name, 'drinkJar') and not(contains(@name, 'Empty'))]/property[@name='Stacknumber']/@value">64</set>
Thank you! I updated the post.

 
I get errors cause i mistype something in the vanilla files(fixing it with trial and error). Your way is another beast.It is not very simple (ofc it needs to be complex for flexibility i know) and when i try a very simple mod i do not know if

i mistype or forgot a </rect> or i mistype the commands to do the appends etc.
For your generic XML issues I recommend using e.g. Notepad++ with the XML plugin. It will tell you when the general XML structure is borked (like if you forget to close an XML element, mismatching open and close names etc).

 
It is still a17 E :)

I'm talking about the future. Happy to hear that there are some tools online.

I'll just enjoy the game a bit (Thats the reason i have not mess enough with the xpath :) )

edit:

I use notepad++ but not with the xml plugin. I will try it out. tyvm.

 
Last edited by a moderator:
First A17e patch and I can already say I LOVE this new method.

Yes, it does take a bit more work upfront.. but man is it worth it!!

 
Back
Top