Difference between revisions of "Modding Tutorials/PatchOperations"

From RimWorld Wiki
Jump to navigation Jump to search
Line 10: Line 10:
 
<Patch>
 
<Patch>
 
   <Operation Class="PatchOperationReplace">
 
   <Operation Class="PatchOperationReplace">
     <xpath>*/TerrainDef[defName="WaterDeep"]/texturePath</xpath>
+
     <xpath>Defs/TerrainDef[defName="WaterDeep"]/texturePath</xpath>
 
     <value>
 
     <value>
 
       <texturePath>Your/Texture/Path/Here</texturePath>
 
       <texturePath>Your/Texture/Path/Here</texturePath>
Line 20: Line 20:
  
 
== Overview of available PatchOperations ==
 
== Overview of available PatchOperations ==
There are 11 available PatchOperation types:
+
These are the available PatchOperation types:
  
 
* Four do basic operations on XML nodes:
 
* Four do basic operations on XML nodes:
Line 31: Line 31:
 
** '''[[#PatchOperationAttributeSet|PatchOperationAttributeSet]]''' sets a provided attribute for the selected node, overwriting the attribute value if the attribute name is already present
 
** '''[[#PatchOperationAttributeSet|PatchOperationAttributeSet]]''' sets a provided attribute for the selected node, overwriting the attribute value if the attribute name is already present
 
** '''[[#PatchOperationAttributeRemove|PatchOperationAttributeRemove]]''' removes an attribute for the selected node
 
** '''[[#PatchOperationAttributeRemove|PatchOperationAttributeRemove]]''' removes an attribute for the selected node
* Four allow for more complex operations:
+
* Three allow for more complex operations:
 
** '''[[#PatchOperationAddModExtension|PatchOperationAddModExtension]]''' adds a ModExtension.
 
** '''[[#PatchOperationAddModExtension|PatchOperationAddModExtension]]''' adds a ModExtension.
** '''[[#PatchOperationSetName|PatchOperationSetName]]''' changes the name of a node. It is generally useless.
+
** '''[[#PatchOperationSetName|PatchOperationSetName]]''' changes the name of a node.
 
** '''[[#PatchOperationSequence|PatchOperationSequence]]''' contains a set of other PatchOperations, and aborts upon any operation failing
 
** '''[[#PatchOperationSequence|PatchOperationSequence]]''' contains a set of other PatchOperations, and aborts upon any operation failing
 +
* Three allow for conditional operations
 
** '''[[#PatchOperationTest|PatchOperationTest]]''' tests nodes, which is useful inside a PatchOperationSequence
 
** '''[[#PatchOperationTest|PatchOperationTest]]''' tests nodes, which is useful inside a PatchOperationSequence
 +
** '''[[#PatchOperationConditional|PatchOperationConditional]]''' tests nodes, and can do different operations depending on the result.
 +
** '''[[#PatchOperationFindMod|PatchOperationFindMod]]''' tests for the presence of another mod, and can do different operations depending on the result.
  
 
==XPath syntax==
 
==XPath syntax==
Each PatchOperation must specify an XML node to modify. It uses XPaths to specify them. An XPath is a "path" for the patch to follow when trying to find XML nodes matching the description. It will follow each part of the path from left to right.
+
Each PatchOperation must specify an XML node to modify. It uses XPaths to specify them. An XPath is a "path" for the patch to follow when trying to find XML nodes matching the description. It will follow each part of the path from left to right. Note that these "paths" have zero relation to the file path or folder structure: They follow the structure of the XML nodes.
  
Most XPaths should start with <code>*/</code>. The <code>*</code> matches any element, so it'll match the root (the top level) of the XML tree. The following slash selects  Using <code>//</code> is technically also valid, but because that construct selects ''all'' the elements in the XML tree, it's an expensive operation that makes a mod needlessly slow to load.
+
XPaths start at the root node. Since the root node of all Defs is enforced to be <Defs>, so <code>Defs/</code> is the start of our xpath. <code>*/</code> is also valid, as it uses the <code>*</code> wildcard: <code>*</code> matches any element, so it'll match the root (the top level) of the XML tree. <code>//</code> is technically also valid, but because that construct selects ''all'' the elements in the XML tree, it's an expensive operation that makes game loading exponentially slower.
  
 
Once the root is selected, the XPath lets the system walk down specific branches of the tree. If we use the example from the lead, we get a specific path:
 
Once the root is selected, the XPath lets the system walk down specific branches of the tree. If we use the example from the lead, we get a specific path:
  
<code>*/TerrainDef[defName="WaterDeep"]/texturePath</code>
+
<code>Defs/TerrainDef[defName="WaterDeep"]/texturePath</code>
  
 
# The root node is selected
 
# The root node is selected
Line 59: Line 62:
 
|-
 
|-
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <exampleNode>Some text</exampleNode>
+
        <defName>Example</defName>
    <exampleList />
+
        <exampleNode>Some text</exampleNode>
</ExampleDef>
+
        <exampleList />
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
Line 75: Line 80:
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <exampleNode>Some text</exampleNode>
+
        <defName>Example</defName>
    <exampleList>
+
        <exampleNode>Some text</exampleNode>
        <li>Foo</li>
+
        <exampleList>
        <li>Bar</li>
+
            <li>Foo</li>
    </exampleList>
+
            <li>Bar</li>
</ExampleDef>
+
        </exampleList>
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
|-
 
|-
Line 93: Line 100:
 
|-
 
|-
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <exampleNode>Some text</exampleNode>
+
        <defName>Example</defName>
    <exampleList />
+
        <exampleNode>Some text</exampleNode>
</ExampleDef>
+
        <exampleList />
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
Line 108: Line 117:
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <exampleNode>Some text</exampleNode>
+
        <defName>Example</defName>
    <anotherExample>A new sibling</anotherExample>
+
        <exampleNode>Some text</exampleNode>
    <exampleList />
+
        <anotherExample>A new sibling</anotherExample>
</ExampleDef>
+
        <exampleList />
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
|-
 
|-
Line 125: Line 136:
 
|-
 
|-
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <exampleNode>Some text</exampleNode>
+
        <defName>Example</defName>
    <exampleList />
+
        <exampleNode>Some text</exampleNode>
</ExampleDef>
+
        <exampleList />
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
Line 137: Line 150:
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <exampleList />
+
        <defName>Example</defName>
</ExampleDef>
+
        <exampleList />
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
|-
 
|-
Line 151: Line 166:
 
|-
 
|-
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <exampleNode>Some text</exampleNode>
+
        <defName>Example</defName>
    <exampleList />
+
        <exampleNode>Some text</exampleNode>
</ExampleDef>
+
        <exampleList />
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
Line 166: Line 183:
 
</source>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
| style="vertical-align: top;" |<source lang="xml">
<ExampleDef>
+
<Defs>
    <defName>Example</defName>
+
    <ExampleDef>
    <anotherExample>An alternate node</anotherExample>
+
        <defName>Example</defName>
    <exampleList />
+
        <anotherExample>An alternate node</anotherExample>
</ExampleDef>
+
        <exampleList />
 +
    </ExampleDef>
 +
</Defs>
 
</source>
 
</source>
 
|-
 
|-
Line 182: Line 201:
 
===PatchOperationSequence===
 
===PatchOperationSequence===
 
===PatchOperationTest===
 
===PatchOperationTest===
 +
===PatchOperationConditional===
 +
===PatchOperationFindMod===
  
 
== Resources ==
 
== Resources ==

Revision as of 12:56, 29 January 2019

Modding Tutorials

RimWorld Alpha 17 added a new modding feature: XML PatchOperations. Using these allows mods to edit specific elements of XML defs, rather than simply overwriting the old def with a new one, which was the required approach until A17. This greatly reduces mod conflicts, because mods that make separate changes to the same def won't necessarily overwrite one another's changes. Mods using PatchOperations can avoid requiring special compatibility mods, and can even add special features conditional on other mods being loaded.

Here's a simple example of replacing the texture path for deep water:

<?xml version="1.0" encoding="utf-8" ?>
<Patch>
  <Operation Class="PatchOperationReplace">
    <xpath>Defs/TerrainDef[defName="WaterDeep"]/texturePath</xpath>
    <value>
      <texturePath>Your/Texture/Path/Here</texturePath>
    </value>
  </Operation>
</Patch>

This xml file simply goes inside YourMod/Patches/ and you're done.

Overview of available PatchOperations

These are the available PatchOperation types:

XPath syntax

Each PatchOperation must specify an XML node to modify. It uses XPaths to specify them. An XPath is a "path" for the patch to follow when trying to find XML nodes matching the description. It will follow each part of the path from left to right. Note that these "paths" have zero relation to the file path or folder structure: They follow the structure of the XML nodes.

XPaths start at the root node. Since the root node of all Defs is enforced to be <Defs>, so Defs/ is the start of our xpath. */ is also valid, as it uses the * wildcard: * matches any element, so it'll match the root (the top level) of the XML tree. // is technically also valid, but because that construct selects all the elements in the XML tree, it's an expensive operation that makes game loading exponentially slower.

Once the root is selected, the XPath lets the system walk down specific branches of the tree. If we use the example from the lead, we get a specific path:

Defs/TerrainDef[defName="WaterDeep"]/texturePath

  1. The root node is selected
  2. All child nodes of the root node called "TerrainDef" are selected
  3. We narrow down from "all TerrainDefs" to only those with the child "defName" node whose value is "WaterDeep", which should give us exactly one node because defNames must be unique
  4. We select the child node "texturePath" from this node

Overview of individual PatchOperations

(to do)

PatchOperationAdd

Def before patch Patch operation Def after patch
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <exampleNode>Some text</exampleNode>
        <exampleList />
    </ExampleDef>
</Defs>
<Operation Class="PatchOperationAdd">
    <xpath>*/ExampleDef[defName="Example"]/exampleList</xpath>
    <value>
        <li>Foo</li>
        <li>Bar</li>
    </value>
</Operation>
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <exampleNode>Some text</exampleNode>
        <exampleList>
            <li>Foo</li>
            <li>Bar</li>
        </exampleList>
    </ExampleDef>
</Defs>

PatchOperationInsert

Def before patch Patch operation Def after patch
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <exampleNode>Some text</exampleNode>
        <exampleList />
    </ExampleDef>
</Defs>
<Operation Class="PatchOperationInsert">
    <xpath>*/ExampleDef[defName="Example"]/exampleNode</xpath>
    <value>
        <anotherExample>A new sibling</anotherExample>
    </value>
</Operation>
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <exampleNode>Some text</exampleNode>
        <anotherExample>A new sibling</anotherExample>
        <exampleList />
    </ExampleDef>
</Defs>


PatchOperationRemove

Def before patch Patch operation Def after patch
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <exampleNode>Some text</exampleNode>
        <exampleList />
    </ExampleDef>
</Defs>
<Operation Class="PatchOperationRemove">
    <xpath>*/ExampleDef[defName="Example"]/exampleNode</xpath>
</Operation>
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <exampleList />
    </ExampleDef>
</Defs>

PatchOperationReplace

Def before patch Patch operation Def after patch
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <exampleNode>Some text</exampleNode>
        <exampleList />
    </ExampleDef>
</Defs>
<Operation Class="PatchOperationReplace">
    <xpath>*/ExampleDef[defName="Example"]/exampleNode</xpath>
    <value>
        <anotherExample>An alternate node</anotherExample>
    </value>
</Operation>
<Defs>
    <ExampleDef>
        <defName>Example</defName>
        <anotherExample>An alternate node</anotherExample>
        <exampleList />
    </ExampleDef>
</Defs>

PatchOperationAttributeAdd

PatchOperationAttributeSet

PatchOperationAttributeRemove

PatchOperationAddModExtension

PatchOperationSetName

PatchOperationSequence

PatchOperationTest

PatchOperationConditional

PatchOperationFindMod

Resources

The best tutorial on PatchOperations created by Zhentar: Introduction to PatchOperation

You can also learn about XPaths here: XPath tutorial

There is also an Overview of PatchOperations on the RimWorldModGuide.