Difference between revisions of "Modding Tutorials/PatchOperations"

From RimWorld Wiki
Jump to navigation Jump to search
(Overhaul pass with custom styling and transclusions)
Line 1: Line 1:
 
{{BackToTutorials}}
 
{{BackToTutorials}}
  
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.
+
PatchOperations are a feature that allows you to modify the content of XML Defs. Prior to RimWorld alpha 17, modders could only modify Defs by creating new ones that overwrote the original; this often resulted in compatibility issues as if more than one mod tried to overwrite the same Def, only the last mod in the load order would succeed as prior ones would themselves be overwritten.
  
Here's a simple example of replacing the texture path for concrete:
+
<div style="float: right;">
 +
__TOC__
 +
</div>
 +
= Basics =
 +
 
 +
PatchOperations are written as XML nodes that go into your mod's <tt>Patches</tt> folder:
 +
 
 +
<source>
 +
MyModFolder
 +
├ About
 +
├ Defs
 +
└ Patches
 +
  └ MyPatchFile.xml
 +
</source>
 +
 
 +
Just as with XML Defs, folder and file names do not matter and you can freely name and organize your patche files in whatever manner you wish. Individual patches themselves are a standard XML file with <tt><Patch></tt> as the root tag:
  
 
<source lang="xml">
 
<source lang="xml">
<?xml version="1.0" encoding="utf-8" ?>
+
<?xml version="1.0" encoding="utf-8"?>
 
<Patch>
 
<Patch>
   <Operation Class="PatchOperationReplace">
+
 
    <xpath>Defs/TerrainDef[defName="Concrete"]/graphicData/texPath</xpath>
+
   <!-- PatchOperations go here -->
    <value>
+
 
      <texPath>Your/Texture/Path/Here</texPath>
 
    </value>
 
  </Operation>
 
 
</Patch>
 
</Patch>
 
</source>
 
</source>
This xml file simply goes inside YourMod/Patches/ and you're done.
 
  
== Overview of available PatchOperations ==
+
== XPath ==
These are the available PatchOperation types:
+
Most PatchOperations must be targeted at one or more XML nodes inside the master XML document. This is done via [https://developer.mozilla.org/en-US/docs/Web/XPath XML Patch Language] or XPath.
  
* Four do basic operations on XML nodes:
+
Note that an XPath is targeting the structure of the XML document after it's been parsed by RimWorld's parser, thus it has nothing to do with the actual file or folder paths. For example, if you wanted to add a stat value to the vanilla Wall building, you might use an XPath like so:
** '''[[#PatchOperationAdd|PatchOperationAdd]]''' adds a provided child node to the selected node
 
** '''[[#PatchOperationInsert|PatchOperationInsert]]''' inserts a provided sibling node above the selected node
 
** '''[[#PatchOperationRemove|PatchOperationRemove]]''' deletes the selected node
 
** '''[[#PatchOperationReplace|PatchOperationReplace]]''' replaces the selected node with the provided node
 
* Three do basic operations on XML attributes:
 
** '''[[#PatchOperationAttributeAdd|PatchOperationAttributeAdd]]''' adds a provided attribute to the selected node if and only if the attribute name is not 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
 
* Three allow for more complex operations:
 
** '''[[#PatchOperationAddModExtension|PatchOperationAddModExtension]]''' adds a ModExtension.
 
** '''[[#PatchOperationSetName|PatchOperationSetName]]''' changes the name of a node.
 
** '''[[#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
 
** '''[[#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==
+
<code>Defs/ThingDef[defName="Wall"]/statBases</code>
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 <code><Defs></code>, our xpath always starts with <code>Defs/</code>. Starting with <code>/Defs/</code> or <code>*/</code> works, but it is not recommended because it incurs additional overhead due to the way the XPath resolver works.
+
* The first segment of any XPath targeting an XML Def will be <tt>Defs/</tt> as all XML Defs use the <tt><Defs></tt> root tag.
 +
* The square brackets denote a predicate, or conditional match. In this case, we are looking for <tt>ThingDef</tt>s that have a child tag <tt>defName</tt> equal to "Wall".
  
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:
+
You can target Defs that do not have a <tt>defName</tt> (such as abstract bases) by targeting their identifying attributes. For example, if you wanted to add another Stuff category to the abstract base Def for all shelves, you might use the following xpath:
  
<code>Defs/TerrainDef[defName="WaterDeep"]/texturePath</code>
+
<code>Defs/ThingDef[@Name="ShelfBase"]/stuffCategories</code>
  
# The root node is selected
+
Additional XPath resources:
# All child nodes of the root node called "TerrainDef" are selected
+
* Ludeon Forum Thread: {{LudeonThread|32785}}
# 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
+
* [https://www.w3schools.com/xml/xml_xpath.asp W3Schools XPath Tutorial]
# We select the child node "texturePath" from this node
+
* [https://stackoverflow.com/questions/tagged/xpath StackOverflow XPath Tag]
## Note that we cannot put <code>.../texturePath/</code> to specify the content of the texturePath node. If we want to replace texturePath, we have to replace it with <code><texturePath>/new/path/to/texture</texturePath></code>
 
  
For more info, see this {{LudeonThread|32785}}. XPath is a [https://www.w3schools.com/xml/xml_xpath.asp well-documented industry standard] (good resource, too); there are plenty of resources available on the subject ranging from exhaustive tutorials to simple [https://stackoverflow.com/questions/tagged/xpath StackOverflow] questions.
+
= PatchOperation Types =
  
=="Success" option==
+
<div class="TwoColumnCollapsibleLayout">
 +
<div class="TwoColumnCollapsibleLayout-section">
 +
<div class="TwoColumnCollapsibleLayout-subtitle">Basic XML node operations:</div>
 +
<div class="TwoColumnCollapsibleLayout-text">
 +
* '''[[#PatchOperationAdd|PatchOperationAdd]]''' adds a provided child node to the selected node
 +
* '''[[#PatchOperationInsert|PatchOperationInsert]]''' inserts a provided sibling node above the selected node
 +
* '''[[#PatchOperationRemove|PatchOperationRemove]]''' deletes the selected node
 +
* '''[[#PatchOperationReplace|PatchOperationReplace]]''' replaces the selected node with the provided node
 +
</div>
 +
</div>
 +
<div class="TwoColumnCollapsibleLayout-section">
 +
<div class="TwoColumnCollapsibleLayout-subtitle">XML attribute operations:</div>
 +
<div class="TwoColumnCollapsibleLayout-text">
 +
* '''[[#PatchOperationAttributeAdd|PatchOperationAttributeAdd]]''' adds a provided attribute to the selected node if and only if the attribute name is not 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
 +
</div>
 +
</div>
 +
<div class="TwoColumnCollapsibleLayout-section">
 +
<div class="TwoColumnCollapsibleLayout-subtitle">Special operations:</div>
 +
<div class="TwoColumnCollapsibleLayout-text">
 +
* '''[[#PatchOperationSequence|PatchOperationSequence]]''' contains a set of other PatchOperations, and aborts upon any operation failing
 +
* '''[[#PatchOperationAddModExtension|PatchOperationAddModExtension]]''' adds a ModExtension.
 +
* '''[[#PatchOperationSetName|PatchOperationSetName]]''' changes the name of a node.
 +
</div>
 +
</div>
 +
<div class="TwoColumnCollapsibleLayout-section">
 +
<div class="TwoColumnCollapsibleLayout-subtitle">Conditional operations:</div>
 +
<div class="TwoColumnCollapsibleLayout-text">
 +
* '''[[#PatchOperationFindMod|PatchOperationFindMod]]''' tests for the presence of another mod, and can do different operations depending on the result.
 +
* '''[[#PatchOperationConditional|PatchOperationConditional]]''' tests nodes, and can do different operations depending on the result.
 +
* '''[[#PatchOperationTest|PatchOperationTest]]''' tests nodes, which is useful inside a PatchOperationSequence
 +
</div>
 +
</div>
 +
</div>
  
The ''<code><success>...</success></code>'' node determines how errors are handled, usually in a PatchOperationSequence. '''Note that use of this tag should be considered obsolete'''; its use was common before PatchOperationConditional was implemented, but it should no longer be required and can cause confusion as it can suppress legitimate errors from showing up.
+
==PatchOperationAdd==
 +
Inserts the specified <code>value</code>s as a child node of the XML nodes targeted by the operation's <code>xpath</code>. By default, the new nodes will be inserted after (appended to) any existing child nodes. You can use <code><order>Prepend</order></code> in the patch operation (order is Append by default).
  
Options available are:
+
Note: PatchOperationAdd will not overwrite any existing tags. If one of your <code>value</code>s overlaps with an existing node and you are not targeting a list node, then it will cause an error on game load.
* Always - This patch operation always succeeds, i.e. it suppresses all errors that might have occurred. This used to be used in PatchOperationSequence along with PatchOperationTest in order to conditionally run patches, but is now obsolete.
 
* Normal - Errors are handled normally
 
* Invert - Errors are considered a success and success is considered a failure. This used to be used in PatchOperationSequences to test the negative of a PatchOperationTest; you should now use <code><nomatch></code> on PatchOperationConditional
 
* Never - This patch operation always fails. Generally only used in testing to see if a Sequence is working correctly, should never be used in published mods.
 
  
==Overview of individual PatchOperations==
+
<div class="TutorialTableWrapper">
===PatchOperationAdd===
+
{| class="TutorialCodeTable"
''PatchOperationAdd'' adds a child node at the end of the selected node. The child can be added at the beginning using <code><order>Prepend</order></code> in the patch operation (order is Append by default).
+
! Before !! Patch operation || After
 
 
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
 
! Def before patch !! Def after patch !! Patch operation
 
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
<ExampleDef>
 
  <defName>NameX</defName>
 
  <aaa>Some text</aaa>
 
 
 
</ExampleDef>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>SampleDef</defName>
 
   <aaa>Some text</aaa>
 
   <aaa>Some text</aaa>
  <bbb>New text</bbb>
 
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 
<Operation Class="PatchOperationAdd">
 
<Operation Class="PatchOperationAdd">
   <xpath>Defs/ExampleDef[defName="NameX"]</xpath>
+
   <xpath>Defs/ExampleDef[defName="SampleDef"]</xpath>
 
   <value>
 
   <value>
 
     <bbb>New text</bbb>
 
     <bbb>New text</bbb>
Line 95: Line 109:
 
</Operation>  
 
</Operation>  
 
</source>
 
</source>
|-
+
| <source lang="xml">
| style="vertical-align: top;" |<source lang="xml">
 
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>SampleDef</defName>
   <exampleList>
+
   <aaa>Some text</aaa>
   
+
   <bbb>New text</bbb>
    <li>Bar</li>
 
   </exampleList>
 
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
|-
 +
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>SampleDef</defName>
 
   <exampleList>
 
   <exampleList>
    <li>Foo</li>
 
 
     <li>Bar</li>
 
     <li>Bar</li>
 
   </exampleList>
 
   </exampleList>
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 
<Operation Class="PatchOperationAdd">
 
<Operation Class="PatchOperationAdd">
   <xpath>Defs/ExampleDef[defName="NameX"]/exampleList</xpath>
+
   <xpath>Defs/ExampleDef[defName="SampleDef"]/exampleList</xpath>
 
   <order>Prepend</order>
 
   <order>Prepend</order>
 
   <value>
 
   <value>
Line 122: Line 133:
 
   </value>
 
   </value>
 
</Operation>  
 
</Operation>  
 +
</source>
 +
| <source lang="xml">
 +
<ExampleDef>
 +
  <defName>SampleDef</defName>
 +
  <exampleList>
 +
    <li>Foo</li>
 +
    <li>Bar</li>
 +
  </exampleList>
 +
</ExampleDef>
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
  
===PatchOperationInsert===
+
==PatchOperationInsert==
''PatchOperationInsert'' inserts a sibling above the selected node. The sibling can be inserted below using <code><order>Append</order></code> in the patch operation (order is Prepend by default). Note that <code><xpath>Defs/ExampleDef[defName="NameX"]/exampleList/li[text()="target"]</xpath></code> would also work as a xpath in this example.
+
Inserts the specified <code>value</code>s as a sibling above the selected node(s). You can specify <code><order>Append</order></code> to insert it after the targeted node(s) instead (default is <code>Prepend</code>).
  
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
<div class="TutorialTableWrapper">
! Def before patch !! Def after patch !! Patch operation
+
{| class="TutorialCodeTable"
 +
! Before !! Patch operation || After
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>Rainbow</defName>
   <exampleList>
+
   <colors>
     <li>some text</li>
+
     <li>Red</li>
      
+
     <li>Yellow</li>
     <li>target</li>
+
    <li>Green</li>
     <li>more text</li>
+
     <li>Blue</li>
   </exampleList>
+
     <li>Violet</li>
 +
   </colors>
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
<ExampleDef>
 
  <defName>NameX</defName>
 
  <exampleList>
 
    <li>some text</li>
 
    <li>new sibling</li>
 
    <li>target</li>
 
    <li>more text</li>
 
  </exampleList>
 
</ExampleDef>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
 
<Operation Class="PatchOperationInsert">
 
<Operation Class="PatchOperationInsert">
   <xpath>Defs/ExampleDef[defName="NameX"]/exampleList/li[1]</xpath>
+
   <xpath>Defs/ExampleDef[defName="Rainbow"]/colors/li[text()="Yellow"]</xpath>
 
   <value>
 
   <value>
     <li>new sibling</li>
+
     <li>Orange</li>
 
   </value>
 
   </value>
 
</Operation>
 
</Operation>
 
</source>
 
</source>
|-
+
| <source lang="xml">
| style="vertical-align: top;" |<source lang="xml">
 
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>Rainbow</defName>
   <exampleList>
+
   <colors>
     <li>some text</li>
+
     <li>Red</li>
     <li>target</li>
+
     <li>Orange</li>
      
+
     <li>Yellow</li>
     <li>more text</li>
+
    <li>Green</li>
   </exampleList>
+
    <li>Blue</li>
 +
     <li>Violet</li>
 +
   </colors>
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
|-
 +
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>Fish</defName>
   <exampleList>
+
   <lines>
     <li>some text</li>
+
     <li>one fish</li>
     <li>target</li>
+
     <li>two fish</li>
    <li>new sibling</li>
+
   </lines>
    <li>more text</li>
 
   </exampleList>
 
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 
<Operation Class="PatchOperationInsert">
 
<Operation Class="PatchOperationInsert">
   <xpath>Defs/ExampleDef[defName="NameX"]/exampleList/li[1]</xpath>
+
   <xpath>Defs/ExampleDef[defName="Fish"]/lines/li[text()="two fish"]</xpath>
 
   <order>Append</order>
 
   <order>Append</order>
 
   <value>
 
   <value>
     <li>new sibling</li>
+
     <li>red fish</li>
 +
    <li>blue fish</li>
 
   </value>
 
   </value>
 
</Operation>
 
</Operation>
 +
</source>
 +
| <source lang="xml">
 +
<ExampleDef>
 +
  <defName>Fish</defName>
 +
  <lines>
 +
    <li>one fish</li>
 +
    <li>two fish</li>
 +
    <li>red fish</li>
 +
    <li>blue fish</li>
 +
  </lines>
 +
</ExampleDef>
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
  
===PatchOperationRemove===
+
==PatchOperationRemove==
''PatchOperationRemove'' removes the selected node.
+
Removes the targeted node.
  
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
<div class="TutorialTableWrapper">
! Def before patch !! Def after patch !! Patch operation
+
{| class="TutorialCodeTable"
 +
! Before !! Patch operation || After
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>Sample</defName>
   <aaa>Some text</aaa>
+
   <foo>Uno</foo>
   <bbb>More text</bbb>
+
   <bar>Dos</bar>
 +
  <baz>Tres</baz>
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 +
<Operation Class="PatchOperationRemove">
 +
  <xpath>Defs/ExampleDef[defName="Sample"]/bar</xpath>
 +
</Operation>
 +
</source>
 +
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>Sample</defName>
 
+
  <foo>Uno</foo>
   <bbb>More text</bbb>
+
   <baz>Tres</baz>
 
</ExampleDef>
 
</ExampleDef>
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
<Operation Class="PatchOperationRemove">
 
  <xpath>Defs/ExampleDef[defName="NameX"]/aaa</xpath>
 
</Operation>
 
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
  
===PatchOperationReplace===
+
==PatchOperationReplace==
''PatchOperationReplace'' replaces the selected node.
+
Replaces the selected node(s) with your <code>value</code>s.
  
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
<div class="TutorialTableWrapper">
! Def before patch !! Def after patch !! Patch operation
+
{| class="TutorialCodeTable"
 +
! Before !! Patch operation || After
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>NameX</defName>
+
   <defName>Sample</defName>
   <aaa>Some text</aaa>
+
   <foo>Uno</foo>
</ExampleDef>
+
   <bar>Dos</bar>
</source>
+
   <baz>Tres</baz>
| style="vertical-align: top;" |<source lang="xml">
 
<ExampleDef>
 
   <defName>NameX</defName>
 
   <bbb>Alternate node</bbb>
 
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 
<Operation Class="PatchOperationReplace">
 
<Operation Class="PatchOperationReplace">
   <xpath>Defs/ExampleDef[defName="NameX"]/aaa</xpath>
+
   <xpath>Defs/ExampleDef[defName="Sample"]/baz</xpath>
 
   <value>
 
   <value>
     <bbb>Alternate node</bbb>
+
     <baz>Drei</baz>
 
   </value>
 
   </value>
 
</Operation>
 
</Operation>
 +
</source>
 +
| <source lang="xml">
 +
<ExampleDef>
 +
  <defName>Sample</defName>
 +
  <foo>Uno</foo>
 +
  <bar>Dos</bar>
 +
  <baz>Drei</baz>
 +
</ExampleDef>
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
  
===PatchOperationAttributeAdd===
+
==PatchOperationAttributeAdd==
''PatchOperationAttributeAdd'' will add an attribute and set its value, but only if that attribute is not yet present.
+
Adds the specified attribute to the targeted node(s), but only if that attribute is not yet present.
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
 
! Def before patch !! Def after patch !! Patch operation
+
<div class="TutorialTableWrapper">
 +
{| class="TutorialCodeTable"
 +
! Before !! Patch operation || After
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>Xyz</defName>
+
   <defName>Sample</defName>
   <aaa>Text</aaa>
+
   <foo>Uno</foo>
 +
  <bar>Dos</bar>
 +
  <baz>Tres</baz>
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
<ExampleDef>
 
  <defName>Xyz</defName>
 
  <aaa ExampleAtt="Foo">Text</aaa>
 
</ExampleDef>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
 
<Operation Class="PatchOperationAttributeAdd">
 
<Operation Class="PatchOperationAttributeAdd">
   <xpath>Defs/ExampleDef[defName="Xyz"]/aaa</xpath>
+
   <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
   <attribute>ExampleAtt</attribute>
+
   <attribute>Name</attribute>
   <value>Foo</value>
+
   <value>SampleBase</value>
 
</Operation>
 
</Operation>
 +
</source>
 +
| <source lang="xml">
 +
<ExampleDef Name="SampleBase">
 +
  <defName>Sample</defName>
 +
  <foo>Uno</foo>
 +
  <bar>Dos</bar>
 +
  <baz>Tres</baz>
 +
</ExampleDef>
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
  
===PatchOperationAttributeSet===
+
==PatchOperationAttributeSet==
''PatchOperationAttributeSet'' will add an attribute and set its value. Unlike PatchAttributeAdd, PatchOperationAttributeSet will overwrite any existing value.  
+
Adds the specified attribute to the targeted node(s), or overwrites the existing value if the specified attribute already exists.  
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
 
! Def before patch !! Def after patch !! Patch operation
+
<div class="TutorialTableWrapper">
 +
{| class="TutorialCodeTable"
 +
! Before !! Patch operation || After
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
<ExampleDef>
+
<ExampleDef Name="SampleSource">
   <defName>Xyz</defName>
+
   <defName>Sample</defName>
   <aaa ExampleAtt="Foo">Text</aaa>
+
   <foo>Uno</foo>
</ExampleDef>
+
   <bar>Dos</bar>
</source>
+
   <baz>Tres</baz>
| style="vertical-align: top;" |<source lang="xml">
 
<ExampleDef>
 
   <defName>Xyz</defName>
 
   <aaa ExampleAtt="Bar">Text</aaa>
 
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 
<Operation Class="PatchOperationAttributeSet">
 
<Operation Class="PatchOperationAttributeSet">
   <xpath>Defs/ExampleDef[defName="Xyz"]/aaa</xpath>
+
   <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
   <attribute>ExampleAtt</attribute>
+
   <attribute>Name</attribute>
   <value>Bar</value>
+
   <value>SampleBase</value>
 
</Operation>
 
</Operation>
 +
</source>
 +
| <source lang="xml">
 +
<ExampleDef Name="SampleBase">
 +
  <defName>Sample</defName>
 +
  <foo>Uno</foo>
 +
  <bar>Dos</bar>
 +
  <baz>Tres</baz>
 +
</ExampleDef>
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
 +
 +
==PatchOperationAttributeRemove==
 +
Removes the specified attribute from the targeted node(s).
  
===PatchOperationAttributeRemove===
+
<div class="TutorialTableWrapper">
''PatchOperationAttributeRemove'' removes the specified attribute.
+
{| class="TutorialCodeTable"
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
! Before !! Patch operation || After
! Def before patch !! Def after patch !! Patch operation
 
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
<ExampleDef>
+
<ExampleDef Name="SampleBase">
   <defName>Xyz</defName>
+
   <defName>Sample</defName>
   <aaa ExampleAtt="Foo">Text</aaa>
+
   <foo>Uno</foo>
 +
  <bar>Dos</bar>
 +
  <baz>Tres</baz>
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 +
<Operation Class="PatchOperationAttributeRemove">
 +
  <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
 +
  <attribute>Name</attribute>
 +
</Operation>
 +
</source>
 +
| <source lang="xml">
 
<ExampleDef>
 
<ExampleDef>
   <defName>Xyz</defName>
+
   <defName>Sample</defName>
   <aaa>Text</aaa>
+
   <foo>Uno</foo>
 +
  <bar>Dos</bar>
 +
  <baz>Tres</baz>
 
</ExampleDef>
 
</ExampleDef>
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
<Operation Class="PatchOperationAttributeRemove">
 
  <xpath>Defs/ExampleDef[defName="Xyz"]/aaa</xpath>
 
  <attribute>ExampleAtt</attribute>
 
</Operation>
 
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
  
===PatchOperationAddModExtension===
+
==PatchOperationAddModExtension==
 
{{Main|Modding Tutorials/DefModExtension}}
 
{{Main|Modding Tutorials/DefModExtension}}
''PatchOperationAddModExtension'' adds a [[Modding Tutorials/DefModExtension|Mod Extension]].
+
Adds the specified [[Modding Tutorials/DefModExtension|DefModExtension]] to the targeted <code>Def</code>. Automatically creates a <code><modExtensions></code> node if one doesn't already exist.
  
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
<div class="TutorialTableWrapper">
! Def before patch !! Def after patch !! Patch operation
+
{| class="TutorialCodeTable"
 +
! Before !! Patch operation || After
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
<ExampleDef>
+
<ExampleDef Name="SampleBase">
   <defName>Xyz</defName>
+
   <defName>Sample</defName>
   <aaa>Some text</aaa>
+
   <foo>Uno</foo>
    
+
   <bar>Dos</bar>
    
+
   <baz>Tres</baz>
 
 
 
 
 
 
 
</ExampleDef>
 
</ExampleDef>
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
<ExampleDef>
 
  <defName>Xyz</defName>
 
  <aaa>Some text</aaa>
 
  <modExtensions>
 
    <li Class="SomeNameSpace.SomeExtension">
 
      <exampleValue>true</exampleValue>
 
    </li>
 
  </modExtensions>
 
</ExampleDef>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
 
<Operation Class="PatchOperationAddModExtension">
 
<Operation Class="PatchOperationAddModExtension">
   <xpath>Defs/ExampleDef[defName="Xyz"]</xpath>
+
   <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
 
   <value>
 
   <value>
     <li Class="SomeNameSpace.SomeExtension">
+
     <li Class="MyNamespace.MyModExtension">
       <exampleValue>true</exampleValue>
+
       <key>Value</exampleValue>
 
     </li>
 
     </li>
 
   </value>
 
   </value>
 
</Operation>
 
</Operation>
 +
</source>
 +
| <source lang="xml">
 +
<ExampleDef Name="SampleBase">
 +
  <defName>Sample</defName>
 +
  <foo>Uno</foo>
 +
  <bar>Dos</bar>
 +
  <baz>Tres</baz>
 +
  <modExtensions>
 +
    <li Class="MyNamespace.MyModExtension">
 +
      <key>Value</exampleValue>
 +
    </li>
 +
  </modExtensions>
 +
</ExampleDef>
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
  
===PatchOperationSetName===
+
==PatchOperationSetName==
''PatchOperationSetName'' changes the name of a node. It is primarily useful for changing the names of the nodes in "Dictionary" types, such as for recipe products.
+
Changes the name of a node. Most useful for changing the names of the nodes in "dictionary"-style nodes without changing their contents, such as for stat blocks or recipe products.
  
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
+
<div class="TutorialTableWrapper">
! Def before patch !! Def after patch !! Patch operation
+
{| class="TutorialCodeTable"
 +
! Before !! Patch operation || After
 +
|-
 +
| <source lang="xml">
 +
<ThingDef>
 +
  <defName>ExampleThing</defName>
 +
  <!-- many nodes omitted -->
 +
  <statBases>
 +
    <Insulation_Cold>10</Insulation_Cold>
 +
  </statBases>
 +
</ThingDef>
 +
</source>
 +
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 +
<Operation Class="PatchOperationSetName">
 +
  <xpath>Defs/ThingDef[defName="ExampleThing"]/statBases/Insulation_Cold</xpath>
 +
  <name>Insulation_Heat</name>
 +
</Operation>
 +
</source>
 +
| <source lang="xml">
 +
<ThingDef>
 +
  <defName>ExampleRecipe</defName>
 +
  <!-- many nodes omitted -->
 +
  <statBases>
 +
    <Insulation_Heat>10</Insulation_Heat>
 +
  </statBases>
 +
</ThingDef>
 +
</source>
 
|-
 
|-
| style="vertical-align: top;" |<source lang="xml">
+
| <source lang="xml">
 
<RecipeDef>
 
<RecipeDef>
   <defName>A</defName>
+
   <defName>ExampleRecipe</defName>
    <!--...-->
+
  <!-- many nodes omitted -->
 
   <products>
 
   <products>
 
     <WoodLog>30</WoodLog>
 
     <WoodLog>30</WoodLog>
 
   </products>
 
   </products>
 
</RecipeDef>
 
</RecipeDef>
<RecipeDef>
 
  <defName>B</defName>
 
  <!--...-->
 
  <products>
 
    <WoodLog>300</WoodLog>
 
  </products>
 
</RecipeDef>
 
 
</source>
 
</source>
| style="vertical-align: top;" |<source lang="xml">
+
| class="TutorialCodeTable-highlighted" |<source lang="xml">
 +
<Operation Class="PatchOperationSetName">
 +
  <xpath>Defs/RecipeDef[defName="ExampleRecipe"]/products/WoodLog</xpath>
 +
  <name>Steel</name>
 +
</Operation>
 +
</source>
 +
| <source lang="xml">
 
<RecipeDef>
 
<RecipeDef>
   <defName>A</defName>
+
   <defName>ExampleRecipe</defName>
   <!--...-->
+
   <!-- many nodes omitted -->
 
   <products>
 
   <products>
 
     <Steel>30</Steel>
 
     <Steel>30</Steel>
 
   </products>
 
   </products>
 
</RecipeDef>
 
</RecipeDef>
<RecipeDef>
 
  <defName>B</defName>
 
  <!--...-->
 
  <products>
 
    <Steel>300</Steel>
 
  </products>
 
</RecipeDef>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
<Operation Class="PatchOperationSetName">
 
  <xpath>Defs/RecipeDef[defName="A" or defName="B"]/products/WoodLog</xpath>
 
  <name>Steel</name>
 
</Operation>
 
 
</source>
 
</source>
 
|-
 
|-
 
|}
 
|}
 +
</div>
 +
 +
==PatchOperationSequence==
 +
Contains one or more additional PatchOperations, which are executed in order. If any of them fail, then the Sequence stops and will not run any additional PatchOperations.
  
===PatchOperationSequence===
+
'''WARNING''': PatchOperations within an XML file will run in order even without a PatchOperationSequence and using a PatchOperationSequence can obfuscate or hide errors, making it difficult to debug if your child PatchOperations have errors. Do not use PatchOperationSequence unless you are sequencing multiple PatchOperations with a single PatchOperationConditional or PatchOperationFindMod or you are using MayRequire on child PatchOperations, and even then it's strongly recommended you write your PatchOperations as independent <code>Operation</code>s first to ensure they are working as intended.
''PatchOperationSequence'' simply completes all operations in the order of their listing, but stops when any of them fails.
 
  
'''Patch Operation'''
 
 
<source lang="xml">
 
<source lang="xml">
 
<Operation Class="PatchOperationSequence">
 
<Operation Class="PatchOperationSequence">
 
   <operations>
 
   <operations>
     <li Class="PatchOperationInsert">
+
     <li Class="PatchOperationAdd">
       <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath>
+
       <xpath>Defs/ExampleDef[defName="Sample"]/statBases</xpath>
 
       <value>
 
       <value>
         <anotherExample>A new sibling</anotherExample>
+
         <Mass>10</Mass>
 
       </value>
 
       </value>
 
     </li>
 
     </li>
     <li Class="PatchOperationAdd">
+
     <li Class="PatchOperationSetName">
       <xpath>Defs/ExampleDef[defName="Example"]</xpath>
+
       <xpath>Defs/ExampleDef[defName="Sample"]/statBases/Flammability</xpath>
       <value>
+
       <name>ToxicEnvironmentResistance</name>
        <label>I'm a node</label>
 
      </value>
 
 
     </li>
 
     </li>
 
     <!-- etc -->
 
     <!-- etc -->
Line 447: Line 515:
 
</source>
 
</source>
  
'''Effect'''
+
As mentioned, you can use {{Modding_Tutorials/MayRequire}} on child operations of a PatchOperationSequence, but you should test them individually before adding them to said Sequence:
{| class="wikitable" style="margin-left: auto; margin-right: auto;"
 
! Def before patch !! Def after patch
 
|-
 
| style="vertical-align: top;" |<source lang="xml">
 
<ExampleDef>
 
  <defName>Example</defName>
 
 
 
  <exampleNode>Some text</exampleNode>
 
 
 
  <exampleList />
 
</ExampleDef>
 
</source>
 
| style="vertical-align: top;" |<source lang="xml">
 
<ExampleDef>
 
  <defName>Example</defName>
 
  <anotherExample>A new sibling</anotherExample>
 
  <exampleNode>Some text</exampleNode>
 
  <label>I'm a node</label>
 
  <exampleList />
 
</ExampleDef>
 
</source>
 
|-
 
|}
 
  
'''Another Example''': You can use this operation as a simple and quick way to patch only if a mod is loaded by using the MayRequire attribute:
 
 
<source lang="xml">
 
<source lang="xml">
 
<Operation Class="PatchOperationSequence">
 
<Operation Class="PatchOperationSequence">
 
   <operations>
 
   <operations>
     <li Class="PatchOperationRemove" MayRequire="ludeon.rimworld.royalty"> <!-- Only runs if Royalty is active -->
+
     <li Class="PatchOperationAdd" MayRequire="Ludeon.Rimworld.Biotech"> <!-- Only runs if Biotech is active -->
       <xpath>Defs/ExampleDef[defName="Example"]<xpath>
+
       <xpath>Defs/ThingDef[defName="MechGestator"]/recipes<xpath>
    </li>
 
    <li Class="PatchOperationAdd" MayRequire="ludeon.rimworld.ideology"><!-- Only runs if Ideology is active -->
 
      <xpath>Defs</xpath>
 
 
       <value>
 
       <value>
         <ExampleDef>
+
         <li>MyCustomMech</li>
          <defName>MyBetterExample</defName>
 
        </ExampleDef>
 
 
       </value>
 
       </value>
 
     </li>
 
     </li>
  </operations>
+
    <li Class="PatchOperationAdd" MayRequire="MyProject.OtherModPackageId"><!-- Only runs if the specific mod is active -->
</Operation>
+
       <xpath>Defs/ThingDef[defName="OtherModWorkbench"]/recipes</xpath>
</source>
 
 
 
===PatchOperationTest===
 
''PatchOperationTest'' tests for the existence/validity of an xpath. Useful to stop a PatchOperationSequence.
 
 
 
'''NOTE: Conditionally applying patches in this way is considered obsolete.''' Using PatchOperationConditional is a much better idea as the use of <success>Always</success> here will also suppress legitimate errors, making Sequences difficult to debug.
 
 
 
The following example patch is from Apparello, by Shinzy.
 
<source lang ="xml">
 
<Operation Class="PatchOperationSequence">
 
  <!-- Must use <success>Always</success> because of the PatchOperationTest -->
 
  <success>Always</success>
 
  <!-- check for worn graphics and if none found, add one -->
 
  <operations>
 
    <li Class="PatchOperationTest">
 
      <xpath>Defs/ThingDef[defName = "Apparel_Pants"]/apparel/wornGraphicPath</xpath>
 
      <success>Invert</success>
 
    </li>
 
    <li Class="PatchOperationAdd">
 
       <xpath>Defs/ThingDef[defName = "Apparel_Pants"]/apparel</xpath>
 
 
       <value>
 
       <value>
         <wornGraphicPath>Accessorello/Pants/Pants</wornGraphicPath>
+
         <li>MyCustomResource</li>
 
       </value>
 
       </value>
 
     </li>
 
     </li>
Line 517: Line 536:
 
</source>
 
</source>
  
===PatchOperationConditional===
+
==PatchOperationFindMod==
''PatchOperationConditional'' can branch logic based on a match/nomatch. The following example patch adds a <code><comps></code> node if it does not yet exist, and adds itself
 
 
 
This patch is courtesy Mehni. [https://github.com/Mehni/MoreFactionInteraction/blob/ab3ab2a22ee529ee4e38a471fe150fd48fd07ce9/Patches/MoreFactionInteraction.xml#L40-L62 See original.]
 
<source lang ="xml">
 
<!-- Add /comps/li/compClass if there are no comps yet. -->
 
<!-- Add /li/compClass to /comps if exists (i.e. other mod already added the comps field first) -->
 
<Operation Class="PatchOperationConditional">
 
  <xpath>Defs/WorldObjectDef[defName="Caravan"]/comps</xpath>
 
  <nomatch Class="PatchOperationAdd">
 
    <xpath>Defs/WorldObjectDef[defName="Caravan"]</xpath>
 
    <value>
 
      <comps>
 
        <li>
 
          <compClass>MoreFactionInteraction.WorldObjectComp_CaravanComp</compClass>
 
        </li>
 
      </comps>
 
    </value>
 
  </nomatch>
 
  <match Class="PatchOperationAdd">
 
    <xpath>Defs/WorldObjectDef[defName="Caravan"]/comps</xpath>
 
    <value>
 
      <li>
 
        <compClass>MoreFactionInteraction.WorldObjectComp_CaravanComp</compClass>
 
      </li>
 
    </value>
 
  </match>
 
</Operation>
 
</source>
 
  
===PatchOperationFindMod===
+
Checks whether any of the specified mods or DLCs are loaded and allows you to run an additional PatchOperation for <code>match</code> or <code>nomatch</code> results.
''PatchOperationFindMod'' searches the list of active mods for the mods listed in {{code | 1=<li></li>}} tags.
 
  
For a successful match, {{code|1=<li>ModName</li>}} has to match the {{code|1=<name>ModName</name>}} in the About.xml of the mod you base your patch behavior on. It only needs to find ''one'' of the mods in the list for a successful match.
+
'''WARNING''': Unlike all other mod-compatibility features in RimWorld, PatchOperationFindMod uses the mod ''name'' and not its <code>packageId</code>.
This (shortened) patch is courtesy Mehni. [https://github.com/Mehni/MoreFactionInteraction/blob/ab3ab2a22ee529ee4e38a471fe150fd48fd07ce9/Patches/MoreFactionInteraction.xml#L64 See original.]
 
  
The following Patch adds the RimQuest mod extension to the listed Defs when RimQuest is found.
+
The following example adds a mod extension to the targeted Defs if RimQuest is loaded ([https://github.com/Mehni/MoreFactionInteraction/blob/ab3ab2a22ee529ee4e38a471fe150fd48fd07ce9/Patches/MoreFactionInteraction.xml#L64 See original]):
<source lang ="xml">
+
<source lang="xml">
 
<Operation Class="PatchOperationFindMod">
 
<Operation Class="PatchOperationFindMod">
 
   <mods>
 
   <mods>
Line 570: Line 559:
 
</source>
 
</source>
  
The following Patch replace the tabWindowClass of the xpath when Relations Tab is '''not''' found.
+
The following example replaces the tabWindowClass of the Factions button when Relations Tab is '''not''' loaded:
 
<source lang ="xml">
 
<source lang ="xml">
 
<Operation Class="PatchOperationFindMod">
 
<Operation Class="PatchOperationFindMod">
Line 585: Line 574:
 
</source>
 
</source>
  
==Common issues==
+
==PatchOperationConditional==
Spelling matters, and XPath is case sensitive. It's ''very'' case sensitive. If you've written something that doesn't work but you're convinced it should, put it through some XPath validator. Case sensitivity includes but is not limited to the <code><Operation Class="PatchOperationWhatever"></code>, the XPath, the value, every tag -- spell it right.
 
  
Another common issue is swapping Insert and Add, which leads to a different position in the node. A common misconception is trying to account for file paths. The file path is completely irrelevant to the expression you're writing. XPath is used to search XML content, not files themselves.
+
Tests for the existence/validity of the specified node(s) and allows you to run optional <code>match</code> or <code>nomatch</code> PatchOperations in response.
 +
 
 +
The following example adds a <code><comps></code> node to the Caravan Def if it does not exist already, then adds a custom comp to said list:
 +
<source lang ="xml">
 +
<?xml version="1.0" encoding="utf-8" ?>
 +
<Patch>
  
Still beating your head against a wall? The main RimWorld [https://discord.gg/RimWorld discord] has helpful people and a bot where you can test XPath, and the forums have a [https://ludeon.com/forums/index.php?board=14.0 help] section as well.
+
  <!-- add comps field to Caravan WorldObjectDef if it doesn't exist -->
 +
  <Operation Class="PatchOperationConditional">
 +
    <xpath>Defs/WorldObjectDef[defName="Caravan"]/comps</xpath>
 +
    <nomatch Class="PatchOperationAdd">
 +
      <xpath>Defs/WorldObjectDef[defName="Caravan"]</xpath>
 +
      <value>
 +
        <comps />
 +
      </value>
 +
    </nomatch>
 +
  </Operation>
  
==Some advanced examples==
+
   <!-- add pyromaniac caravan handler comp to Caravan WorldObjectDef -->
This (shortened) patch is courtesy XeoNovaDan. [https://github.com/XeoNovaDan/SurvivalTools/blob/62a4316c81733dde417257bf4bc5c34e3e781c7d/Patches/ExpandedWoodworkingVGP/Patch.xml See original]. Viewer discretion is advised. Parents please take precautions if young children are present.
+
  <Operation Class="PatchOperationAdd">
<source lang = "XML">
+
    <xpath>Defs/WorldObjectDef[defName="Caravan"]/comps</xpath>
<Operation Class="PatchOperationFindMod">
+
    <value>
  <mods>
+
      <li Class="BetterPyromania.WorldObjectCompProperties_Pyromania">
    <li>Expanded Woodworking for Vegetable Garden Project</li>
+
        <fuelCount>20</fuelCount>
  </mods>
+
        <cooldown>30000</cooldown>
   <match Class="PatchOperationSequence">
+
        <needThreshold>0.5</needThreshold>
    <operations>
 
      <!-- Nested PatchOpFindMod. Used because Expanded Woodworking adds more Wood when VGP Xtra is installed. -->
 
      <li Class="PatchOperationFindMod">
 
        <mods>
 
          <li>VGP Xtra Trees and Flowers</li>
 
        </mods>
 
        <match Class="PatchOperationSequence">
 
          <success>Always</success>
 
          <operations>
 
            <!-- Processed camellia lumber. Mostly useful for building long-lasting, simple structures and furniture.-->
 
            <li Class="PatchOperationAddModExtension">
 
              <xpath>Defs/ThingDef[defName="LumberCamellia"]</xpath>
 
              <value>
 
                <li Class="SurvivalTools.StuffPropsTool">
 
                  <toolStatFactors>
 
                    <ConstructionSpeed>0.55</ConstructionSpeed>
 
                  </toolStatFactors>
 
                </li>
 
              </value>
 
            </li>
 
          </operations>
 
        </match>
 
 
       </li>
 
       </li>
     </operations>
+
     </value>
   </match>
+
  </Operation>
 +
 
 +
</Patch>
 +
</source>
 +
 
 +
==PatchOperationTest==
 +
 
 +
{{:Modding_Tutorials/Obsolete}}
 +
 
 +
Tests for the existence/validity of an xpath. Useful as a way to intentionally stop a PatchOperationSequence.
 +
 
 +
'''NOTE: Conditionally applying patches in this way is considered obsolete.''' Using PatchOperationConditional is a much better idea as the use of <success>Always</success> here will also suppress legitimate errors, making Sequences difficult to debug.
 +
 
 +
The following example patch is from Apparello, by Shinzy.
 +
<source lang ="xml">
 +
<Operation Class="PatchOperationSequence">
 +
  <!-- Must use <success>Always</success> because of the PatchOperationTest -->
 +
  <success>Always</success>
 +
  <!-- check for worn graphics and if none found, add one -->
 +
  <operations>
 +
    <li Class="PatchOperationTest">
 +
      <xpath>Defs/ThingDef[defName = "Apparel_Pants"]/apparel/wornGraphicPath</xpath>
 +
      <success>Invert</success>
 +
    </li>
 +
    <li Class="PatchOperationAdd">
 +
      <xpath>Defs/ThingDef[defName = "Apparel_Pants"]/apparel</xpath>
 +
      <value>
 +
        <wornGraphicPath>Accessorello/Pants/Pants</wornGraphicPath>
 +
      </value>
 +
    </li>
 +
   </operations>
 
</Operation>
 
</Operation>
 
</source>
 
</source>
  
===Random stuff===
+
= Miscellaneous =
* I wanted to patch to change thingCategories on Apparel_KidPants but it's defined on ParentName="ChildApparelMakeableBase", I don't want to change it for all ChildApparelMakeableBase. The trick is to add a new node with Inherit="False".
+
 
 +
== Custom PatchOperations ==
 +
Custom PatchOperation types can be created in C# by subclassing <code>Verse.PatchOperation</code>, which is useful for performing a patch based on ModSettings values or other custom behavior.
 +
 
 +
A tutorial for this process does not exist yet, but you can check out the following references of custom PatchOperations:
 +
9 [https://github.com/15adhami/XmlExtensions XML Extensions] - An entire framework mod containing many useful custom PatchOperations.
 +
- [https://github.com/CombatExtended-Continued/CombatExtended/blob/Development/Source/CombatExtended/CombatExtended/PatchOperationMakeGunCECompatible.cs PatchOperationMakeGunCECompatible] - Used by Combat Extended to automatically apply multiple changes to a single firearm for compatibility.
 +
- [https://gist.github.com/Lanilor/e326e33e268e78f68a2f5cd3cdbbe8c0 PatchOperationAddOrReplace] - An example of a custom variant of PatchOperationAdd that replaces an existing value.</br>
 +
 
 +
== "Success" Option ==
 +
 
 +
{{:Modding_Tutorials/Obsolete}}
 +
 
 +
The ''<code><success>...</success></code>'' node determines how errors are handled, usually in a PatchOperationSequence. '''Note that use of this tag should be considered obsolete'''; its use was common before PatchOperationConditional was implemented, but it should no longer be required and can cause confusion as it can suppress legitimate errors from showing up.
 +
 
 +
Options available are:
 +
* Always - This patch operation always succeeds, i.e. it suppresses all errors that might have occurred. This used to be used in PatchOperationSequence along with PatchOperationTest in order to conditionally run patches, but is now obsolete.
 +
* Normal - Errors are handled normally
 +
* Invert - Errors are considered a success and success is considered a failure. This used to be used in PatchOperationSequences to test the negative of a PatchOperationTest; you should now use <code><nomatch></code> on PatchOperationConditional
 +
* Never - This patch operation always fails. Generally only used in testing to see if a Sequence is working correctly, should never be used in published mods.
 +
 
 +
== Tips and Tricks ==
 +
* Patching is run after all XML Defs are loaded into memory and in mod list order. If you are encountering compatibility issues with another mod's PatchOperations, you can use <code>loadBefore</code> and <code>loadAfter</code> in your <code>About.xml</code> to help players resolve these issues.
 +
* Patching is done before Def inheritance takes place. This means that you cannot target tags that are inherited from a parent, but also that if you alter a parent, all of its children will inherit the patched value.
 +
* You can override a value inherited from a parent Def without changing that value for all Defs by using <code>Inherit="False"</code>:
 
<source lang = "xml">
 
<source lang = "xml">
 
<Operation Class="PatchOperationAdd">
 
<Operation Class="PatchOperationAdd">
    <xpath>/Defs/ThingDef[defName = "Apparel_KidPants"]</xpath>
+
  <xpath>/Defs/ThingDef[defName = "Apparel_KidPants"]</xpath>
    <value>
+
  <value>
      <thingCategories Inherit="False">
+
    <thingCategories Inherit="False">
        <li>NewValue</li>
+
      <li>NewValue</li>
      </thingCategories>
+
    </thingCategories>
    </value>
+
  </value>
 
</Operation>
 
</Operation>
 
</source>
 
</source>
* @ select by attribute. Useful for patching (Abstract) bases. <source lang = "xml">Defs/ThingDef[@Name="BuildingBase"]</source>
+
* You can use <code>or</code> in predicates to target multiple nodes: <code>Defs/ThingDef[defName="Cassowary" or defName = "Emu" or defName = "Ostrich" or defName = "Turkey"]</code> This is generally better than using multiple PatchOperations so long as you are making the same changes to each target.
* ''or'' is inclusive. <source lang = "xml">Defs/ThingDef[defName="Cassowary" or defName = "Emu" or defName = "Ostrich" or defName = "Turkey"]</source> selects all of those in a single (faster) operation.
+
* You can use <code>/text()</code> to target the text contents of a tag instead of the whole tag for operations like PatchOperationReplace. This is especially useful if you do not want to accidentally remove attributes.
* PatchOperationAdd and PatchOperationInsert have an ''order'' tag which allows you to either Prepend or Append your add. Default behaviour of Add is to Append, insert Prepends.
 
* In most cases, you just want to replace the value of some text, not replace the whole tag itself. You can use a PatchOperationReplace, and simplify your <value> by selecting the text() node, like this: <source lang = "xml">ExampleTag/text()</source>
 
* There is a ''success'' tag whose values can be Normal, Invert, Always and Never. This is used for the sake of error reporting: by default an error is logged when a patch fails to apply. For PatchOperationSequences used with PatchOperationTests, you can set success to Always to avoid error logging.
 
* Inheritance is handled after patches, since you can change attributes with patches. Therefore, you cannot patch nodes that do not exist before inheritance.
 
* More useful things:
 
** Select nodes by text <source lang = "xml">ExampleTag/li[text()="Submit"]</source>
 
** Select nodes by text with a type of wildcard <source lang = "xml">ExampleTag[contains(text(), "MFI_")]</source>
 
** Exclude nodes with not() <source lang = "xml">[not(xxxxx)]</source>
 
** Go to a specific li in the list. Useful for patching hediff givers. "count" here represents the entry number you want to edit. <source lang = "xml">li[count]</source>
 
** Go back to parentnode with <source lang = "xml">..</source>
 
** Select a list by the value of an item <source lang = "xml">ExampleTag[li/text()="content"]</source>
 
  
== Write your own ==
+
== Common Issues / Troubleshooting ==
Outside the scope of this wiki, but it's possible to write your own PatchOperation.</br>
 
- [https://github.com/NoImageAvailable/CombatExtended/blob/88ef1f86d30e6c285e3370bd9faafa92c84195b0/Source/CombatExtended/CombatExtended/PatchOperationMakeGunCECompatible.cs Example PatchOperationMakeGunCECompatible]</br>
 
- [https://gist.github.com/Lanilor/e326e33e268e78f68a2f5cd3cdbbe8c0 Example PatchOperationAddOrReplace]</br>
 
  
== Resources ==
+
* XPath and XML nodes in general is case-sensitive and must be exact. Copying values such as <code>defName</code> fields is strongly recommended to avoid errors.
 +
* Malformed XML such as unclosed or mismatched tags can cause the XML parser to crash completely, which can result in a blank screen on startup or a "Recovered from a fatal error" screen and the removal of all active mods. If this occurs, run all of your XML through an XML validator to ensure that it is structurally correct. Checking your Player.log file can help in diagnosing the exact cause as well.
 +
* It's very easy to confuse Insert vs Add; Insert will "add" the <code>value</code> as a ''sibling'' of the target node(s), while Add will "insert" the <code>value</code> as a ''child'' of the target node(s).
 +
* Remember that XPath targets the XML data structure, not the file path.
 +
* If you're still stumped, feel free to join the [https://discord.gg/RimWorld RimWorld Discord server] and ask questions in the <code>#mod-development</code> channel.
  
The best tutorial on PatchOperations created by Zhentar: [https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782 Introduction to PatchOperation]
+
== References and Links ==
  
You can also learn about XPaths here: [https://www.w3schools.com/xml/xpath_intro.asp XPath tutorial]
+
{{:Modding_Tutorials/Obsolete}}
 
 
There is also an [https://github.com/roxxploxx/RimWorldModGuide/wiki/SHORTTUTORIAL:-DefPatches Overview of PatchOperations]  on the [https://github.com/roxxploxx/RimWorldModGuide/wiki RimWorldModGuide].
 
  
 +
The best tutorial on PatchOperations created by Zhentar: [https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782 Introduction to PatchOperation]
  
 
[[Category:Modding]]
 
[[Category:Modding]]
 
[[Category:Modding tutorials]]
 
[[Category:Modding tutorials]]

Revision as of 20:37, 10 May 2023

Modding Tutorials

PatchOperations are a feature that allows you to modify the content of XML Defs. Prior to RimWorld alpha 17, modders could only modify Defs by creating new ones that overwrote the original; this often resulted in compatibility issues as if more than one mod tried to overwrite the same Def, only the last mod in the load order would succeed as prior ones would themselves be overwritten.

Basics

PatchOperations are written as XML nodes that go into your mod's Patches folder:

MyModFolder
├ About
├ Defs
└ Patches
  └ MyPatchFile.xml

Just as with XML Defs, folder and file names do not matter and you can freely name and organize your patche files in whatever manner you wish. Individual patches themselves are a standard XML file with <Patch> as the root tag:

<?xml version="1.0" encoding="utf-8"?>
<Patch>

  <!-- PatchOperations go here -->

</Patch>

XPath

Most PatchOperations must be targeted at one or more XML nodes inside the master XML document. This is done via XML Patch Language or XPath.

Note that an XPath is targeting the structure of the XML document after it's been parsed by RimWorld's parser, thus it has nothing to do with the actual file or folder paths. For example, if you wanted to add a stat value to the vanilla Wall building, you might use an XPath like so:

Defs/ThingDef[defName="Wall"]/statBases

  • The first segment of any XPath targeting an XML Def will be Defs/ as all XML Defs use the <Defs> root tag.
  • The square brackets denote a predicate, or conditional match. In this case, we are looking for ThingDefs that have a child tag defName equal to "Wall".

You can target Defs that do not have a defName (such as abstract bases) by targeting their identifying attributes. For example, if you wanted to add another Stuff category to the abstract base Def for all shelves, you might use the following xpath:

Defs/ThingDef[@Name="ShelfBase"]/stuffCategories

Additional XPath resources:

PatchOperation Types

Basic XML node operations:
XML attribute operations:
Special operations:
Conditional operations:

PatchOperationAdd

Inserts the specified values as a child node of the XML nodes targeted by the operation's xpath. By default, the new nodes will be inserted after (appended to) any existing child nodes. You can use <order>Prepend</order> in the patch operation (order is Append by default).

Note: PatchOperationAdd will not overwrite any existing tags. If one of your values overlaps with an existing node and you are not targeting a list node, then it will cause an error on game load.

Before Patch operation After
<ExampleDef>
  <defName>SampleDef</defName>
  <aaa>Some text</aaa>
</ExampleDef>
<Operation Class="PatchOperationAdd">
  <xpath>Defs/ExampleDef[defName="SampleDef"]</xpath>
  <value>
    <bbb>New text</bbb>
  </value>
</Operation>
<ExampleDef>
  <defName>SampleDef</defName>
  <aaa>Some text</aaa>
  <bbb>New text</bbb>
</ExampleDef>
<ExampleDef>
  <defName>SampleDef</defName>
  <exampleList>
    <li>Bar</li>
  </exampleList>
</ExampleDef>
<Operation Class="PatchOperationAdd">
  <xpath>Defs/ExampleDef[defName="SampleDef"]/exampleList</xpath>
  <order>Prepend</order>
  <value>
    <li>Foo</li>
  </value>
</Operation>
<ExampleDef>
  <defName>SampleDef</defName>
  <exampleList>
    <li>Foo</li>
    <li>Bar</li>
  </exampleList>
</ExampleDef>

PatchOperationInsert

Inserts the specified values as a sibling above the selected node(s). You can specify <order>Append</order> to insert it after the targeted node(s) instead (default is Prepend).

Before Patch operation After
<ExampleDef>
  <defName>Rainbow</defName>
  <colors>
    <li>Red</li>
    <li>Yellow</li>
    <li>Green</li>
    <li>Blue</li>
    <li>Violet</li>
  </colors>
</ExampleDef>
<Operation Class="PatchOperationInsert">
  <xpath>Defs/ExampleDef[defName="Rainbow"]/colors/li[text()="Yellow"]</xpath>
  <value>
    <li>Orange</li>
  </value>
</Operation>
<ExampleDef>
  <defName>Rainbow</defName>
  <colors>
    <li>Red</li>
    <li>Orange</li>
    <li>Yellow</li>
    <li>Green</li>
    <li>Blue</li>
    <li>Violet</li>
  </colors>
</ExampleDef>
<ExampleDef>
  <defName>Fish</defName>
  <lines>
    <li>one fish</li>
    <li>two fish</li>
  </lines>
</ExampleDef>
<Operation Class="PatchOperationInsert">
  <xpath>Defs/ExampleDef[defName="Fish"]/lines/li[text()="two fish"]</xpath>
  <order>Append</order>
  <value>
    <li>red fish</li>
    <li>blue fish</li>
  </value>
</Operation>
<ExampleDef>
  <defName>Fish</defName>
  <lines>
    <li>one fish</li>
    <li>two fish</li>
    <li>red fish</li>
    <li>blue fish</li>
  </lines>
</ExampleDef>

PatchOperationRemove

Removes the targeted node.

Before Patch operation After
<ExampleDef>
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>
<Operation Class="PatchOperationRemove">
  <xpath>Defs/ExampleDef[defName="Sample"]/bar</xpath>
</Operation>
<ExampleDef>
  <defName>Sample</defName>
  <foo>Uno</foo>
  <baz>Tres</baz>
</ExampleDef>

PatchOperationReplace

Replaces the selected node(s) with your values.

Before Patch operation After
<ExampleDef>
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>
<Operation Class="PatchOperationReplace">
  <xpath>Defs/ExampleDef[defName="Sample"]/baz</xpath>
  <value>
    <baz>Drei</baz>
  </value>
</Operation>
<ExampleDef>
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Drei</baz>
</ExampleDef>

PatchOperationAttributeAdd

Adds the specified attribute to the targeted node(s), but only if that attribute is not yet present.

Before Patch operation After
<ExampleDef>
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>
<Operation Class="PatchOperationAttributeAdd">
  <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
  <attribute>Name</attribute>
  <value>SampleBase</value>
</Operation>
<ExampleDef Name="SampleBase">
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>

PatchOperationAttributeSet

Adds the specified attribute to the targeted node(s), or overwrites the existing value if the specified attribute already exists.

Before Patch operation After
<ExampleDef Name="SampleSource">
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>
<Operation Class="PatchOperationAttributeSet">
  <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
  <attribute>Name</attribute>
  <value>SampleBase</value>
</Operation>
<ExampleDef Name="SampleBase">
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>

PatchOperationAttributeRemove

Removes the specified attribute from the targeted node(s).

Before Patch operation After
<ExampleDef Name="SampleBase">
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>
<Operation Class="PatchOperationAttributeRemove">
  <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
  <attribute>Name</attribute>
</Operation>
<ExampleDef>
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>

PatchOperationAddModExtension

Adds the specified DefModExtension to the targeted Def. Automatically creates a <modExtensions> node if one doesn't already exist.

Before Patch operation After
<ExampleDef Name="SampleBase">
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
</ExampleDef>
<Operation Class="PatchOperationAddModExtension">
  <xpath>Defs/ExampleDef[defName="Sample"]</xpath>
  <value>
    <li Class="MyNamespace.MyModExtension">
      <key>Value</exampleValue>
    </li>
  </value>
</Operation>
<ExampleDef Name="SampleBase">
  <defName>Sample</defName>
  <foo>Uno</foo>
  <bar>Dos</bar>
  <baz>Tres</baz>
  <modExtensions>
    <li Class="MyNamespace.MyModExtension">
      <key>Value</exampleValue>
    </li>
  </modExtensions>
</ExampleDef>

PatchOperationSetName

Changes the name of a node. Most useful for changing the names of the nodes in "dictionary"-style nodes without changing their contents, such as for stat blocks or recipe products.

Before Patch operation After
<ThingDef>
  <defName>ExampleThing</defName>
  <!-- many nodes omitted -->
  <statBases>
    <Insulation_Cold>10</Insulation_Cold>
  </statBases>
</ThingDef>
<Operation Class="PatchOperationSetName">
  <xpath>Defs/ThingDef[defName="ExampleThing"]/statBases/Insulation_Cold</xpath>
  <name>Insulation_Heat</name>
</Operation>
<ThingDef>
  <defName>ExampleRecipe</defName>
  <!-- many nodes omitted -->
  <statBases>
    <Insulation_Heat>10</Insulation_Heat>
  </statBases>
</ThingDef>
<RecipeDef>
  <defName>ExampleRecipe</defName>
  <!-- many nodes omitted -->
  <products>
    <WoodLog>30</WoodLog>
  </products>
</RecipeDef>
<Operation Class="PatchOperationSetName">
  <xpath>Defs/RecipeDef[defName="ExampleRecipe"]/products/WoodLog</xpath>
  <name>Steel</name>
</Operation>
<RecipeDef>
  <defName>ExampleRecipe</defName>
  <!-- many nodes omitted -->
  <products>
    <Steel>30</Steel>
  </products>
</RecipeDef>

PatchOperationSequence

Contains one or more additional PatchOperations, which are executed in order. If any of them fail, then the Sequence stops and will not run any additional PatchOperations.

WARNING: PatchOperations within an XML file will run in order even without a PatchOperationSequence and using a PatchOperationSequence can obfuscate or hide errors, making it difficult to debug if your child PatchOperations have errors. Do not use PatchOperationSequence unless you are sequencing multiple PatchOperations with a single PatchOperationConditional or PatchOperationFindMod or you are using MayRequire on child PatchOperations, and even then it's strongly recommended you write your PatchOperations as independent Operations first to ensure they are working as intended.

<Operation Class="PatchOperationSequence">
  <operations>
    <li Class="PatchOperationAdd">
      <xpath>Defs/ExampleDef[defName="Sample"]/statBases</xpath>
      <value>
        <Mass>10</Mass>
      </value>
    </li>
    <li Class="PatchOperationSetName">
      <xpath>Defs/ExampleDef[defName="Sample"]/statBases/Flammability</xpath>
      <name>ToxicEnvironmentResistance</name>
    </li>
    <!-- etc -->
  </operations>
</Operation>

As mentioned, you can use Template:Modding Tutorials/MayRequire on child operations of a PatchOperationSequence, but you should test them individually before adding them to said Sequence:

<Operation Class="PatchOperationSequence">
  <operations>
    <li Class="PatchOperationAdd" MayRequire="Ludeon.Rimworld.Biotech"> <!-- Only runs if Biotech is active -->
      <xpath>Defs/ThingDef[defName="MechGestator"]/recipes<xpath>
      <value>
        <li>MyCustomMech</li>
      </value>
    </li>
    <li Class="PatchOperationAdd" MayRequire="MyProject.OtherModPackageId"><!-- Only runs if the specific mod is active -->
      <xpath>Defs/ThingDef[defName="OtherModWorkbench"]/recipes</xpath>
      <value>
        <li>MyCustomResource</li>
      </value>
    </li>
  </operations>
</Operation>

PatchOperationFindMod

Checks whether any of the specified mods or DLCs are loaded and allows you to run an additional PatchOperation for match or nomatch results.

WARNING: Unlike all other mod-compatibility features in RimWorld, PatchOperationFindMod uses the mod name and not its packageId.

The following example adds a mod extension to the targeted Defs if RimQuest is loaded (See original):

<Operation Class="PatchOperationFindMod">
  <mods>
    <li>RimQuest</li>
  </mods>
  <match Class="PatchOperationAddModExtension">
    <xpath>Defs/IncidentDef[defName="MFI_DiplomaticMarriage" or defName="MFI_HuntersLodge" or defName="MFI_Quest_PeaceTalks"]</xpath>
    <value>
      <li Class = "RimQuest.RimQuest_ModExtension">
        <canBeARimQuest>false</canBeARimQuest>
      </li>
    </value>
  </match>
</Operation>

The following example replaces the tabWindowClass of the Factions button when Relations Tab is not loaded:

<Operation Class="PatchOperationFindMod">
  <mods>
    <li>Relations Tab</li>
  </mods>
  <nomatch Class="PatchOperationReplace">
    <xpath>/Defs/MainButtonDef[defName="Factions"]/tabWindowClass</xpath>
    <value>
      <tabWindowClass>MyNameSpace.MyTabWindowClass</tabWindowClass>
    </value>
  </nomatch>
</Operation>

PatchOperationConditional

Tests for the existence/validity of the specified node(s) and allows you to run optional match or nomatch PatchOperations in response.

The following example adds a <comps> node to the Caravan Def if it does not exist already, then adds a custom comp to said list:

<?xml version="1.0" encoding="utf-8" ?>
<Patch>

  <!-- add comps field to Caravan WorldObjectDef if it doesn't exist -->
  <Operation Class="PatchOperationConditional">
    <xpath>Defs/WorldObjectDef[defName="Caravan"]/comps</xpath>
    <nomatch Class="PatchOperationAdd">
      <xpath>Defs/WorldObjectDef[defName="Caravan"]</xpath>
      <value>
        <comps />
      </value>
    </nomatch>
  </Operation>

  <!-- add pyromaniac caravan handler comp to Caravan WorldObjectDef -->
  <Operation Class="PatchOperationAdd">
    <xpath>Defs/WorldObjectDef[defName="Caravan"]/comps</xpath>
    <value>
      <li Class="BetterPyromania.WorldObjectCompProperties_Pyromania">
        <fuelCount>20</fuelCount>
        <cooldown>30000</cooldown>
        <needThreshold>0.5</needThreshold>
      </li>
    </value>
  </Operation>

</Patch>

PatchOperationTest

!
Obsolete
The following content was written for a previous version of RimWorld or is otherwise no longer fully relevant today. Please be mindful of any potential inaccuracies.

Tests for the existence/validity of an xpath. Useful as a way to intentionally stop a PatchOperationSequence.

NOTE: Conditionally applying patches in this way is considered obsolete. Using PatchOperationConditional is a much better idea as the use of <success>Always</success> here will also suppress legitimate errors, making Sequences difficult to debug.

The following example patch is from Apparello, by Shinzy.

<Operation Class="PatchOperationSequence">
  <!-- Must use <success>Always</success> because of the PatchOperationTest -->
  <success>Always</success>
  <!-- check for worn graphics and if none found, add one -->
  <operations>
    <li Class="PatchOperationTest">
      <xpath>Defs/ThingDef[defName = "Apparel_Pants"]/apparel/wornGraphicPath</xpath>
      <success>Invert</success>
    </li>
    <li Class="PatchOperationAdd">
      <xpath>Defs/ThingDef[defName = "Apparel_Pants"]/apparel</xpath>
      <value>
        <wornGraphicPath>Accessorello/Pants/Pants</wornGraphicPath>
      </value>
    </li>
  </operations>
</Operation>

Miscellaneous

Custom PatchOperations

Custom PatchOperation types can be created in C# by subclassing Verse.PatchOperation, which is useful for performing a patch based on ModSettings values or other custom behavior.

A tutorial for this process does not exist yet, but you can check out the following references of custom PatchOperations: 9 XML Extensions - An entire framework mod containing many useful custom PatchOperations. - PatchOperationMakeGunCECompatible - Used by Combat Extended to automatically apply multiple changes to a single firearm for compatibility. - PatchOperationAddOrReplace - An example of a custom variant of PatchOperationAdd that replaces an existing value.

"Success" Option

!
Obsolete
The following content was written for a previous version of RimWorld or is otherwise no longer fully relevant today. Please be mindful of any potential inaccuracies.

The <success>...</success> node determines how errors are handled, usually in a PatchOperationSequence. Note that use of this tag should be considered obsolete; its use was common before PatchOperationConditional was implemented, but it should no longer be required and can cause confusion as it can suppress legitimate errors from showing up.

Options available are:

  • Always - This patch operation always succeeds, i.e. it suppresses all errors that might have occurred. This used to be used in PatchOperationSequence along with PatchOperationTest in order to conditionally run patches, but is now obsolete.
  • Normal - Errors are handled normally
  • Invert - Errors are considered a success and success is considered a failure. This used to be used in PatchOperationSequences to test the negative of a PatchOperationTest; you should now use <nomatch> on PatchOperationConditional
  • Never - This patch operation always fails. Generally only used in testing to see if a Sequence is working correctly, should never be used in published mods.

Tips and Tricks

  • Patching is run after all XML Defs are loaded into memory and in mod list order. If you are encountering compatibility issues with another mod's PatchOperations, you can use loadBefore and loadAfter in your About.xml to help players resolve these issues.
  • Patching is done before Def inheritance takes place. This means that you cannot target tags that are inherited from a parent, but also that if you alter a parent, all of its children will inherit the patched value.
  • You can override a value inherited from a parent Def without changing that value for all Defs by using Inherit="False":
<Operation Class="PatchOperationAdd">
  <xpath>/Defs/ThingDef[defName = "Apparel_KidPants"]</xpath>
  <value>
    <thingCategories Inherit="False">
      <li>NewValue</li>
    </thingCategories>
  </value>
</Operation>
  • You can use or in predicates to target multiple nodes: Defs/ThingDef[defName="Cassowary" or defName = "Emu" or defName = "Ostrich" or defName = "Turkey"] This is generally better than using multiple PatchOperations so long as you are making the same changes to each target.
  • You can use /text() to target the text contents of a tag instead of the whole tag for operations like PatchOperationReplace. This is especially useful if you do not want to accidentally remove attributes.

Common Issues / Troubleshooting

  • XPath and XML nodes in general is case-sensitive and must be exact. Copying values such as defName fields is strongly recommended to avoid errors.
  • Malformed XML such as unclosed or mismatched tags can cause the XML parser to crash completely, which can result in a blank screen on startup or a "Recovered from a fatal error" screen and the removal of all active mods. If this occurs, run all of your XML through an XML validator to ensure that it is structurally correct. Checking your Player.log file can help in diagnosing the exact cause as well.
  • It's very easy to confuse Insert vs Add; Insert will "add" the value as a sibling of the target node(s), while Add will "insert" the value as a child of the target node(s).
  • Remember that XPath targets the XML data structure, not the file path.
  • If you're still stumped, feel free to join the RimWorld Discord server and ask questions in the #mod-development channel.

References and Links

!
Obsolete
The following content was written for a previous version of RimWorld or is otherwise no longer fully relevant today. Please be mindful of any potential inaccuracies.

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