Guides/11

Project and build

Build a mod from one folder with esx build, and set its options in esx.toml.

A project is one folder for one mod. esx build makes the plugin, the behavior files and the archives from that folder, and checks the release. The build needs no configuration: it finds each input by its place.

Folder layout

mymod/
  esx.toml                      # optional manifest
  records/                      # records files, applied in file name order
    10-ammo.json
    20-gun.json
  behavior/
    mygun/
      mygun.bhv                 # one behavior source for each weapon
      mygun.lock
      animations/first_person/  # the clips of this weapon
  assets/                       # a Data-style tree: Meshes, Materials, Textures, Sound, ...
  papyrus/                      # Papyrus source (.psc)
  build/                        # the output
  • The plugin has the name of the folder with .esp: mymod.esp.
  • A small mod can have one records.json and one <name>.bhv at the top level.
  • A records file is a batch document. In a project it needs no plugin field and no create header. The build makes the plugin and names it. A file that has them still works.
  • One .bhv file is one weapon. A mod with two weapons has two folders in behavior/.
  • Each folder is optional. A project with only assets/ builds an empty plugin and its archives.

Build

cd mymod
esx --data '/path/to/Fallout 4/Data' build

You can also give the folder: esx build path/to/mymod.

The build runs these steps in order. It stops at the first step that fails, and the error names the step.

Step What it does Output
records Makes the plugin and applies each records file. build/records/<Plugin>
behavior Builds each behavior source on the plugin, one after the other. build/behavior/, build/behavior.lock.json
assets Copies the asset folders and the behavior files into one tree. build/stage/
papyrus Compiles papyrus/ with the Creation Kit compiler. build/stage/Scripts/
pack Packs <Name> - Main.ba2 and <Name> - Textures.ba2, with the plugin. build/candidate/
check Runs release check. When it passes, the files become the release. build/release/

A build writes only into build/. It does not write into the sources or into a game folder. There are two exceptions. --update-lock writes the .lock file beside each behavior source. The Papyrus compiler keeps its job logs and its source cache in the esx state folder of the user, as esx ck compile does. A build that fails does not change build/release/, so the last good release stays. This is also true when an after command fails: the new files go back to build/candidate/. When the release check finds issues, the exit code is 7 and the packed files stay in build/candidate/.

The papyrus step needs the Creation Kit. When its tools are not ready, the step is skipped with the warning PAPYRUS_SKIPPED and the reason. The build fails only when the plugin binds a script that has source in papyrus/ and no compiled file in Scripts/ of an asset folder. esx ck doctor shows what is missing.

Options

Option Effect
--dry-run Lists the steps, their inputs, their outputs and the commands of the manifest. Writes nothing and runs nothing.
--step STEP Runs only this step. Repeat it for more steps.
--until STEP Stops after this step.
--skip STEP Leaves this step out. Repeat it for more steps.
--no-hooks Does not run the before and after commands of the manifest.
--update-lock Writes the .lock file beside each behavior source.

A step that runs removes the output of each later step, because that output is then out of date. For example, esx build --until records removes build/stage/ and build/candidate/. Run the later steps again before you use their output.

--archive FILE names game archives for the behavior step, as in behavior build.

The report

The build prints one JSON envelope. data.steps has one section for each step:

{ "step": "behavior", "status": "ok",
  "inputs": ["build/records/mymod.esp", "behavior/mygun/mygun.bhv", "assets"],
  "outputs": ["build/behavior", "build/behavior.lock.json"],
  "warnings": [], "elapsed_ms": 48211,
  "report": { "sources": [{ "source": "behavior/mygun/mygun.bhv", "records_added": 295 }] } }

status is ok, skipped or failed. Each warning in the envelope has a step field. data.plugin has the record count. data.release lists the release files with their SHA-256. data.hooks has each command of the manifest with its exit code. The full report of each step is in build/reports/.

When a step fails, error.details.step names it and error.details.reason gives a stable reason:

Reason Meaning
STEP_INPUT_MISSING The output of an earlier step is not there. Run that step first.
BEHAVIOR_COLLISION Two behavior sources use the same name. The error names both.
BEHAVIOR_PLUGIN A behavior source names another plugin than the project.
LIGHT_LIMIT The plugin has more new records than a light plugin holds.
PAPYRUS_TOOLS_MISSING The plugin needs a compiled script, and the compiler is not ready.
RELEASE_ISSUES The release check found issues.
HOOK_FAILED A before or after command failed.
BUILD_OUTPUT_UNSAFE The output folder is not safe for a build.
BEHAVIOR_LOCK_UNSAFE The lock of a behavior source is not a .lock file of its own.
REQUIRES_ESX The manifest needs another version of esx.

Light plugin limit

A new plugin is a light plugin by default. A light plugin with header version 1.0 or later can use the object IDs 001 to FFF, which is 4,095 new records. With header version 0.95, it can use 800 to FFF, which is 2,048. esx gives new object IDs upward from 800, so a build has 2,048 IDs.

One behavior source adds about 290 records. data.plugin.light_limit.ids_left shows how many IDs are left. The build warns with LIGHT_LIMIT_NEAR when fewer than 410 are left. It stops with LIGHT_LIMIT when a record needs an ID above FFF. Set light = false to build a full plugin.

Several weapons in one mod

Give each weapon its own .bhv file. The build runs the sources in the order of their folder names. Each source builds on the plugin that the source before it made. All sources write into one folder, build/behavior/.

build/behavior.lock.json lists the files of each source. The pack step keeps a copy for the candidate, and the check step keeps it for the release (build/release.behavior.lock.json). release check on a plugin in build/release/ or build/candidate/ uses the lock of that folder, so it checks the files of every weapon. A later partial build does not change the lock of the release.

Before the build writes a behavior file, it checks that the sources do not collide. Two sources must not have:

  • the same mod name (the first line of the source). The generated record editor IDs start with it;
  • the same weapon;
  • the same animation keyword;
  • the same prefix with the same weapon name, which gives the same clip folder;
  • the same output graph file, or another output file with different content. Give each source its own prefix;
  • a generated record with the same editor ID as a record of the plugin.

The error has the reason BEHAVIOR_COLLISION and names both sources.

Two weapons can change the same action. For an action whose IDLE records are in a vanilla tree (the first-person grenade throw), the selections of each weapon stay in that tree, in build order.

The .lock file beside a source pins the hashes of the vanilla graphs. esx build does not write it, because a build does not write into the sources. Run esx build --update-lock to write it. Without the file, the build gives the warning BEHAVIOR_BASE_NOT_PINNED.

The manifest

esx.toml in the project folder replaces a convention. Each key is optional.

plugin = "MyMod.esp"
light = true
author = "Me"
masters = ["Fallout4.esm"]
requires_esx = ">=0.1"
records = ["records/*.json"]
assets = ["assets", "input/donor"]
papyrus = "papyrus"

[[behavior]]
source = "behavior/mygun/mygun.bhv"
asset_dirs = ["input/donor-clips"]

[[behavior]]
source = "behavior/myrifle/myrifle.bhv"

[build]
output = "build"
before = ["7z x input/donor.7z -oinput/donor -aos"]
after = ["zip -j dist/MyMod.zip build/release/*"]
Key Meaning Default
plugin The file name of the plugin. The folder name with .esp
light Make a light plugin. true
author The author in the plugin header. None
masters The masters of a new plugin. ["Fallout4.esm"]
requires_esx The esx versions that can build the project, in the notation of Cargo: >=0.2, >=0.2, <0.5, =0.3.1. Any
records The records files, in this order. * and ? are for the file name only. records.json, then records/*.json
assets Data-style folders. A file of a later folder replaces the same file of an earlier one. assets
papyrus The folder with Papyrus source. papyrus
[[behavior]] source A behavior source. The tables are in build order. Each .bhv at the top level, then each one in behavior/*/
[[behavior]] asset_dirs Clip folders for this source only. They are not packed. None
[[behavior]] lock The lock file of the source. The source path with .lock
[build] output The output folder. build
[build] before Commands that run before the first step. None
[build] after Commands that run after the check step passed. None

The values light, author and masters are for a new plugin. When the manifest sets one, it wins over the create header of a records file.

The asset folders are also clip folders of each behavior source. They must not contain behavior graphs or AnimTextData of another mod. A plugin or an archive in an asset folder is not packed.

esx resolves relative paths from the folder of the manifest. An unknown key is an error that names the valid keys. Each command that reads the manifest checks requires_esx.

Commands before and after the build

before and after are shell commands that the mod author writes. They do the work that is not a step of the build: extract an input, or make a ZIP file of the release.

These commands can do anything that you can do on your computer. Read them before you build the project of another person. esx build --dry-run prints them, and --no-hooks skips them.

  • They run in the project folder, in the order of the list.
  • On Linux and macOS they run through sh -c. On Windows they run through cmd /C, so write commands that cmd knows.
  • The environment has ESX_PROJECT_DIR, ESX_BUILD_DIR, ESX_RELEASE_DIR and ESX_PLUGIN.
  • before runs before the first step, also for a partial build. A before command can make inputs: the build reads the folders after it.
  • after runs only when the check step passed.
  • A command that fails stops the build with the reason HOOK_FAILED.
  • The report has each command with its exit code and the end of its output.

The output folder

A build removes and makes again the folders records, behavior, stage, candidate, release and reports in the output folder. To protect other files, the build refuses an output folder that:

  • is the project folder or contains it;
  • contains a source of the project, or is in one;
  • is in a game folder;
  • has files and no .esx-build file. The build writes this file into its output folder;
  • has a link in the place of one of the build folders or files.

The error has the reason BUILD_OUTPUT_UNSAFE.

Defaults for other commands

The manifest also gives default values to options of other commands. esx reads esx.toml in the current directory. To use a file in another folder, give --project FILE.

records_plugin = "build/records/MyMod.esp"
behavior = "mymod.bhv"
asset_dirs = ["build/inputs"]
output = "build/main"
plugin = "release/MyMod.esp"
archives = ["release/MyMod - Main.ba2", "release/MyMod - Textures.ba2"]
Key Default value of
records_plugin --plugin of behavior check, plan, expand, clips and build. PLUGIN of plugin collect-assets. The plugin of batch, when the document and the command line name no plugin.
behavior SPECIFICATION of behavior check, plan, expand, clips and build. --behavior of plugin collect-assets. One path, or one [[behavior]] table. With more tables, name the source on the command line.
asset_dirs --asset-dir of behavior check, plan, expand, clips and build. In esx build, these folders are clip folders of each behavior source.
output -o of behavior build. This is not the output folder of esx build.
plugin PLUGIN of release check. A path is used as written. A name is the file of that name in the project folder, or else build/release/<name>.
archives --archive of release check, plugin assets and behavior verify.

seed is the first name of records_plugin. It still works.

After esx build made the output folder, its files are defaults too: the records plugin (build/records/<Plugin>), the release plugin, the one behavior source of the project and its asset folders. Then these commands need no argument:

esx behavior check
esx release check

Command line options override the file. esx build and the commands in the table reject unknown keys. Any command also rejects them when you select the file with --project. Otherwise, other commands warn with PROJECT_FILE_INVALID and ignore the file.

Only the commands and options in the table use these defaults. For example, behavior build uses --archive for game archives, so the archives key does not apply to that option.

What the file supplied

The JSON envelope lists the defaults used by a command in its project field:

{ "ok": true, "command": "behavior check",
  "project": { "file": "esx.toml", "defaults": { "SPECIFICATION": "mymod.bhv", "--plugin": "build/records/MyMod.esp" } },
  "data": {} }

In text mode, esx prints one note for each value on stderr.

esx also reads bethkit.toml, which is the old name of the file, when there is no esx.toml.