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 singletonengine/modules/studio_bridge/config.py- Build configurationengine/modules/studio_bridge/SCsub- Build scriptengine/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
- STUDIO_INTEGRATION.md - Complete 5-week plan
- modules/studio_bridge/README.md - API reference
- modules/aethex_ai/README.md - AI module docs