Sims 4 Tuning 101: A Deep Dive into how Tuning is Generated from Python — Part 1

The Sims 4 is an extremely data-driven game. While it’s often hidden from the end-user (and to an extent, modders), The Sims 4 contains an extensive amount of external data, mostly in the form of XML tuning files, that gets dynamically loaded into the game by Python. In this this series of articles, I am going to take a deep dive into both sides of the loading equation, seeing both the Python that goes into defining the tuning, as well as how the XML tuning gets transformed into Python. Mastering these two frames of mind is crucial to having an intimate understanding of how to modify the game’s code using Python scripting — and how to design your own custom XML tuning as well!

These articles will assume you have some understanding of XML tuning and Python scripting as it pertains to the Sims 4 codebase, but I will try my best to explain things in a way that doesn’t force you to know Python in depth.

In this section, I will go over the basics of XML tuning tags and how to interpret what they mean.

Section 1: XML Tuning

Most modders may just simply accept the fact that the XML tuning is the way it is, and that the tuning may change between versions of the game. However, the curious and astute may wonder how the game knows what XML is valid and what is not. The answer is that these files are defined in the game’s core Python files. Understanding how these files are defined is particularly pertinent for those modders who wish to create custom tuning, but may also be of interest for the casual modder who simply wants to better understand the codebase of the game they are tampering with.

Tunables from the Perspective of XML

The ABCs of Sims 4 Tuning XML

High-Level Tags

What makes <I> and <M> unique, and to an extent <C>, is that they are the main “building blocks” of tuning files: they are the top-level tag, and all other tags must be contained within either an <I> or a <C> within an <M>.

In this section, I will overview how these three tags function within the context of XML tuning for the Sims 4.

The <I> Tag

The definition of an <I> is composed of 5 main parts: the c attribute, the i attribute, the m attribute, the n attribute, and the s attribute. Each attribute of the tag is important to defining what tuning you’re working with.

  • The c attribute defines the class the tuning is. What this means is a little obscure when you think of the XML in a vacuum, and we will come back to this later, but for now it can be thought of as the “subtype” of tuning. For example, there are many different types of Interactions, and the c attribute tells the game which one you want.
  • The i attribute defines the instance the tuning is comprised of. This is also a little obscure, but it in essence goes hand-in-hand with the type of tuning you’re using (think: is this an Interaction? a Buff? a Statistic? etc.). When we come back to the Python side of things, we will discuss a little about how this attribute tells the game which instance manager loads the file.
  • The m attribute defines the module where the game’s Python can find the class. Again, this is more important when discussing the Python side of things, but for now, know that it is used to help the game find where the necessary code to read and load the file is.
  • The n attribute defines the name of the tunable. Ideally, this should uniquely identify the tuning type and function. Different engineers on the Sims Team have over the years used different naming schemes, and modders tend to have their own preferred way of referring to tuning, but a general rule of thumb is something to the effect of CreatorName:ModName_TuningType_TuningDescription, such as LeRoiDeTout:DeathByTerror_commodity_FearTracker.
  • The s attribute defines the hash, i.e. the instance ID of the tunable. Every tunable in the game is identified by a unique ID (with some exceptions — for example, SimData tuning must have the same ID as the corresponding XML tuning; objects follow a similar pattern; and for string tables, the instance IDs are the same except for 8 highest bits/first 2 hex digits, which define the language locale the string is for). This ID in general should be the FVN hash of the n attribute (at least for tuning that’s not from the game). Whether it uses 32 bits or 64 bits depends on the type of tuning and what you plan on doing with it (traits that are not personality traits, for example, can be 64 bits safely, but personality traits must be 32 bits for them to appear correctly in the UI).

The <M> Tag

The game uses tuning modules to define what in Python would be essentially static variables. One of the benefits and powers of Modules, however, is that they can reference tuning files just like everything else, which means that the Devs (and you, too, if you make custom modules!) can have the game load and create tuning for you instead of having to load/create it yourself.

On the technical side of things, a tuning module only has 2 attributes, n and s, which serve the same function as they do for tuning.

  • The n attribute for a module not only defines its name, but also serves a similar purpose as the m tag on tuning files. It defines the path to the Python file that “backs up” the module definition.
  • The s attribute here is special because it must follow certain rules. Unlike tuning, which can be hashed with much more flexibility, a tuning module’s hash must be derived from its name, substituting any periods with hyphens (i.e. a.tuning.module would need to be hashed as a-tuning-module) and you must leave the “high” bit natural. Many programs that help you make tuning will allow you to check something called the “high bit” and for tuning modules, you must leave this setting alone.
The high bit option on the hash generator in Sims 4 Studio.

The <C> Tag

<C>s only have one attribute, n, which corresponds to the name of the Python class.

Any of the other tunables can be used within a class definition, and we will explore them next.

Lower-Level Tags

  • The n attribute stands for name and is used to associate a tunable field with a name that can be used to reference the value from the Python side of things. There are some exceptions to this, but they will be discussed below.
  • The t attribute stands for type and is used to differentiate between tunings that can have the same name but different types. This attribute is unique in that it really is only needed for Tunable Variants (i.e. the <V> tag). I will discuss this more in depth briefly.

There is also on occasion the p attribute, seen when referencing image files in tuning. The p stands for path, but (at least from what I can tell) it is largely unnecessary for the actual code to function. Only the <T> tag can have the p attribute.

The <V> Tag

The type of the variant that is chosen is determined by the t attribute on the <V> tag. From the XML side of thing, the n attribute of the tag inside of the <V> must match the name of the t attribute.

Example of a LootAction class from my Death By Terror mod, demonstrating a <V> tag in tuning.

Using an example from some of my own code, we can see above the <V t="buff"> and the corresponding <U n="buff"> within.

There is also special type of TunableVariant called an OptionalTunable, which can most often be seen in the game as a <V> whose t attribute can either be enabled or disabled. OptionalTunables are used as switches basically, where you either have a value or you do not (though, the “not value” can be customised as well to be practically anything, but this is important for the Python).

One interesting fact to keep in mind, and which will be covered more when I tackle the Python side of things, is that the game is agnostic to the type of variant you choose. To go back to the example I used earlier with vehicles, the game will just know you have a vehicle. To get around this, there are a few methods that will be discussed later, but one of the best solutions is to use what are known as TunableFactories.

The <U> Tag

A TunableTuple object is basically a way to group related tunable values together, giving them a specific (and hopefully descriptive) name.

On the Python side of things, TunableTuples can be defined in two main ways: just a TunableTuple object itself, or a more elaborate object called a TunableFactory. In a later tutorial, I will explore TunableFactories and what you can do with them, but for now, what’s important to know about them is that they take the <U> tag and they are often used in conjunction with <V>.

As a bit of a teaser, the real power of TunableFactories is that they are self-contained Python classes, and so they “know” what they are — this is especially powerful for use with TunableVariants, which do not have a way of knowing what variant type was chosen. They also get all the benefits of Python classes — namely access to their own data and methods that can be used to manipulate said data.

The <L> Tag

Lists come in a few different flavours so to speak and each one has slightly different semantics. The prominent ones will be discussed below:

  • “Regular” <L>s: These are just a group of items. Some lists have restrictions, such as the maximum or minimum amount of elements allowed within the list, but for the most part, they’re what you expect.
  • Mappings: These are special <L>s that are made up of <U>s with two values, usually named key and value. A mapping, also called a dictionary, is an association between a name (the key) and a value (the value). When we discuss the Python side of things, we will talk more in-depth about dictionaries, which in Python are found in the form of dict objects.
  • Sets: Sets are a math term for a group of unordered elements (i.e. {1, 2, 3} and {3, 2, 1}, which are to be considered the same, even though the order is different). Each element in a set is by definition unique: multiple instances of the same value would be ignored.
  • Enumerable Sets: These are <L>s whose values are of <E>s. This means that the values you can put in are limited.

The type of <L> that you encounter in the tuning depends on how the tuning is defined in the Python, and likewise how the XML tuning is loaded into the game as Python objects is also dependent on the type of tuning. This will be explored more when I discuss the Python side of things.

The <T> Tag

There are three main types of <T> tags:

  • “Primitive” values: These values are primitive in the sense that they are “simple” values and mirror the primitives of Python. These can be numeric — more specifically an integer int or a decimal float; Boolean — a bool object, being either True or False; or raw text, in the form of a str object.
  • Reference values: These are the type of <T> we are all most familiar with. This type of <T> references another tuning class (an <I>), and the value between the tags matches the instance ID (s attribute) of the tuning file.
  • Resource values: These are <T>s that reference values that are not other types of XML tuning (that is, not an <I>). Notable examples of this include Strings, Object Definition IDs, Icons, and CAS Parts.

The <E> Tag

Two notable examples of enumerations used in the game are the ParticipantType enumeration and the Tag enumeration.

We will discuss more in depth how enumerations are declared and defined when we take a deeper look at the Python side of things, but the E tag is more so than some of the other tags deeply dependent on its Python equivalent, the Enum class.

Enums come in two main categories, those that are defined purely in Python, and those that are termed “dynamic”. Dynamic enumerations, such as those from the Tag enum, have their values loaded by the game and are described in tuning modules.

Dynamic Enums have special syntax, and uses tags we have already discussed: <M> and <C> to define the Python class and Enum name; an <L n="_elements"></L> declaration which defines the dynamically loaded elements, and a list of <T>s with the special attribute ev for enum value.

Excerpt from the Tag enum declaration detailing the tags for available Moods in game.

An excerpt from the Tag enum is pictured above, showing how dynamic enums are declared in module tuning. Note that each ev attribute must be a unique integer, as this is how the game differentiates one enum value from the next.

In the next section of this series of articles, I will be exploring the Python side of things.

Continue to section 2 of this series here.

Edited by the wonderful Kuttoe.

A Sims 4 Modder, CS Student, & Designer of the Sims 4 Support Bot Bella Goth