AeThex-Engine-Core/docs/STUDIO_BRIDGE_GUIDE.md

6.5 KiB

Studio Bridge Integration Guide

Created: February 2026
Status: Phase 1 - Initial Implementation
Next: HTTP Server + WebSocket Event System

What Was Built

Core Files

  • engine/modules/studio_bridge/studio_bridge.h/cpp - Main bridge class (10 RPC methods)
  • engine/modules/studio_bridge/register_types.h/cpp - Module registration as singleton
  • engine/modules/studio_bridge/config.py - Build configuration
  • engine/modules/studio_bridge/SCsub - Build script
  • engine/modules/studio_bridge/README.md - API documentation

Key Features Implemented

StudioBridge singleton accessible from GDScript
10 RPC methods for scene/node control
Scene management (load, save, get tree)
Node operations (create, delete, select)
Property manipulation (get/set)
Game control (run/stop placeholders)
Event emission system (console, selection, property changes)
Error handling with standardized response format

API Methods

1. Scene Management

// Load scene
POST /rpc
{
  "method": "loadScene",
  "params": { "path": "res://main.tscn" }
}
 { "success": true, "result": { "path": "...", "root": {...} } }

// Save scene
POST /rpc
{
  "method": "saveScene",
  "params": { "path": "res://main.tscn" }
}
 { "success": true, "result": { "path": "..." } }

// Get scene tree
POST /rpc
{
  "method": "getSceneTree",
  "params": {}
}
 { "success": true, "result": { "name": "Root", "children": [...] } }

2. Node Operations

// Create node
POST /rpc
{
  "method": "createNode",
  "params": {
    "type": "Sprite2D",
    "parent": "/root",
    "name": "MySprite"
  }
}
 { "success": true, "result": { "name": "MySprite", "type": "Sprite2D", ... } }

// Delete node
POST /rpc
{
  "method": "deleteNode",
  "params": { "path": "/root/MySprite" }
}
 { "success": true, "result": { "deleted_path": "/root/MySprite" } }

// Select node
POST /rpc
{
  "method": "selectNode",
  "params": { "path": "/root/Player" }
}
 { "success": true, "result": { "node": {...} } }

3. Property Manipulation

// Set property
POST /rpc
{
  "method": "setProperty",
  "params": {
    "path": "/root/Player",
    "property": "position",
    "value": { "x": 100, "y": 200 }
  }
}
 { "success": true, "result": { "path": "...", "property": "position", "value": {...} } }

// Get property
POST /rpc
{
  "method": "getProperty",
  "params": {
    "path": "/root/Player",
    "property": "position"
  }
}
 { "success": true, "result": { "value": { "x": 100, "y": 200 } } }

Testing the Bridge

1. From GDScript (Engine Side)

Create a test scene with this script:

extends Node

func _ready():
    # Get the bridge singleton
    var bridge = StudioBridge
    
    # Start the server
    var err = bridge.start_server(6007)
    if err == OK:
        print("StudioBridge started on port 6007")
    else:
        print("Failed to start StudioBridge")
    
    # Test: Create a simple scene
    var sprite = Sprite2D.new()
    sprite.name = "TestSprite"
    add_child(sprite)
    
    # Emit test output
    bridge.emit_console_output("Test scene initialized", "info")
    
    # Test RPC call manually
    var params = Dictionary()
    params["path"] = "TestSprite"
    var result = bridge.handle_rpc_call("selectNode", params)
    print("RPC result: ", result)

func _exit_tree():
    # Stop server on exit
    StudioBridge.stop_server()

2. From Studio (Web Side)

Create studio/src/engine/bridge.ts:

export class EngineBridge {
  private baseUrl = 'http://localhost:6007';
  
  async call(method: string, params: any = {}) {
    const response = await fetch(`${this.baseUrl}/rpc`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ method, params })
    });
    
    const result = await response.json();
    if (!result.success) {
      throw new Error(result.error);
    }
    return result.result;
  }
  
  async loadScene(path: string) {
    return this.call('loadScene', { path });
  }
  
  async createNode(type: string, parent: string, name: string) {
    return this.call('createNode', { type, parent, name });
  }
  
  async setProperty(path: string, property: string, value: any) {
    return this.call('setProperty', { path, property, value });
  }
}

// Usage
const bridge = new EngineBridge();
const scene = await bridge.loadScene('res://main.tscn');
console.log('Scene loaded:', scene);

Next Steps (Phase 2)

1. Implement HTTP Server

Currently the start_server() method is a placeholder. Need to:

  • Set up TCPServer to listen on port 6007
  • Parse HTTP requests manually (header + body)
  • Route POST /rpc requests to handle_rpc_call()
  • Return proper HTTP responses with JSON body

Implementation Priority: HIGH
Estimated Time: 2-3 days

2. Add WebSocket Support

For real-time events (scene changes, node selection):

  • Integrate WebSocket library or implement protocol
  • Maintain connected client list
  • Broadcast events to all connected clients
  • Handle client disconnections gracefully

Implementation Priority: MEDIUM
Estimated Time: 3-5 days

3. Add Headless Mode Flag

Modify engine/main/main.cpp:

if (has_headless_editor_flag) {
    // Don't initialize EditorNode
    // Start StudioBridge server
    // Wait for Studio connection
}

Implementation Priority: HIGH
Estimated Time: 1-2 days

Current Limitations

⚠️ HTTP Server Not Implemented:
The server start/stop methods are placeholders. RPC calls work internally but can't be called from external clients yet.

⚠️ No WebSocket Events:
Event emission methods (emit_scene_changed, etc.) just print to console. Need WebSocket implementation to actually notify Studio.

⚠️ No Authentication:
Once HTTP server is implemented, add basic token auth to prevent unauthorized access.

⚠️ No Viewport Streaming:
3D viewport not yet accessible from Studio. Will need texture streaming in Phase 4.

Build & Run

# Compile engine with studio_bridge module
cd engine
scons platform=linuxbsd target=editor -j4

# Run with bridge enabled
./bin/aethex.linuxbsd.editor.x86_64

# In Studio (separate terminal)
cd /workspaces/aethex-studio
npm install
npm run dev
# Open http://localhost:3000

See Also