[TUTORIAL] XML Patch Guide

The place to discuss scripting and game modifications for X Rebirth.

Moderators: Moderators for English X Forum, Scripting / Modding Moderators

UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

xml patch wors by using the xml hierarchy, it does not work with lines. (although often they are the same for readability)
this is for example also a valid single node opening:

Code: Select all

<do_if
value="some really long expression you better
split into multiple lines to avoid annoying
horizontal scrolling"
chance="another really long expression you better
split into multiple lines to avoid annoying
horizontal scrolling which resolves to 0 or 100"
/>
that just as examle to show that a node isnt necesarily the same as a line.

1. the ws attribute is for removing whitespaces so you can keep the xml indents in the result document, i dont think it can be used to remove multiple nodes at once. same for replace - each adressed node must be unique.
there is an unofficial msel extension which allows the adressing of multiple nodes in one operation, but its afaik not supported by EGO yet.

2. yes, otherwise your xml is not valid. note that you would also replace the actions inside this <do_if/> since you adress the whole Node, not just the Line where it starts.
you can replace the attribute values themselves though:

Code: Select all

<replace sel="//*****/do_if[@value='not $Ship.buildmodule and $Ship.primarypurpose != objectpurpose.mine']/@value">( not $Ship.buildmodule and $Ship.primarypurpose != objectpurpose.mine ) or $someothercondition</replace>
(note the @value at the end of the sel path)

alternatively you could also add a chance attribute as further restriction if you just want the do_if to apply in less cases:

Code: Select all

<add sel="//*****/do_if[@value='not $Ship.buildmodule and $Ship.primarypurpose != objectpurpose.mine']" type="@chance">$someothercondition * 100</add>
if not stated otherwise everything i post is licensed under WTFPL

Ich mache keine S&M-Auftragsarbeiten, aber wenn es fragen gibt wie man etwas umsetzen kann helfe ich gerne weiter ;)

I wont do Script&Mod Request work, but if there are questions how to do something i will GLaDly help ;)
User avatar
Simoom
Posts: 1110
Joined: Sat, 30. Oct 10, 14:14
x4

Post by Simoom »

Thanks UniTrader.

Hmm.... okay, so let me make sure I understand this:

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<mdscript name="Upkeep_Management" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="md.xsd">
  <cues>
    <library name="CheckObject">
      <actions>
        <do_if value="$Object.exists and @$Object.isplayerowned">
          <do_if value="$Object != player.primaryship">
            <do_if value="md.$AllowMultiMissions or not @$HasMission">
              <do_all exact="1">

                
                <do_elseif value="$Object.isclass.station"> <!-- <<< This is the line I want to change -->
                  <[... WHOLE LOT OF STUFF]/>
                </do_elseif>


              </do_all>
            </do_if>
          </do_if>
        </do_if>
      </actions>
    </library>

  </cues>
</mdscript>
If I only want to change that opening <do_elseif> line... but it's an open node that contains a WHOLE LOT of stuff in between... in my patch file I need to include that WHOLE LOT of stuff between the <do_elseif></do_elseif> ?

So like...

Code: Select all

  <replace sel="cues/library[@name='CheckObject']/actions/do_if/do_if/do_if/do_all/do_elseif[@value='$Object.isclass.station']">
    <do_elseif value="($Object.isclass.station) and not ($Object.macro.ismacro.[...])">
        <[... WHOLE LOT OF STUFF]/>
    </do_elseif>
  </replace>
UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

if you want to replace the node as whole yes, but as i said in my previous post you can also replace the attribute Value only. like this:

Code: Select all

<replace sel="cues/library[@name='CheckObject']/actions/do_if/do_if/do_if/do_all/do_elseif[@value='$Object.isclass.station']/@value">($Object.isclass.station) and not ($Object.macro.ismacro.[...])</replace>
if not stated otherwise everything i post is licensed under WTFPL

Ich mache keine S&M-Auftragsarbeiten, aber wenn es fragen gibt wie man etwas umsetzen kann helfe ich gerne weiter ;)

I wont do Script&Mod Request work, but if there are questions how to do something i will GLaDly help ;)
User avatar
Simoom
Posts: 1110
Joined: Sat, 30. Oct 10, 14:14
x4

Post by Simoom »

Ok, I got it. Thank you! :)
User avatar
Simoom
Posts: 1110
Joined: Sat, 30. Oct 10, 14:14
x4

Post by Simoom »

Sorry to be back here again with more questions...

How should I handle changing a specific node that has the same name as another one?

For example:

Code: Select all

    <actions>
      <do_if value="a">
      </do_if>
      <do_else>
      </do_else>

      <do_if value="b">
      </do_if>
      <do_else>
      </do_else>
    </actions>
If I want to change the <do_else> node under <do_if value="b">, how do I point to that? Or do I have to replace everything under <actions> entirely?
UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

you could either use the positoon in the node as identifier (i dont recommend this method since its unreliable when using multiple mods):
sel="//do_if[**]/do_else[2]

or you could use the values of some child nodes that differ between them:
sel="//do_if[**]/do_else[some_node_which_is_only_in_the_second_do_else]
for better example the contents of both do_else would be helpful. also a Search for XPath with a short description of your problem might help ;)
if not stated otherwise everything i post is licensed under WTFPL

Ich mache keine S&M-Auftragsarbeiten, aber wenn es fragen gibt wie man etwas umsetzen kann helfe ich gerne weiter ;)

I wont do Script&Mod Request work, but if there are questions how to do something i will GLaDly help ;)
User avatar
Simoom
Posts: 1110
Joined: Sat, 30. Oct 10, 14:14
x4

Post by Simoom »

Hey guys, sorry question again...

What do I do when the of a node contains ' '? It seems to mess up the pathfinding...

Code: Select all

<replace sel="//cue[@name='SectionHandler']/actions/do_elseif[@value='event.param == 'cStaff_move'']/@value">
The 'cStaff_move' is messing things up... how do I prevent that?
UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

this stuff can get rally tricky..
first thing i do is swapping "" and ''

Code: Select all

<replace sel='//cue[@name="SectionHandler"]/actions/do_elseif[@value="event.param == 'cStaff_move'"]/@value'>
then you can use html entities instead of characters directly for the innermost "" and '':
" instead of "
&apos; instead of '

Code: Select all

<replace sel='//cue[@name="SectionHandler"]/actions/do_elseif[@value="event.param == &apos;cStaff_move&apos;"]/@value'>
there are also other tricks you could use - some like co use @value contains(***snippet inside value attribute***) or something, but not familiar with that possibility myself..
if not stated otherwise everything i post is licensed under WTFPL

Ich mache keine S&M-Auftragsarbeiten, aber wenn es fragen gibt wie man etwas umsetzen kann helfe ich gerne weiter ;)

I wont do Script&Mod Request work, but if there are questions how to do something i will GLaDly help ;)
User avatar
Simoom
Posts: 1110
Joined: Sat, 30. Oct 10, 14:14
x4

Post by Simoom »

Thanks as always, UniTrader!

Backtracking a bit, regarding your answer to my previous question:
UniTrader wrote:you could either use the positoon in the node as identifier (i dont recommend this method since its unreliable when using multiple mods):
sel="//do_if[**]/do_else[2]

or you could use the values of some child nodes that differ between them:
sel="//do_if[**]/do_else[some_node_which_is_only_in_the_second_do_else]
for better example the contents of both do_else would be helpful. also a Search for XPath with a short description of your problem might help ;)
I am trying to remove these two nodes in Upkeep_Management.xml:

Code: Select all

  <do_if value="$Object.primarypurpose == objectpurpose.mine">
    <do_if value="$Object.cargo.tags.indexof.{tag.bulk}">
      <do_if value="$Object.units.{unitcategory.orecollector}.count lt 5">
        <signal_cue_instantly cue="md.Upkeep.Deliver_Unit_Start" param="[null, $Object, table[{unitcategory.orecollector} = 5]]"/>
        <include_actions ref="PostMissionAddedActions"/>
      </do_if>
    </do_if>
  </do_if>

  <do_if value="$Object.primarypurpose == objectpurpose.mine">
    <do_if value="$Object.cargo.tags.indexof.{tag.liquid}">
      <do_if value="$Object.units.{unitcategory.gascollector}.count lt 5">
        <signal_cue_instantly cue="md.Upkeep.Deliver_Unit_Start" param="[null, $Object, table[{unitcategory.gascollector} = 5]]"/>
        <include_actions ref="PostMissionAddedActions"/>
      </do_if>
    </do_if>
  </do_if>
Note that both are <do_if> with the exact same value (why would you write it like that Egosoft?? WHY???)

So... to remove them, what do I write? I could do:

Code: Select all

  <remove sel="//library[@name='CheckObject']/actions/do_if/do_if/do_if[@value='md.$AllowMultiMissions or not @$HasMission']/do_all/do_if[@value='$Object.isclass.ship_l or $Object.isclass.ship_xl']/do_if[6]"/>
  <remove sel="//library[@name='CheckObject']/actions/do_if/do_if/do_if[@value='md.$AllowMultiMissions or not @$HasMission']/do_all/do_if[@value='$Object.isclass.ship_l or $Object.isclass.ship_xl']/do_if[7]"/>
But you said that's unreliable. How do I format a child node to the do_if inside the bracket?
pref
Posts: 5625
Joined: Sat, 10. Nov 12, 17:55
x4

Post by pref »

You can also use the contains function, and omit the quotes.
UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

2 possibilities:

either use brackets as i initially said:

Code: Select all

  <remove sel="//library[@name='CheckObject']/actions/do_if/do_if/do_if[@value='md.$AllowMultiMissions or not @$HasMission']/do_all/do_if[@value='$Object.isclass.ship_l or $Object.isclass.ship_xl']/do_if[do_if/do_if/signal_cue_instantly[@cue='md.Upkeep.Deliver_Unit_Start']]"/>
  <remove sel="//library[@name='CheckObject']/actions/do_if/do_if/do_if[@value='md.$AllowMultiMissions or not @$HasMission']/do_all/do_if[@value='$Object.isclass.ship_l or $Object.isclass.ship_xl']/do_if[do_if/do_if/signal_cue_instantly[@cue='md.Upkeep.Deliver_Unit_Start']]"/>
or go down the Path a few more levels and then go back up:

Code: Select all

  <remove sel="//library[@name='CheckObject']/actions/do_if/do_if/do_if[@value='md.$AllowMultiMissions or not @$HasMission']/do_all/do_if[@value='$Object.isclass.ship_l or $Object.isclass.ship_xl']/do_if/do_if/do_if/signal_cue_instantly[@cue='md.Upkeep.Deliver_Unit_Start']/../../.."/>
  <remove sel="//library[@name='CheckObject']/actions/do_if/do_if/do_if[@value='md.$AllowMultiMissions or not @$HasMission']/do_all/do_if[@value='$Object.isclass.ship_l or $Object.isclass.ship_xl']/do_if/do_if/do_if/signal_cue_instantly[@cue='md.Upkeep.Deliver_Unit_Start']/../../.."/>
if not stated otherwise everything i post is licensed under WTFPL

Ich mache keine S&M-Auftragsarbeiten, aber wenn es fragen gibt wie man etwas umsetzen kann helfe ich gerne weiter ;)

I wont do Script&Mod Request work, but if there are questions how to do something i will GLaDly help ;)
User avatar
Simoom
Posts: 1110
Joined: Sat, 30. Oct 10, 14:14
x4

Post by Simoom »

I see! Thank you again UniTrader! :)
ns88ns
Posts: 91
Joined: Sun, 11. Sep 11, 22:00
x4

Post by ns88ns »

Hi, folks.

There is directive <patch/> in MD scripts. I checked XR Wiki and Google but I found almost nothing... So the questions are:

- What is it?
- What it does?
- How it works?

Perhaps I missed somethig so if you point me to the threads/resources where <patch/> is described - then it will be nice.

Thank you in advance.
UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

patch nodes in md are basically actions performed when the version of the cue in the md script file has increased compared to the version stored in the savegame. you can also filtler if the respective patch node should be performed when the cue is complete, canceled, waiting etc.

see also the libraries/md.xsd:

Code: Select all

      <xs:element name="patch" minOccurs="0" maxOccurs="unbounded">
        <xs:annotation>
          <xs:documentation>
            Actions for patching a cue of an older version (will be performed when loading the MD script from a save).
            Patches are applied to waiting, active or complete cues (see state attribute).
            This includes library users and instances, except for completed instances that have already been deleted.
          </xs:documentation>
        </xs:annotation>
        <xs:complexType>
          <xs:group ref="actions" />
          <xs:attribute name="sinceversion" use="required" type="xs:positiveInteger">
            <xs:annotation>
              <xs:documentation>
                Cue version at which this patch element was added (it will only be applied when loading a cue of an older version)
              </xs:documentation>
            </xs:annotation>
          </xs:attribute>
          <xs:attribute name="state">
            <xs:annotation>
              <xs:documentation>
                Required state that this cue must be in for this patch to be applied (default is complete)
              </xs:documentation>
            </xs:annotation>
            <xs:simpleType>
              <xs:restriction base="xs:string">
                <xs:enumeration value="complete">
                  <xs:annotation>
                    <xs:documentation>
                      Apply patch only if this cue is complete (default)
                    </xs:documentation>
                  </xs:annotation>
                </xs:enumeration>
                <xs:enumeration value="waiting">
                  <xs:annotation>
                    <xs:documentation>
                      Apply patch only if this cue is waiting (conditions are being checked)
                    </xs:documentation>
                  </xs:annotation>
                </xs:enumeration>
                <xs:enumeration value="active">
                  <xs:annotation>
                    <xs:documentation>
                      Apply patch only if this cue is active (not complete yet because of a delay)
                    </xs:documentation>
                  </xs:annotation>
                </xs:enumeration>
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
          <xs:attribute name="comment" type="comment" />
        </xs:complexType>
      </xs:element>
if not stated otherwise everything i post is licensed under WTFPL

Ich mache keine S&M-Auftragsarbeiten, aber wenn es fragen gibt wie man etwas umsetzen kann helfe ich gerne weiter ;)

I wont do Script&Mod Request work, but if there are questions how to do something i will GLaDly help ;)
ns88ns
Posts: 91
Joined: Sun, 11. Sep 11, 22:00
x4

Post by ns88ns »

Clear... Will read the XDS more attentively.
Thanks a lot.
ns88ns
Posts: 91
Joined: Sun, 11. Sep 11, 22:00
x4

Post by ns88ns »

Hi.

I would like to ask is there a way to perform mass-replacement? Take a look on data below:
source is:

Code: Select all

<alpha>
  <bravo>
    <chalie attr="a_class_1" />
      <delta attr="b_class_1" />
    </charlie>
    <chalie attr="a_class_2" />
      <delta attr="b_class_2" />
    </charlie>
    <chalie attr="a_class_3" />
      <delta attr="b_class_3" />
    </charlie>
    <chalie attr="a_class_4" />
      <delta attr="c_class_1" />
    </charlie>
    <chalie attr="a_class_5" />
      <delta attr="c_class_2" />
    </charlie>
  </bravo>
</alpha>
Now I''m trying to replace all

Code: Select all

<delta attr="b_class_*" />
with

Code: Select all

<delta attr="e_class_0" />
I tried code below:

Code: Select all

<diff>
  <replace sel="//delta[starts-with(@attr,'b_class_')]">
    <delta attr="e_class_0" />
  </replace>
</diff>
It is just example and selector properly returns set of elements to replace but XR doesn't allow. It logs error:

Code: Select all

["message"]="Error: Multiple matching nodes for path '//macro[starts-with(@ref,'struct_econ_prod_') and contains(@ref,'_stor_')]' in patch file 'extensions\\long_path_here'. Skipping node.",
Thank you.
UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

nope, no mass replacements. (there is an inofficial Extension which has an msel Attribute, but EGO has not implemented it)

but you can make the sel to apply to the first occurence and duplicate/copy the line as often as needed:
<replace sel="(//delta[starts-with(@attr,'b_class_')])[1]">
if the Path still applies after the change also increase the number in square brackets ;)
if not stated otherwise everything i post is licensed under WTFPL

Ich mache keine S&M-Auftragsarbeiten, aber wenn es fragen gibt wie man etwas umsetzen kann helfe ich gerne weiter ;)

I wont do Script&Mod Request work, but if there are questions how to do something i will GLaDly help ;)
ns88ns
Posts: 91
Joined: Sun, 11. Sep 11, 22:00
x4

Post by ns88ns »

Clear. Nice workarround. It will work for me.
Thank you a lot for explanation.
user1679
Posts: 986
Joined: Fri, 20. Jul 18, 23:20

Re: [TUTORIAL] XML Patch Guide

Post by user1679 »

How would I patch an MD script from another mod?

For example, I use the mod Rise of the Ossian Raider and usually set it to difficulty Normal or Hard. Since this defaults to "very easy", how would I patch the MD script instead of having to package the whole 300 Mb mod into a separate version? Would I use a DIFF patching method or could I package my modified \md\ror_setup.xml into a 'sub_01.cat' (I don't know what sub cats are)?

The line I'm interested in changing is:

Code: Select all

<mdscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Ror_setup" xsi:noNamespaceSchemaLocation="md.xsd">
	<cues>	
		<cue name="Ror_Setup_root" version="3">
			<conditions>
			<!--	<event_cue_signalled cue="md.Setup.GameStart"/> -->
				<event_player_created/>
			</conditions>
			<delay exact="10s"/>
			<actions>
			<set_value name="global.$RorSetting.$Difficulty" exact="0" comment="Very Easy - No Invasions"/>
and I want to create two patches that can be enabled as mods that change it to:

Code: Select all

<set_value name="global.$RorSetting.$Difficulty" exact="2" comment="Normal - Invasions"/>

Code: Select all

<set_value name="global.$RorSetting.$Difficulty" exact="3" comment="Hard - Invasions"/>
Clownmug
Posts: 418
Joined: Wed, 11. Dec 13, 02:39
x4

Re: [TUTORIAL] XML Patch Guide

Post by Clownmug »

user1679 wrote: Thu, 20. Aug 20, 05:04 How would I patch an MD script from another mod?

For example, I use the mod Rise of the Ossian Raider and usually set it to difficulty Normal or Hard. Since this defaults to "very easy", how would I patch the MD script instead of having to package the whole 300 Mb mod into a separate version? Would I use a DIFF patching method or could I package my modified \md\ror_setup.xml into a 'sub_01.cat' (I don't know what sub cats are)?

The line I'm interested in changing is:

Code: Select all

<mdscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Ror_setup" xsi:noNamespaceSchemaLocation="md.xsd">
	<cues>	
		<cue name="Ror_Setup_root" version="3">
			<conditions>
			<!--	<event_cue_signalled cue="md.Setup.GameStart"/> -->
				<event_player_created/>
			</conditions>
			<delay exact="10s"/>
			<actions>
			<set_value name="global.$RorSetting.$Difficulty" exact="0" comment="Very Easy - No Invasions"/>
and I want to create two patches that can be enabled as mods that change it to:

Code: Select all

<set_value name="global.$RorSetting.$Difficulty" exact="2" comment="Normal - Invasions"/>

Code: Select all

<set_value name="global.$RorSetting.$Difficulty" exact="3" comment="Hard - Invasions"/>
I don't think you need to patch their file since the difficulty is set as a global variable, it should be accessible from other mdscripts.

If you do need to make a patch though, you would have to create an extensions folder within your own mod, then put the file being patched from the other mod in there.

Example: your_mod\extensions\ror\md\ror_setup.xml

Return to “X Rebirth - Scripts and Modding”