260 lines
6.5 KiB
Markdown
260 lines
6.5 KiB
Markdown
# 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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
// 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:
|
|
|
|
```gdscript
|
|
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`:
|
|
|
|
```typescript
|
|
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`:
|
|
```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
|
|
|
|
```bash
|
|
# 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](../../docs/STUDIO_INTEGRATION.md) - Complete 5-week plan
|
|
- [modules/studio_bridge/README.md](README.md) - API reference
|
|
- [modules/aethex_ai/README.md](../aethex_ai/README.md) - AI module docs
|