--- name: unreal-bridge description: Execute Python scripts inside a running Unreal Engine 5.3+ editor via TCP bridge. Use when the user asks to interact with UE, manipulate assets, query scenes, automate workflows, or run Python in Unreal. allowed-tools: Bash Read Write Edit Glob Grep Monitor --- # UnrealBridge Execute Python directly inside a running UE 5.3+ editor. Protocol-v2 auto-discovery defaults to paired host-local UDP probes: multicast `239.255.42.99:9876` with TTL 0 plus loopback `127.0.0.1:9876`. Use `--discovery-scope=lan` only when another host must be discovered; it raises multicast TTL to 1 while retaining loopback. Malformed replies and Windows UDP reset notifications are skipped independently, valid responses are de-duplicated by Server-start UUID, and wildcard binds use the UDP response source IP. The six pre-existing exact commands remain the minimum capability set; `exact_editor_status` is advertised and negotiated separately, so an older protocol-v2 endpoint remains usable for its supported base commands. Unique future capabilities remain forward-compatible. Every bounded TCP response is rechecked against the frozen identity. The TCP data port is OS-assigned per editor. ## Preconditions If `bridge.py` returns `discovery: no UnrealBridge editors found`, walk these in order — don't troubleshoot Python or firewalls first: 1. **Plugin and matching skill installed** with `sync_project.bat `. Check: `/Plugins/UnrealBridge/UnrealBridge.uplugin` exists. The legacy `sync_plugin.bat` command name accepts the same project-root argument. 2. **Plugin enabled** — check `/.uproject` `"Plugins"` block for `{"Name":"UnrealBridge", "Enabled":false}` and flip if present. 3. **Editor up and ready** — `bridge.py ping` returns `"ready": true`. `false` means MainFrame still loading; wait 10–60s. The loopback probe keeps local discovery working when multicast is blocked by a VPN, virtual NIC, or Windows firewall policy. The discovery group must be an IPv4 multicast address; direct unicast mode is intentionally fail-closed: `--endpoint`, `--instance-id`, `--expected-pid`, and `--expected-project-path` (or matching `UNREAL_BRIDGE_*` variables) are one inseparable tuple. Copy the identity values verbatim from one discovery response or Server startup line; `project_path` is case- and representation-sensitive on every OS. A host/port alone can be stale and is never used as a legacy fallback. Python 3.7+ stdlib only. ## Waiting for the editor to become ready (post-launch / post-relaunch) After launching/relaunching the editor, poll readiness with **`Monitor`** (streams progress events, recommended) or **`Bash` with `run_in_background: true`** (one completion event). Don't write a foreground `for/sleep` countdown — the harness blocks long leading sleeps and you'll miss readiness. Paste this verbatim as the `command` for either tool: ```bash end=$(( $(date +%s) + 300 )) i=0 until python .claude/skills/unreal-bridge/scripts/bridge.py --json --timeout 3 ping 2>/dev/null | grep -q '"ready": *true'; do now=$(date +%s) [ "$now" -ge "$end" ] && { echo "[wait] TIMEOUT after 300s"; exit 1; } i=$((i + 1)) [ $((i % 3)) -eq 0 ] && echo "[wait] still booting ($((end - now))s left)" sleep 10 done echo "[wait] READY" ``` Then call one of: ``` Monitor(description="UE editor ready-poll", command=, timeout_ms=360000, persistent=false) Bash(description="Wait for UE editor ready", command=, run_in_background=true, timeout=360000) ``` **Locks (don't change without reading)**: - Grep `"ready": *true` — TCP-up ≠ MainFrame-ready. `success:true, ready:false` means `exec` calls will be rejected; ping success alone is not enough. - Ping FIRST then sleep — no leading sleep (harness blocks long ones). - `i % 3` echo gate — caps notifications at ~10 over 5 min so Monitor doesn't trip its event-flood auto-stop. - `end` deadline must stay **below** the tool `timeout_ms`/`timeout` so you get `[wait] TIMEOUT` rather than a silent harness kill. - Path is relative to repo root; `${CLAUDE_SKILL_DIR}` is **not** reliably set in Monitor/Bash subshells — don't substitute it here. - After the loop returns, do **one foreground** `bridge.py ping` before real work — bg success is past tense. - Multi-editor host: append `--project=` to the ping inside the loop. - Skip this entirely when invoking `rebuild_relaunch.py` — it polls internally and prints `[rebuild] bridge is ready.` on success. ## Bridge CLI ```bash python "${CLAUDE_SKILL_DIR}/scripts/bridge.py" [options] [args] ``` | Command | Purpose | |---------|---------| | `ping` | Check UE connection (TCP-only, doesn't touch GameThread) | | `exec ""` | Execute single inline statement | | `exec --stdin <<'EOF' ... EOF` | Multi-line script from stdin (default for >1 line; `-` is shorthand for `--stdin`) | | `exec-file ` | Execute a .py file (use when iterating, debugging, or keeping the script) | | `preflight ` | Lint a script for bridge-call errors WITHOUT sending to UE | | `suggest [pattern]` | Look up the bridge equivalent for a raw `unreal.*` fallback | | `status` | Read cached Engine/Slate tick ages, readiness and modal attention without fresh GameThread dispatch | | `gamethread-ping` | Probe GameThread liveness (bypasses exec queue; use when `exec` hangs) | | `resume` | Unstick a paused BP breakpoint | | `modal-status` | Inspect a blocking Slate dialog: title, body, buttons, inputs and checkboxes | | `modal-click