Build Packages With Sims 4 Toolkit
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
- Laying the Groundwork
- Structuring the Source Code
- Writing the Build Script
- 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:
* 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:
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:
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:
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
ands
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:
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 toMyProject
, your package will be calledMyProject.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 thesrc
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).
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.
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.