Skip to main content

WebSocket bridge for headless / scripted control of Godot scenes.

Project description

godot-cli-control

WebSocket bridge for headless / scripted control of Godot 4 scenes — Python client + CLI.

Install

pipx install godot-cli-control

# or, for unreleased main:
pipx install "git+https://github.com/ClaymanTwinkle/godot-cli-control.git"

The wheel ships the Godot plugin source so the init command can drop it into your project.

Requires Python ≥ 3.10.

One-shot setup of a Godot project

cd path/to/your_godot_project
godot-cli-control init        # copies plugin, patches project.godot, detects Godot binary
godot-cli-control daemon start
godot-cli-control tree 3
godot-cli-control daemon stop

Re-running init refreshes both addons/godot_cli_control/ and the SKILL.md files to match the installed CLI version (the plugin directory is wiped and re-copied; project.godot patching stays idempotent). Pass --keep-addon to keep an existing addons/godot_cli_control/ untouched.

init also writes .claude/skills/godot-cli-control/SKILL.md and .codex/skills/godot-cli-control/SKILL.md so AI agents working in your Godot project can pick up this CLI surface automatically. Use --no-skills to skip, or --skills-only to refresh just those files after a CLI upgrade. See the top-level README for details.

Async API

import asyncio
from godot_cli_control import GameClient

async def main():
    # Omitting port lets GameClient auto-discover from .cli_control/port (written by daemon start)
    async with GameClient() as client:
        tree = await client.get_scene_tree(depth=3)
        await client.click("/root/MyScene/Button")
        await client.action_press("jump")
        await client.wait_game_time(0.5)
        await client.action_release("jump")
        png_bytes = await client.screenshot()
        open("frame.png", "wb").write(png_bytes)

asyncio.run(main())

Sync API (for scripts and tests)

# script.py
def run(bridge):
    bridge.click("/root/MyScene/StartButton")
    bridge.wait(2)
    bridge.tap("attack")
godot-cli-control run script.py --headless     # auto-starts and stops the daemon

pytest fixtures

pip install "godot-cli-control[pytest]"

The package ships a pytest plugin (auto-loaded via pytest11 entry-point):

# tests/test_jump.py — no fixture boilerplate needed
def test_jump(godot_daemon, bridge):
    bridge.click("/root/Game/Start")
    bridge.tap("jump")
    assert bridge.get_property("/root/Player", "on_floor") is False
  • godot_daemon (session-scoped) starts headless Godot once and stops it after all tests; if a daemon is already running it's reused (and not stopped at teardown — keeps your IDE workflow alive).
  • bridge (function-scoped) gives a fresh GameBridge; on teardown it calls release_all() so a hold left behind by one case can't bleed into the next, then closes the connection.

CLI options:

--godot-cli-port=N           # GameBridge port (default: read from .cli_control/port)
--godot-cli-no-headless      # open a real Godot window
--godot-cli-project-root=DIR # default: pytest rootdir

CLI

The CLI is the canonical surface — every GameClient method has a one-line equivalent. Default output is a JSON envelope (--text for legacy strings).

# Lifecycle
godot-cli-control init [--path DIR] [--keep-addon]
godot-cli-control daemon start [--headless | --gui] [--port N --idle-timeout 30m]
godot-cli-control daemon start --record --movie-path X [--fps N]   # 录制需真实渲染器,不能与 --headless 同用
godot-cli-control daemon stop [--all | --project PATH]
godot-cli-control daemon status
godot-cli-control daemon ls                  # list running daemons across all projects
godot-cli-control run <script.py> [--headless ...]

# Read
godot-cli-control tree [depth]
godot-cli-control get      <node_path> <prop>
godot-cli-control text     <node_path>
godot-cli-control exists   <node_path>      # exit 0=true, 1=false, 2=infra
godot-cli-control visible  <node_path>      # exit 0=true, 1=false, 2=infra
godot-cli-control children <node_path> [type-filter]
godot-cli-control pressed
godot-cli-control actions [--all]

# Write / call
godot-cli-control set   <node_path> <prop>   <json-value>
godot-cli-control call  <node_path> <method> [json-args...]
godot-cli-control click <node_path>

# Input
godot-cli-control press|release <action>
godot-cli-control tap   <action> [duration]
godot-cli-control hold  <action>  <duration>
godot-cli-control combo --steps-json '[...]'   # or `combo file.json` / `combo -` (stdin)
godot-cli-control combo-cancel
godot-cli-control release-all

# Wait
godot-cli-control wait-node <node_path> [timeout]   # exit 0=found, 1=timeout
godot-cli-control wait-time <seconds>

# Render (path is required as of 0.2.0)
godot-cli-control screenshot <output.png>

Output contract

  • success: {"ok": true, "result": <data>} on stdout, exit 0
  • error: {"ok": false, "error": {"code": N, "message": "..."}} on stdout, exit 1 (RPC) or 2 (connection / usage)
  • --text / --no-json switches back to the legacy human-readable strings; errors then go to stderr.

exists / visible / wait-node propagate their boolean result to the exit code, so shell if works:

if godot-cli-control exists /root/Main/Boss; then
  godot-cli-control click /root/Main/Boss
fi

The port is read from .cli_control/port if you don't pass --port, so RPC calls just work after daemon start.

Testing

# Python unit tests + coverage (fails if below 80%)
pip install -e ".[test]"
coverage run -m pytest python/tests/
coverage report

# GUT tests for the Godot plugin (needs GODOT_BIN env var)
# bash (Linux/macOS):
GODOT_BIN=/path/to/godot ./addons/godot_cli_control/tests/run_gut.sh
# cross-platform (Linux/macOS/Windows) — what CI runs:
GODOT_BIN=/path/to/godot python addons/godot_cli_control/tests/run_gut.py

Documentation

See the Godot plugin README for the full RPC reference, activation modes, security model, and known limitations.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

godot_cli_control-0.2.17.tar.gz (258.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

godot_cli_control-0.2.17-py3-none-any.whl (204.9 kB view details)

Uploaded Python 3

File details

Details for the file godot_cli_control-0.2.17.tar.gz.

File metadata

  • Download URL: godot_cli_control-0.2.17.tar.gz
  • Upload date:
  • Size: 258.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for godot_cli_control-0.2.17.tar.gz
Algorithm Hash digest
SHA256 c5a9a79a49cef15df68085d1496bc92db910690d859f4d7fb7dca388fd8cdf4f
MD5 d640482c7db936a9cba2332900f8e91c
BLAKE2b-256 be0502d7b6e24107db7430649c90226d387fd81e79c280ca8281cf120bdc9ebe

See more details on using hashes here.

Provenance

The following attestation bundles were made for godot_cli_control-0.2.17.tar.gz:

Publisher: release.yml on ClaymanTwinkle/godot-cli-control

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file godot_cli_control-0.2.17-py3-none-any.whl.

File metadata

File hashes

Hashes for godot_cli_control-0.2.17-py3-none-any.whl
Algorithm Hash digest
SHA256 754107f35079c31b639606eca9859ca9a11e241cfc9bab6eb54599fd491be4a4
MD5 e7dddc3015b30e9bd44d02a185f7739b
BLAKE2b-256 1f24538ff3e7aa36a698e886b601923080fc60be8363b2ec6ccc0aee66ca3918

See more details on using hashes here.

Provenance

The following attestation bundles were made for godot_cli_control-0.2.17-py3-none-any.whl:

Publisher: release.yml on ClaymanTwinkle/godot-cli-control

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page