threlte-mcp 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +306 -0
- package/dist/bridge-server.d.ts +101 -0
- package/dist/bridge-server.d.ts.map +1 -0
- package/dist/bridge-server.js +155 -0
- package/dist/bridge-server.js.map +1 -0
- package/dist/camera-presets.d.ts +62 -0
- package/dist/camera-presets.d.ts.map +1 -0
- package/dist/camera-presets.js +114 -0
- package/dist/camera-presets.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +134 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/MCPBridge.d.ts +93 -0
- package/dist/client/MCPBridge.d.ts.map +1 -0
- package/dist/client/MCPBridge.js +580 -0
- package/dist/client/MCPBridge.js.map +1 -0
- package/dist/client/MCPBridge.svelte +59 -0
- package/dist/client/index.d.ts +18 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +18 -0
- package/dist/client/index.js.map +1 -0
- package/dist/gltf-io.d.ts +11 -0
- package/dist/gltf-io.d.ts.map +1 -0
- package/dist/gltf-io.js +64 -0
- package/dist/gltf-io.js.map +1 -0
- package/dist/gltf-tools.d.ts +120 -0
- package/dist/gltf-tools.d.ts.map +1 -0
- package/dist/gltf-tools.js +360 -0
- package/dist/gltf-tools.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +808 -0
- package/dist/index.js.map +1 -0
- package/dist/svelte-generator.d.ts +17 -0
- package/dist/svelte-generator.d.ts.map +1 -0
- package/dist/svelte-generator.js +154 -0
- package/dist/svelte-generator.js.map +1 -0
- package/package.json +86 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Raul Contreras
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Threlte MCP
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/threlte-mcp)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://modelcontextprotocol.io/)
|
|
6
|
+
|
|
7
|
+
An MCP (Model Context Protocol) server that enables AI agents to inspect and manipulate Three.js/Threlte scenes in real-time.
|
|
8
|
+
|
|
9
|
+
## โ
Compatible With
|
|
10
|
+
|
|
11
|
+
- **Claude Desktop** - Anthropic's desktop app
|
|
12
|
+
- **Antigravity** - Google's AI IDE
|
|
13
|
+
- **Claude Code** - CLI tool for Claude
|
|
14
|
+
- **Cursor** - AI-powered code editor
|
|
15
|
+
- **Windsurf** - Codeium's AI IDE
|
|
16
|
+
- **Continue** - VS Code AI extension
|
|
17
|
+
- **Any MCP-compatible client**
|
|
18
|
+
|
|
19
|
+
[See detailed compatibility guide โ](./COMPATIBILITY.md)
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- ๐ **Scene Inspection** - View the full 3D scene hierarchy, find objects by name/type
|
|
24
|
+
- ๐ฏ **Object Manipulation** - Move, rotate, scale, show/hide objects
|
|
25
|
+
- ๐จ **Materials & Assets** - Apply materials, load GLTF models, change environment
|
|
26
|
+
- ๐งช **Asset Analysis** - Inspect GLTF structure and validate performance
|
|
27
|
+
- dYZ+ **Asset Optimization** - Simplify meshes, compress textures, prune unused data
|
|
28
|
+
- dY"? **Svelte Export** - Generate Threlte/Svelte components from GLTF
|
|
29
|
+
- dYZ? **Camera Presets** - Save, load, and animate camera views
|
|
30
|
+
- โก **Physics Control** - Add physics bodies, apply impulses, set gravity
|
|
31
|
+
- ๐ญ **Vibe Presets** - Apply mood presets (cozy, spooky, neon, etc.)
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install threlte-mcp
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### 1. Install the package
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install threlte-mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Configure your IDE (one-time setup)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx threlte-mcp setup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This auto-detects your IDE (Antigravity, Cursor, Claude Desktop) and creates the MCP config.
|
|
54
|
+
|
|
55
|
+
### 3. Add the component to your Threlte app
|
|
56
|
+
|
|
57
|
+
Requires Svelte 5 for the MCPBridge component.
|
|
58
|
+
|
|
59
|
+
```svelte
|
|
60
|
+
<script>
|
|
61
|
+
import { MCPBridgeComponent } from 'threlte-mcp/client';
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<!-- Add inside your <Canvas> -->
|
|
65
|
+
<MCPBridgeComponent />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**That's it!** The AI can now inspect and manipulate your scene.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary>๐ Manual Configuration (Alternative)</summary>
|
|
74
|
+
|
|
75
|
+
If `npx threlte-mcp setup` doesn't work for your IDE, manually add to your config:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"threlte-mcp": {
|
|
81
|
+
"command": "npx",
|
|
82
|
+
"args": ["-y", "threlte-mcp"]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Config file locations:
|
|
89
|
+
- **Antigravity**: `~/.gemini/antigravity/mcp_config.json`
|
|
90
|
+
- **Cursor**: `~/.cursor/mcp.json`
|
|
91
|
+
- **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac)
|
|
92
|
+
|
|
93
|
+
</details>
|
|
94
|
+
|
|
95
|
+
<details>
|
|
96
|
+
<summary>๐ง Advanced: TypeScript API</summary>
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { MCPBridge } from 'threlte-mcp/client';
|
|
100
|
+
import { useThrelte } from '@threlte/core';
|
|
101
|
+
|
|
102
|
+
const { scene } = useThrelte();
|
|
103
|
+
|
|
104
|
+
// Create bridge (auto-connects in dev mode)
|
|
105
|
+
const bridge = new MCPBridge(scene, {
|
|
106
|
+
url: 'ws://127.0.0.1:8082', // default
|
|
107
|
+
autoConnect: true, // default in dev
|
|
108
|
+
reconnectDelay: 60000, // 1 minute
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// In render loop
|
|
112
|
+
bridge.update();
|
|
113
|
+
|
|
114
|
+
// Get scene state
|
|
115
|
+
bridge.getSceneState();
|
|
116
|
+
|
|
117
|
+
// Find objects
|
|
118
|
+
bridge.findObjects({ nameContains: 'player' });
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
</details>
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
**Option B: Per-Scene (Simple for prototyping)**
|
|
125
|
+
|
|
126
|
+
```svelte
|
|
127
|
+
<script lang="ts">
|
|
128
|
+
import { onMount, onDestroy } from 'svelte';
|
|
129
|
+
import { useThrelte } from '@threlte/core';
|
|
130
|
+
|
|
131
|
+
const { scene } = useThrelte();
|
|
132
|
+
let ws: WebSocket | null = null;
|
|
133
|
+
|
|
134
|
+
onMount(() => {
|
|
135
|
+
ws = new WebSocket('ws://localhost:8082');
|
|
136
|
+
|
|
137
|
+
ws.onopen = () => {
|
|
138
|
+
console.log('[MCPBridge] Connected');
|
|
139
|
+
// Send initial scene state
|
|
140
|
+
ws?.send(JSON.stringify({
|
|
141
|
+
type: 'sceneState',
|
|
142
|
+
data: serializeScene(scene)
|
|
143
|
+
}));
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
ws.onmessage = (event) => {
|
|
147
|
+
const command = JSON.parse(event.data);
|
|
148
|
+
handleCommand(command);
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
onDestroy(() => {
|
|
153
|
+
ws?.close();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
function serializeScene(obj: THREE.Object3D, depth = 0, maxDepth = 3) {
|
|
157
|
+
if (depth > maxDepth) return null;
|
|
158
|
+
return {
|
|
159
|
+
name: obj.name,
|
|
160
|
+
type: obj.type,
|
|
161
|
+
position: obj.position.toArray(),
|
|
162
|
+
rotation: obj.rotation.toArray().slice(0, 3),
|
|
163
|
+
scale: obj.scale.toArray(),
|
|
164
|
+
visible: obj.visible,
|
|
165
|
+
children: obj.children.map(c => serializeScene(c, depth + 1, maxDepth)).filter(Boolean)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function handleCommand(cmd: any) {
|
|
170
|
+
const { action, requestId, ...params } = cmd;
|
|
171
|
+
let result;
|
|
172
|
+
|
|
173
|
+
switch (action) {
|
|
174
|
+
case 'getFullSceneState':
|
|
175
|
+
result = { data: serializeScene(scene, 0, params.maxDepth || 3) };
|
|
176
|
+
break;
|
|
177
|
+
case 'moveSceneObject':
|
|
178
|
+
const obj = scene.getObjectByName(params.name || params.path);
|
|
179
|
+
if (obj && params.position) {
|
|
180
|
+
obj.position.set(...params.position);
|
|
181
|
+
result = { success: true };
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
// Add more handlers as needed
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (requestId) {
|
|
188
|
+
ws?.send(JSON.stringify({ ...result, requestId }));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
</script>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 3. Run your app and start using MCP tools
|
|
195
|
+
|
|
196
|
+
Once both the MCP server and your Threlte app are running, AI agents can:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
"Get the scene state"
|
|
200
|
+
"Find all objects named 'Player'"
|
|
201
|
+
"Move the 'Camera' to position [0, 5, 10]"
|
|
202
|
+
"Apply the 'neon' vibe to the scene"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Available Tools
|
|
206
|
+
|
|
207
|
+
### Scene Inspection
|
|
208
|
+
| Tool | Description |
|
|
209
|
+
|------|-------------|
|
|
210
|
+
| `get_scene_state` | Get full scene hierarchy |
|
|
211
|
+
| `find_objects` | Search by name, type, or userData |
|
|
212
|
+
| `get_object_position` | Get position of specific object |
|
|
213
|
+
| `log_positions` | Export positions for code |
|
|
214
|
+
|
|
215
|
+
### Camera
|
|
216
|
+
| Tool | Description |
|
|
217
|
+
|------|-------------|
|
|
218
|
+
| `set_camera_position` | Set camera position, lookAt, and lens settings |
|
|
219
|
+
| `save_camera_preset` | Save current camera view as a preset |
|
|
220
|
+
| `load_camera_preset` | Load a saved camera view |
|
|
221
|
+
| `list_camera_presets` | List all saved camera presets |
|
|
222
|
+
| `delete_camera_preset` | Delete a saved camera preset |
|
|
223
|
+
| `animate_camera_presets` | Animate through a sequence of presets |
|
|
224
|
+
|
|
225
|
+
### Hierarchy Management
|
|
226
|
+
| Tool | Description |
|
|
227
|
+
|------|-------------|
|
|
228
|
+
| `spawn_entity` | Create primitive (box, sphere, etc.) |
|
|
229
|
+
| `destroy_entity` | Remove object from scene |
|
|
230
|
+
| `move_object` | Set object position |
|
|
231
|
+
| `set_transform` | Set position, rotation, scale |
|
|
232
|
+
| `set_visibility` | Show/hide object |
|
|
233
|
+
| `rename_entity` | Rename object |
|
|
234
|
+
| `duplicate_entity` | Clone object |
|
|
235
|
+
|
|
236
|
+
### Physics
|
|
237
|
+
| Tool | Description |
|
|
238
|
+
|------|-------------|
|
|
239
|
+
| `make_physical` | Add physics body |
|
|
240
|
+
| `remove_physics` | Remove physics body |
|
|
241
|
+
| `apply_impulse` | Apply force |
|
|
242
|
+
| `set_gravity` | Set global gravity |
|
|
243
|
+
|
|
244
|
+
### Asset Processing
|
|
245
|
+
| Tool | Description |
|
|
246
|
+
|------|-------------|
|
|
247
|
+
| `analyze_gltf` | Inspect GLTF/GLB structure |
|
|
248
|
+
| `validate_asset` | Validate GLTF/GLB for issues |
|
|
249
|
+
| `optimize_gltf` | Optimize GLTF/GLB assets |
|
|
250
|
+
| `export_to_svelte` | Generate Threlte/Svelte component |
|
|
251
|
+
|
|
252
|
+
### Materials & Assets
|
|
253
|
+
| Tool | Description |
|
|
254
|
+
|------|-------------|
|
|
255
|
+
| `load_asset` | Load GLTF/GLB model |
|
|
256
|
+
| `apply_material` | Set material |
|
|
257
|
+
| `set_environment` | Set skybox/environment |
|
|
258
|
+
|
|
259
|
+
### Atmosphere
|
|
260
|
+
| Tool | Description |
|
|
261
|
+
|------|-------------|
|
|
262
|
+
| `apply_vibe` | Apply mood preset |
|
|
263
|
+
| `get_bridge_status` | Check connection status |
|
|
264
|
+
|
|
265
|
+
## Configuration
|
|
266
|
+
|
|
267
|
+
### WebSocket Connection
|
|
268
|
+
|
|
269
|
+
The server uses port 8082 by default for WebSocket communication.
|
|
270
|
+
|
|
271
|
+
### Environment Variables
|
|
272
|
+
|
|
273
|
+
The MCPBridge auto-connects in development mode. For production, you can enable it via environment variable:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Add to .env file:
|
|
277
|
+
VITE_MCP_ENABLED=true
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
This is useful for:
|
|
281
|
+
- Testing in production builds
|
|
282
|
+
- Demo environments
|
|
283
|
+
- Debugging production issues
|
|
284
|
+
|
|
285
|
+
**Note:** Auto-enabled in development mode (`npm run dev`), no configuration needed.
|
|
286
|
+
|
|
287
|
+
## Development
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# Clone the repo
|
|
291
|
+
git clone https://github.com/RaulContreras123/threlte-mcp.git
|
|
292
|
+
cd threlte-mcp
|
|
293
|
+
|
|
294
|
+
# Install dependencies
|
|
295
|
+
npm install
|
|
296
|
+
|
|
297
|
+
# Run in development
|
|
298
|
+
npm run dev
|
|
299
|
+
|
|
300
|
+
# Build for production
|
|
301
|
+
npm run build
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT ยฉ Raul Contreras
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threlte MCP - WebSocket Bridge Server
|
|
3
|
+
*
|
|
4
|
+
* Provides a WebSocket server that game clients connect to for real-time
|
|
5
|
+
* scene inspection and manipulation by AI agents.
|
|
6
|
+
*/
|
|
7
|
+
export interface MCPCommand {
|
|
8
|
+
action: string;
|
|
9
|
+
requestId?: string;
|
|
10
|
+
maxDepth?: number;
|
|
11
|
+
name?: string;
|
|
12
|
+
path?: string;
|
|
13
|
+
position?: number[];
|
|
14
|
+
rotation?: number[];
|
|
15
|
+
scale?: number[];
|
|
16
|
+
lookAt?: number[];
|
|
17
|
+
fov?: number;
|
|
18
|
+
near?: number;
|
|
19
|
+
far?: number;
|
|
20
|
+
filter?: {
|
|
21
|
+
nameContains?: string;
|
|
22
|
+
type?: string;
|
|
23
|
+
hasUserData?: string;
|
|
24
|
+
};
|
|
25
|
+
id?: string;
|
|
26
|
+
type?: string;
|
|
27
|
+
material?: {
|
|
28
|
+
color?: string;
|
|
29
|
+
metalness?: number;
|
|
30
|
+
roughness?: number;
|
|
31
|
+
};
|
|
32
|
+
parent?: string;
|
|
33
|
+
visible?: boolean;
|
|
34
|
+
newName?: string;
|
|
35
|
+
offset?: number[];
|
|
36
|
+
colliders?: string;
|
|
37
|
+
mass?: number;
|
|
38
|
+
friction?: number;
|
|
39
|
+
restitution?: number;
|
|
40
|
+
gravityScale?: number;
|
|
41
|
+
vector?: number[];
|
|
42
|
+
url?: string;
|
|
43
|
+
preset?: string;
|
|
44
|
+
prop?: string;
|
|
45
|
+
value?: string | number | boolean;
|
|
46
|
+
background?: string;
|
|
47
|
+
color?: string;
|
|
48
|
+
sheet?: string;
|
|
49
|
+
sequence?: string;
|
|
50
|
+
rate?: number;
|
|
51
|
+
range?: [number, number];
|
|
52
|
+
reverse?: boolean;
|
|
53
|
+
time?: number;
|
|
54
|
+
object?: string;
|
|
55
|
+
key?: string;
|
|
56
|
+
event?: string;
|
|
57
|
+
payload?: object;
|
|
58
|
+
questId?: string;
|
|
59
|
+
status?: string;
|
|
60
|
+
progress?: number;
|
|
61
|
+
category?: string;
|
|
62
|
+
vibe?: string;
|
|
63
|
+
targets?: string;
|
|
64
|
+
properties?: string[];
|
|
65
|
+
intensity?: number;
|
|
66
|
+
animate?: boolean;
|
|
67
|
+
duration?: number;
|
|
68
|
+
}
|
|
69
|
+
export interface BridgeServerOptions {
|
|
70
|
+
port?: number;
|
|
71
|
+
commandTimeout?: number;
|
|
72
|
+
}
|
|
73
|
+
export declare class BridgeServer {
|
|
74
|
+
private wss;
|
|
75
|
+
private client;
|
|
76
|
+
private pendingRequests;
|
|
77
|
+
private requestId;
|
|
78
|
+
private lastSceneState;
|
|
79
|
+
private listening;
|
|
80
|
+
private lastError;
|
|
81
|
+
private startupError;
|
|
82
|
+
private port;
|
|
83
|
+
private commandTimeout;
|
|
84
|
+
constructor(options?: BridgeServerOptions);
|
|
85
|
+
private startServer;
|
|
86
|
+
getStatus(): {
|
|
87
|
+
listening: boolean;
|
|
88
|
+
port: number;
|
|
89
|
+
connected: boolean;
|
|
90
|
+
startupError: string | null;
|
|
91
|
+
lastError: string | null;
|
|
92
|
+
pendingRequests: number;
|
|
93
|
+
};
|
|
94
|
+
isConnected(): boolean;
|
|
95
|
+
waitForConnection(timeoutMs?: number): Promise<void>;
|
|
96
|
+
connect(): Promise<void>;
|
|
97
|
+
private handleMessage;
|
|
98
|
+
sendCommand(command: MCPCommand): Promise<unknown>;
|
|
99
|
+
close(): void;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=bridge-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-server.d.ts","sourceRoot":"","sources":["../src/bridge-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threlte MCP - WebSocket Bridge Server
|
|
3
|
+
*
|
|
4
|
+
* Provides a WebSocket server that game clients connect to for real-time
|
|
5
|
+
* scene inspection and manipulation by AI agents.
|
|
6
|
+
*/
|
|
7
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
8
|
+
export class BridgeServer {
|
|
9
|
+
wss = null;
|
|
10
|
+
client = null;
|
|
11
|
+
pendingRequests = new Map();
|
|
12
|
+
requestId = 0;
|
|
13
|
+
lastSceneState = null;
|
|
14
|
+
listening = false;
|
|
15
|
+
lastError = null;
|
|
16
|
+
startupError = null;
|
|
17
|
+
port;
|
|
18
|
+
commandTimeout;
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.port = options.port ?? 8083;
|
|
21
|
+
this.commandTimeout = options.commandTimeout ?? 5000;
|
|
22
|
+
this.startServer();
|
|
23
|
+
}
|
|
24
|
+
startServer() {
|
|
25
|
+
try {
|
|
26
|
+
console.error(`[BridgeServer] Starting WebSocket server on port ${this.port}...`);
|
|
27
|
+
this.wss = new WebSocketServer({ port: this.port });
|
|
28
|
+
this.wss.on('listening', () => {
|
|
29
|
+
console.error(`[BridgeServer] WebSocket server listening on port ${this.port}`);
|
|
30
|
+
this.listening = true;
|
|
31
|
+
this.startupError = null;
|
|
32
|
+
});
|
|
33
|
+
this.wss.on('connection', (ws) => {
|
|
34
|
+
console.error('[BridgeServer] Game client connected');
|
|
35
|
+
this.client = ws;
|
|
36
|
+
ws.on('message', (data) => {
|
|
37
|
+
try {
|
|
38
|
+
const message = JSON.parse(data.toString());
|
|
39
|
+
this.handleMessage(message);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('[BridgeServer] Failed to parse message:', error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
ws.on('close', () => {
|
|
46
|
+
console.error('[BridgeServer] Game client disconnected');
|
|
47
|
+
this.client = null;
|
|
48
|
+
});
|
|
49
|
+
ws.on('error', (error) => {
|
|
50
|
+
console.error('[BridgeServer] Client error:', error);
|
|
51
|
+
this.lastError = error.message;
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
this.wss.on('error', (error) => {
|
|
55
|
+
console.error('[BridgeServer] Server error:', error);
|
|
56
|
+
this.startupError = error.message;
|
|
57
|
+
this.listening = false;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error('[BridgeServer] Failed to start server:', error);
|
|
62
|
+
this.startupError = error.message;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
getStatus() {
|
|
66
|
+
return {
|
|
67
|
+
listening: this.listening,
|
|
68
|
+
port: this.port,
|
|
69
|
+
connected: this.client !== null && this.client.readyState === WebSocket.OPEN,
|
|
70
|
+
startupError: this.startupError,
|
|
71
|
+
lastError: this.lastError,
|
|
72
|
+
pendingRequests: this.pendingRequests.size
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
isConnected() {
|
|
76
|
+
return this.client !== null && this.client.readyState === WebSocket.OPEN;
|
|
77
|
+
}
|
|
78
|
+
async waitForConnection(timeoutMs = 10000) {
|
|
79
|
+
if (this.isConnected())
|
|
80
|
+
return;
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
const timeout = setTimeout(() => {
|
|
83
|
+
reject(new Error('Timeout waiting for game client to connect'));
|
|
84
|
+
}, timeoutMs);
|
|
85
|
+
const interval = setInterval(() => {
|
|
86
|
+
if (this.isConnected()) {
|
|
87
|
+
clearTimeout(timeout);
|
|
88
|
+
clearInterval(interval);
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
}, 100);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async connect() {
|
|
95
|
+
return this.waitForConnection();
|
|
96
|
+
}
|
|
97
|
+
handleMessage(message) {
|
|
98
|
+
if (typeof message === 'object' && message !== null && 'data' in message) {
|
|
99
|
+
this.lastSceneState = message;
|
|
100
|
+
}
|
|
101
|
+
if (typeof message === 'object' &&
|
|
102
|
+
message !== null &&
|
|
103
|
+
'requestId' in message) {
|
|
104
|
+
const { requestId, ...data } = message;
|
|
105
|
+
const pending = this.pendingRequests.get(requestId);
|
|
106
|
+
if (pending) {
|
|
107
|
+
clearTimeout(pending.timeout);
|
|
108
|
+
this.pendingRequests.delete(requestId);
|
|
109
|
+
pending.resolve(data);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async sendCommand(command) {
|
|
114
|
+
if (!this.isConnected()) {
|
|
115
|
+
throw new Error('No game client connected');
|
|
116
|
+
}
|
|
117
|
+
const requestId = `req_${++this.requestId}`;
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
const timeout = setTimeout(() => {
|
|
120
|
+
this.pendingRequests.delete(requestId);
|
|
121
|
+
if (command.action === 'getFullSceneState' || command.action === 'findObjects') {
|
|
122
|
+
resolve(this.lastSceneState);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
reject(new Error('Command timeout'));
|
|
126
|
+
}
|
|
127
|
+
}, this.commandTimeout);
|
|
128
|
+
this.pendingRequests.set(requestId, { resolve, reject, timeout });
|
|
129
|
+
try {
|
|
130
|
+
this.client.send(JSON.stringify({
|
|
131
|
+
...command,
|
|
132
|
+
requestId,
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
clearTimeout(timeout);
|
|
137
|
+
this.pendingRequests.delete(requestId);
|
|
138
|
+
reject(error);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
close() {
|
|
143
|
+
if (this.wss) {
|
|
144
|
+
this.wss.close();
|
|
145
|
+
this.wss = null;
|
|
146
|
+
}
|
|
147
|
+
this.client = null;
|
|
148
|
+
this.pendingRequests.forEach((req) => {
|
|
149
|
+
clearTimeout(req.timeout);
|
|
150
|
+
req.reject(new Error('Server closed'));
|
|
151
|
+
});
|
|
152
|
+
this.pendingRequests.clear();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=bridge-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-server.js","sourceRoot":"","sources":["../src/bridge-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Camera Preset System
|
|
3
|
+
*
|
|
4
|
+
* Manages saving and loading camera positions for quick scene navigation
|
|
5
|
+
* Presets are stored in-memory and can optionally be persisted to localStorage
|
|
6
|
+
*/
|
|
7
|
+
export interface CameraPreset {
|
|
8
|
+
name: string;
|
|
9
|
+
position: [number, number, number];
|
|
10
|
+
lookAt?: [number, number, number];
|
|
11
|
+
fov?: number;
|
|
12
|
+
near?: number;
|
|
13
|
+
far?: number;
|
|
14
|
+
timestamp?: number;
|
|
15
|
+
description?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CameraPresetCollection {
|
|
18
|
+
presets: Record<string, CameraPreset>;
|
|
19
|
+
lastModified: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* In-memory preset storage
|
|
23
|
+
* Will be synced with browser-side storage via WebSocket
|
|
24
|
+
*/
|
|
25
|
+
declare class CameraPresetManager {
|
|
26
|
+
private presets;
|
|
27
|
+
/**
|
|
28
|
+
* Save a camera preset
|
|
29
|
+
*/
|
|
30
|
+
savePreset(preset: CameraPreset): void;
|
|
31
|
+
/**
|
|
32
|
+
* Load a camera preset by name
|
|
33
|
+
*/
|
|
34
|
+
loadPreset(name: string): CameraPreset | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* List all available presets
|
|
37
|
+
*/
|
|
38
|
+
listPresets(): CameraPreset[];
|
|
39
|
+
/**
|
|
40
|
+
* Delete a preset
|
|
41
|
+
*/
|
|
42
|
+
deletePreset(name: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Import presets from JSON
|
|
45
|
+
*/
|
|
46
|
+
importPresets(data: CameraPresetCollection): void;
|
|
47
|
+
/**
|
|
48
|
+
* Export presets to JSON
|
|
49
|
+
*/
|
|
50
|
+
exportPresets(): CameraPresetCollection;
|
|
51
|
+
/**
|
|
52
|
+
* Clear all presets
|
|
53
|
+
*/
|
|
54
|
+
clear(): void;
|
|
55
|
+
}
|
|
56
|
+
export declare const cameraPresets: CameraPresetManager;
|
|
57
|
+
/**
|
|
58
|
+
* Default presets for common viewpoints
|
|
59
|
+
*/
|
|
60
|
+
export declare const DEFAULT_PRESETS: CameraPreset[];
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=camera-presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-presets.d.ts","sourceRoot":"","sources":["../src/camera-presets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,cAAM,mBAAmB;IACvB,OAAO,CAAC,OAAO,CAAwC;IAEvD;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAKtC;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIlD;;OAEG;IACH,WAAW,IAAI,YAAY,EAAE;IAM7B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAInC;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI;IAMjD;;OAEG;IACH,aAAa,IAAI,sBAAsB;IAWvC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAGD,eAAO,MAAM,aAAa,qBAA4B,CAAC;AAEvD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,YAAY,EAwCzC,CAAC"}
|