XPATH surgery - Need help with xpath? post here.

Started by skullywag, June 02, 2017, 04:26:23 AM

Previous topic - Next topic

shadowstitch

AngleWyrm, skullywag, thank you.

I had read through the documentation so many times my eyes were bleeding, and in all the examples, creating an entirely new parent node under a thingdef was never explicitly addressed; I assumed if you added a record with values that it would automatically add the declared path, not just look for the path to only insert values under it. It took me a minute to really wrap my head around the fact that this operation just adds a dumb string -- I was apparently giving the operations too much credit and overthinking it. I changed the add code to this:


<Operation Class="PatchOperationAdd">
<xpath>/Defs/ThingDef[defName = "Vent"]</xpath>
<value>
    <stuffCategories>
      <li>Metallic</li>
      <li>Woody</li>
      <li>Stony</li>
    </stuffCategories>
    <costStuffCount>30</costStuffCount>
</value>
</Operation>


This may not be the INTENDED syntax, like, I don't know if I should be adding the subnode in one operation to declare it properly, then adding the values in another operation, but this seems to be working. Thanks for the guidance!

skullywag

Thatll work for now. You can do things like test for the existence of a node and add if not found using the patch system. It looks more complicated than it actually is.
Skullywag modded to death.
I'd never met an iterator I liked....until Zhentar saved me.
Why Unity5, WHY do you forsake me?

The-Eroks

Is there any way to target an abstract without a defName? If so, what is the most elegant/efficient method?

For example, I'm looking to patch FurnitureBase and change the <DesignationCategory> to something else (for example, "Medieval").

Thanks!

kaptain_kavern


CannibarRechter

#64
Indeed, you can do that, like this:

   <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef[@Name = "ChunkRockBase"]/graphicData/texPath</xpath>
      <value><texPath>Items/StoneChunk</texPath></value>
   </Operation>

You can also combine queries, like this:

   <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef[defName = "GlowPod"]/comps/li[@Class = "CompProperties_Glower"]/glowColor</xpath>
      <value><glowColor>(113,0,117,0)</glowColor></value>
   </Operation>

Note how this one looks for thing thingDef named "GlowPod," and then finds the <li> item with a Class name as shown. Find the GlowPod definition to see what I did there. This method of fetching a named <li> is better than the li[1], etc, method you often see here, as it is order independent. I.e., if the dev moves its position, or adds an item, causing it to not be in the same place, the named query still works.

CR All Mods and Tools Download Link
CR Total Texture Overhaul : Gives RimWorld a Natural Feel
CR Moddable: make RimWorld more moddable.
CR CompFX: display dynamic effects over RimWorld objects

The-Eroks

#65
Awesome! Thank you and thank you.

Follow up question, what is the most efficient way of patching the same single entry multiple times? For example, if one were to change the label, description, and graphics path for the double bed? Three separate operations (which requires three separate searches) doesn't seem as efficient as searching for a target once and applying three changes.

CannibarRechter

#66
Yeah; I don't think there is such a thing, sadly. If you think about it, it kinda can't exist, because while label and description may be roughly in the same area, graphics path isn't quite. Anyway, if you can select the right region, what you CAN do is include an entire paragraph of XML. For example, here is a (mostly pointless) patch that replaces the entire comps section of the WindTurbine:

   <Operation Class="PatchOperationReplace">
      <xpath>*/ThingDef[defName = "WindTurbine"]/comps</xpath>
      <value>
      <comps>
         <li Class="CompProperties_Power">
            <compClass>CompPowerPlantWind</compClass>
            <basePowerConsumption>-3000</basePowerConsumption>
            <transmitsPower>true</transmitsPower>
      </li>
      <li Class="CompProperties_Breakdownable"/>
      </comps>    
      </value>
   </Operation>
CR All Mods and Tools Download Link
CR Total Texture Overhaul : Gives RimWorld a Natural Feel
CR Moddable: make RimWorld more moddable.
CR CompFX: display dynamic effects over RimWorld objects

The-Eroks

Thanks for the reply. I do use a technique similar to what you posted for larger blocks that include nested lists and such.

XeoNovaDan


TeflonJim

I can tell you how to use PatchOperationFindMod since I've been experimenting with it today.

Syntax is as follows where "node" is a context dependent name (such as Operation, or li, or match):

<node Class="PatchOperationFindMod">
  <mods>
    <li>Mod Name 1</li>
    <li>Mod Name 2</li>
  </mods>
  <match Class="PatchOperationClassName">
  </match>
  <nomatch Class="PatchOperationClassName">
  </nomatch>
</node>

You'll find a working example of this in the repo of my fork of Extended Woodworking.

https://github.com/indented-automation/ExtendedWoodworking/blob/master/generated/ExtendedWoodworking/Patches/EW-Building_CompProperties_Refuelable_fuelFilter.xml

HTH

Chris

XeoNovaDan


Nightinggale

Quote from: XeoNovaDan on November 26, 2017, 06:12:37 AM
Anything on the new PatchOperationFindMod?
PatchOperationFindMod works on folder names. Be aware that steam actually renames the folder into some number (the ID in the steam URL), which in turn mean steam and non-steam users will not use the same folder name for the same mod.

ModCheck.isModLoaded has the same goal, but it works on Name tag from About.xml. Also ModCheck caches the output, ensuring that it will only execute once. PatchOperationFindMod will execute once for every def xml file in every mod, including core.

Writing ModCheck has given me some insights in how patching works and how it affects performance. It turns out that poorly written patches can slow down the game startup quite a bit and I have decided to write some guidelines for cases where the same patching can be written in multiple ways and the time spend on each differs greatly. I will post a link here when I have actually written it. It could take a while to write though as I want to get it right and may have to conduct research and measurements.
ModCheck - boost your patch loading times and include patchmods in your main mod.

XeoNovaDan

I guess the multiple mod names is to take the folder name fiasco into account then.

TeflonJim

The biggest problem with PatchOperationFindMod for me at this point is not the need to list by ID and name. Rather that it gives no way of differentiating between mods that are present on the system and mods that are active in a load order. If a mod is present, match will execute.

Nightinggale

Quote from: TeflonJim on November 27, 2017, 04:29:09 PM
The biggest problem with PatchOperationFindMod for me at this point is not the need to list by ID and name. Rather that it gives no way of differentiating between mods that are present on the system and mods that are active in a load order. If a mod is present, match will execute.
:o
Checking if a mod is on the HD, but not loaded is so useless while patching that it did not cross my mind, not even testing if it works like that.

ModCheck.isModLoaded will test to see if the mod is in the list from ModsConfig.ActiveModsInLoadOrder, meaning it will not care what is on the HD, but rather it checks for enabled mods. ModCheck.loadOrder can tell the load order of two mods while ModCheck.isVersion tells if a mod is of at least a specific version. Each can be used with success Invert, meaning it should pretty much cover everything I can think of anybody would need when making conditional patching.

Each of those can print errors to the log on failure, or just messages on success or failure. Default is not writing to the log, but it allows the patch file to write an error if a required mod is not present, an incompatible mod loaded, incorrect order etc. This too should cover any case I could think of being useful when writing patch files.

If you think need anything else, let me know and I will consider adding it.
ModCheck - boost your patch loading times and include patchmods in your main mod.