Agent overview

The editor can be driven entirely by text commands. Everything a person does in the window (make a project, build a scene, write a script, play the game, export it) is also a command, so a program or an AI agent can do it with no window at all.

A command and its answer

A command is one JSON object with a "command" name and that command's parameters:

{"command": "entity.create", "name": "Beacon", "mesh": "Sphere", "data": {"Transform": {"Translation": [0, 3, 0]}}}

The answer is one line of JSON:

{"ok": true, "result": {"id": "13050716652590719703"}}
{"ok": false, "error": "no entity matches \"Beacan\""}

An answer also has "errors" when anything was logged at error level while the command ran. That is where the reason for a failure is, such as the line of a script that broke.

A failed command changes nothing.

Running a batch

Put commands in a file, one per line. Blank lines and lines that start with # are skipped.

./build/Cairn-Editor/Cairn-Editor MyGame/MyGame.cairnproject --headless --commands commands.jsonl

Standard output is exactly one line of JSON per command, in order, and the editor then exits.

--headless runs with no window and needs no display, only a GPU. Leave it out and the same commands run in the editor with its window open, where a person can watch.

A session that stays open

Start the editor with a command server, and send commands with the CLI:

./build/Cairn-Editor/Cairn-Editor MyGame/MyGame.cairnproject --headless --server &
./build/Cairn-CLI/Cairn-CLI '{"command": "entity.list"}'
./build/Cairn-CLI/Cairn-CLI '{"command": "play.run", "seconds": 2}' '{"command": "screenshot", "path": "/tmp/shot.png"}'
./build/Cairn-CLI/Cairn-CLI '{"command": "editor.quit"}'

The scene, the undo history and play mode live in the editor, so they carry over from one CLI call to the next.

The CLI exits with 0 when every answer was "ok": true, 1 when one was not, and 2 when no editor is listening.

In an editor with its window open, Server on in Preferences runs the server with no need for --server. The top bar then shows a green Agent server tag with the port.

Warning

The server listens on 127.0.0.1 only, on port 47800 unless told otherwise, and has no password. Anything on the same machine can send it commands.

Things to know

  • Assets go by path. An asset is named by its ID or by its path in the project, such as "Assets/Materials/Gold.cairnmat". A part of a model is "Assets/Models/Ship.gltf#Mesh/0". Prefer paths: IDs differ from one project to the next.
  • Entities go by name or ID. IDs are 64-bit and are given as strings. Pass them back as strings, since many JSON tools round numbers that large.
  • Updates are partial. entity.update takes the shape entity.get returns. Only what is given changes. A missing component is added, null removes one, and a list is replaced whole.
  • Commands that change the scene are undo steps, the same ones the editor's panels make. Commands that write files are not.
  • The scene cannot be edited during play mode. Send play.stop first.
  • Time is not real time. play.run advances the game in steps of 1/60 second without waiting, so the same commands always give the same result.
  • Look at the picture. A command that succeeds does not prove the scene looks right. Take a screenshot after anything visual changes.

Next