• 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:
<effect_group><triggered_effect trigger="onSelfAttackedOther" action="ModifyCVar" target="other" cvar="KillStreakTargetCounter" operation="add" value="1"/>

<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="self" buff="buffPerkKillStreakCleanup"/>

</effect_group>

<effect_group>

<requirement name="HoldingItemHasTags" tags="perkDeadEye"/>

<passive_effect name="WeaponHandling" operation="perc_add" value="0.1,0.5" level="1,5"/>

<passive_effect name="ReloadSpeedMultiplier" operation="perc_add" value="0.1" level="1"/>

<passive_effect name="ReloadSpeedMultiplier" operation="perc_add" value="0.1,0.3" level="2,5"/>
Does anyone know how to add to the second effect group in this instance. Its the Deadeye perk, want to append lines after passive_effect name="ReloadSpeedMultiplier" but theres two Effect Groups and neither are named.

 
Does anyone know how to add to the second effect group in this instance. Its the Deadeye perk, want to append lines after passive_effect name="ReloadSpeedMultiplier" but theres two Effect Groups and neither are named.
If you have multiple nodes, that do not have anything unique about them, you can use their element

Something like //effect_group/passive_effect[3]. which will change the third entry. It can be a bit risky, since any kind of change in nodes would break the mod, but if you don't have anything else unique about it, you can use it.

 
If you have multiple nodes, that do not have anything unique about them, you can use their element
Something like //effect_group/passive_effect[3]. which will change the third entry. It can be a bit risky, since any kind of change in nodes would break the mod, but if you don't have anything else unique about it, you can use it.
thanks bud, used <append xpath="/progression/perks/perk[@name=perkDeadEye]/effect_group[2]"> and it works great

 
Could you change multiple recipes with one line of code? I want to remove the RNG of needing to find a schematic every time I want to make a mod for my items and was wondering if it is easily done... maybe something like:

Code:
<remove xpath="/recipes/*/ingredient[contains(@name, 'Schematic')]"/>
 
I've never tried; have you tried <remove xpath="/recipes/recipe[@name=foodGrilledMeat]/@tags" /> ?
I did now, but it returns an error.
Here is another problem:

Code:
	<buff name="buffReloadMovementPenalty" name_key="Reloading" icon="ui_game_symbol_pack_mule" icon_color="255,128,0" hidden="true">>
	<stack_type value="ignore"/>
	<damage_type value="stun"/>
	<duration value="7"/>
	<effect_group>
		<triggered_effect trigger="onReloadStop" action="RemoveBuff" buff="buffReloadMovementPenalty"/>
	</effect_group>

	<effect_group>
		<requirement name="ProgressionLevel" progression_name="perkRunAndGun" operation="Equals" value="0"/>
			<passive_effect name="WalkSpeed" operation="perc_subtract" value=".5"/>
			<passive_effect name="RunSpeed" operation="perc_subtract" value=".5"/>
			<passive_effect name="JumpStrength" operation="perc_subtract" value=".5"/>
	</effect_group>
	<effect_group>
		<requirement name="ProgressionLevel" progression_name="perkRunAndGun" operation="Equals" value="1"/>
			<passive_effect name="WalkSpeed" operation="perc_subtract" value=".4"/>
			<passive_effect name="RunSpeed" operation="perc_subtract" value=".4"/>
			<passive_effect name="JumpStrength" operation="perc_subtract" value=".4"/>
	</effect_group>
	<effect_group>
		<requirement name="ProgressionLevel" progression_name="perkRunAndGun" operation="Equals" value="2"/>
			<passive_effect name="WalkSpeed" operation="perc_subtract" value=".3"/>
			<passive_effect name="RunSpeed" operation="perc_subtract" value=".3"/>
			<passive_effect name="JumpStrength" operation="perc_subtract" value=".3"/>
	</effect_group>
	<effect_group>
		<requirement name="ProgressionLevel" progression_name="perkRunAndGun" operation="Equals" value="3"/>
			<passive_effect name="WalkSpeed" operation="perc_subtract" value=".2"/>
			<passive_effect name="RunSpeed" operation="perc_subtract" value=".2"/>
			<passive_effect name="JumpStrength" operation="perc_subtract" value=".2"/>
	</effect_group>
	<effect_group>
		<requirement name="ProgressionLevel" progression_name="perkRunAndGun" operation="Equals" value="4"/>
			<passive_effect name="WalkSpeed" operation="perc_subtract" value=".1"/>
			<passive_effect name="RunSpeed" operation="perc_subtract" value=".1"/>
			<passive_effect name="JumpStrength" operation="perc_subtract" value=".1"/>
	</effect_group>
</buff>
I want to reduce the values for WalkSpeed and RunSpeed and JumpStrength. But it seems that I can't adress them because there are multiple effect_groups, these two don't work:

Code:
<set xpath="/buffs/buff[@name='buffReloadMovementPenalty']/effect_group/requirement[@value='0']/passive_effect[@value='.5']/@value">0.005</set>

<set xpath="/buffs/buff[@name='buffReloadMovementPenalty']/effect_group/requirement[@value='1']/passive_effect/@value">0.004</set>
(The values are that low to test if it certainly works.)

Is there a solution besides removing the complete buff and appending buffs with the modded one?

 
I modified it slightly, but am at work so cant test to make sure it functions right

Code:
<set xpath="/buffs/buff[@name='buffReloadMovementPenalty']/effect_group/requirement[@name='ProgressionLevel' and @value='0']/passive_effect[@value='.5']/@value">0.005</set>
<set xpath="/buffs/buff[@name='buffReloadMovementPenalty']/effect_group/requirement[@name='ProgressionLevel' and @value='1']/passive_effect[@value='.4']/@value">0.004</set>
<set xpath="/buffs/buff[@name='buffReloadMovementPenalty']/effect_group/requirement[@name='ProgressionLevel' and @value='2']/passive_effect[@value='.3']/@value">0.003</set>
<set xpath="/buffs/buff[@name='buffReloadMovementPenalty']/effect_group/requirement[@name='ProgressionLevel' and @value='3']/passive_effect[@value='.2']/@value">0.002</set>
<set xpath="/buffs/buff[@name='buffReloadMovementPenalty']/effect_group/requirement[@name='ProgressionLevel' and @value='4']/passive_effect[@value='.1']/@value">0.001</set>
 
Do servers send the modlets to clients that connect?

Modlets really seems to open the door for proper steam workshop support. Has there been any dev comments on this?

 
Do servers send the modlets to clients that connect?
Modlets really seems to open the door for proper steam workshop support. Has there been any dev comments on this?
Devs have added all these hooks. And I believe it's being prepped for proper workshop support. However, they did say that workshop support would probably be post-gold.

Until then, we get to play with the hooks, and we'll design a gap-solution so we can easily find and install mods.

And yes, servers should be pushing all XML-based modlets. They still won't push icons though.

 
Ok, trying to expand and adapt Guppy's BM mod for a17 since a lot of my players are complaining about running out of zeds.

Basically, I want to re-add that 4th line in that has been removed in a17, but I want to customize it for each gamestage.

Looking at the stock feed, stages 1, 2, 4, 7 only have one line. Stages 10, 13, 16, and 19 have two lines. The rest only have three lines.

So I wanted to start with stage 23, and add a 4th line to each of them.

Initially I'm thinking to add in groups like 23-153 will get <spawn group="ZombiesNight" num="10" maxAlive="8"/>, and step them up in groups like that.

Looking at the example, <set xpath="/gamestages/spawner[@name=BloodMoonHorde]/*/spawn[4]/@maxAlive">50</set>

I need to select gamestage lines specifically.

What I theorized would work is....

<append xpath="/gamestages/spawner[@name=BloodMoonHorde] and [@stage=23]/*/spawn[4]/"><spawn group="ZombiesNight" num="10" maxAlive="8"/></append>

I'll need to make a line for each one unless I can set a range to the stage, but I'm fine with that. :)

 
Ok, trying to expand and adapt Guppy's BM mod for a17 since a lot of my players are complaining about running out of zeds.
Basically, I want to re-add that 4th line in that has been removed in a17, but I want to customize it for each gamestage.

Looking at the stock feed, stages 1, 2, 4, 7 only have one line. Stages 10, 13, 16, and 19 have two lines. The rest only have three lines.

So I wanted to start with stage 23, and add a 4th line to each of them.

Initially I'm thinking to add in groups like 23-153 will get <spawn group="ZombiesNight" num="10" maxAlive="8"/>, and step them up in groups like that.

Looking at the example, <set xpath="/gamestages/spawner[@name=BloodMoonHorde]/*/spawn[4]/@maxAlive">50</set>

I need to select gamestage lines specifically.

What I theorized would work is....

<append xpath="/gamestages/spawner[@name=BloodMoonHorde] and [@stage=23]/*/spawn[4]/"><spawn group="ZombiesNight" num="10" maxAlive="8"/></append>

I'll need to make a line for each one unless I can set a range to the stage, but I'm fine with that. :)
This is a completely theoretical few lines, but may get you on the right track:

Code:
<!-- Remove all 4th lines so start off fresh -->
<remove xpath="/gamestages/spawn[@name='BloodMoonHorde']/spawn[4]" />

<!-- Let's add a potential range for the stage variable. Since we aren't access the attribute itself, but the value, I think we can drop the @ -->
<append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage > 23 and stage < 153]" />
    <spawn group="ZombiesNight" num="10" maxAlive="8"/>
</append>

<append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage >  153]" />
    <spawn group="ZombiesNight" num="10" maxAlive="666"/>
</append>
 
Can I append an attribute to element.

For example:

from this

Code:
<attribute name="attPerception" name_key="attPerceptionName" desc_key="attPerceptionDesc" icon="ui_game_symbol_stealth">
to this

Code:
<attribute name="attPerception" [b]max_level="1000"[/b] name_key="attPerceptionName" desc_key="attPerceptionDesc" icon="ui_game_symbol_stealth">
I want to do this with every <attribute>

Or I have to replace the whole line. (Can I? I have to replace the whole element with it's sub-elements, don't I?)

 
Last edited by a moderator:
Can I append an attribute to element.For example:

from this

Code:
<attribute name="attPerception" name_key="attPerceptionName" desc_key="attPerceptionDesc" icon="ui_game_symbol_stealth">
to this

Code:
<attribute name="attPerception" [b]max_level="1000"[/b] name_key="attPerceptionName" desc_key="attPerceptionDesc" icon="ui_game_symbol_stealth">
I want to do this with every <attribute>

Or I have to replace the whole line. (Can I? I have to replace the whole element with it's sub-elements, don't I?)
We've tried a few ways, and so far haven't figured out how to remove or add a new attribute yet. I think the intention is for us to do so, so it may just be an unimplemented feature so far.

Until we get that, we'll have to replace entire lines.

 
This is a completely theoretical few lines, but may get you on the right track:

Code:
<!-- Remove all 4th lines so start off fresh -->
<remove xpath="/gamestages/spawn[@name='BloodMoonHorde']/spawn[4]" />

<!-- Let's add a potential range for the stage variable. Since we aren't access the attribute itself, but the value, I think we can drop the @ -->
<append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage > 23 and stage < 153]" />
    <spawn group="ZombiesNight" num="10" maxAlive="8"/>
</append>

<append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage >  153]" />
    <spawn group="ZombiesNight" num="10" maxAlive="666"/>
</append>
Yeah, that throws an error here....

<append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage > 23 and stage < 153]">

If I leave it at <append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage > 23]">, then it just errors out in a later line.

So looks like I'll need to just make one for each line. Which is fine. I can toss something together in Excel to do that.

 
Also, any idea why a modlet would work on Windows SP/MP, and Windows Dedi, but NOT work on Linux dedi?

I have this one I've been working on for my servers. All local tests are working perfect. Toss it onto my Linux dedi, and it's like it's not there. Zero errors in the log. In fact, it's even listed as loading. Yet none of the blocks from the mod exist in game.

First I thought it might be order, or some other mod interfering because I didn't have everything on my test machine that I did on the live server. Can't find a single issue. No matter the load order, or what other mods are on, it works in Windows, and not on the Linux server.

I'm already going bald, so losing more hair over this silliness isn't great. :strawberry:

 
Yeah, that throws an error here....<append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage > 23 and stage < 153]">

If I leave it at <append xpath="/gamestages/spawn[@name='BloodMoonHorde' and stage > 23]">, then it just errors out in a later line.

So looks like I'll need to just make one for each line. Which is fine. I can toss something together in Excel to do that.
The angle brackets and syntax needed to be escaped; Sorry about that. You can just duplicate the appends and clip the conditions around until you get everything you want.

Code:
<configs>
<!-- Updates the Game stages, under the BloodMoonHorde, to change the maxAlive to 50 for all nodes -->
<!-- By default, it only looks at the the game stages that have a 4 element into it, so it doesn't kick in until gamestage 23 -->
<remove xpath="/gamestages/spawner[@name='BloodMoonHorde']/*/spawn[4]" />
<append xpath='/gamestages/spawner[@name="BloodMoonHorde"]/gamestage[ @stage >23 and @stage<153]' >
	<spawn group="ZombiesNight" num="10" maxAlive="8"/>
</append>
</configs>
Produces:

Code:
	<gamestage stage="23">
		<spawn group="feralHordeStageGS16" num="22" maxAlive="9" duration="1" interval="28" />
		<spawn group="feralHordeStageGS19" num="22" maxAlive="9" duration="1" interval="14" />
		<spawn group="feralHordeStageGS23" num="22" maxAlive="9" duration="1" />
	</gamestage>
	<gamestage stage="27">
		<spawn group="feralHordeStageGS19" num="23" maxAlive="11" duration="1" interval="21" />
		<spawn group="feralHordeStageGS23" num="23" maxAlive="11" duration="1" interval="30" />
		<spawn group="feralHordeStageGS27" num="23" maxAlive="11" duration="1" />
		<spawn group="ZombiesNight" num="10" maxAlive="8">
			<!--Element appended by: "Guppycur's BloodMoonTickle"-->
		</spawn>
	</gamestage>
 
Also, any idea why a modlet would work on Windows SP/MP, and Windows Dedi, but NOT work on Linux dedi?
I have this one I've been working on for my servers. All local tests are working perfect. Toss it onto my Linux dedi, and it's like it's not there. Zero errors in the log. In fact, it's even listed as loading. Yet none of the blocks from the mod exist in game.

First I thought it might be order, or some other mod interfering because I didn't have everything on my test machine that I did on the live server. Can't find a single issue. No matter the load order, or what other mods are on, it works in Windows, and not on the Linux server.

I'm already going bald, so losing more hair over this silliness isn't great. :strawberry:

What about your case sensitive on your folder and files names? The Config folder structure needs to match the case exactly.

Do you see that the mod is loaded in your output?

 
Hi Every one, I'm trying to add a chemist perk that will add 7 new skills and recipes at different levels

Perk= chemist

skills= Adrenaline_Syringe , ZombrexTablets , EnergyDrink , Health_Syringe , F15PurificationPill , SpeedCapsules , CalciumTablets

they can be moved around

and with the attribute attIntellect. any help would be great thanks

and the line to add a new item and recipes. thanks in advance.

 
What about your case sensitive on your folder and files names? The Config folder structure needs to match the case exactly.
Do you see that the mod is loaded in your output?
Yes, it's listed as loaded in the server output log, and also in the client connecting to the server. Case hasn't changed between Windows and Linux as I just direct copied the folders over, and then corrected permissions for the server to use them.

I even though maybe it was because I had a couple of spaces in the folder name, but other mods with spaces in the folder are working without an issue.

It's got me completely baffled because based on all available data, the blocks should be in the game. Yet they aren't.

Update: Tested replacing the spaces in the folder names with _, and it's still an issue.

I'm sure eventually I'll figure it out and just facepalm.

 
Last edited by a moderator:
Back
Top