Editing Modding Tutorials/PatchOperations
Jump to navigation
Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.
Latest revision | Your text | ||
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. | |
− | + | Here's a simple example of replacing the texture path for deep water: | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<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="WaterDeep"]/texturePath</xpath> |
− | + | <value> | |
+ | <texturePath>Your/Texture/Path/Here</texturePath> | ||
+ | </value> | ||
+ | </Operation> | ||
</Patch> | </Patch> | ||
</source> | </source> | ||
+ | This xml file simply goes inside YourMod/Patches/ and you're done. | ||
− | == | + | == Overview of available PatchOperations == |
− | + | These are the available PatchOperation types: | |
− | + | * Four do basic operations on XML nodes: | |
+ | ** '''[[#PatchOperationAdd|PatchOperationAdd]]''' adds a provided child node to the selected node | ||
+ | ** '''[[#PatchOperationInsert|PatchOperationInsert]]''' adds a provided sibling node to 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== | |
+ | 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 <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: | |
− | + | <code>/Defs/TerrainDef[defName="WaterDeep"]/texturePath</code> | |
− | <code> | + | # The root node is selected |
+ | # All child nodes of the root node called "TerrainDef" are selected | ||
+ | # 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 | ||
+ | # We select the child node "texturePath" from this node | ||
+ | ## 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. | |
− | <code> | + | =="Success" option== |
+ | The ''<code><success>...</success></code>'' term specifies whether the entire operation is considered a success or not. If the overall patch operation is not considered a success, an error is thrown. This is particularly used in Sequences and Tests. | ||
− | + | Options available are: | |
− | + | * Always - this patch operation is considered a success - sometimes used for Sequences so that if an early test in the sequence fails, an error is not reported (because the sequence is still a successful patch) | |
− | + | * Normal - this patch operation is considered a success if it succeeded (content patched, test passed, etc) | |
− | + | * Invert - If the patch failed (e.g., a test), then it's considered a success (useful for a negative test in a sequence) | |
− | * | + | * Never - this patch operation "failed." Even if it patched whatever it was supposed to correctly. If you ever use this, leave a note here for the curious. |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | * | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | * | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ==Overview of individual PatchOperations== | |
+ | ===PatchOperationAdd=== | ||
+ | ''PatchOperationAdd'' adds a child node to the selected node. | ||
− | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" | |
− | + | ! Def before patch !! Patch operation !! Def after patch | |
− | ! | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | </ExampleDef> | + | <exampleNode>Some text</exampleNode> |
+ | <exampleList /> | ||
+ | </ExampleDef> | ||
+ | </Defs> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationAdd"> | <Operation Class="PatchOperationAdd"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleList</xpath> | |
− | + | <value> | |
− | < | + | <li>Foo</li> |
− | + | <li>Bar</li> | |
− | < | + | </value> |
− | </ | + | </Operation> |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList> | |
− | + | <li>Foo</li> | |
− | </ExampleDef> | + | <li>Bar</li> |
+ | </exampleList> | ||
+ | </ExampleDef> | ||
+ | </Defs> | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | ==PatchOperationInsert== | + | ===PatchOperationInsert=== |
− | + | ''PatchOperationInsert'' adds a sibling to the selected node. | |
− | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" | |
− | + | ! Def before patch !! Patch operation !! Def after patch | |
− | ! | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | < | + | <Defs> |
− | < | + | <ExampleDef> |
− | + | <defName>Example</defName> | |
− | < | + | <exampleNode>Some text</exampleNode> |
− | + | <exampleList /> | |
− | < | + | </ExampleDef> |
− | < | + | </Defs> |
− | |||
− | </ | ||
− | </ | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationInsert"> | <Operation Class="PatchOperationInsert"> | ||
− | <xpath>Defs/ExampleDef[defName=" | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath> |
<value> | <value> | ||
− | < | + | <anotherExample>A new sibling</anotherExample> |
</value> | </value> | ||
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
− | + | <Defs> | |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <anotherExample>A new sibling</anotherExample> | |
− | + | <exampleList /> | |
− | + | </ExampleDef> | |
− | + | </Defs> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | < | ||
− | < | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | < | ||
− | |||
− | < | ||
− | < | ||
− | </ | ||
− | </ | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | ==PatchOperationRemove== | + | ===PatchOperationRemove=== |
− | + | ''PatchOperationRemove'' removes the selected node. | |
− | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" | |
− | + | ! Def before patch !! Patch operation !! Def after patch | |
− | ! | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationRemove"> | <Operation Class="PatchOperationRemove"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath> | |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | ==PatchOperationReplace== | + | ===PatchOperationReplace=== |
− | + | ''PatchOperationReplace'' replaces the selected node. | |
− | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" | |
− | + | ! Def before patch !! Patch operation !! Def after patch | |
− | ! | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationReplace"> | <Operation Class="PatchOperationReplace"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath> | |
− | + | <value> | |
− | + | <anotherExample>An alternate node</anotherExample> | |
− | + | </value> | |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <anotherExample>An alternate node</anotherExample> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | |||
− | |||
− | |||
− | + | ===PatchOperationAttributeAdd=== | |
− | {| class=" | + | ''PatchOperationAttributeAdd'' will add an attribute and set its value, but only if that attribute is not yet present. |
− | ! | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" |
+ | ! Def before patch !! Patch operation !! Def after patch | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationAttributeAdd"> | <Operation Class="PatchOperationAttributeAdd"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath> | |
− | + | <attribute>ExampleAttribute</attribute> | |
− | + | <value>ExampleValue</value> | |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode ExampleAttribute"ExampleValue">Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | |||
− | |||
− | |||
− | + | ===PatchOperationAttributeSet=== | |
− | {| class=" | + | ''PatchOperationAttributeSet'' will add an attribute and set its value. Unlike PatchAttributeAdd, PatchOperationAttributeSet will overwrite any existing value. |
− | ! | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" |
+ | ! Def before patch !! Patch operation !! Def after patch | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode ExampleAttribute"ExampleValue">Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationAttributeSet"> | <Operation Class="PatchOperationAttributeSet"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath> | |
− | + | <attribute>ExampleAttribute</attribute> | |
− | + | <value>DifferentExampleValue</value> | |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode ExampleAttribute"DifferentExampleValue">Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | |||
− | |||
− | |||
− | + | ===PatchOperationAttributeRemove=== | |
− | {| class=" | + | ''PatchOperationAttributeRemove'' removes the specified attribute. |
− | ! | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" |
+ | ! Def before patch !! Patch operation !! Def after patch | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode ExampleAttribute"ExampleValue">Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationAttributeRemove"> | <Operation Class="PatchOperationAttributeRemove"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath> | |
− | + | <attribute>ExampleAttribute</attribute> | |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef> | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | ==PatchOperationAddModExtension== | + | ===PatchOperationAddModExtension=== |
{{Main|Modding Tutorials/DefModExtension}} | {{Main|Modding Tutorials/DefModExtension}} | ||
− | + | ''PatchOperationAddModExtension'' adds a [[Modding Tutorials/DefModExtension|Mod Extension]]. | |
− | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" | |
− | + | ! Def before patch !! Patch operation !! Def after patch | |
− | ! | ||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList /> | |
− | </ | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
+ | |||
<Operation Class="PatchOperationAddModExtension"> | <Operation Class="PatchOperationAddModExtension"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]</xpath> | |
− | + | <value> | |
− | + | <li Class="ExampleNameSpace.ExampleModExtension"> | |
− | + | <exampleValue>true</exampleValue> | |
− | + | </li> | |
− | + | </value> | |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | <ExampleDef | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList /> | |
− | + | <modExtensions> | |
− | + | <li Class="ExampleNameSpace.ExampleModExtension"> | |
− | + | <exampleValue>true</exampleValue> | |
− | + | </li> | |
− | + | </modExtensions> | |
− | </ExampleDef> | + | </ExampleDef> |
+ | </Defs> | ||
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | |||
− | |||
− | |||
− | + | ===PatchOperationSetName=== | |
− | {| class=" | + | ''PatchOperationSetName'' changes the name of a node. |
− | ! | + | {| class="wikitable" style="margin-left: auto; margin-right: auto;" |
− | + | ! Def before patch !! Patch operation !! Def after patch | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
|- | |- | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | < | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <exampleNode>Some text</exampleNode> | |
− | + | <exampleList /> | |
− | + | </ExampleDef> | |
− | </ | + | </Defs> |
</source> | </source> | ||
− | | | + | | style="vertical-align: top;" |<source lang="xml"> |
<Operation Class="PatchOperationSetName"> | <Operation Class="PatchOperationSetName"> | ||
− | + | <xpath>Defs/ExampleDef[defName="Example"]/exampleNode</xpath> | |
− | + | <name>newExampleName</name> | |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | | <source lang="xml"> | + | | style="vertical-align: top;" |<source lang="xml"> |
− | < | + | <Defs> |
− | + | <ExampleDef> | |
− | + | <defName>Example</defName> | |
− | + | <newExampleName>Some text</newExampleName> | |
− | + | <exampleList /> | |
− | + | </ExampleDef> | |
− | </ | + | </Defs> |
</source> | </source> | ||
|- | |- | ||
|} | |} | ||
− | |||
− | ==PatchOperationSequence== | + | ===PatchOperationSequence=== |
− | + | ''PatchOperationSequence'' will complete all operations in the order of their listing, and stop when any of them fails. | |
− | + | Modders will often specify <code><success>Always</success></code>, which will mean that even if one part of the sequence fails (e.g., a test), the whole is still considered a success and no unneeded errors show up. | |
+ | '''Patch Operation''' | ||
<source lang="xml"> | <source lang="xml"> | ||
<Operation Class="PatchOperationSequence"> | <Operation Class="PatchOperationSequence"> | ||
+ | <!-- put <success>Always</success> if using tests? Leave out for simple sequences --> | ||
<operations> | <operations> | ||
− | <li Class=" | + | <li Class="PatchOperationInsert"><!-- Always happens! --> |
− | <xpath>Defs/ExampleDef[defName=" | + | <xpath>/Defs/ExampleDef[defName="Example"]/exampleNode</xpath> |
<value> | <value> | ||
− | < | + | <anotherExample>A new sibling</anotherExample> |
</value> | </value> | ||
</li> | </li> | ||
− | <li Class=" | + | <li Class="PatchOperationAdd"><!-- Always happens! --> |
− | <xpath>Defs/ExampleDef[defName=" | + | <xpath>/Defs/ExampleDef[defName="Example"]</xpath> |
− | < | + | <value><label>I'm a node</label></value> |
− | < | + | <li> |
<!-- etc --> | <!-- etc --> | ||
− | < | + | <operations> |
</Operation> | </Operation> | ||
</source> | </source> | ||
− | + | '''Effect''' | |
+ | {| class="wikitable" style="margin-left: auto; margin-right: auto;" | ||
+ | ! Def before patch !! Def after patch | ||
+ | |- | ||
+ | | style="vertical-align: top;" |<source lang="xml"> | ||
+ | <Defs> | ||
+ | <ExampleDef> | ||
+ | |||
+ | <defName>Example</defName> | ||
+ | <exampleNode>Some text</exampleNode> | ||
+ | <exampleList /> | ||
+ | </ExampleDef> | ||
+ | </Defs> | ||
+ | </source> | ||
+ | | style="vertical-align: top;" |<source lang="xml"> | ||
+ | <Defs> | ||
+ | <ExampleDef> | ||
+ | <label>I'm a node</label> | ||
+ | <defName>Example</defName> | ||
+ | <exampleNode>Some text</exampleNode> | ||
+ | <anotherExample>A new sibling</anotherExample> | ||
+ | <exampleList /> | ||
+ | </ExampleDef> | ||
+ | </Defs> | ||
+ | </source> | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | '''Another Example''': Maybe you don't like the "Example" ExampleDef... | ||
<source lang="xml"> | <source lang="xml"> | ||
<Operation Class="PatchOperationSequence"> | <Operation Class="PatchOperationSequence"> | ||
+ | <success>Always</success><!-- No errors thrown --> | ||
<operations> | <operations> | ||
− | <li Class=" | + | <li Class="PatchOperationRemove"><!-- :o ...this returns true --> |
− | <xpath>Defs/ | + | <xpath>/Defs/ExampleDef[defName="Example"]<xpath> |
− | |||
− | |||
− | |||
</li> | </li> | ||
− | <li Class="PatchOperationAdd | + | <li Class="PatchOperationAdd"><!-- so this runs --> |
− | <xpath>Defs | + | <xpath>/Defs</xpath> |
<value> | <value> | ||
− | < | + | <ExampleDef><defName>MyBetterExample</defName></ExampleDef> |
</value> | </value> | ||
− | |||
</operations> | </operations> | ||
</Operation> | </Operation> | ||
</source> | </source> | ||
− | + | If ExampleDef were already gone, MyBetterExample would not get added. | |
− | + | ===PatchOperationTest=== | |
+ | ''PatchOperationTest'' tests for the existence/validity of an xpath. Useful to stop a PatchOperationSequence. Often used as a simpler, less powerful version of PatchOperationConditional. | ||
+ | The following example patch is from Apparello, by Shinzy. | ||
+ | <source lang ="xml> | ||
+ | <Operation Class="PatchOperationSequence"> | ||
+ | <!-- check for worn graphics and if none found, add one --> | ||
+ | <success>Always</success> | ||
+ | <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> | ||
+ | </source> | ||
− | ''' | + | ===PatchOperationConditional=== |
+ | ''PatchOperationConditional'' can branch logic based on a match/nomatch. The following example patch adds a <comps> 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=== |
+ | ''PatchOperationFindMod'' searches the list of active mods for the mods listed in <nowiki><li></li></nowiki> tags. | ||
− | + | For a successful match, <nowiki><li>ModName</li></nowiki> has to match the <name>ModName</name> in the About.xml of the mod you base your patch behaviour on. | |
− | + | This (shortened) patch is courtesy Mehni. [https://github.com/Mehni/MoreFactionInteraction/blob/ab3ab2a22ee529ee4e38a471fe150fd48fd07ce9/Patches/MoreFactionInteraction.xml#L64 See original.] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | The following | + | The following Patch adds the RimQuest mod extension to the listed Defs when RimQuest is found. |
<source lang ="xml"> | <source lang ="xml"> | ||
<Operation Class="PatchOperationFindMod"> | <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> | </Operation> | ||
</source> | </source> | ||
− | + | The following Patch replace the tabWindowClass of the xpath when Relations Tab is '''not''' found. | |
− | |||
− | |||
− | |||
− | |||
<source lang ="xml"> | <source lang ="xml"> | ||
− | < | + | <Operation Class="PatchOperationFindMod"> |
− | < | + | <mods> |
− | + | <li>Relations Tab</li> | |
− | + | </mods> | |
− | + | <nomatch Class="PatchOperationReplace"> | |
− | + | <xpath>/Defs/MainButtonDef[defName="Factions"]/tabWindowClass</xpath> | |
− | <nomatch Class=" | + | <value> |
− | + | <tabWindowClass>MyNameSpace.MyTabWindowClass</tabWindowClass> | |
− | + | </value> | |
− | |||
− | |||
</nomatch> | </nomatch> | ||
− | + | </Operation> | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
</source> | </source> | ||
− | == | + | ==Some advanced examples== |
− | + | 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. | |
− | + | <source lang = "XML"> | |
− | + | <Operation Class="PatchOperationFindMod"> | |
− | + | <mods> | |
− | + | <li>Expanded Woodworking for Vegetable Garden Project</li> | |
− | + | </mods> | |
− | + | <match Class="PatchOperationSequence"> | |
− | + | <success>Always</success> | |
− | <source lang =" | + | <operations> |
− | <Operation Class=" | + | <!-- 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> | ||
+ | </operations> | ||
+ | </match> | ||
</Operation> | </Operation> | ||
</source> | </source> | ||
− | = | + | ===Random stuff=== |
− | + | * @ select by attribute. Useful for patching (Abstract) bases. <source lang = "xml">/Defs/ThingDef[@Name="BuildingBase"]</source> | |
− | == | + | * ''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. |
− | + | * PatchOperationAdd and PatchOperation insert have an ''order'' tag which allows you to either Prepend or Append your add. Default behaviour of Add is to Append, insert Prepends. | |
− | + | * 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 and PatchOperationTests, you can set success to Always to avoid error logging. | |
− | |||
− | |||
− | * | ||
− | |||
− | |||
− | == " | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | * Normal | ||
− | |||
− | |||
− | == | + | == Write your own == |
− | + | 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 == |
− | + | The best tutorial on PatchOperations created by Zhentar: [https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782 Introduction to PatchOperation] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | You can also learn about XPaths here: [https://www.w3schools.com/xml/xpath_intro.asp XPath tutorial] | |
− | + | There is also an [https://github.com/roxxploxx/RimWorldModGuide/wiki/SHORTTUTORIAL:-DefPatches Overview of PatchOperations] on the [https://github.com/roxxploxx/RimWorldModGuide/wiki RimWorldModGuide]. | |
− | |||
[[Category:Modding]] | [[Category:Modding]] | ||
− |