Skip to content

feat: scene/UI/avatar/app-UI inspection WebSocket server#2181

Merged
kuruk-mm merged 5 commits into
mainfrom
feat/scene-debug-ws-server
May 29, 2026
Merged

feat: scene/UI/avatar/app-UI inspection WebSocket server#2181
kuruk-mm merged 5 commits into
mainfrom
feat/scene-debug-ws-server

Conversation

@EibrielInv
Copy link
Copy Markdown
Collaborator

@EibrielInv EibrielInv commented May 29, 2026

Summary

A developer-only WebSocket inspection server that lets you query the live state of any DCL scene, the per-scene SDK UI tree, every tracked avatar (including yourself), and the Explorer's own UI tree — from
outside the client over a localhost WS. Off by default; toggled from Settings → Developer; bound to 127.0.0.1 only.

Pure GDScript on the wire (TCPServer + WebSocketPeer.accept_stream, no multiplayer-peer framing), with thin #[func] hooks for the data only the Rust side can reach.

Example response when asked to describe currently loaded scene:
imagen

Closes #2182

Why

Most "why is this rendering wrong / why is this entity missing / why is the wrong avatar emote playing?" sessions are bottlenecked on getting a clear read of the live state. The existing scene inspector
covers CRDT but not the rendered Godot side; print_tree() covers Godot but not the SDK side; nothing covers avatars or app UI. This unifies them with one cmd-style protocol per tree.

Five trees, one protocol

cmd Tree Identified by Lookup mechanism
scene / entity 3D entity tree (scene_id, entity_id) walks DclSceneNodeDclNodeEntity3d (type-checked)
ui_scene / ui_entity per-scene SDK UI (scene_id, entity_id) Rust debug_get_entity_ui_control_idUiNode.base_control
avatars / avatar global AvatarScene by ∈ {address, alias, entity, local} typed HashMaps; local player synthesized from SceneManager.player_avatar_node
app_ui Explorer's own UI auto-detected (/root/explorer/UI or /root/Menu) Godot path; per-scene SDK UI subtree pruned by default

All four data cmds (scene, ui_scene, avatar, app_ui) accept a shared filters dict:

  • component: ["TextShape", "MeshRenderer", …] — OR-match on SDK component names; uses a cheap names-list path that doesn't decode proto payloads
  • property_is: {component, field, contains} — generic substring filter on any (SDK component, field) pair
  • collect_nodes: {<child_node_name>: [<property>, …]} — per-child-node property dump via Object.get(); works for any node class
  • include_parents, include_children, limit, offset, depth, class_filter, name_contains — pagination and traversal knobs

Every Godot-side value runs through a _variant_to_json helper, so Vector3 / Color / AABB / Transform3D / dictionaries / packed arrays all serialize cleanly.

Cross-tree safety

Each lookup path is type-isolated:

  • 3D / UI / avatar use strongly-typed Rust HashMaps or is checks; the resolved node can't accidentally be from another tree.
  • app_ui is path-based and could in principle traverse into the scene UI subtree, so by default we skip <root>/SceneUIContainer/scenes_ui. Lift the skip with include_scene_ui: true if you want
    everything.

Drive-by fix

GrowOnlySet::to_binary (lib/src/dcl/crdt/grow_only_set.rs:88) underflowed when probed past the end. Existing call sites in message.rs never trigger it (they iterate 0..elements_count), but the WS
server's component sweep does. Replaced the raw subtraction with checked_sub returning the same Err("Index out of range") other paths already return. No production behavior change.

Files

  • godot/src/tool/debug_server/debug_ws_server.gd — WebSocket dispatch
  • godot/src/tool/debug_server/debug_collector.gd — collectors for all four trees
  • godot/project.godotDebugWs autoload registration
  • godot/src/ui/pages/settings/settings.{gd,tscn} — Developer-section toggle
  • lib/src/scene_runner/scene_manager.rsdebug_* #[func]s for CRDT and UI access
  • lib/src/avatars/avatar_scene.rsdebug_list_avatars + by-address/alias/entity/local lookups
  • lib/src/dcl/crdt/grow_only_set.rs — GOS underflow guard

Security / shipping posture

  • Bound to 127.0.0.1 only — no remote attack surface.
  • Toggle hidden in production builds via the existing !Global.is_production() Developer-section guard.
  • Server idle until the toggle is on; _process(false) outside that window.
  • Read-only — no command mutates client state.

How to test

Prereqs: a non-production build, Claude Code available in this repo.

This branch ships a Claude Code skill at .claude/skills/debug-ws-inspector/ that documents the WS protocol and bundles a scripts/debug-ws.sh wrapper (websocat with the right buffer size baked in). The skill auto-triggers on debug-WS keywords (port 9230, DebugWs, websocat against the client), so you can drive the whole test plan by talking to Claude in plain English — it picks the right cmd, runs the wrapper, and interprets the reply.

1. Toggle on

  • Launch a desktop client.
  • Settings → Developer → enable the "Debug WS Server" toggle.
  • Confirm the log line: DebugWsServer: listening on ws://127.0.0.1:9230.

2. Basic dispatch

  • Ask Claude: "ping the debug WS server and list loaded scenes." Expect a successful ping reply and an array (empty in the lobby is fine).

3. 3D scene inspection

  • Walk into any scene with TextShapes (the bug-scene at -99,103 is a known good one).
  • Ask Claude: "find TextShape entities in scene <id> and show their text." Verify the reply has matched entities with both SDK payload and a Godot dump.
  • Ask Claude to narrow with a property_is substring filter on TextShape.text — confirm the result set shrinks accordingly.

4. UI inspection (ui_scene / ui_entity)

  • Enter a scene with UI elements (Tower of Madness, Genesis Plaza dialogs, etc.).
  • Ask Claude: "list UiText entities in scene <id>'s UI." Verify the godot block reports rendered Control properties (position, size, anchors, modulate).
  • Pick one matched entity and ask Claude to dump its child text node properties — should return the live DclUiText child's text / horizontal_alignment.

5. Avatar inspection

  • In a populated parcel (Genesis Plaza is reliable), ask Claude: "list all avatars." Confirm one row has is_local: true (you) and at least one remote player.
  • Ask Claude: "what animation is <address> playing?" — should resolve via by: address and return AnimationPlayer.current_animation.
  • Ask Claude: "what animation tree state is on my own avatar?" — should resolve via by: local and return AnimationTree.active.

6. Explorer UI (app_ui)

  • From the lobby, ask Claude: "show the explorer's own UI tree, depth 2." Confirm resolved_from is /root/Menu.
  • Load any scene and re-ask — resolved_from should switch to /root/explorer/UI and the per-scene SDK UI subtree should be pruned. Ask Claude to lift the prune with include_scene_ui: true and confirm the subtree reappears.

7. Error paths

  • Ask Claude to probe each of: a nonexistent scene id (9999), an invalid by value, by: local without value (should succeed for local specifically), and an unknown address. The first, second, and fourth should come back as readable ok:false errors.

8. Production gate

  • Build with the production flag (cargo run -- export --target <platform> --release, or use the Production toggle in CI builds) and confirm the Developer section of Settings — including the Debug WS toggle — is not visible.

9. Cleanup

  • Toggle the server off and ask Claude to ping again; should fail with connection refused.

@EibrielInv EibrielInv changed the title feat(debug): scene/UI/avatar/app-UI inspection WebSocket server feat: scene/UI/avatar/app-UI inspection WebSocket server May 29, 2026
@EibrielInv EibrielInv marked this pull request as ready for review May 29, 2026 13:40
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

📦 Build Report

🤖 Android

Artifact Status
APK 📱 Download APK
AAB 📦 Download AAB
Debug Symbols 🔧 Debug Symbols

Build Status: ✅ Success

🍏 iOS

iOS builds are triggered manually. Add the build-ios label to trigger an iOS build.


🔗 Workflow Run: View logs

🔄 Updated: 2026-05-29 16:31:00 UTC

@kuruk-mm kuruk-mm merged commit 5c20102 into main May 29, 2026
5 checks passed
@kuruk-mm kuruk-mm deleted the feat/scene-debug-ws-server branch May 29, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: debug server

2 participants