RimWorld

RimWorld

RIMMSqol
 This topic has been pinned, so it's probably important
Razuhl  [developer] Sep 10, 2021 @ 1:14am
Import/Export Settings
WIP see documentation on the first comment.
Last edited by Razuhl; Sep 10, 2021 @ 1:15am
< >
Showing 1-4 of 4 comments
Razuhl  [developer] Sep 10, 2021 @ 1:14am 
Merging Settings

What can it do?

It allows saving and sharing parts of this mods settings in Merge-Files(short MF). These MFs can be imported to combine into a new mod configuration file that contains all or parts of the modifications and can be based on the existing configuration.

A user can import a damage rebalancing mod that contains all necessary changes to weapons and projectiles from 500+ mods. Only the objects available in the users mod list are imported and only specific properties in those categories are overridden so that e.g. the weapon tags are retained.

It is also possible to to store and recall entire objects, discarding any changes made to them or entire categories.

How does it work?

The merge files are based on the standard xml modding using custom patches and supporting all default patches to modify an xml representation of the mods configuration file.

In the mod menu is a new button called import/export. It leads to a user interface that allows creation of simple MFs based on the current mod configuration. It also allows importing MFs. The MFs, that can be created, support the overwriting of individual properties or objects based on their object ids. That means the specific building "Stove" can be changed instead of mass processing via "all refuelable buildings".

The export targets are grouped into categories(yellow), objects(green) and properties(white). Either an object can be exported or any number of it's properties. Selecting an object will deselect it's properties and vice versa. The quick select offers the ability to perform a specific selection for entire categories.

Advanced MFs must be created by hand and follow the same style as patch files for rimworld.

The mod now creates subfolders in rimworlds configuration folder. On windows that would be "C:\Users\[Username]\AppData\LocalLow\Ludeon Studios\RimWorld by Ludeon Studios\Config". The subfolders are "Mod_1084452457_QOLMod\MergeFiles" for the steam version. If the mod is installed manually then the folder name of where the mod is loaded from is used instead of the numbers. If modders want to copy merge files into the directory they can find it like this.


//If this takes place during the mods constructor the mod must be loaded after the qolMod in order to find it's mod handle.
Mod qolMod = LoadedModManager.ModHandles.FirstOrDefault(m =>m.Content.PackageId == "MalteSchulze.RIMMSqol");
string pathMergeFiles = Path.Combine(GenFilePaths.ConfigFolderPath,
GenText.SanitizeFilename(string.Format("Mod_{0}_{1}", qolMod.Content.FolderName, qolMod.GetType().Name)),
"MergeFiles");

The folder "Mod_1084452457_QOLMod\MergeFiles" contains all "installed" MFs and "Mod_1084452457_QOLMod" contains the initial and final version of the new mod configuration file.

How does it REALLY work?

The format of a MF looks like this.


<?xml version="1.0" encoding="utf-8"?>
<SettingsBlock>
<MergeFile Class="RIMMSqol.genericSettings.MergeFile">
<label>My Settings</label>
<description>Resets all main buttons. Then it makes all main dropdown buttons visible and adds a fixed set of buttons.</description>
<requiresAllProperties>false</requiresAllProperties>
<resetCategories>
<li>mainButtons</li>
</resetCategories>
<patches>
<li Class="RIMMSqol.genericSettings.PatchOperation_QoLSetProperty">
<xpath>/SettingsBlock/ModSettings/mainButtons/li[./baseObjectKey[starts-with(normalize-space(), 'MSqolDD')]]</xpath>
<value>
<Visible>True</Visible>
<Buttons>
<li>MSqolSearch</li>
<li>MSqolRPG</li>
</Buttons>
</value>
</li>
</patches>
</MergeFile>
</SettingsBlock>

The label and description are displayed in the import export dialog to give the user concrete information about what the file hopes to achieve.

The entry requiresAllProperties contains either true or false and decides if the current mod configuration file should be expanded with all the properties that weren't changed by the user before merging begins. This is not necessary for any MF created by the UI but almost mandatory for mass or complex processing.

The list entry resetCategories contains list items(li) with the id of a category. If a category is listed than it's current properties will be treated as if they do not exist. This is useful for a complete overhaul or a rollback.

The list entry patches contains list items(li) with patch configurations. Any patch can be used to modify the xml during importing. However there are custom patches that deal with the intricacies of managing properties.

When importing MFs their requiresAllProperties and resetCategories settings are combined to determine how the initial xml file should look like. Then the initial file is created based on the users mods and their current mod configuration. Lastly all patches defined in the MFs are executed file by file in order of top to bottom.

When performing an import a file called Mod_1084452457_QOLMod_Initial.xml(for the steam version) is created in the subfolder "Mod_1084452457_QOLMod". That initial file can be used to inspect the xml before patches have been applied.

Format for initial Xml

As seen in the mod menu there are multiply categories that can contain multiple objects and then multiple properties ith several layers.


<?xml version="1.0" encoding="utf-8"?>
<SettingsBlock>
<ModSettings Class="RIMMSqol.QOLModSettingsFull">
<ea_ForbiddenPatchNamespaces /><!-- special status for the string list containing a shorthand for the forbidden patches -->
<Category>
<li>
<id>Category</id>
<isConfigured>property1;t;property2;f;property3;t;property4;t;propertyDef;t;</isConfigured>
<baseObjectKey>ObjectID</baseObjectKey>
<label>InternalLabel</label>
<property1>...</property1>
<property4>...</property4>
<propertyDef>MaxHitPoints<propertyDef>
<propertyDef_type>RimWorld.StatDef, Assembly-CSharp, Version=1.3.7907.24831, Culture=neutral, PublicKeyToken=null</propertyDef_type>
...
</li>
...
</Category>
...
</ModSettings>
</SettingsBlock>

When looking up a specific root object it's identity is established with the baseObjectKey inside a category. Looking at the baseObjectKey alone will not gurantee that there are no duplicates in other categories. A typical path to lookup an object could look like this:

/*/*/Category/li[./baseObjectKey[normalize-space() = 'ObjectID']]

Objects that are defined inside root objects have no baseObjectKey and are instead referenced through the property names leading up to it from the root object.

/*/*/backstories/li[./baseObjectKey[normalize-space() = 'AbandonedChild23']]/SkillGainsResolved/li[1]

The id and label can be ignored. The lowercase label is for displaying objects in the mod menu, it is not the uppercase property named Label and can be omitted if the object has no fixed label.

The isConfigured node allows custom treatment of properties in objects that support it, otherwise the node is absent which equates to all properties are configured.

"property1" and "property4" are configured and declared => the values from the xml file are used.

"property2" is not configured => original value will be used.

"property3" is configured but not declared => default value will be used.

"propertyDef" defines, in this case, a reference to a StatDef object by name. The full class reference is listed under "propertyDef_type" and together they allow looking up the relevant object in the database.

A complex object that has no strong path(e.g. entries in a list) won't have the isConfigured node.

Custom Patch Operations

RIMMSqol.genericSettings.PatchOperation_QoLSetProperty
RIMMSqol.genericSettings.PatchOperation_QoLRemoveProperty
RIMMSqol.genericSettings.PatchOperation_QoLSetInstance

<li Class="RIMMSqol.genericSettings.*">
<xpath>...</xpath><!-- xml xpath lookup path to determine which nodes are affect -->
<value><!-- property nodes as they appear in the mods configuration file, can be empty for the remove patch -->
<Visible>True</Visible>
<Buttons>
<li>MSqolSearch</li>
<li>MSqolRPG</li>
</Buttons>
</value>
</li>

SetProperty will copy all nodes under the value node into the new configuration file and mark them as configured(if necessary).

RemoveProperty instead deletes the nodes in the configuration file and marks them as unconfigured(if necessary). The nodes can be empty since only their name is being used(e.g. "<Visible />").

SetInstance is a combination in that it sets the existing nodes and removes all absent nodes.

RIMMSqol.genericSettings.PatchOperation_QoLMarkPropertiesConfigurationStatus


<li Class="RIMMSqol.genericSettings.PatchOperation_QoLMarkPropertiesConfigurationStatus">
<xpath>...</xpath><!-- xml xpath lookup path to determine which nodes are affect -->
<properties><!-- list of property ids that will have their configuration status changed -->
<li>Visible</li>
<li>Buttons</li>
</properties>
<configured>true</configured><!-- whether the configuration status should be true or false -->
</li>

Using this patch allows custom manipulation of properties while correctly managing the configuration status of affected properties. Marking properties as configured if they do not exist in the node means their default value will be set(not necessarily the same as their original value). It is best used with all properties included, since otherwise that usecase could change the result of the merging. Useful for editing list entries without replacing the entire list.
bearhiderug Jun 11, 2022 @ 12:24pm 
This
Is
Too
Nice

Thankyou for all your work on rimmsqol. Your mod supercedes so many on the workshop in ease of use and how dynamic it allows us to adjust anything. Lets the two of us here balance all the different mods just perfectly.
Perry Dec 11, 2023 @ 4:01am 
I am looking into learning XML to make some standalone balance patches, especially for some of the Vanilla Expanded mods.

So I was wondering: could the code that defines the changes I made ingame to items, research projects, etc, inside the export file be copied, at least in small parts, to help with creating actual XML mods?
Razuhl  [developer] Dec 11, 2023 @ 8:54am 
You can use the properties in the configuration file as "inspiration" but it's not identical. Some defs are generated by c# so those are out of reach for xml modding.
< >
Showing 1-4 of 4 comments
Per page: 1530 50