Operations ​

An operation (op) is one data change your addon asks for: set a database field, replace a resource in an archive, add a string, register an asset group, copy a loose file, add a track. You don't write code for it. You list op files in the operations block of swofty.json, and every swofty build (and every Play) applies the ops of each enabled addon to a copy of the game's files, or hands them to swofty_core to apply in memory while the game boots. The game folder keeps its stock files.

An addon with only operations is a data addon. An addon with code can list operations too.

An operation file ​

An op file is JSON with a schema_version and a list of ops. Each op names its kind in op:

json
{
  "schema_version": 1,
  "ops": [
    { "op": "set", "select": "rules.stop_go_penalty", "field": "penalty_duration", "value": "10" }
  ]
}

List the file under the field for its kind:

json
"operations": {
  "database": ["db/changes.json"]
}
Field in an op fileMeaning
schema_version1
opsThe ops, applied in order
fieldsOptional. For add-row: extra key names to copy, keyed by <table>.<row name>, or the only ones with copy: listed (Key names)
strictOptional. true fails the build on an add-row completeness warning (The completeness check)

Every op takes two extras from your addon's db settings: {{id}} in any string is replaced with the setting's value, and "when": { "id": value } keeps the op only when each listed setting has that value.

The operations fields ​

FieldHoldsChangesReference
databaseOp files of database ops, no prefix: set, add-row, put-record, remove-recordRecords in gamedatabases.2025.binDatabase operations
erpOp files of ERP ops, no prefix: replace-resource, add-resource, copy-resource, put-resource, remove-resource, add-mipmaps, patch-pkg, warnResources in .erp archivesERP operations
assetsOp files that mix every kind, each op with a prefix: db.set, erp.put-resource, lang.set, group.register, file.put, track.add, ...Everything above, plus text, asset groups, loose files and tracksAsset op files, Asset API
files[{ "source", "target" }] entries, no op fileLoose files in the game folderFile operations
screens[{ "name", "file", "flow", "anchor", "view_model" }] entriesNew in-game screens, installed as swofty/ui/<name>.uiIn-game UI
ui*.pkgpatch.json patch filesMenus. Not built yet: the build skips them with a warningUI patches

A database file and an assets file with db. ops are the same model: the build adds db. to every op in a database file and erp. to every op in an erp file, then plans them together. Each files entry becomes a file.put. Choose assets when one file should change several kinds at once.

native sits in the same block but is not an operation: it names an engine plugin, a DLL with code that runs in the game. launcher, outside operations, names a launcher plugin, whose build hook can return ops of its own. An addon with either is a code addon.

Asset op files ​

In an assets file, the prefix before the dot names the kind:

json
{
  "schema_version": 1,
  "ops": [
    { "op": "erp.put-resource", "erp": "2025_asset_groups/f1_2026_vehicle_package/teams/toro_rosso/wep/toro_rosso.erp",
      "resource": "eaid://f1_2026_vehicle_package/teams/toro_rosso/wep/toro_rosso.wep", "source": "erp/car.erp" },
    { "op": "db.set", "select": "entrants.teams.team[_metadata_=toro_rosso_26]", "field": "display_name_code", "value": "AT" },
    { "op": "lang.set", "language": "*", "key": "lng_team_tor_hashtag", "value": "AlphaTauri" },
    { "op": "warn", "message": "Racing Bulls livery" }
  ]
}
KindOpsChangesReference
db.*db.set, db.add-row, db.put-record, db.remove-recordGame database recordsDatabase operations
erp.*erp.replace-resource, erp.add-resource, erp.copy-resource, erp.put-resource, erp.remove-resource, erp.merge-slices, erp.add-mipmaps, erp.patch-pkgArchive resources, and mip files in the game folderERP operations
lang.*lang.set, lang.table, lang.defaultIn-game strings, in one language or all (*); new keys are addedIn-game text
group.*group.registerAn asset group in the engine's asset manifestAsset API
file.*file.putA loose file in the game folderFile operations
track.addsource: a folder holding an unpacked track packageA whole track: database rows, archive, menus, ERSA track with its own name
item.showid, name, optional author, version, descriptionA read-only tile in the in-game ADDON SETTINGS screenAsset API
warnmessageNothing; prints the message at build

db.remove-row is defined but not built: a build with one fails. A patch-pkg edit other than clone-entry and set-value makes the build skip every ERP op of that addon, with a warning.

An unknown op name or field fails the build and names the file.

Sources ​

A source is a path in your addon folder. To read from the addon's data or cache folder, give an object:

json
"source": "erp/car.erp"
"source": { "root": "data", "path": "library/my-mod/files/car.erp" }

root is addon (the default), data ($DATA/addon-data/<id>/) or cache ($DATA/cache/addons/<id>/). A path that leaves its folder fails the build. See Sources.

Build route and runtime route ​

Each kind reaches the game one of two ways:

  • Build route. swofty build writes a changed copy of the game file into the overlay folder, and Play swaps it into the game folder for the session through the journal.
  • Runtime route. swofty build writes the changes to swofty/assets.json, and swofty_core, the in-game loader, applies them in memory while the game boots. The game's files stay stock.
KindBuild routeRuntime routeWith the in-game loader
groupnoneRegistered when the engine parses its asset manifestRuntime
erpThe archive is rewrittenAn overlay archive the game reads firstRuntime
dbgamedatabases.2025.bin is rewrittendb_edits, applied as the database loadsRuntime
langnoneA string table added after the stock oneRuntime
fileThe file is copied into the game folderServed from swofty/overlay/Build (runtime is experimental)
  • Without the in-game loader, erp, db and file take the build route, and group and lang ops wait: the build lists them and the game doesn't see them.
  • track.add, item.show and warn have no route: the build handles them itself.
  • Some changes force the build route: an erp.remove-resource, a replaced resource whose layout differs from the stock one, and database edits the loader can't apply.
  • To force a kind onto the build route, add it to "build_assets" in $DATA/config.json, for example "build_assets": ["db"]. To turn on the experimental runtime file route, add "runtime_assets": ["file"].

The full rules are in Deliveries.

Order and conflicts ​

A build applies ops in this order:

  1. track.add ops from every addon, in load order. Track activation clones the base track's database rows before any other database op runs.
  2. Addons in the profile's load order.
  3. Within one addon: its database files, then erp files, then assets files, each in the order listed, then files entries, then the ops its build hook returns.

A later write to the same target wins. Replacing what another addon wrote counts as a conflict, and the build prints a line per pair of addons, the winner first:

text
warning: Scuderia AlphaTauri overrides Example Livery on 12 database values and 1 text string
TargetReplaced byComposes with
A database recorddb.set, db.put-record, db.remove-recorddb.add-row
A resource in an archiveerp.put-resource, erp.replace-resource, erp.remove-resourceerp.add-resource, erp.copy-resource, erp.patch-pkg
A slice of a texture arrayerp.merge-slices
A string in a languagelang.setlang.default, which never conflicts
A game pathfile.put, erp.add-mipmaps
An asset groupgroup.register

To make your addon win, move it later in the load order, or name the other addon in load_after (manifest). See Order and conflicts.

Check what a build did ​

sh
swofty build
OutputMeaning
runtime: swofty/assets.json with <n> asset group(s), <n> resource(s), <n> database edit(s), <n> language table(s), <n> overlay file(s)What goes the runtime route
replace <path>, add <n> file(s) under <folder>Game files the build route replaces or adds
warning: <addon>: <message>A warn op
warning: <addon>: <n> lang op(s) wait for lang support in swofty_coreOps with no route: the in-game loader is missing
<addon>: <n> database values, <n> archive resources, <n> files, <n> text strings, <n> tracksWhat each addon writes
warning: <addon> overrides <addon> on <n> database values, ...A later addon replaced targets an earlier one wrote; swofty conflicts lists them
warning: conflict: <target>: <addon> (<item>) overrides <addon> (<item>)Two items of one addon wrote the same target
overlay <buildhash>: <folder>The build's folder, with plan.json and swofty/assets.json

A build that can't apply an op fails and names the addon and the cause, such as <select>: no row matches.

Reference ​

Publishing