Skip to content

ts2.config.json

A ts2 project is a directory containing ts2.config.json and a folder of .ts2 sources. Every setting has a default, so the file can be empty — or absent, if the defaults suit.

ts2.config.json
{
"src": "./src",
"outDir": "./dist",
"registry": "./ts2-state.lock.json",
"bytecode": true,
"scriptIdBase": 22000,
"interfaceIdBase": 1200,
"externalTypes": ["./src/game/config-params.d.ts2"]
}

The build looks for ts2.config.json in the directory you point it at and then in each parent. Every path in it resolves against the directory the file is in.

Which client this project targets.

Default: "rift-extended"

"rift-extended"The Rift client. Everything in the language is available.
"rift-standard"A stock client. Anything the Rift client added is unavailable.

Building on rift-standard refuses anything the stock client would not understand (TS2E222), naming what you used. Engine hooks — defineEngineHooks — exist only on rift-extended (TS2E350).

Every build prints which dialect it used, including the default.

The directory scanned for .ts2 sources, recursively. Every file under it is an entry point: compiled whether or not anything imports it, so a component nobody uses yet is still checked.

Default: "./src"

The directory a source’s name is spelled from. A function’s qualified name — and so the number the build gives it — is its path relative to this directory.

Default: the value of src

You set it only in a workspace: a folder that is one project of many names the workspace’s root here, so its files and the shared ones it imports keep the names the workspace’s full build gives them.

Directories a source may import from without their files being entry points. Only the files src reaches through imports, transitively, are loaded from them. Each must exist.

Default: []

A JSON file mapping module-scope ref names to the component each is bound to, written by a full build of the workspace as dist/module-refs.json. A createRef declared in the compiled set but bound to an element outside it resolves through this table instead of failing the rule that every ref is bound.

Default: none

Files of declarations this project adds to the global scope — chiefly the parameter fields an item, an NPC or a world object carries, so that item.levelrequire! is a field. Each path must exist.

Default: []

See Reading game data.

Where build outputs are written. Everything under it is derived and rewritten on every build. Do not commit it, and do not edit anything in it. Project outputs says what each file is for.

Default: "./dist"

Path to the persisted-state lockfile. See The state registry below.

Default: "./ts2-state.lock.json"

Whether the build also writes the form the game loads for each function beside its readable one, under dist/scripts/.

Default: true

Every number the build hands out — to an interface, to a function, to a collection you declared — is counted up from a base. Two projects packed into one game must start from different bases, or the second overwrites the first.

SettingNumbersDefault
scriptIdBasefunctions, components and handlers20000
interfaceIdBaseinterfaces1000
collectionIdBaselists and maps declared with listOf and mapOfthe build’s own base
dbTableIdBasethe tables behind collections of recordsthe build’s own base
dbRowIdBasethe rows of those tablesthe build’s own base

Change them only when packing more than one project into one game.

Set by the toolchain, for the build to find what the client can do. You should not need it.

A folder of a workspace compiles alone. Three settings make it one:

panels/quest-log/ts2.config.json
{
"root": "..",
"src": ".",
"shared": [".."],
"externalRefs": "../dist/module-refs.json"
}
  • root spells the folder’s names as the workspace’s full build spells them.
  • shared lets the folder import from its neighbours and the library, loading only what it reaches.
  • externalRefs resolves the refs the shared code declares but binds in another folder — a frame’s slots, say — to where the full build put them.

A ref bound in an interface this folder hosts is this folder’s, whatever the table says. A hosting refusal for a shared component that no interface here opens is not raised; every other refusal in a shared file stands.

Ship the workspace’s full build. Give each folder’s collections and tables bases above the workspace’s, so nothing it numbers shadows what the full build numbered.

ts2-state.lock.json records every persistedState key the project has ever declared, and which slot it was given.

Commit it. The build maintains the file; you review the diff.

The rules it enforces:

  • A slot never moves, even when a declaration sorting before it is added.
  • A removed key is retired rather than deleted — its slot must never be reused, because saved values still sit under it.
  • The same key coming back reclaims its original slot, recovering those values.
  • Two declarations cannot share a key (TS2E126).

Run CI with --frozen so a build that would add a key fails instead:

Terminal window
ts2 . --frozen

It walks up from the directory you gave it. Either you pointed it somewhere outside the project, or the file is named something other than ts2.config.json. A missing config is not fatal — it builds with the defaults — so check the path it reports on the first line of output.

The build says a source directory does not exist

Section titled “The build says a source directory does not exist”

src resolves against the directory holding the config. shared directories and externalTypes files are checked the same way, and a missing one is refused before anything compiles.

Most often --frozen: you added a persisted key and did not commit the lockfile.

A ref is “never bound to a node” in a folder that compiles fine as part of the whole

Section titled “A ref is “never bound to a node” in a folder that compiles fine as part of the whole”

The folder declares or imports a createRef that another folder binds. Point externalRefs at the full build’s dist/module-refs.json.