Ludeon Forums

RimWorld => Mods => Help => Topic started by: Fluffy (l2032) on May 06, 2017, 08:40:27 AM

Title: A17; changes
Post by: Fluffy (l2032) on May 06, 2017, 08:40:27 AM
Let's consolidate our efforts, and report changes in the modding API in this thread.

Patches
You can now use xpath selectors to surgically change properties in vanilla and other mods' defs - so you don't have to replace them!.
Some examples; https://gist.github.com/zorbathut/99a3858fafd2dc9bea8f685ed6b35556
Xpath selectors; https://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx

I'd like to invite Zorba and/or any modders that have tested this system to provide more examples and pointers.

Scribing
All the Scribe_XXX.LookXXX() methods are now just Scribe_XXX.Look(), API doesn't appear to have any further changes.

Under the hood, saving and loading of data has been split into two subcomponents of the Scribe; Scribe.loader & Scribe.saver. If you used Scribe to manually load/save data, you'll have to do some minor refactoring. API doesn't appear to have any further changes. Note that the addition of Settings (see below) might mean you don't need to manually call the scribe at all anymore.

Mod Class
There's now a 'Mod' class you can inherit from. It's a simple base class, which gets constructed after all defs are loaded. As such, it's a useful entry point, but it can't completely replace HugsLib's ModBase class (specifically, there's no hook for running code after this mod is loaded, but before other mods further down the list are loaded).

Settings Class
The Mod class also gives access to a Settings object. Settings can basically be anything (but not references to objects in a game!), you'll have to provide your own ExposeData method to save/load the data. You'll also need to provide a Mod.DoSettingsWindowContents() method to render the UI parts. As far as I can see, there's no default settings handlers for simple data types, but they're easy enough to make yourself (hint; Verse.Widgets, or Verse.Listing).

Components
GameComponent, WorldComponent, MapComponent
All of these now have a handy amount of virtual methods corresponding to various events in their lifecycles, making it much easier to attach data to the correct 'scope'. (e.g. attaching data to game/world/mapComponent to make sure it doesn't 'bleed' over when a player starts/loads another game, world, map). Finally, MapComponents are now injected into existing save games when the map is loaded and new mods are added! (or at least I think they are, that's what the code seems to imply).

MainTabs
'Pawn list' main tabs have been completely refactored, and are now def-based, with a def for each table and column. MainTabDefs themselves are now MainButtonDefs.

Commonality
ApparelProperties.commonality appears to have been refactored into ThingDef.generateCommonality. The same may be true for weapons, etc.
Title: Re: A17; changes
Post by: Zhentar on May 06, 2017, 01:18:13 PM
A simple patch example, adding a designator to the orders category: https://github.com/Zhentar/ZhentarTweaks/blob/a2007f07340f47e17ed5a63109b7c589dc5ca6a9/Patches/Tweaks_Patches.xml

A simple mod settings example: https://github.com/Zhentar/ZhentarTweaks/blob/3e107f7518f55305406f3bccf96f53a846a1a4d8/Source/LetterStackDetour.cs


edit: And I can verify, MapComponent injection is no longer necessary; they will be automatically added to in progress games.
Title: Re: A17; changes
Post by: Shinzy on May 06, 2017, 02:03:27 PM
Quote from: Fluffy (l2032) on May 06, 2017, 08:40:27 AM
Let's consolidate our efforts, and report changes in the modding API in this thread.

Patches
You can now use xpath selectors to surgically change properties in vanilla and other mods' defs - so you don't have to replace them!.
Some examples; https://gist.github.com/zorbathut/99a3858fafd2dc9bea8f685ed6b35556
Xpath selectors; https://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx

I'd like to invite Zorba and/or any modders that have tested this system to provide more examples and pointers.

Zorba the hutt has promised to (eventually) write up proper documentationmabobthingie for these
But to get anyone started here's a VERY very simple mod I made (with tonnes of help from Skully and lil tip from Zorbs to understand the thing)
for vanilla pants to have worn graphics without overwriting the vanilla def

Edit: Check out Zhentar's introduction to patching few posts below for more indepth help! ;D


[attachment deleted by admin due to age]
Title: Re: A17; changes
Post by: Zhentar on May 07, 2017, 01:01:33 AM
Some other changes of note:

Many enums have been moved to Defs (such as Letter types and Flesh types, for example). This isn't intended to eliminate detouring to modify these behaviors; for the most part behaviors are still hardcoded (Zorba doesn't think he can guess which flags are/aren't worth adding, and I think he's right).

A "ModDefExtension" list was added to the base Def definition. This is to allow mods to add their own custom fields to defs without having to subclass defs (and patch operations can be used to add your fields to core defs as well).
Title: Re: A17; changes
Post by: RemingtonRyder on May 07, 2017, 02:27:57 AM
Comments aren't allowed in Patch files (an error about expected Operation results). Bug or intended?
Title: Re: A17; changes
Post by: Shinzy on May 07, 2017, 06:12:32 AM
Quote from: MarvinKosh on May 07, 2017, 02:27:57 AM
Comments aren't allowed in Patch files (an error about expected Operation results). Bug or intended?

<!-- these things, yeah? -->
They should work, I tried just now!

Edit: Having used them some more now. It seems like they only work if they're inside the operation tags
Update: Having tried again, cause I'm stubborn like that. They seem to work regardless where you put them now
Title: Re: A17; changes
Post by: Zhentar on May 07, 2017, 11:08:16 PM
I've written up a draft Introduction to PatchOperation (https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782)
Title: Re: A17; changes
Post by: AngleWyrm on May 07, 2017, 11:50:38 PM

(http://img10.deviantart.net/4193/i/2014/361/4/a/tome__basati_by_dna_1-d8bdzuz.jpg)
Quote from: Zhentar on May 07, 2017, 11:08:16 PM
I've written up a draft Introduction to PatchOperation (https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782)
Thanks, great stuff!

I've got to go play around with this puppy
Title: Re: A17; changes
Post by: dburgdorf on May 08, 2017, 03:35:32 PM
Quote from: Zhentar on May 07, 2017, 11:08:16 PMI've written up a draft Introduction to PatchOperation (https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782)

I'm guessing that you'll still have a conflict if mod A, which replaces a def, is loaded after mod B, which patched the vanilla def. Is that correct?

However, if mod A is loaded first... mod B will end up patching mod A's def?
Title: Re: A17; changes
Post by: Shinzy on May 08, 2017, 04:51:59 PM
Quote from: dburgdorf on May 08, 2017, 03:35:32 PM
Quote from: Zhentar on May 07, 2017, 11:08:16 PMI've written up a draft Introduction to PatchOperation (https://gist.github.com/Zhentar/4a1b71cea45b9337f70b30a21d868782)

I'm guessing that you'll still have a conflict if mod A, which replaces a def, is loaded after mod B, which patched the vanilla def. Is that correct?

However, if mod A is loaded first... mod B will end up patching mod A's def?

The really smart people have told me patching happens after all the defs are loaded so in the case you describe there's shouldn't be no worries
but there's a definite possibility for some bad patchin' and crazy combination of things'n'stuff ruining every ones day. But I think that goes with just about anything
Title: Re: A17; changes
Post by: Zhentar on May 08, 2017, 05:01:59 PM
To be clear, this means Mod A can patch core Defs and Mod B Defs, and Mod B can patch core Defs and Mod A defs, regardless of what order Mod A and Mod B load in. Patches get applied in mod load order, so if both mods try to patch the same thing, the mod load order still determines who wins.
Title: Re: A17; changes
Post by: XeoNovaDan on May 20, 2017, 09:15:16 AM
Pretty neat feature going on here, and I've been playing around with it a little, but I have a question: how do you go about adding something to a list item. Say I wanted to add a new pawn type to a faction's pawnGroupMakers, but I only wanted to modify one listed item as opposed to all of them. Would I set up the xpath like this if I wanted to add to the first item in the list (as an example):


<Operation Class="PatchOperationAdd">
  <xpath>//FactionDef[defName = "Tribe"]/pawnGroupMakers/li[0]</xpath>
  <value>
    <PawnDefNameHere>123</PawnDefNameHere>
  </value>
</Operation>


Bearing in mind this is the section I want to modify:


    <pawnGroupMakers>
      <li>
        <kindDef>Normal</kindDef>
        <options>
          <TribalWarrior>60</TribalWarrior>
          <TribalArcher>100</TribalArcher>
          <TribalChief>70</TribalChief>
        </options>
      </li>
      <li>
        <kindDef>Trader</kindDef>
        <traders>
          <TribalTrader>1</TribalTrader>
        </traders>
        <carriers>
          <Muffalo>1</Muffalo>
          <Dromedary>1</Dromedary>
        </carriers>
        <guards>
          <TribalWarrior>60</TribalWarrior>
          <TribalArcher>100</TribalArcher>
        </guards>
      </li>
      <li>
        <kindDef>FactionBase</kindDef>
        <options>
          <TribalWarrior>60</TribalWarrior>
          <TribalArcher>100</TribalArcher>
          <TribalChief>70</TribalChief>
        </options>
      </li>
    </pawnGroupMakers>


And of course this is the end-outcome I'd want with the patch:


<Operation Class="PatchOperationAdd">
  <xpath>//FactionDef[defName = "Tribe"]/pawnGroupMakers/li[0]</xpath>
  <value>
    <PawnDefNameHere>123</PawnDefNameHere>
  </value>
</Operation>


Bearing in mind this is the section I want to modify:


    <pawnGroupMakers>
      <li>
        <kindDef>Normal</kindDef>
        <options>
          <TribalWarrior>60</TribalWarrior>
          <TribalArcher>100</TribalArcher>
          <TribalChief>70</TribalChief>
          <PawnDefNameHere>123</PawnDefNameHere>
        </options>
      </li>
      <li>
        <kindDef>Trader</kindDef>
        <traders>
          <TribalTrader>1</TribalTrader>
        </traders>
        <carriers>
          <Muffalo>1</Muffalo>
          <Dromedary>1</Dromedary>
        </carriers>
        <guards>
          <TribalWarrior>60</TribalWarrior>
          <TribalArcher>100</TribalArcher>
        </guards>
      </li>
      <li>
        <kindDef>FactionBase</kindDef>
        <options>
          <TribalWarrior>60</TribalWarrior>
          <TribalArcher>100</TribalArcher>
          <TribalChief>70</TribalChief>
        </options>
      </li>
    </pawnGroupMakers>
Title: Re: A17; changes
Post by: Shinzy on May 20, 2017, 09:28:57 AM
Quote from: XeoNovaDan on May 20, 2017, 09:15:16 AM
Pretty neat feature going on here, and I've been playing around with it a little, but I have a question: how do you go about adding something to a list item.

I used the insert operation to add thing in a list,
<Operation Class="PatchOperationInsert">

<!-- Add facility link to vanilla bench-->

<order>Append</order>
  <xpath>//ThingDef[defName = "HiTechResearchBench"]/comps/li[contains(@Class,'CompProperties_AffectedByFacilities')]/linkableFacilities/li</xpath>
  <value>
  <li>ApparelloSchematics</li>
  </value>
</Operation>


Yours should work in similar fashion if you switch to the operationInsert
The difference between insert and add as Zorba explained it:
Quote from: Zorba the hutt"add" adds a child. "insert" adds a sibling. I possibly should have chosen better names for those
;D
Title: Re: A17; changes
Post by: XeoNovaDan on May 20, 2017, 09:33:53 AM
OK, thanks!

Also, what are all of the parameters (is that the right term?) for each aspect - so there's

WhateverDef[defName = "DefNameHere"]

but I didn't know you could search for what a list item contains with

li[contains(@WhateverYouAreLookingFor)]
Title: Re: A17; changes
Post by: Shinzy on May 20, 2017, 09:49:47 AM
Quote from: XeoNovaDan on May 20, 2017, 09:33:53 AM
OK, thanks!

Also, what are all of the parameters (is that the right term?) for each aspect - so there's

WhateverDef[defName = "DefNameHere"]

but I didn't know you could search for what a list item contains with

li[contains(@WhateverYouAreLookingFor)]

These may be of help, I'm still very new with xpath stuff
https://www.w3schools.com/xml/xpath_syntax.asp
Title: Re: A17; changes
Post by: AngleWyrm on May 21, 2017, 05:52:16 AM

(http://commons.oreilly.com/wiki/images/e/e6/XPath_and_XPointer_cover.gif)XPath and XPointer (http://commons.oreilly.com/wiki/index.php/XPath_and_XPointer) on O'Reilly Commons
Downloadable PDF (https://docs.google.com/file/d/0B4K4VAX1hkTqclllU2pKTmxSZFE/view)
Title: Re: A17; changes
Post by: SihvMan on May 22, 2017, 07:45:09 AM
Something to note, having made a half dozen patches recently. This might be a "no duh" for some, but I'll include it for completeness' sake.

When patching using the <Patch> method, you cannot patch to an Base/Abstract. The way RimWorld loads mods makes it load Abstract->Def->Patch, such that the inheritors receive the un-patched abstract.

That being said, you can patch Defs that have inherited abstracts, as though the stuff in the abstract is actually in the inheritor's Def. Ex: If the abstract has <comps>, but the inheritor does not, a patch's <xpath> can be
<xpath>//ThingDef[defName = "InheritorDef"]/comps</xpath>
but not
<xpath>//ThingDef[defName = "AbstractDef"]/comps</xpath>

Also, when patching to <comps> you can patch to specific comps with
<xpath>//ThingDef[defName = "ExampleDef"]/comps/li[@Class="CompProperties_ExampleProperty"]</xpath>

This can used in the place of li(#) if you're patching multiple things with similar comps, in case the comp being patched is in multiple Defs at different list #'s (fringe case, but was useful when I made the patch for Anonemous2's MoreFurniture).
Title: Re: A17; changes
Post by: NoImageAvailable on May 22, 2017, 08:47:04 AM
Quote from: SihvMan on May 22, 2017, 07:45:09 AM
When patching using the <Patch> method, you cannot patch to an Base/Abstract. The way RimWorld loads mods makes it load Abstract->Def->Patch, such that the inheritors receive the un-patched abstract.

Just did a test mod and this is not the case as of 0.17.1543. For reference, the patch I used was:

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

<Operation Class="PatchOperationReplace">
  <xpath>//DamageDef[@Name = "LocalInjuryBase"]/workerClass</xpath>
  <value>
    <workerClass>DamageWorker_Flame</workerClass>
  </value>
</Operation>

</Patch>


The change carried through to all child defs.

From what I've been told the under-the-hood process is XML files are loaded into memory, patches are applied and then the XML parser interprets things like abstracts into actual game objects.

Edit: Another thing you can do with def inheritance is use PatchOperationAddAttribute to add a Name to a vanilla def, then have your own defs inherit from it. I imagine you could also use that method to mess around with inheritance of vanilla defs to change their base defs and such (didn't test this one though).