Build Packages With Sims 4 Toolkit

Take Control of Your File Structure (and Use Git!)

Frankk
10 min readJan 29, 2023

STOP!

This article was written before the Sims 4 Toolkit VS Code extension was published. Most users should be using the extension instead of following this setup, since it does not require you to write any scripts on your own, and has many more features other than building.

Advanced users familiar with Node.js may still find value in this tutorial, as it will allow you to have more granular control over how S4TK works in your projects.

This article will show you a basic setup for building loose XML into TS4 package files using Sims 4 Toolkit (S4TK). Creating mods like this allows for more control over file organization and enables the use of Git. This is the primary reason why S4TK was created in the first place.

While you are free to use any IDE you prefer, this tutorial assumes that you are using Visual Studio Code (VSC) and will provide VSC-specific tips.

This article is a supplement to Beginner’s Guide to Sims 4 Toolkit — if you have not read it yet, I strongly urge you to do so.

Table of Contents

  1. Laying the Groundwork
  2. Structuring the Source Code
  3. Writing the Build Script
  4. Download the Sample Repository

NOTE: If you are already familiar with setting up Node projects, most of these sections may be tedious or unnecessary for you. Feel free to skip ahead to the “Download the Sample Repository” section, as it shows you the finished product without walking you through every step.

Laying the Groundwork

This section explains how the package builder project works, and walks you through the boilerplate file/folder structure that you will need.

What Are We Making?

The goal is to be able to press a button that builds all of your mod’s loose files into a package. You can structure your files however you wish, and the resulting package can be output directly to your mods folder.

Build scripts are infinitely customizable, but I will only be showing you one basic setup. Limitations of it include:

  • It’s only for tuning/SimData; other resources must be in packages.
  • You are responsible for hashing your own instance IDs and strings. You can use S4S’s hasher, Lot51’s hasher (Tools > Hash Generator), or STBL Studio and its hasher (Tools Icon > Hasher).
  • It only outputs one single package for all of your files.

If you want to generate a string table from a text/JSON file, output multiple packages for use as add-ons, or scan your files for issues at build time, you have to make some modifications — this is more advanced than this tutorial is intended to be, however.

Create the Project Skeleton

First things first, you must set up a Node project with npm to use S4TK. If you do not know how to do this, refer back to this article, specifically the section titled “Setting Up an S4TK Node Environment”.

After creating your folder and its package.json , install two dependencies with npm i @s4tk/models glob — you should already be familiar with @s4tk/models , and glob will be used for locating your source XML files.

Next, create two folders, one called src (for your mod’s source code) and another called scripts (for your S4TK build scripts). Within scripts , create a file called build.cjs *. Within build.cjs, paste the following:

// temporary, just to validate that the script is being called
console.log("Hello from build.cjs!");

Your project’s file structure should now look like this:

File Explorer — Project Skeleton

* The .cjs extension stands for “CommonJS”, which is the flavor of JavaScript that we will be using in this tutorial. You *can* use the .js extension, but VSC will falsely yell at you about your require() imports if you do.

Call Your Build Script

In order to call your build script, you have some options. You can either call it directly with node scripts/build.cjs , or you can create a more concise and easier to remember npm command, such as npm run build . To do this, add a scripts object to yourpackage.json , like so:

{
"name": "your-project-name",
"scripts": {
"build": "node scripts/build.cjs"
},
"dependencies": {
"@s4tk/models": "^0.6.5",
"glob": "^8.1.0"
}
}

Finally, if you are using VSC, you can use a launch.json file to create a button that runs your script (plus, you get the added benefit of being able to use a debugger). To do this, create a folder called .vscode in the root of your project, and within it, create a file called launch.json . Within launch.json, paste the following content:

{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Build Package",
"skipFiles": [
"<node_internals>/**"
],
"program": "scripts/build.cjs"
}
]
}

Your file structure should now look like this:

File Explorer — VSC Folder Added

Now, within the Run and Debug menu (play button with beetle on it), you should see an option to run the “Build Package” script, like so:

Run and Debug Menu

Go ahead, press the button — you should see Hello from build.cjs! printed in the “DEBUG CONSOLE” in the bottom of VSC. If you see this message, you have everything hooked up correctly.

If you’d like to use a debugger, just place some breakpoints in build.cjs by clicking to the left of the line numbers. This will cause the script to pause at that line whenever it is reached, and you can hover your mouse over any variables/values to inspect them.

Structuring the Source Code

How you set up your src folder can vary greatly depending on what exactly you want to do, but I’ll be showing a simple setup that only differentiates between loose XMLs and packages.

Create Subfolders

Within your src folder, there should be two subfolders called xml and packages. The structure within xml and packages does NOT matter — you can name and structure your files however you please, as long as packages are kept in packages and tuning/SimDatas are kept in xml.

We’ll be using glob patterns to find your files based on their extensions, so go wild and organize your mod however you want. Ideas for organization include creating subfolders by file types, functions, or features — but in the end, it’s 100% up to you.

For this tutorial, let’s organize by file type. To keep this step simple, you can download this sample ZIP file and extract its contents into your src folder. Your file structure should now look like:

Sample Source Folder Structure

Let’s break this down. The packages folder is self-explanatory — it contains packages. You must use packages for anything that isn’t tuning/SimData, even if it is XML (such as ASMs).

Within xml , files ending in .SimData.xml are SimData, and files ending in .xml are tuning (both instance and module tuning are supported). Other than these extensions, file names do not matter. It’s recommended to keep the names the same as in your tuning, but you may omit any prefix you use, such as “creatorName_modName:fileType_”.

You may be wondering what the G12345678 is on the VanillaLoot file , and it is to specify that that tuning file should be using a group of 0x12345678 rather than the default of 0x00000000 (this is required for some resources, such as when overriding pack tuning).

This brings up a major topic — how is the build script going to know the resource keys for your tuning/SimData files?

How to Get Resource Keys

Within packages, resource keys are entirely separated from their files — they are in a list called an “index.” So, with these files standing on their own, how is your build script supposed to know which type, group, and instance to use for them? You have some options:

  • Parse each XML file: Infer keys with the i and s attributes.
  • Use the S4S approach: Include the resource key in file names.
  • Create an index file: List the resource keys in a standalone file.

To me, a mixture of all three is ideal. The type and instance can be inferred from each tuning’s i and s attributes, and the group can default to 0. When the group matters, you can specify it as a suffix on the file name. You could then build a cached index while generating these keys, and use that index on subsequent builds — this would make builds faster, since you’re not parsing the same XML files every time.

If this section confuses you at all, don’t worry about it. All of this will be implemented for you in the sample build script — I just want to explain exactly how it works, and the pros and cons of doing it this way.

Writing the Build Script

This is where the magic happens — this section will demonstrate how you can bundle the contents of the src folder into a single package, and even output it directly to your game’s Mods folder.

Create a Config File

This will make it easier to alter how your script works in the future. Create a file called s4tk-config.json in the root of your project, like this:

File Structure with Config

Within this file, paste the following content:

{
"buildFolders": [
"./out"
],
"buildName": "MyProject",
"cachePath": "./s4tk-cache.json",
"cancelOnError": true,
"sourceFolder": "./src",
"sourcePatterns": {
"packages": [
"./packages/**/*.package"
],
"simdata": [
"./xml/**/*.SimData.xml"
],
"tuning": [
"./xml/**/!(*.SimData).xml"
]
}
}

Let’s break this down:

  • buildFolders is a list of folders to write your package to. These folders can be either relative or absolute. If you want to output your package to your Mods folder, add its full path to this list.
  • buildName is the name of your package. If it is set to MyProject , your package will be called MyProject.package .
  • cache can be true or false. If true, then the script will build a cache of the resource keys and use it on subsequent builds. If false, it will not. It is HIGHLY recommended to keep this true, as it will significantly speed up your build times for larger mods.
  • cachePath is a relative path to a JSON file containing the cached keys.
  • cancelOnError can be true or false. If true, then the built script will terminate with an error if a file could not be built for some reason. If false, then the build script will skip over erroneous files.
  • sourceFolder is a relative path to the folder that contains your mod’s source code, which is currently the src folder in the project root.
  • sourcePatterns contains glob patterns (relative to the source folder) that tell the build script which files are of which type. These default patterns have been tested — modify them at your own risk.

This config file will be read by build.cjs to know which files to build, and where to write the final package.

Create the Build Script

Open up the scripts/build.cjs file that you created at the beginning of the tutorial, and paste in the contents of the script on GitHub (it ended up being a bit long, so it’s better not to embed it here).

Build Script on GitHub

Admittedly, the script ended up being more complex than anticipated, but it should cover all of the cases explained in this article. Feel free to read through the script to see how it works, and if you have any questions about it, contact me.

Take It for a Spin

Now that you have all of the pieces, try running the build script either with the npm run build command or by clicking the play button in the Run and Debug tab of VSC. If all goes well, you should see something like:

Tuning built successfully
SimData built successfully
Packages merged successfully
Package built successfully: MyProject.package
Wrote package: /path-to-project/out/MyProject.package
Saved cached: /path-to-project/s4tk-cache.json
Build complete

If you do not see this message, or if you see an error, you may have set something up incorrectly. Continue onto the next step to download the complete project — running this should work without any changes.

Watch Out for the Cache!

The cache maps file paths to pre-calculated resource keys. This means that tuning files only have to be parsed once, rather than every single time you run the build script. This helps speed things up, but can also cause some unexpected behavior if not handled properly.

As a rule of thumb, you should delete s4tk-cache.json every time you change an existing XML file’s type, group, or instance (i.e. changes to the i or s attributes, or adding/removing/editing the G######## suffix).

If you are ever experiencing a bug where your resource keys are not correct, it is probably a cache issue. Delete s4tk-cache.json and try again.

Download the Sample Repository

Whether you’re skipping ahead or got stuck along the way, you can view the complete sample project at this GitHub repository. You can download the entire project by clicking on Code > Download ZIP.

Download the Complete Project

Once downloaded, open the project up in VSC, and run the npm i command to install all dependencies. After this, you should be ready to run the build script without issue.

Feel free to revisit this repo at any point in the future to download it as a starting point for every new mod you create with S4TK.

--

--

Frankk
Frankk

Written by Frankk

I'm a modder for The Sims 4.

No responses yet