@beremaran/godot-agent-loop 1.0.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 +23 -0
- package/README.md +925 -0
- package/addons/godot_agent_loop/LICENSE +23 -0
- package/addons/godot_agent_loop/README.md +31 -0
- package/addons/godot_agent_loop/plugin.cfg +8 -0
- package/addons/godot_agent_loop/plugin.gd +417 -0
- package/agent-plugin/.claude-plugin/plugin.json +19 -0
- package/agent-plugin/.codex-plugin/plugin.json +37 -0
- package/agent-plugin/.mcp.json +14 -0
- package/agent-plugin/adapter-manifest.json +45 -0
- package/agent-plugin/pi/extension.ts +136 -0
- package/agent-plugin/skills/build-godot-game/SKILL.md +41 -0
- package/agent-plugin/skills/debug-godot-game/SKILL.md +37 -0
- package/agent-plugin/skills/ship-godot-game/SKILL.md +46 -0
- package/agent-plugin/skills/ship-godot-game/agents/openai.yaml +4 -0
- package/agent-plugin/skills/verify-godot-change/SKILL.md +35 -0
- package/build/authoring-session-manager.js +237 -0
- package/build/domain-tool-registries.js +207 -0
- package/build/editor-bridge-protocol.js +1 -0
- package/build/editor-connection.js +112 -0
- package/build/editor-mutation-guard.js +30 -0
- package/build/editor-plugin-installer.js +220 -0
- package/build/engine-api/extension_api-4.7.stable.official.5b4e0cb0f.json +356830 -0
- package/build/game-command-service.js +49 -0
- package/build/game-connection.js +337 -0
- package/build/godot-executable.js +156 -0
- package/build/godot-process-manager.js +112 -0
- package/build/godot-subprocess.js +23 -0
- package/build/headless-operation-runner.js +68 -0
- package/build/headless-operation-service.js +65 -0
- package/build/index.js +478 -0
- package/build/interaction-server-installer.js +234 -0
- package/build/opencode-setup.js +199 -0
- package/build/project-support.js +219 -0
- package/build/runtime-protocol.js +202 -0
- package/build/scripts/godot_operations.gd +1534 -0
- package/build/scripts/mcp_editor_plugin.gd +417 -0
- package/build/scripts/mcp_interaction_server.gd +1255 -0
- package/build/scripts/mcp_runtime/audio_animation_domain.gd +568 -0
- package/build/scripts/mcp_runtime/command_params.gd +339 -0
- package/build/scripts/mcp_runtime/core_domain.gd +591 -0
- package/build/scripts/mcp_runtime/input_domain.gd +695 -0
- package/build/scripts/mcp_runtime/networking_domain.gd +356 -0
- package/build/scripts/mcp_runtime/physics_domain.gd +653 -0
- package/build/scripts/mcp_runtime/privileged_command_policy.gd +81 -0
- package/build/scripts/mcp_runtime/rendering_domain.gd +807 -0
- package/build/scripts/mcp_runtime/runtime_domain.gd +108 -0
- package/build/scripts/mcp_runtime/scene_2d_domain.gd +527 -0
- package/build/scripts/mcp_runtime/scene_3d_domain.gd +573 -0
- package/build/scripts/mcp_runtime/system_domain.gd +277 -0
- package/build/scripts/mcp_runtime/ui_domain.gd +619 -0
- package/build/scripts/mcp_runtime/variant_codec.gd +335 -0
- package/build/scripts/validate_script.gd +28 -0
- package/build/server-instructions.js +11 -0
- package/build/session-timing.js +20 -0
- package/build/tool-argument-validation.js +120 -0
- package/build/tool-definitions.js +2727 -0
- package/build/tool-handlers/game-tool-handlers.js +1345 -0
- package/build/tool-handlers/lifecycle-tool-handlers.js +346 -0
- package/build/tool-handlers/project-handler-services.js +1458 -0
- package/build/tool-handlers/project-tool-handlers.js +1506 -0
- package/build/tool-handlers/visual-regression-service.js +139 -0
- package/build/tool-manifest.js +1193 -0
- package/build/tool-mutation-policy.js +122 -0
- package/build/tool-registry.js +34 -0
- package/build/tool-surface.js +70 -0
- package/build/utils.js +273 -0
- package/package.json +110 -0
- package/product.json +52 -0
- package/scripts/prepare-package.js +42 -0
|
@@ -0,0 +1,1345 @@
|
|
|
1
|
+
import { createErrorResponse, errorMessage, normalizeParameters } from '../utils.js';
|
|
2
|
+
import { VisualRegressionService } from './visual-regression-service.js';
|
|
3
|
+
/**
|
|
4
|
+
* ParticleProcessMaterial fields the runtime reads, keyed by the camelCase name
|
|
5
|
+
* the MCP surface uses. `processMaterial` is a free-form object, so its keys are
|
|
6
|
+
* normalized like any other argument; without this translation the multi-word
|
|
7
|
+
* fields arrived as camelCase and the runtime — which looks for snake_case —
|
|
8
|
+
* silently dropped them.
|
|
9
|
+
*/
|
|
10
|
+
const PARTICLE_MATERIAL_FIELDS = {
|
|
11
|
+
direction: 'direction',
|
|
12
|
+
spread: 'spread',
|
|
13
|
+
gravity: 'gravity',
|
|
14
|
+
initialVelocityMin: 'initial_velocity_min',
|
|
15
|
+
initialVelocityMax: 'initial_velocity_max',
|
|
16
|
+
color: 'color',
|
|
17
|
+
scaleMin: 'scale_min',
|
|
18
|
+
scaleMax: 'scale_max',
|
|
19
|
+
};
|
|
20
|
+
function particleProcessMaterial(source) {
|
|
21
|
+
if (!source || typeof source !== 'object')
|
|
22
|
+
return {};
|
|
23
|
+
const input = source;
|
|
24
|
+
const mapped = {};
|
|
25
|
+
for (const [camel, snake] of Object.entries(PARTICLE_MATERIAL_FIELDS)) {
|
|
26
|
+
if (input[camel] !== undefined)
|
|
27
|
+
mapped[snake] = input[camel];
|
|
28
|
+
}
|
|
29
|
+
return mapped;
|
|
30
|
+
}
|
|
31
|
+
/** Implements the tools that operate on a running Godot game. */
|
|
32
|
+
export class GameToolHandlers {
|
|
33
|
+
context;
|
|
34
|
+
visualRegression;
|
|
35
|
+
constructor(context) {
|
|
36
|
+
// Keep the per-tool mapping functions local to this handler while the
|
|
37
|
+
// runtime checks, transport, and response handling live in the service.
|
|
38
|
+
this.context = {
|
|
39
|
+
...context,
|
|
40
|
+
getActiveProcess: () => context.commands.hasActiveProcess(),
|
|
41
|
+
readNewErrors: limit => context.commands.readNewErrors(limit),
|
|
42
|
+
readNewLogs: limit => context.commands.readNewLogs(limit),
|
|
43
|
+
gameCommand: context.commands.execute.bind(context.commands),
|
|
44
|
+
};
|
|
45
|
+
this.visualRegression = new VisualRegressionService(context.commands);
|
|
46
|
+
}
|
|
47
|
+
async handleGameScreenshot() {
|
|
48
|
+
if (!this.context.commands.hasActiveProcess()) {
|
|
49
|
+
return createErrorResponse('No active Godot process. Use run_project first.');
|
|
50
|
+
}
|
|
51
|
+
if (!this.context.commands.isConnected()) {
|
|
52
|
+
return createErrorResponse('Not connected to game interaction server. Wait a moment and try again.');
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const response = await this.context.commands.send('screenshot');
|
|
56
|
+
if ('error' in response) {
|
|
57
|
+
return createErrorResponse(`Screenshot failed: ${response.error.message}`);
|
|
58
|
+
}
|
|
59
|
+
const result = response.result;
|
|
60
|
+
return {
|
|
61
|
+
content: [
|
|
62
|
+
{
|
|
63
|
+
type: 'image',
|
|
64
|
+
data: result.data,
|
|
65
|
+
mimeType: 'image/png',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'text',
|
|
69
|
+
text: `Screenshot captured: ${result.width}x${result.height}`,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return createErrorResponse(`Screenshot failed: ${errorMessage(error)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async handleGameVisualRegression(args) {
|
|
79
|
+
if (args.action !== 'capture_baseline' && args.action !== 'compare') {
|
|
80
|
+
return createErrorResponse('action must be capture_baseline or compare.');
|
|
81
|
+
}
|
|
82
|
+
return this.visualRegression.execute(args);
|
|
83
|
+
}
|
|
84
|
+
async handleGameClick(args) {
|
|
85
|
+
return this.context.commands.execute('click', args, a => ({ x: a.x ?? 0, y: a.y ?? 0, button: a.button ?? 1 }));
|
|
86
|
+
}
|
|
87
|
+
async handleGameKeyPress(args) {
|
|
88
|
+
args = args || {};
|
|
89
|
+
if (!args.key && !args.action && !args.text)
|
|
90
|
+
return createErrorResponse('Must provide exactly one of "key", "action", or "text".');
|
|
91
|
+
const params = {};
|
|
92
|
+
if (args.key)
|
|
93
|
+
params.key = args.key;
|
|
94
|
+
if (args.action)
|
|
95
|
+
params.action = args.action;
|
|
96
|
+
if (args.text)
|
|
97
|
+
params.text = args.text;
|
|
98
|
+
if (args.pressed !== undefined)
|
|
99
|
+
params.pressed = args.pressed;
|
|
100
|
+
for (const option of ['physical', 'shift', 'ctrl', 'alt', 'meta']) {
|
|
101
|
+
if (args[option] !== undefined)
|
|
102
|
+
params[option] = args[option];
|
|
103
|
+
}
|
|
104
|
+
return this.context.commands.execute('key_press', args, () => params);
|
|
105
|
+
}
|
|
106
|
+
async handleGameMouseMove(args) {
|
|
107
|
+
return this.context.commands.execute('mouse_move', args, a => ({
|
|
108
|
+
x: a.x ?? 0, y: a.y ?? 0, relative_x: a.relative_x ?? 0, relative_y: a.relative_y ?? 0,
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
async handleGameGetUi() {
|
|
112
|
+
return this.context.gameCommand('get_ui_elements', {}, () => ({}));
|
|
113
|
+
}
|
|
114
|
+
async handleGameGetSceneTree(args) {
|
|
115
|
+
return this.context.gameCommand('get_scene_tree', args, a => ({ max_nodes: a.maxNodes ?? 1000 }));
|
|
116
|
+
}
|
|
117
|
+
async handleGameEval(args) {
|
|
118
|
+
args = normalizeParameters(args || {});
|
|
119
|
+
if (!args.code)
|
|
120
|
+
return createErrorResponse('code parameter is required.');
|
|
121
|
+
return this.context.gameCommand('eval', args, a => ({ code: a.code }), 30000);
|
|
122
|
+
}
|
|
123
|
+
async handleGameGetProperty(args) {
|
|
124
|
+
args = normalizeParameters(args || {});
|
|
125
|
+
if (!args.nodePath || !args.property)
|
|
126
|
+
return createErrorResponse('nodePath and property are required.');
|
|
127
|
+
return this.context.gameCommand('get_property', args, a => ({ node_path: a.nodePath, property: a.property }));
|
|
128
|
+
}
|
|
129
|
+
async handleGameSetProperty(args) {
|
|
130
|
+
args = normalizeParameters(args || {});
|
|
131
|
+
if (!args.nodePath || !args.property)
|
|
132
|
+
return createErrorResponse('nodePath and property are required.');
|
|
133
|
+
return this.context.gameCommand('set_property', args, a => ({
|
|
134
|
+
node_path: a.nodePath, property: a.property, value: a.value, type_hint: a.typeHint || '',
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
async handleGameCallMethod(args) {
|
|
138
|
+
args = normalizeParameters(args || {});
|
|
139
|
+
if (!args.nodePath || !args.method)
|
|
140
|
+
return createErrorResponse('nodePath and method are required.');
|
|
141
|
+
return this.context.gameCommand('call_method', args, a => ({
|
|
142
|
+
node_path: a.nodePath, method: a.method, args: a.args || [],
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
async handleGameGetNodeInfo(args) {
|
|
146
|
+
args = normalizeParameters(args || {});
|
|
147
|
+
if (!args.nodePath)
|
|
148
|
+
return createErrorResponse('nodePath is required.');
|
|
149
|
+
return this.context.gameCommand('get_node_info', args, a => ({ node_path: a.nodePath }));
|
|
150
|
+
}
|
|
151
|
+
async handleGameInstantiateScene(args) {
|
|
152
|
+
args = normalizeParameters(args || {});
|
|
153
|
+
if (!args.scenePath)
|
|
154
|
+
return createErrorResponse('scenePath is required.');
|
|
155
|
+
return this.context.gameCommand('instantiate_scene', args, a => ({
|
|
156
|
+
scene_path: a.scenePath, parent_path: a.parentPath || '/root',
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
async handleGameRemoveNode(args) {
|
|
160
|
+
args = normalizeParameters(args || {});
|
|
161
|
+
if (!args.nodePath)
|
|
162
|
+
return createErrorResponse('nodePath is required.');
|
|
163
|
+
return this.context.gameCommand('remove_node', args, a => ({ node_path: a.nodePath }));
|
|
164
|
+
}
|
|
165
|
+
async handleGameChangeScene(args) {
|
|
166
|
+
args = normalizeParameters(args || {});
|
|
167
|
+
if (!args.scenePath)
|
|
168
|
+
return createErrorResponse('scenePath is required.');
|
|
169
|
+
return this.context.gameCommand('change_scene', args, a => ({ scene_path: a.scenePath }));
|
|
170
|
+
}
|
|
171
|
+
async handleGamePause(args) {
|
|
172
|
+
return this.context.gameCommand('pause', args, a => ({ paused: a.paused !== undefined ? a.paused : true }));
|
|
173
|
+
}
|
|
174
|
+
async handleGamePerformance(args = {}) {
|
|
175
|
+
args = normalizeParameters(args || {});
|
|
176
|
+
const action = args.action ?? 'sample';
|
|
177
|
+
if (!['sample', 'start', 'stop', 'report', 'leaks'].includes(action)) {
|
|
178
|
+
return createErrorResponse('action must be sample, start, stop, report, or leaks.');
|
|
179
|
+
}
|
|
180
|
+
return this.context.gameCommand('get_performance', args, a => ({
|
|
181
|
+
action: a.action ?? 'sample', sample_count: a.sampleCount ?? a.sample_count ?? 1, sampleCount: a.sampleCount ?? a.sample_count ?? 1,
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
async handleGameWait(args) {
|
|
185
|
+
args = normalizeParameters(args || {});
|
|
186
|
+
if (args.frames !== undefined && (!Number.isInteger(args.frames) || args.frames < 1)) {
|
|
187
|
+
return createErrorResponse('frames must be a positive integer.');
|
|
188
|
+
}
|
|
189
|
+
return this.context.gameCommand('wait', args, a => ({ frames: a.frames ?? 1, frame_type: a.frameType || 'render' }), 30000);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Handle the read_scene tool - Read a scene file structure
|
|
193
|
+
*/
|
|
194
|
+
async handleGameConnectSignal(args) {
|
|
195
|
+
args = normalizeParameters(args || {});
|
|
196
|
+
if (!args.nodePath || !args.signalName || !args.targetPath || !args.method)
|
|
197
|
+
return createErrorResponse('nodePath, signalName, targetPath, and method are required.');
|
|
198
|
+
return this.context.gameCommand('connect_signal', args, a => ({
|
|
199
|
+
node_path: a.nodePath, signal_name: a.signalName, target_path: a.targetPath, method: a.method,
|
|
200
|
+
...(a.binds ? { binds: a.binds } : {}),
|
|
201
|
+
...(a.deferred !== undefined ? { deferred: a.deferred } : {}),
|
|
202
|
+
...(a.oneShot !== undefined ? { one_shot: a.oneShot } : {}),
|
|
203
|
+
...(a.referenceCounted !== undefined ? { reference_counted: a.referenceCounted } : {}),
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
async handleGameDisconnectSignal(args) {
|
|
207
|
+
args = normalizeParameters(args || {});
|
|
208
|
+
if (!args.nodePath || !args.signalName || !args.targetPath || !args.method)
|
|
209
|
+
return createErrorResponse('nodePath, signalName, targetPath, and method are required.');
|
|
210
|
+
return this.context.gameCommand('disconnect_signal', args, a => ({
|
|
211
|
+
node_path: a.nodePath, signal_name: a.signalName, target_path: a.targetPath, method: a.method,
|
|
212
|
+
...(a.binds ? { binds: a.binds } : {}),
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
async handleGameEmitSignal(args) {
|
|
216
|
+
args = normalizeParameters(args || {});
|
|
217
|
+
if (!args.nodePath || !args.signalName)
|
|
218
|
+
return createErrorResponse('nodePath and signalName are required.');
|
|
219
|
+
return this.context.gameCommand('emit_signal', args, a => ({
|
|
220
|
+
node_path: a.nodePath, signal_name: a.signalName, args: a.args || [],
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
async handleGamePlayAnimation(args) {
|
|
224
|
+
args = normalizeParameters(args || {});
|
|
225
|
+
if (!args.nodePath)
|
|
226
|
+
return createErrorResponse('nodePath is required.');
|
|
227
|
+
return this.context.gameCommand('play_animation', args, a => ({
|
|
228
|
+
node_path: a.nodePath, action: a.action || 'play', animation: a.animation || '',
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
async handleGameTweenProperty(args) {
|
|
232
|
+
args = normalizeParameters(args || {});
|
|
233
|
+
if (!args.nodePath || !args.property || args.finalValue === undefined)
|
|
234
|
+
return createErrorResponse('nodePath, property, and finalValue are required.');
|
|
235
|
+
return this.context.gameCommand('tween_property', args, a => ({
|
|
236
|
+
node_path: a.nodePath, property: a.property, final_value: a.finalValue,
|
|
237
|
+
duration: a.duration ?? 1.0, trans_type: a.transType ?? 0, ease_type: a.easeType ?? 2,
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
async handleGameGetNodesInGroup(args) {
|
|
241
|
+
args = normalizeParameters(args || {});
|
|
242
|
+
if (!args.group)
|
|
243
|
+
return createErrorResponse('group is required.');
|
|
244
|
+
return this.context.gameCommand('get_nodes_in_group', args, a => ({ group: a.group }));
|
|
245
|
+
}
|
|
246
|
+
async handleGameFindNodesByClass(args) {
|
|
247
|
+
args = normalizeParameters(args || {});
|
|
248
|
+
if (!args.className)
|
|
249
|
+
return createErrorResponse('className is required.');
|
|
250
|
+
return this.context.gameCommand('find_nodes_by_class', args, a => ({
|
|
251
|
+
class_name: a.className, root_path: a.rootPath || '/root',
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
async handleGameReparentNode(args) {
|
|
255
|
+
args = normalizeParameters(args || {});
|
|
256
|
+
if (!args.nodePath || !args.newParentPath)
|
|
257
|
+
return createErrorResponse('nodePath and newParentPath are required.');
|
|
258
|
+
return this.context.gameCommand('reparent_node', args, a => ({
|
|
259
|
+
node_path: a.nodePath, new_parent_path: a.newParentPath, keep_global_transform: a.keepGlobalTransform !== false,
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
async handleGameGetErrors(args) {
|
|
263
|
+
if (!this.context.getActiveProcess())
|
|
264
|
+
return createErrorResponse('No active Godot process. Use run_project first.');
|
|
265
|
+
const { items: errors, remaining } = this.context.readNewErrors(args?.maxItems ?? 1000);
|
|
266
|
+
return { content: [{ type: 'text', text: JSON.stringify({ count: errors.length, errors, remaining, hasMore: remaining > 0 }, null, 2) }] };
|
|
267
|
+
}
|
|
268
|
+
async handleGameGetLogs(args) {
|
|
269
|
+
if (!this.context.getActiveProcess())
|
|
270
|
+
return createErrorResponse('No active Godot process. Use run_project first.');
|
|
271
|
+
const { items: logs, remaining } = this.context.readNewLogs(args?.maxItems ?? 1000);
|
|
272
|
+
return { content: [{ type: 'text', text: JSON.stringify({ count: logs.length, logs, remaining, hasMore: remaining > 0 }, null, 2) }] };
|
|
273
|
+
}
|
|
274
|
+
// --- Enhanced input handlers ---
|
|
275
|
+
async handleGameKeyHold(args) {
|
|
276
|
+
args = args || {};
|
|
277
|
+
if (!args.key && !args.action)
|
|
278
|
+
return createErrorResponse('Must provide either "key" or "action" parameter.');
|
|
279
|
+
const params = {};
|
|
280
|
+
if (args.key)
|
|
281
|
+
params.key = args.key;
|
|
282
|
+
if (args.action)
|
|
283
|
+
params.action = args.action;
|
|
284
|
+
return this.context.gameCommand('key_hold', args, () => params);
|
|
285
|
+
}
|
|
286
|
+
async handleGameKeyRelease(args) {
|
|
287
|
+
args = args || {};
|
|
288
|
+
if (!args.key && !args.action)
|
|
289
|
+
return createErrorResponse('Must provide either "key" or "action" parameter.');
|
|
290
|
+
const params = {};
|
|
291
|
+
if (args.key)
|
|
292
|
+
params.key = args.key;
|
|
293
|
+
if (args.action)
|
|
294
|
+
params.action = args.action;
|
|
295
|
+
return this.context.gameCommand('key_release', args, () => params);
|
|
296
|
+
}
|
|
297
|
+
async handleGameScroll(args) {
|
|
298
|
+
return this.context.gameCommand('scroll', args, a => ({
|
|
299
|
+
x: a.x ?? 0, y: a.y ?? 0, direction: a.direction || 'up', amount: a.amount || 1,
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
async handleGameMouseDrag(args) {
|
|
303
|
+
args = normalizeParameters(args || {});
|
|
304
|
+
if (args.fromX === undefined || args.fromY === undefined || args.toX === undefined || args.toY === undefined)
|
|
305
|
+
return createErrorResponse('fromX, fromY, toX, and toY are required.');
|
|
306
|
+
return this.context.gameCommand('mouse_drag', args, a => ({
|
|
307
|
+
from_x: a.fromX, from_y: a.fromY, to_x: a.toX, to_y: a.toY,
|
|
308
|
+
button: a.button ?? 1, steps: a.steps ?? 10,
|
|
309
|
+
}), 30000);
|
|
310
|
+
}
|
|
311
|
+
async handleGameGamepad(args) {
|
|
312
|
+
args = normalizeParameters(args || {});
|
|
313
|
+
if (!args.type || args.index === undefined || args.value === undefined)
|
|
314
|
+
return createErrorResponse('type, index, and value are required.');
|
|
315
|
+
return this.context.gameCommand('gamepad', args, a => ({
|
|
316
|
+
type: a.type, index: a.index, value: a.value, device: a.device ?? 0,
|
|
317
|
+
...(a.deadzone !== undefined ? { deadzone: a.deadzone } : {}),
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
// --- Project management handlers ---
|
|
321
|
+
async handleGameGetCamera() {
|
|
322
|
+
return this.context.gameCommand('get_camera', {}, () => ({}));
|
|
323
|
+
}
|
|
324
|
+
async handleGameSetCamera(args) {
|
|
325
|
+
return this.context.gameCommand('set_camera', args, a => ({
|
|
326
|
+
...(a.position ? { position: a.position } : {}),
|
|
327
|
+
...(a.rotation ? { rotation: a.rotation } : {}),
|
|
328
|
+
...(a.zoom ? { zoom: a.zoom } : {}),
|
|
329
|
+
...(a.fov !== undefined ? { fov: a.fov } : {}),
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
async handleGameRaycast(args) {
|
|
333
|
+
args = normalizeParameters(args || {});
|
|
334
|
+
if (!args.from || !args.to)
|
|
335
|
+
return createErrorResponse('from and to are required.');
|
|
336
|
+
return this.context.gameCommand('raycast', args, a => ({
|
|
337
|
+
from: a.from, to: a.to, collision_mask: a.collisionMask ?? 0xFFFFFFFF,
|
|
338
|
+
}));
|
|
339
|
+
}
|
|
340
|
+
async handleGameGetAudio() {
|
|
341
|
+
return this.context.gameCommand('get_audio', {}, () => ({}));
|
|
342
|
+
}
|
|
343
|
+
async handleGameSpawnNode(args) {
|
|
344
|
+
args = normalizeParameters(args || {});
|
|
345
|
+
if (!args.type)
|
|
346
|
+
return createErrorResponse('type is required.');
|
|
347
|
+
return this.context.gameCommand('spawn_node', args, a => ({
|
|
348
|
+
type: a.type, name: a.name || '', parent_path: a.parentPath || '/root',
|
|
349
|
+
...(a.properties ? { properties: a.properties } : {}),
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
async handleGameSetShaderParam(args) {
|
|
353
|
+
args = normalizeParameters(args || {});
|
|
354
|
+
if (!args.nodePath || !args.paramName)
|
|
355
|
+
return createErrorResponse('nodePath and paramName are required.');
|
|
356
|
+
return this.context.gameCommand('set_shader_param', args, a => ({
|
|
357
|
+
node_path: a.nodePath, param_name: a.paramName, value: a.value,
|
|
358
|
+
...(a.typeHint ? { type_hint: a.typeHint } : {}),
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
async handleGameAudioPlay(args) {
|
|
362
|
+
args = normalizeParameters(args || {});
|
|
363
|
+
if (!args.nodePath)
|
|
364
|
+
return createErrorResponse('nodePath is required.');
|
|
365
|
+
return this.context.gameCommand('audio_play', args, a => ({
|
|
366
|
+
node_path: a.nodePath, action: a.action || 'play',
|
|
367
|
+
...(a.stream ? { stream: a.stream } : {}),
|
|
368
|
+
...(a.volume !== undefined ? { volume: a.volume } : {}),
|
|
369
|
+
...(a.pitch !== undefined ? { pitch: a.pitch } : {}),
|
|
370
|
+
...(a.bus ? { bus: a.bus } : {}),
|
|
371
|
+
...(a.fromPosition !== undefined ? { from_position: a.fromPosition } : {}),
|
|
372
|
+
}));
|
|
373
|
+
}
|
|
374
|
+
async handleGameAudioBus(args) {
|
|
375
|
+
return this.context.gameCommand('audio_bus', args, a => ({
|
|
376
|
+
bus_name: a.busName || 'Master',
|
|
377
|
+
...(a.volume !== undefined ? { volume: a.volume } : {}),
|
|
378
|
+
...(a.mute !== undefined ? { mute: a.mute } : {}),
|
|
379
|
+
...(a.solo !== undefined ? { solo: a.solo } : {}),
|
|
380
|
+
}));
|
|
381
|
+
}
|
|
382
|
+
async handleGameNavigatePath(args) {
|
|
383
|
+
args = normalizeParameters(args || {});
|
|
384
|
+
if (!args.start || !args.end)
|
|
385
|
+
return createErrorResponse('start and end are required.');
|
|
386
|
+
return this.context.gameCommand('navigate_path', args, a => ({
|
|
387
|
+
start: a.start, end: a.end, optimize: a.optimize ?? true,
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
async handleGameTilemap(args) {
|
|
391
|
+
args = normalizeParameters(args || {});
|
|
392
|
+
if (!args.nodePath)
|
|
393
|
+
return createErrorResponse('nodePath is required.');
|
|
394
|
+
if (!args.action)
|
|
395
|
+
return createErrorResponse('action is required.');
|
|
396
|
+
return this.context.gameCommand('tilemap', args, a => ({
|
|
397
|
+
node_path: a.nodePath, action: a.action,
|
|
398
|
+
...(a.x !== undefined ? { x: a.x } : {}),
|
|
399
|
+
...(a.y !== undefined ? { y: a.y } : {}),
|
|
400
|
+
...(a.cells ? {
|
|
401
|
+
cells: a.cells.map(cell => ({
|
|
402
|
+
x: cell.x,
|
|
403
|
+
y: cell.y,
|
|
404
|
+
...(cell.sourceId !== undefined ? { source_id: cell.sourceId } : {}),
|
|
405
|
+
...(cell.atlasX !== undefined ? { atlas_x: cell.atlasX } : {}),
|
|
406
|
+
...(cell.atlasY !== undefined ? { atlas_y: cell.atlasY } : {}),
|
|
407
|
+
...(cell.altTile !== undefined ? { alt_tile: cell.altTile } : {}),
|
|
408
|
+
})),
|
|
409
|
+
} : {}),
|
|
410
|
+
...(a.sourceId !== undefined ? { source_id: a.sourceId } : {}),
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
async handleGameAddCollision(args) {
|
|
414
|
+
args = normalizeParameters(args || {});
|
|
415
|
+
if (!args.parentPath || !args.shapeType)
|
|
416
|
+
return createErrorResponse('parentPath and shapeType are required.');
|
|
417
|
+
return this.context.gameCommand('add_collision', args, a => ({
|
|
418
|
+
parent_path: a.parentPath, shape_type: a.shapeType,
|
|
419
|
+
...(a.shapeParams ? { shape_params: a.shapeParams } : {}),
|
|
420
|
+
...(a.collisionLayer !== undefined ? { collision_layer: a.collisionLayer } : {}),
|
|
421
|
+
...(a.collisionMask !== undefined ? { collision_mask: a.collisionMask } : {}),
|
|
422
|
+
...(a.disabled !== undefined ? { disabled: a.disabled } : {}),
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
425
|
+
async handleGameEnvironment(args) {
|
|
426
|
+
args = normalizeParameters(args || {});
|
|
427
|
+
const params = { action: args.action || 'set' };
|
|
428
|
+
// Pass through all environment settings
|
|
429
|
+
const envKeys = [
|
|
430
|
+
'backgroundMode', 'backgroundColor', 'ambientLightColor', 'ambientLightEnergy',
|
|
431
|
+
'fogEnabled', 'fogDensity', 'fogLightColor',
|
|
432
|
+
'glowEnabled', 'glowIntensity', 'glowBloom',
|
|
433
|
+
'tonemapMode', 'ssaoEnabled', 'ssaoRadius', 'ssaoIntensity', 'ssrEnabled',
|
|
434
|
+
'brightness', 'contrast', 'saturation',
|
|
435
|
+
];
|
|
436
|
+
const snakeMap = {
|
|
437
|
+
backgroundMode: 'background_mode', backgroundColor: 'background_color',
|
|
438
|
+
ambientLightColor: 'ambient_light_color', ambientLightEnergy: 'ambient_light_energy',
|
|
439
|
+
fogEnabled: 'fog_enabled', fogDensity: 'fog_density', fogLightColor: 'fog_light_color',
|
|
440
|
+
glowEnabled: 'glow_enabled', glowIntensity: 'glow_intensity', glowBloom: 'glow_bloom',
|
|
441
|
+
tonemapMode: 'tonemap_mode', ssaoEnabled: 'ssao_enabled', ssaoRadius: 'ssao_radius',
|
|
442
|
+
ssaoIntensity: 'ssao_intensity', ssrEnabled: 'ssr_enabled',
|
|
443
|
+
brightness: 'brightness', contrast: 'contrast', saturation: 'saturation',
|
|
444
|
+
};
|
|
445
|
+
for (const key of envKeys) {
|
|
446
|
+
if (args[key] !== undefined) {
|
|
447
|
+
params[snakeMap[key]] = args[key];
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return this.context.gameCommand('environment', { ...args }, () => params);
|
|
451
|
+
}
|
|
452
|
+
async handleGameManageGroup(args) {
|
|
453
|
+
args = normalizeParameters(args || {});
|
|
454
|
+
if (!args.action)
|
|
455
|
+
return createErrorResponse('action is required.');
|
|
456
|
+
return this.context.gameCommand('manage_group', args, a => ({
|
|
457
|
+
action: a.action,
|
|
458
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
459
|
+
...(a.group ? { group: a.group } : {}),
|
|
460
|
+
}));
|
|
461
|
+
}
|
|
462
|
+
async handleGameCreateTimer(args) {
|
|
463
|
+
return this.context.gameCommand('create_timer', args, a => ({
|
|
464
|
+
parent_path: a.parentPath || '/root',
|
|
465
|
+
wait_time: a.waitTime ?? 1.0,
|
|
466
|
+
one_shot: a.oneShot ?? false,
|
|
467
|
+
autostart: a.autostart ?? false,
|
|
468
|
+
...(a.name ? { name: a.name } : {}),
|
|
469
|
+
}));
|
|
470
|
+
}
|
|
471
|
+
async handleGameSetParticles(args) {
|
|
472
|
+
args = normalizeParameters(args || {});
|
|
473
|
+
if (!args.nodePath)
|
|
474
|
+
return createErrorResponse('nodePath is required.');
|
|
475
|
+
return this.context.gameCommand('set_particles', args, a => ({
|
|
476
|
+
node_path: a.nodePath,
|
|
477
|
+
...(a.emitting !== undefined ? { emitting: a.emitting } : {}),
|
|
478
|
+
...(a.amount !== undefined ? { amount: a.amount } : {}),
|
|
479
|
+
...(a.lifetime !== undefined ? { lifetime: a.lifetime } : {}),
|
|
480
|
+
...(a.oneShot !== undefined ? { one_shot: a.oneShot } : {}),
|
|
481
|
+
...(a.speedScale !== undefined ? { speed_scale: a.speedScale } : {}),
|
|
482
|
+
...(a.explosiveness !== undefined ? { explosiveness: a.explosiveness } : {}),
|
|
483
|
+
...(a.randomness !== undefined ? { randomness: a.randomness } : {}),
|
|
484
|
+
...(a.processMaterial ? { process_material: particleProcessMaterial(a.processMaterial) } : {}),
|
|
485
|
+
}));
|
|
486
|
+
}
|
|
487
|
+
async handleGameCreateAnimation(args) {
|
|
488
|
+
args = normalizeParameters(args || {});
|
|
489
|
+
if (!args.nodePath || !args.animationName)
|
|
490
|
+
return createErrorResponse('nodePath and animationName are required.');
|
|
491
|
+
return this.context.gameCommand('create_animation', args, a => ({
|
|
492
|
+
node_path: a.nodePath,
|
|
493
|
+
animation_name: a.animationName,
|
|
494
|
+
length: a.length ?? 1.0,
|
|
495
|
+
loop_mode: a.loopMode ?? 0,
|
|
496
|
+
tracks: a.tracks || [],
|
|
497
|
+
...(a.library !== undefined ? { library: a.library } : {}),
|
|
498
|
+
}));
|
|
499
|
+
}
|
|
500
|
+
async handleGameSerializeState(args) {
|
|
501
|
+
args = normalizeParameters(args || {});
|
|
502
|
+
return this.context.gameCommand('serialize_state', args, a => ({
|
|
503
|
+
node_path: a.nodePath || '/root',
|
|
504
|
+
action: a.action || 'save',
|
|
505
|
+
max_depth: a.maxDepth ?? 5,
|
|
506
|
+
...(a.data ? { data: a.data } : {}),
|
|
507
|
+
}));
|
|
508
|
+
}
|
|
509
|
+
async handleGamePhysicsBody(args) {
|
|
510
|
+
args = normalizeParameters(args || {});
|
|
511
|
+
if (!args.nodePath)
|
|
512
|
+
return createErrorResponse('nodePath is required.');
|
|
513
|
+
return this.context.gameCommand('physics_body', args, a => ({
|
|
514
|
+
node_path: a.nodePath,
|
|
515
|
+
...(a.gravityScale !== undefined ? { gravity_scale: a.gravityScale } : {}),
|
|
516
|
+
...(a.mass !== undefined ? { mass: a.mass } : {}),
|
|
517
|
+
...(a.linearVelocity ? { linear_velocity: a.linearVelocity } : {}),
|
|
518
|
+
...(a.angularVelocity !== undefined ? { angular_velocity: a.angularVelocity } : {}),
|
|
519
|
+
...(a.linearDamp !== undefined ? { linear_damp: a.linearDamp } : {}),
|
|
520
|
+
...(a.angularDamp !== undefined ? { angular_damp: a.angularDamp } : {}),
|
|
521
|
+
...(a.friction !== undefined ? { friction: a.friction } : {}),
|
|
522
|
+
...(a.bounce !== undefined ? { bounce: a.bounce } : {}),
|
|
523
|
+
...(a.freeze !== undefined ? { freeze: a.freeze } : {}),
|
|
524
|
+
...(a.sleeping !== undefined ? { sleeping: a.sleeping } : {}),
|
|
525
|
+
}));
|
|
526
|
+
}
|
|
527
|
+
async handleGameCreateJoint(args) {
|
|
528
|
+
args = normalizeParameters(args || {});
|
|
529
|
+
if (!args.parentPath || !args.jointType)
|
|
530
|
+
return createErrorResponse('parentPath and jointType are required.');
|
|
531
|
+
return this.context.gameCommand('create_joint', args, a => ({
|
|
532
|
+
parent_path: a.parentPath,
|
|
533
|
+
joint_type: a.jointType,
|
|
534
|
+
...(a.nodeAPath ? { node_a_path: a.nodeAPath } : {}),
|
|
535
|
+
...(a.nodeBPath ? { node_b_path: a.nodeBPath } : {}),
|
|
536
|
+
...(a.stiffness !== undefined ? { stiffness: a.stiffness } : {}),
|
|
537
|
+
...(a.damping !== undefined ? { damping: a.damping } : {}),
|
|
538
|
+
...(a.length !== undefined ? { length: a.length } : {}),
|
|
539
|
+
...(a.restLength !== undefined ? { rest_length: a.restLength } : {}),
|
|
540
|
+
...(a.softness !== undefined ? { softness: a.softness } : {}),
|
|
541
|
+
...(a.initialOffset !== undefined ? { initial_offset: a.initialOffset } : {}),
|
|
542
|
+
}));
|
|
543
|
+
}
|
|
544
|
+
async handleGameBonePose(args) {
|
|
545
|
+
args = normalizeParameters(args || {});
|
|
546
|
+
if (!args.nodePath)
|
|
547
|
+
return createErrorResponse('nodePath is required.');
|
|
548
|
+
return this.context.gameCommand('bone_pose', args, a => ({
|
|
549
|
+
node_path: a.nodePath,
|
|
550
|
+
action: a.action || 'list',
|
|
551
|
+
...(a.boneIndex !== undefined ? { bone_index: a.boneIndex } : {}),
|
|
552
|
+
...(a.boneName ? { bone_name: a.boneName } : {}),
|
|
553
|
+
...(a.position ? { position: a.position } : {}),
|
|
554
|
+
...(a.rotation ? { rotation: a.rotation } : {}),
|
|
555
|
+
...(a.scale ? { scale: a.scale } : {}),
|
|
556
|
+
}));
|
|
557
|
+
}
|
|
558
|
+
async handleGameUiTheme(args) {
|
|
559
|
+
args = normalizeParameters(args || {});
|
|
560
|
+
if (!args.nodePath || !args.overrides)
|
|
561
|
+
return createErrorResponse('nodePath and overrides are required.');
|
|
562
|
+
return this.context.gameCommand('ui_theme', args, a => ({
|
|
563
|
+
node_path: a.nodePath,
|
|
564
|
+
overrides: a.overrides,
|
|
565
|
+
}));
|
|
566
|
+
}
|
|
567
|
+
async handleGameViewport(args) {
|
|
568
|
+
args = normalizeParameters(args || {});
|
|
569
|
+
return this.context.gameCommand('viewport', args, a => ({
|
|
570
|
+
action: a.action || 'create',
|
|
571
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
572
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
573
|
+
...(a.width !== undefined ? { width: a.width } : {}),
|
|
574
|
+
...(a.height !== undefined ? { height: a.height } : {}),
|
|
575
|
+
...(a.msaa !== undefined ? { msaa: a.msaa } : {}),
|
|
576
|
+
...(a.transparentBg !== undefined ? { transparent_bg: a.transparentBg } : {}),
|
|
577
|
+
...(a.name ? { name: a.name } : {}),
|
|
578
|
+
}));
|
|
579
|
+
}
|
|
580
|
+
async handleGameDebugDraw(args) {
|
|
581
|
+
args = normalizeParameters(args || {});
|
|
582
|
+
if (!args.action)
|
|
583
|
+
return createErrorResponse('action is required.');
|
|
584
|
+
return this.context.gameCommand('debug_draw', args, a => ({
|
|
585
|
+
action: a.action,
|
|
586
|
+
...(a.from ? { from: a.from } : {}),
|
|
587
|
+
...(a.to ? { to: a.to } : {}),
|
|
588
|
+
...(a.center ? { center: a.center } : {}),
|
|
589
|
+
...(a.radius !== undefined ? { radius: a.radius } : {}),
|
|
590
|
+
...(a.size ? { size: a.size } : {}),
|
|
591
|
+
...(a.color ? { color: a.color } : {}),
|
|
592
|
+
...(a.duration !== undefined ? { duration: a.duration } : {}),
|
|
593
|
+
}));
|
|
594
|
+
}
|
|
595
|
+
// --- Batch 1: Networking + Input + System + Signals + Script ---
|
|
596
|
+
async handleGameHttpRequest(args) {
|
|
597
|
+
args = normalizeParameters(args || {});
|
|
598
|
+
if (!args.url)
|
|
599
|
+
return createErrorResponse('url is required.');
|
|
600
|
+
return this.context.gameCommand('http_request', args, a => ({
|
|
601
|
+
url: a.url, method: a.method || 'GET',
|
|
602
|
+
...(a.headers ? { headers: a.headers } : {}),
|
|
603
|
+
...(a.body !== undefined ? { body: a.body } : {}),
|
|
604
|
+
...(a.timeout !== undefined ? { timeout: a.timeout } : {}),
|
|
605
|
+
}), 35000);
|
|
606
|
+
}
|
|
607
|
+
async handleGameWebsocket(args) {
|
|
608
|
+
args = normalizeParameters(args || {});
|
|
609
|
+
if (!args.action)
|
|
610
|
+
return createErrorResponse('action is required.');
|
|
611
|
+
if (args.action === 'connect' && !args.url)
|
|
612
|
+
return createErrorResponse('url is required for connect.');
|
|
613
|
+
if (args.action === 'send' && args.message === undefined)
|
|
614
|
+
return createErrorResponse('message is required for send.');
|
|
615
|
+
return this.context.gameCommand('websocket', args, a => ({
|
|
616
|
+
action: a.action,
|
|
617
|
+
...(a.url ? { url: a.url } : {}),
|
|
618
|
+
...(a.message !== undefined ? { message: a.message } : {}),
|
|
619
|
+
...(a.timeout !== undefined ? { timeout: a.timeout } : {}),
|
|
620
|
+
}), 15000);
|
|
621
|
+
}
|
|
622
|
+
async handleGameMultiplayer(args) {
|
|
623
|
+
args = normalizeParameters(args || {});
|
|
624
|
+
if (!args.action)
|
|
625
|
+
return createErrorResponse('action is required.');
|
|
626
|
+
return this.context.gameCommand('multiplayer', args, a => ({
|
|
627
|
+
action: a.action,
|
|
628
|
+
...(a.port !== undefined ? { port: a.port } : {}),
|
|
629
|
+
...(a.address ? { address: a.address } : {}),
|
|
630
|
+
...(a.maxClients !== undefined ? { max_clients: a.maxClients } : {}),
|
|
631
|
+
}));
|
|
632
|
+
}
|
|
633
|
+
async handleGameRpc(args) {
|
|
634
|
+
args = normalizeParameters(args || {});
|
|
635
|
+
if (!args.nodePath || !args.action || !args.method)
|
|
636
|
+
return createErrorResponse('nodePath, action, and method are required.');
|
|
637
|
+
return this.context.gameCommand('rpc', args, a => ({
|
|
638
|
+
node_path: a.nodePath, action: a.action, method: a.method,
|
|
639
|
+
...(a.args !== undefined ? { args: a.args } : {}),
|
|
640
|
+
...(a.peerId !== undefined ? { peer_id: a.peerId } : {}),
|
|
641
|
+
...(a.mode ? { mode: a.mode } : {}),
|
|
642
|
+
...(a.sync !== undefined ? { sync: a.sync } : {}),
|
|
643
|
+
...(a.transferMode !== undefined ? { transfer_mode: a.transferMode } : {}),
|
|
644
|
+
...(a.channel !== undefined ? { channel: a.channel } : {}),
|
|
645
|
+
}));
|
|
646
|
+
}
|
|
647
|
+
async handleGameTouch(args) {
|
|
648
|
+
args = normalizeParameters(args || {});
|
|
649
|
+
if (!args.action)
|
|
650
|
+
return createErrorResponse('action is required.');
|
|
651
|
+
return this.context.gameCommand('touch', args, a => ({
|
|
652
|
+
action: a.action, x: a.x ?? 0, y: a.y ?? 0,
|
|
653
|
+
...(a.index !== undefined ? { index: a.index } : {}),
|
|
654
|
+
...(a.toX !== undefined ? { to_x: a.toX } : {}),
|
|
655
|
+
...(a.toY !== undefined ? { to_y: a.toY } : {}),
|
|
656
|
+
...(a.steps !== undefined ? { steps: a.steps } : {}),
|
|
657
|
+
}), 15000);
|
|
658
|
+
}
|
|
659
|
+
async handleGameInputState(args) {
|
|
660
|
+
args = normalizeParameters(args || {});
|
|
661
|
+
return this.context.gameCommand('input_state', args, a => ({
|
|
662
|
+
action: a.action || 'query',
|
|
663
|
+
...(a.x !== undefined ? { x: a.x } : {}),
|
|
664
|
+
...(a.y !== undefined ? { y: a.y } : {}),
|
|
665
|
+
...(a.mouseMode ? { mouse_mode: a.mouseMode } : {}),
|
|
666
|
+
...(a.keys !== undefined ? { keys: a.keys } : {}),
|
|
667
|
+
...(a.actions !== undefined ? { actions: a.actions } : {}),
|
|
668
|
+
...(a.mouseButtons !== undefined ? { mouse_buttons: a.mouseButtons } : {}),
|
|
669
|
+
}));
|
|
670
|
+
}
|
|
671
|
+
async handleGameInputAction(args) {
|
|
672
|
+
args = normalizeParameters(args || {});
|
|
673
|
+
if (!args.action)
|
|
674
|
+
return createErrorResponse('action is required.');
|
|
675
|
+
return this.context.gameCommand('input_action', args, a => ({
|
|
676
|
+
action: a.action,
|
|
677
|
+
...(a.actionName ? { action_name: a.actionName } : {}),
|
|
678
|
+
...(a.strength !== undefined ? { strength: a.strength } : {}),
|
|
679
|
+
...(a.key ? { key: a.key } : {}),
|
|
680
|
+
}));
|
|
681
|
+
}
|
|
682
|
+
async handleGameListSignals(args) {
|
|
683
|
+
args = normalizeParameters(args || {});
|
|
684
|
+
if (!args.nodePath)
|
|
685
|
+
return createErrorResponse('nodePath is required.');
|
|
686
|
+
return this.context.gameCommand('list_signals', args, a => ({ node_path: a.nodePath }));
|
|
687
|
+
}
|
|
688
|
+
async handleGameAwaitSignal(args) {
|
|
689
|
+
args = normalizeParameters(args || {});
|
|
690
|
+
if (!args.nodePath || !args.signalName)
|
|
691
|
+
return createErrorResponse('nodePath and signalName are required.');
|
|
692
|
+
const timeout = (args.timeout ?? 10) * 1000 + 2000;
|
|
693
|
+
return this.context.gameCommand('await_signal', args, a => ({
|
|
694
|
+
node_path: a.nodePath, signal_name: a.signalName, timeout: a.timeout ?? 10,
|
|
695
|
+
}), timeout);
|
|
696
|
+
}
|
|
697
|
+
async handleGameScript(args) {
|
|
698
|
+
args = normalizeParameters(args || {});
|
|
699
|
+
if (!args.nodePath || !args.action)
|
|
700
|
+
return createErrorResponse('nodePath and action are required.');
|
|
701
|
+
return this.context.gameCommand('script', args, a => ({
|
|
702
|
+
node_path: a.nodePath, action: a.action,
|
|
703
|
+
...(a.source ? { source: a.source } : {}),
|
|
704
|
+
...(a.className ? { class_name: a.className } : {}),
|
|
705
|
+
}));
|
|
706
|
+
}
|
|
707
|
+
async handleGameWindow(args) {
|
|
708
|
+
args = normalizeParameters(args || {});
|
|
709
|
+
return this.context.gameCommand('window', args, a => ({
|
|
710
|
+
action: a.action || 'get',
|
|
711
|
+
...(a.width !== undefined ? { width: a.width } : {}),
|
|
712
|
+
...(a.height !== undefined ? { height: a.height } : {}),
|
|
713
|
+
...(a.fullscreen !== undefined ? { fullscreen: a.fullscreen } : {}),
|
|
714
|
+
...(a.borderless !== undefined ? { borderless: a.borderless } : {}),
|
|
715
|
+
...(a.title ? { title: a.title } : {}),
|
|
716
|
+
...(a.position ? { position: a.position } : {}),
|
|
717
|
+
...(a.vsync !== undefined ? { vsync: a.vsync } : {}),
|
|
718
|
+
}));
|
|
719
|
+
}
|
|
720
|
+
async handleGameOsInfo(_args) {
|
|
721
|
+
return this.context.gameCommand('os_info', {}, () => ({}));
|
|
722
|
+
}
|
|
723
|
+
async handleGameTimeScale(args) {
|
|
724
|
+
args = normalizeParameters(args || {});
|
|
725
|
+
return this.context.gameCommand('time_scale', args, a => ({
|
|
726
|
+
action: a.action || 'get',
|
|
727
|
+
...(a.timeScale !== undefined ? { time_scale: a.timeScale } : {}),
|
|
728
|
+
}));
|
|
729
|
+
}
|
|
730
|
+
async handleGameProcessMode(args) {
|
|
731
|
+
args = normalizeParameters(args || {});
|
|
732
|
+
if (!args.nodePath || !args.mode)
|
|
733
|
+
return createErrorResponse('nodePath and mode are required.');
|
|
734
|
+
return this.context.gameCommand('process_mode', args, a => ({
|
|
735
|
+
node_path: a.nodePath, mode: a.mode,
|
|
736
|
+
}));
|
|
737
|
+
}
|
|
738
|
+
async handleGameWorldSettings(args) {
|
|
739
|
+
args = normalizeParameters(args || {});
|
|
740
|
+
return this.context.gameCommand('world_settings', args, a => ({
|
|
741
|
+
action: a.action || 'get',
|
|
742
|
+
...(a.gravity !== undefined ? { gravity: a.gravity } : {}),
|
|
743
|
+
...(a.gravityDirection ? { gravity_direction: a.gravityDirection } : {}),
|
|
744
|
+
...(a.physicsFps !== undefined ? { physics_fps: a.physicsFps } : {}),
|
|
745
|
+
}));
|
|
746
|
+
}
|
|
747
|
+
// --- Batch 2: 3D Rendering + Lighting + Sky + Physics ---
|
|
748
|
+
async handleGameCsg(args) {
|
|
749
|
+
args = normalizeParameters(args || {});
|
|
750
|
+
if (!args.action)
|
|
751
|
+
return createErrorResponse('action is required.');
|
|
752
|
+
return this.context.gameCommand('csg', args, a => ({
|
|
753
|
+
action: a.action,
|
|
754
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
755
|
+
...(a.csgType ? { csg_type: a.csgType } : {}),
|
|
756
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
757
|
+
...(a.operation ? { operation: a.operation } : {}),
|
|
758
|
+
...(a.size ? { size: a.size } : {}),
|
|
759
|
+
...(a.radius !== undefined ? { radius: a.radius } : {}),
|
|
760
|
+
...(a.height !== undefined ? { height: a.height } : {}),
|
|
761
|
+
...(a.material ? { material: a.material } : {}),
|
|
762
|
+
...(a.name ? { name: a.name } : {}),
|
|
763
|
+
}));
|
|
764
|
+
}
|
|
765
|
+
async handleGameMultimesh(args) {
|
|
766
|
+
args = normalizeParameters(args || {});
|
|
767
|
+
if (!args.action)
|
|
768
|
+
return createErrorResponse('action is required.');
|
|
769
|
+
return this.context.gameCommand('multimesh', args, a => ({
|
|
770
|
+
action: a.action,
|
|
771
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
772
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
773
|
+
...(a.meshType ? { mesh_type: a.meshType } : {}),
|
|
774
|
+
...(a.count !== undefined ? { count: a.count } : {}),
|
|
775
|
+
...(a.index !== undefined ? { index: a.index } : {}),
|
|
776
|
+
...(a.transform ? { transform: a.transform } : {}),
|
|
777
|
+
...(a.name ? { name: a.name } : {}),
|
|
778
|
+
}));
|
|
779
|
+
}
|
|
780
|
+
async handleGameProceduralMesh(args) {
|
|
781
|
+
args = normalizeParameters(args || {});
|
|
782
|
+
if (!args.parentPath || !args.vertices)
|
|
783
|
+
return createErrorResponse('parentPath and vertices are required.');
|
|
784
|
+
return this.context.gameCommand('procedural_mesh', args, a => ({
|
|
785
|
+
parent_path: a.parentPath, vertices: a.vertices,
|
|
786
|
+
...(a.normals ? { normals: a.normals } : {}),
|
|
787
|
+
...(a.uvs ? { uvs: a.uvs } : {}),
|
|
788
|
+
...(a.indices ? { indices: a.indices } : {}),
|
|
789
|
+
...(a.name ? { name: a.name } : {}),
|
|
790
|
+
}));
|
|
791
|
+
}
|
|
792
|
+
async handleGameLight3d(args) {
|
|
793
|
+
args = normalizeParameters(args || {});
|
|
794
|
+
if (!args.action)
|
|
795
|
+
return createErrorResponse('action is required.');
|
|
796
|
+
return this.context.gameCommand('light_3d', args, a => ({
|
|
797
|
+
action: a.action,
|
|
798
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
799
|
+
...(a.lightType ? { light_type: a.lightType } : {}),
|
|
800
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
801
|
+
...(a.color ? { color: a.color } : {}),
|
|
802
|
+
...(a.energy !== undefined ? { energy: a.energy } : {}),
|
|
803
|
+
...(a.range !== undefined ? { range: a.range } : {}),
|
|
804
|
+
...(a.shadows !== undefined ? { shadows: a.shadows } : {}),
|
|
805
|
+
...(a.spotAngle !== undefined ? { spot_angle: a.spotAngle } : {}),
|
|
806
|
+
...(a.name ? { name: a.name } : {}),
|
|
807
|
+
}));
|
|
808
|
+
}
|
|
809
|
+
async handleGameMeshInstance(args) {
|
|
810
|
+
args = normalizeParameters(args || {});
|
|
811
|
+
if (!args.parentPath || !args.meshType)
|
|
812
|
+
return createErrorResponse('parentPath and meshType are required.');
|
|
813
|
+
return this.context.gameCommand('mesh_instance', args, a => ({
|
|
814
|
+
parent_path: a.parentPath, mesh_type: a.meshType,
|
|
815
|
+
...(a.size ? { size: a.size } : {}),
|
|
816
|
+
...(a.radius !== undefined ? { radius: a.radius } : {}),
|
|
817
|
+
...(a.height !== undefined ? { height: a.height } : {}),
|
|
818
|
+
...(a.material ? { material: a.material } : {}),
|
|
819
|
+
...(a.name ? { name: a.name } : {}),
|
|
820
|
+
}));
|
|
821
|
+
}
|
|
822
|
+
async handleGameGridmap(args) {
|
|
823
|
+
args = normalizeParameters(args || {});
|
|
824
|
+
if (!args.nodePath || !args.action)
|
|
825
|
+
return createErrorResponse('nodePath and action are required.');
|
|
826
|
+
return this.context.gameCommand('gridmap', args, a => ({
|
|
827
|
+
node_path: a.nodePath, action: a.action,
|
|
828
|
+
...(a.x !== undefined ? { x: a.x } : {}),
|
|
829
|
+
...(a.y !== undefined ? { y: a.y } : {}),
|
|
830
|
+
...(a.z !== undefined ? { z: a.z } : {}),
|
|
831
|
+
...(a.item !== undefined ? { item: a.item } : {}),
|
|
832
|
+
...(a.orientation !== undefined ? { orientation: a.orientation } : {}),
|
|
833
|
+
}));
|
|
834
|
+
}
|
|
835
|
+
async handleGame3dEffects(args) {
|
|
836
|
+
args = normalizeParameters(args || {});
|
|
837
|
+
if (!args.parentPath || !args.effectType)
|
|
838
|
+
return createErrorResponse('parentPath and effectType are required.');
|
|
839
|
+
return this.context.gameCommand('3d_effects', args, a => ({
|
|
840
|
+
parent_path: a.parentPath, effect_type: a.effectType,
|
|
841
|
+
...(a.size ? { size: a.size } : {}),
|
|
842
|
+
...(a.intensity !== undefined ? { intensity: a.intensity } : {}),
|
|
843
|
+
...(a.name ? { name: a.name } : {}),
|
|
844
|
+
}));
|
|
845
|
+
}
|
|
846
|
+
async handleGameGi(args) {
|
|
847
|
+
args = normalizeParameters(args || {});
|
|
848
|
+
if (!args.parentPath || !args.giType)
|
|
849
|
+
return createErrorResponse('parentPath and giType are required.');
|
|
850
|
+
return this.context.gameCommand('gi', args, a => ({
|
|
851
|
+
parent_path: a.parentPath, gi_type: a.giType,
|
|
852
|
+
...(a.size ? { size: a.size } : {}),
|
|
853
|
+
...(a.name ? { name: a.name } : {}),
|
|
854
|
+
}));
|
|
855
|
+
}
|
|
856
|
+
async handleGamePath3d(args) {
|
|
857
|
+
args = normalizeParameters(args || {});
|
|
858
|
+
if (!args.action)
|
|
859
|
+
return createErrorResponse('action is required.');
|
|
860
|
+
if (args.action === 'create' && !args.parentPath)
|
|
861
|
+
return createErrorResponse('parentPath is required for create.');
|
|
862
|
+
if (args.action !== 'create' && !args.nodePath)
|
|
863
|
+
return createErrorResponse('nodePath is required for this action.');
|
|
864
|
+
if (args.action === 'add_point' && !args.point)
|
|
865
|
+
return createErrorResponse('point is required for add_point.');
|
|
866
|
+
if (args.action === 'set_points' && !args.points)
|
|
867
|
+
return createErrorResponse('points is required for set_points.');
|
|
868
|
+
return this.context.gameCommand('path_3d', args, a => ({
|
|
869
|
+
action: a.action,
|
|
870
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
871
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
872
|
+
...(a.points ? { points: a.points } : {}),
|
|
873
|
+
...(a.point ? { point: a.point } : {}),
|
|
874
|
+
...(a.name ? { name: a.name } : {}),
|
|
875
|
+
}));
|
|
876
|
+
}
|
|
877
|
+
async handleGameSky(args) {
|
|
878
|
+
args = normalizeParameters(args || {});
|
|
879
|
+
if (!args.action)
|
|
880
|
+
return createErrorResponse('action is required.');
|
|
881
|
+
return this.context.gameCommand('sky', args, a => ({
|
|
882
|
+
action: a.action,
|
|
883
|
+
...(a.skyType ? { sky_type: a.skyType } : {}),
|
|
884
|
+
...(a.topColor ? { top_color: a.topColor } : {}),
|
|
885
|
+
...(a.bottomColor ? { bottom_color: a.bottomColor } : {}),
|
|
886
|
+
...(a.sunEnergy !== undefined ? { sun_energy: a.sunEnergy } : {}),
|
|
887
|
+
...(a.groundColor ? { ground_color: a.groundColor } : {}),
|
|
888
|
+
}));
|
|
889
|
+
}
|
|
890
|
+
async handleGameCameraAttributes(args) {
|
|
891
|
+
args = normalizeParameters(args || {});
|
|
892
|
+
return this.context.gameCommand('camera_attributes', args, a => ({
|
|
893
|
+
action: a.action || 'get',
|
|
894
|
+
...(a.dofBlurFar !== undefined ? { dof_blur_far: a.dofBlurFar } : {}),
|
|
895
|
+
...(a.dofBlurNear !== undefined ? { dof_blur_near: a.dofBlurNear } : {}),
|
|
896
|
+
...(a.dofBlurAmount !== undefined ? { dof_blur_amount: a.dofBlurAmount } : {}),
|
|
897
|
+
...(a.exposureMultiplier !== undefined ? { exposure_multiplier: a.exposureMultiplier } : {}),
|
|
898
|
+
...(a.autoExposure !== undefined ? { auto_exposure: a.autoExposure } : {}),
|
|
899
|
+
...(a.autoExposureScale !== undefined ? { auto_exposure_scale: a.autoExposureScale } : {}),
|
|
900
|
+
}));
|
|
901
|
+
}
|
|
902
|
+
async handleGameNavigation3d(args) {
|
|
903
|
+
args = normalizeParameters(args || {});
|
|
904
|
+
if (!args.action)
|
|
905
|
+
return createErrorResponse('action is required.');
|
|
906
|
+
return this.context.gameCommand('navigation_3d', args, a => ({
|
|
907
|
+
action: a.action,
|
|
908
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
909
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
910
|
+
...(a.cellSize !== undefined ? { cell_size: a.cellSize } : {}),
|
|
911
|
+
...(a.agentRadius !== undefined ? { agent_radius: a.agentRadius } : {}),
|
|
912
|
+
...(a.agentHeight !== undefined ? { agent_height: a.agentHeight } : {}),
|
|
913
|
+
...(a.name ? { name: a.name } : {}),
|
|
914
|
+
}), 30000);
|
|
915
|
+
}
|
|
916
|
+
async handleGamePhysics3d(args) {
|
|
917
|
+
args = normalizeParameters(args || {});
|
|
918
|
+
if (!args.action)
|
|
919
|
+
return createErrorResponse('action is required.');
|
|
920
|
+
return this.context.gameCommand('physics_3d', args, a => ({
|
|
921
|
+
action: a.action,
|
|
922
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
923
|
+
...(a.from ? { from: a.from } : {}),
|
|
924
|
+
...(a.to ? { to: a.to } : {}),
|
|
925
|
+
...(a.collisionMask !== undefined ? { collision_mask: a.collisionMask } : {}),
|
|
926
|
+
}), 15000);
|
|
927
|
+
}
|
|
928
|
+
// --- Batch 3: 2D Systems + Animation Advanced + Audio Effects ---
|
|
929
|
+
async handleGameCanvas(args) {
|
|
930
|
+
args = normalizeParameters(args || {});
|
|
931
|
+
if (!args.action)
|
|
932
|
+
return createErrorResponse('action is required.');
|
|
933
|
+
return this.context.gameCommand('canvas', args, a => ({
|
|
934
|
+
action: a.action,
|
|
935
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
936
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
937
|
+
...(a.layer !== undefined ? { layer: a.layer } : {}),
|
|
938
|
+
...(a.offset ? { offset: a.offset } : {}),
|
|
939
|
+
...(a.visible !== undefined ? { visible: a.visible } : {}),
|
|
940
|
+
...(a.color ? { color: a.color } : {}),
|
|
941
|
+
...(a.name ? { name: a.name } : {}),
|
|
942
|
+
}));
|
|
943
|
+
}
|
|
944
|
+
async handleGameCanvasDraw(args) {
|
|
945
|
+
args = normalizeParameters(args || {});
|
|
946
|
+
if (!args.action)
|
|
947
|
+
return createErrorResponse('action is required.');
|
|
948
|
+
return this.context.gameCommand('canvas_draw', args, a => ({
|
|
949
|
+
action: a.action,
|
|
950
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
951
|
+
...(a.from ? { from: a.from } : {}),
|
|
952
|
+
...(a.to ? { to: a.to } : {}),
|
|
953
|
+
...(a.center ? { center: a.center } : {}),
|
|
954
|
+
...(a.radius !== undefined ? { radius: a.radius } : {}),
|
|
955
|
+
...(a.rect ? { rect: a.rect } : {}),
|
|
956
|
+
...(a.points ? { points: a.points } : {}),
|
|
957
|
+
...(a.position ? { position: a.position } : {}),
|
|
958
|
+
...(a.text ? { text: a.text } : {}),
|
|
959
|
+
...(a.fontSize !== undefined ? { font_size: a.fontSize } : {}),
|
|
960
|
+
...(a.color ? { color: a.color } : {}),
|
|
961
|
+
...(a.width !== undefined ? { width: a.width } : {}),
|
|
962
|
+
...(a.filled !== undefined ? { filled: a.filled } : {}),
|
|
963
|
+
}));
|
|
964
|
+
}
|
|
965
|
+
async handleGameLight2d(args) {
|
|
966
|
+
args = normalizeParameters(args || {});
|
|
967
|
+
if (!args.action)
|
|
968
|
+
return createErrorResponse('action is required.');
|
|
969
|
+
return this.context.gameCommand('light_2d', args, a => ({
|
|
970
|
+
action: a.action,
|
|
971
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
972
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
973
|
+
...(a.color ? { color: a.color } : {}),
|
|
974
|
+
...(a.energy !== undefined ? { energy: a.energy } : {}),
|
|
975
|
+
...(a.range !== undefined ? { range: a.range } : {}),
|
|
976
|
+
...(a.points ? { points: a.points } : {}),
|
|
977
|
+
...(a.name ? { name: a.name } : {}),
|
|
978
|
+
}));
|
|
979
|
+
}
|
|
980
|
+
async handleGameParallax(args) {
|
|
981
|
+
args = normalizeParameters(args || {});
|
|
982
|
+
if (!args.action)
|
|
983
|
+
return createErrorResponse('action is required.');
|
|
984
|
+
return this.context.gameCommand('parallax', args, a => ({
|
|
985
|
+
action: a.action,
|
|
986
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
987
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
988
|
+
...(a.motionScale ? { motion_scale: a.motionScale } : {}),
|
|
989
|
+
...(a.motionOffset ? { motion_offset: a.motionOffset } : {}),
|
|
990
|
+
...(a.mirroring ? { mirroring: a.mirroring } : {}),
|
|
991
|
+
...(a.scrollOffset ? { scroll_offset: a.scrollOffset } : {}),
|
|
992
|
+
...(a.scrollBaseOffset ? { scroll_base_offset: a.scrollBaseOffset } : {}),
|
|
993
|
+
...(a.name ? { name: a.name } : {}),
|
|
994
|
+
}));
|
|
995
|
+
}
|
|
996
|
+
async handleGameShape2d(args) {
|
|
997
|
+
args = normalizeParameters(args || {});
|
|
998
|
+
if (!args.nodePath || !args.action)
|
|
999
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1000
|
+
return this.context.gameCommand('shape_2d', args, a => ({
|
|
1001
|
+
node_path: a.nodePath, action: a.action,
|
|
1002
|
+
...(a.points ? { points: a.points } : {}),
|
|
1003
|
+
...(a.point ? { point: a.point } : {}),
|
|
1004
|
+
...(a.width !== undefined ? { width: a.width } : {}),
|
|
1005
|
+
...(a.color ? { color: a.color } : {}),
|
|
1006
|
+
}));
|
|
1007
|
+
}
|
|
1008
|
+
async handleGamePath2d(args) {
|
|
1009
|
+
args = normalizeParameters(args || {});
|
|
1010
|
+
if (!args.action)
|
|
1011
|
+
return createErrorResponse('action is required.');
|
|
1012
|
+
return this.context.gameCommand('path_2d', args, a => ({
|
|
1013
|
+
action: a.action,
|
|
1014
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
1015
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
1016
|
+
...(a.points ? { points: a.points } : {}),
|
|
1017
|
+
...(a.point ? { point: a.point } : {}),
|
|
1018
|
+
...(a.name ? { name: a.name } : {}),
|
|
1019
|
+
}));
|
|
1020
|
+
}
|
|
1021
|
+
async handleGamePhysics2d(args) {
|
|
1022
|
+
args = normalizeParameters(args || {});
|
|
1023
|
+
if (!args.action)
|
|
1024
|
+
return createErrorResponse('action is required.');
|
|
1025
|
+
return this.context.gameCommand('physics_2d', args, a => ({
|
|
1026
|
+
action: a.action,
|
|
1027
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
1028
|
+
...(a.from ? { from: a.from } : {}),
|
|
1029
|
+
...(a.to ? { to: a.to } : {}),
|
|
1030
|
+
...(a.position ? { position: a.position } : {}),
|
|
1031
|
+
...(a.point ? { point: a.point } : {}),
|
|
1032
|
+
...(a.radius !== undefined ? { radius: a.radius } : {}),
|
|
1033
|
+
...(a.size ? { size: a.size } : {}),
|
|
1034
|
+
...(a.shapeType ? { shape_type: a.shapeType } : {}),
|
|
1035
|
+
...(a.maxResults !== undefined ? { max_results: a.maxResults } : {}),
|
|
1036
|
+
...(a.collideWithAreas !== undefined ? { collide_with_areas: a.collideWithAreas } : {}),
|
|
1037
|
+
...(a.collideWithBodies !== undefined ? { collide_with_bodies: a.collideWithBodies } : {}),
|
|
1038
|
+
...(a.collisionMask !== undefined ? { collision_mask: a.collisionMask } : {}),
|
|
1039
|
+
}), 15000);
|
|
1040
|
+
}
|
|
1041
|
+
async handleGameAnimationTree(args) {
|
|
1042
|
+
args = normalizeParameters(args || {});
|
|
1043
|
+
if (!args.nodePath || !args.action)
|
|
1044
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1045
|
+
return this.context.gameCommand('animation_tree', args, a => ({
|
|
1046
|
+
node_path: a.nodePath, action: a.action,
|
|
1047
|
+
...(a.stateName ? { state_name: a.stateName } : {}),
|
|
1048
|
+
...(a.paramName ? { param_name: a.paramName } : {}),
|
|
1049
|
+
...(a.paramValue !== undefined ? { param_value: a.paramValue } : {}),
|
|
1050
|
+
}));
|
|
1051
|
+
}
|
|
1052
|
+
async handleGameAnimationControl(args) {
|
|
1053
|
+
args = normalizeParameters(args || {});
|
|
1054
|
+
if (!args.nodePath || !args.action)
|
|
1055
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1056
|
+
if (args.action === 'seek' && args.position === undefined)
|
|
1057
|
+
return createErrorResponse('position is required for seek.');
|
|
1058
|
+
if (args.action === 'queue' && !args.animationName)
|
|
1059
|
+
return createErrorResponse('animationName is required for queue.');
|
|
1060
|
+
if (args.action === 'set_speed' && args.speed === undefined)
|
|
1061
|
+
return createErrorResponse('speed is required for set_speed.');
|
|
1062
|
+
return this.context.gameCommand('animation_control', args, a => ({
|
|
1063
|
+
node_path: a.nodePath, action: a.action,
|
|
1064
|
+
...(a.animationName ? { animation_name: a.animationName } : {}),
|
|
1065
|
+
...(a.position !== undefined ? { position: a.position } : {}),
|
|
1066
|
+
...(a.speed !== undefined ? { speed: a.speed } : {}),
|
|
1067
|
+
}));
|
|
1068
|
+
}
|
|
1069
|
+
async handleGameSkeletonIk(args) {
|
|
1070
|
+
args = normalizeParameters(args || {});
|
|
1071
|
+
if (!args.nodePath || !args.action)
|
|
1072
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1073
|
+
return this.context.gameCommand('skeleton_ik', args, a => ({
|
|
1074
|
+
node_path: a.nodePath, action: a.action,
|
|
1075
|
+
...(a.target ? { target: a.target } : {}),
|
|
1076
|
+
}));
|
|
1077
|
+
}
|
|
1078
|
+
async handleGameAudioEffect(args) {
|
|
1079
|
+
args = normalizeParameters(args || {});
|
|
1080
|
+
if (!args.action)
|
|
1081
|
+
return createErrorResponse('action is required.');
|
|
1082
|
+
return this.context.gameCommand('audio_effect', args, a => ({
|
|
1083
|
+
action: a.action, bus_name: a.busName || 'Master',
|
|
1084
|
+
...(a.effectType ? { effect_type: a.effectType } : {}),
|
|
1085
|
+
...(a.index !== undefined ? { index: a.index } : {}),
|
|
1086
|
+
...(a.properties ? { properties: a.properties } : {}),
|
|
1087
|
+
...(a.enabled !== undefined ? { enabled: a.enabled } : {}),
|
|
1088
|
+
}));
|
|
1089
|
+
}
|
|
1090
|
+
async handleGameAudioBusLayout(args) {
|
|
1091
|
+
args = normalizeParameters(args || {});
|
|
1092
|
+
if (!args.action)
|
|
1093
|
+
return createErrorResponse('action is required.');
|
|
1094
|
+
if (['add', 'remove', 'move', 'set_send'].includes(args.action) && !args.busName) {
|
|
1095
|
+
return createErrorResponse('busName is required for this action.');
|
|
1096
|
+
}
|
|
1097
|
+
if (args.action === 'set_send' && !args.sendTo)
|
|
1098
|
+
return createErrorResponse('sendTo is required for set_send.');
|
|
1099
|
+
if (args.action === 'move' && args.index === undefined)
|
|
1100
|
+
return createErrorResponse('index is required for move.');
|
|
1101
|
+
return this.context.gameCommand('audio_bus_layout', args, a => ({
|
|
1102
|
+
action: a.action,
|
|
1103
|
+
...(a.busName ? { bus_name: a.busName } : {}),
|
|
1104
|
+
...(a.sendTo ? { send_to: a.sendTo } : {}),
|
|
1105
|
+
...(a.index !== undefined ? { index: a.index } : {}),
|
|
1106
|
+
}));
|
|
1107
|
+
}
|
|
1108
|
+
async handleGameAudioSpatial(args) {
|
|
1109
|
+
args = normalizeParameters(args || {});
|
|
1110
|
+
if (!args.nodePath || !args.action)
|
|
1111
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1112
|
+
return this.context.gameCommand('audio_spatial', args, a => ({
|
|
1113
|
+
node_path: a.nodePath, action: a.action,
|
|
1114
|
+
...(a.maxDistance !== undefined ? { max_distance: a.maxDistance } : {}),
|
|
1115
|
+
...(a.unitSize !== undefined ? { unit_size: a.unitSize } : {}),
|
|
1116
|
+
...(a.maxDb !== undefined ? { max_db: a.maxDb } : {}),
|
|
1117
|
+
...(a.attenuationModel ? { attenuation_model: a.attenuationModel } : {}),
|
|
1118
|
+
}));
|
|
1119
|
+
}
|
|
1120
|
+
// --- Batch 4: Editor/Headless + Localization + Resource ---
|
|
1121
|
+
async handleGameLocale(args) {
|
|
1122
|
+
args = normalizeParameters(args || {});
|
|
1123
|
+
if (!args.action)
|
|
1124
|
+
return createErrorResponse('action is required.');
|
|
1125
|
+
return this.context.gameCommand('locale', args, a => ({
|
|
1126
|
+
action: a.action,
|
|
1127
|
+
...(a.locale ? { locale: a.locale } : {}),
|
|
1128
|
+
...(a.key ? { key: a.key } : {}),
|
|
1129
|
+
}));
|
|
1130
|
+
}
|
|
1131
|
+
// --- Batch 5: UI Controls + Rendering + Resource Runtime ---
|
|
1132
|
+
async handleGameUiControl(args) {
|
|
1133
|
+
args = normalizeParameters(args || {});
|
|
1134
|
+
if (!args.nodePath || !args.action)
|
|
1135
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1136
|
+
return this.context.gameCommand('ui_control', args, a => ({
|
|
1137
|
+
node_path: a.nodePath, action: a.action,
|
|
1138
|
+
...(a.anchorPreset !== undefined ? { anchor_preset: a.anchorPreset } : {}),
|
|
1139
|
+
...(a.tooltip !== undefined ? { tooltip: a.tooltip } : {}),
|
|
1140
|
+
...(a.mouseFilter ? { mouse_filter: a.mouseFilter } : {}),
|
|
1141
|
+
...(a.minSize ? { min_size: a.minSize } : {}),
|
|
1142
|
+
}));
|
|
1143
|
+
}
|
|
1144
|
+
async handleGameUiText(args) {
|
|
1145
|
+
args = normalizeParameters(args || {});
|
|
1146
|
+
if (!args.nodePath || !args.action)
|
|
1147
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1148
|
+
if (['set', 'append', 'bbcode'].includes(args.action) && args.text === undefined) {
|
|
1149
|
+
return createErrorResponse('text is required for this action.');
|
|
1150
|
+
}
|
|
1151
|
+
if ((args.selectionFrom === undefined) !== (args.selectionTo === undefined)) {
|
|
1152
|
+
return createErrorResponse('selectionFrom and selectionTo must be provided together.');
|
|
1153
|
+
}
|
|
1154
|
+
return this.context.gameCommand('ui_text', args, a => ({
|
|
1155
|
+
node_path: a.nodePath, action: a.action,
|
|
1156
|
+
...(a.text !== undefined ? { text: a.text } : {}),
|
|
1157
|
+
...(a.caretPosition !== undefined ? { caret_position: a.caretPosition } : {}),
|
|
1158
|
+
...(a.selectionFrom !== undefined ? { selection_from: a.selectionFrom } : {}),
|
|
1159
|
+
...(a.selectionTo !== undefined ? { selection_to: a.selectionTo } : {}),
|
|
1160
|
+
}));
|
|
1161
|
+
}
|
|
1162
|
+
async handleGameUiPopup(args) {
|
|
1163
|
+
args = normalizeParameters(args || {});
|
|
1164
|
+
if (!args.nodePath || !args.action)
|
|
1165
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1166
|
+
return this.context.gameCommand('ui_popup', args, a => ({
|
|
1167
|
+
node_path: a.nodePath, action: a.action,
|
|
1168
|
+
...(a.size ? { size: a.size } : {}),
|
|
1169
|
+
...(a.title !== undefined ? { title: a.title } : {}),
|
|
1170
|
+
...(a.text !== undefined ? { text: a.text } : {}),
|
|
1171
|
+
}));
|
|
1172
|
+
}
|
|
1173
|
+
async handleGameUiTree(args) {
|
|
1174
|
+
args = normalizeParameters(args || {});
|
|
1175
|
+
if (!args.nodePath || !args.action)
|
|
1176
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1177
|
+
if (args.action === 'add' && args.text === undefined)
|
|
1178
|
+
return createErrorResponse('text is required for add.');
|
|
1179
|
+
if (['select', 'collapse', 'expand', 'remove'].includes(args.action) && !args.itemPath) {
|
|
1180
|
+
return createErrorResponse('itemPath is required for this action.');
|
|
1181
|
+
}
|
|
1182
|
+
return this.context.gameCommand('ui_tree', args, a => ({
|
|
1183
|
+
node_path: a.nodePath, action: a.action,
|
|
1184
|
+
...(a.itemPath ? { item_path: a.itemPath } : {}),
|
|
1185
|
+
...(a.text !== undefined ? { text: a.text } : {}),
|
|
1186
|
+
...(a.column !== undefined ? { column: a.column } : {}),
|
|
1187
|
+
}));
|
|
1188
|
+
}
|
|
1189
|
+
async handleGameUiItemList(args) {
|
|
1190
|
+
args = normalizeParameters(args || {});
|
|
1191
|
+
if (!args.nodePath || !args.action)
|
|
1192
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1193
|
+
if (['select', 'remove'].includes(args.action) && args.index === undefined) {
|
|
1194
|
+
return createErrorResponse('index is required for this action.');
|
|
1195
|
+
}
|
|
1196
|
+
if (args.action === 'add' && args.text === undefined)
|
|
1197
|
+
return createErrorResponse('text is required for add.');
|
|
1198
|
+
return this.context.gameCommand('ui_item_list', args, a => ({
|
|
1199
|
+
node_path: a.nodePath, action: a.action,
|
|
1200
|
+
...(a.index !== undefined ? { index: a.index } : {}),
|
|
1201
|
+
...(a.text ? { text: a.text } : {}),
|
|
1202
|
+
}));
|
|
1203
|
+
}
|
|
1204
|
+
async handleGameUiTabs(args) {
|
|
1205
|
+
args = normalizeParameters(args || {});
|
|
1206
|
+
if (!args.nodePath || !args.action)
|
|
1207
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1208
|
+
if (['set_current', 'set_title'].includes(args.action) && args.index === undefined) {
|
|
1209
|
+
return createErrorResponse('index is required for this action.');
|
|
1210
|
+
}
|
|
1211
|
+
if (args.action === 'set_title' && args.title === undefined)
|
|
1212
|
+
return createErrorResponse('title is required for set_title.');
|
|
1213
|
+
return this.context.gameCommand('ui_tabs', args, a => ({
|
|
1214
|
+
node_path: a.nodePath, action: a.action,
|
|
1215
|
+
...(a.index !== undefined ? { index: a.index } : {}),
|
|
1216
|
+
...(a.title !== undefined ? { title: a.title } : {}),
|
|
1217
|
+
}));
|
|
1218
|
+
}
|
|
1219
|
+
async handleGameUiMenu(args) {
|
|
1220
|
+
args = normalizeParameters(args || {});
|
|
1221
|
+
if (!args.nodePath || !args.action)
|
|
1222
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1223
|
+
if (args.action === 'add' && args.text === undefined)
|
|
1224
|
+
return createErrorResponse('text is required for add.');
|
|
1225
|
+
if (['remove', 'set_checked'].includes(args.action) && args.index === undefined) {
|
|
1226
|
+
return createErrorResponse('index is required for this action.');
|
|
1227
|
+
}
|
|
1228
|
+
if (args.action === 'set_checked' && args.checked === undefined)
|
|
1229
|
+
return createErrorResponse('checked is required for set_checked.');
|
|
1230
|
+
return this.context.gameCommand('ui_menu', args, a => ({
|
|
1231
|
+
node_path: a.nodePath, action: a.action,
|
|
1232
|
+
...(a.index !== undefined ? { index: a.index } : {}),
|
|
1233
|
+
...(a.text !== undefined ? { text: a.text } : {}),
|
|
1234
|
+
...(a.checked !== undefined ? { checked: a.checked } : {}),
|
|
1235
|
+
...(a.id !== undefined ? { id: a.id } : {}),
|
|
1236
|
+
...(a.shortcutKey !== undefined ? { shortcut_key: a.shortcutKey } : {}),
|
|
1237
|
+
}));
|
|
1238
|
+
}
|
|
1239
|
+
async handleGameUiRange(args) {
|
|
1240
|
+
args = normalizeParameters(args || {});
|
|
1241
|
+
if (!args.nodePath || !args.action)
|
|
1242
|
+
return createErrorResponse('nodePath and action are required.');
|
|
1243
|
+
if (args.action === 'set' && args.value === undefined && args.minValue === undefined
|
|
1244
|
+
&& args.maxValue === undefined && args.step === undefined && !args.color) {
|
|
1245
|
+
return createErrorResponse('set requires a value, range setting, or color.');
|
|
1246
|
+
}
|
|
1247
|
+
return this.context.gameCommand('ui_range', args, a => ({
|
|
1248
|
+
node_path: a.nodePath, action: a.action,
|
|
1249
|
+
...(a.value !== undefined ? { value: a.value } : {}),
|
|
1250
|
+
...(a.minValue !== undefined ? { min_value: a.minValue } : {}),
|
|
1251
|
+
...(a.maxValue !== undefined ? { max_value: a.maxValue } : {}),
|
|
1252
|
+
...(a.step !== undefined ? { step: a.step } : {}),
|
|
1253
|
+
...(a.color ? { color: a.color } : {}),
|
|
1254
|
+
}));
|
|
1255
|
+
}
|
|
1256
|
+
async handleGameRenderSettings(args) {
|
|
1257
|
+
args = normalizeParameters(args || {});
|
|
1258
|
+
return this.context.gameCommand('render_settings', args, a => ({
|
|
1259
|
+
action: a.action || 'get',
|
|
1260
|
+
...(a.msaa2d !== undefined ? { msaa_2d: a.msaa2d } : {}),
|
|
1261
|
+
...(a.msaa3d !== undefined ? { msaa_3d: a.msaa3d } : {}),
|
|
1262
|
+
...(a.fxaa !== undefined ? { fxaa: a.fxaa } : {}),
|
|
1263
|
+
...(a.taa !== undefined ? { taa: a.taa } : {}),
|
|
1264
|
+
...(a.scalingMode !== undefined ? { scaling_mode: a.scalingMode } : {}),
|
|
1265
|
+
...(a.scalingScale !== undefined ? { scaling_scale: a.scalingScale } : {}),
|
|
1266
|
+
}));
|
|
1267
|
+
}
|
|
1268
|
+
async handleGameResource(args) {
|
|
1269
|
+
args = normalizeParameters(args || {});
|
|
1270
|
+
if (!args.action || !args.path)
|
|
1271
|
+
return createErrorResponse('action and path are required.');
|
|
1272
|
+
return this.context.gameCommand('resource', args, a => ({
|
|
1273
|
+
action: a.action, path: a.path,
|
|
1274
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
1275
|
+
...(a.property ? { property: a.property } : {}),
|
|
1276
|
+
}));
|
|
1277
|
+
}
|
|
1278
|
+
// --- Batch 6: Visual Shader + Terrain + Video + CI/CD ---
|
|
1279
|
+
async handleGameVisualShader(args) {
|
|
1280
|
+
args = normalizeParameters(args || {});
|
|
1281
|
+
if (!args.action)
|
|
1282
|
+
return createErrorResponse('action is required.');
|
|
1283
|
+
return this.context.gameCommand('visual_shader', args, a => ({
|
|
1284
|
+
action: a.action,
|
|
1285
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
1286
|
+
...(a.shaderType ? { shader_type: a.shaderType } : {}),
|
|
1287
|
+
...(a.nodeClass ? { node_class: a.nodeClass } : {}),
|
|
1288
|
+
...(a.position ? { position: a.position } : {}),
|
|
1289
|
+
...(a.fromNode !== undefined ? { from_node: a.fromNode } : {}),
|
|
1290
|
+
...(a.fromPort !== undefined ? { from_port: a.fromPort } : {}),
|
|
1291
|
+
...(a.toNode !== undefined ? { to_node: a.toNode } : {}),
|
|
1292
|
+
...(a.toPort !== undefined ? { to_port: a.toPort } : {}),
|
|
1293
|
+
...(a.shaderId !== undefined ? { shader_id: a.shaderId } : {}),
|
|
1294
|
+
}));
|
|
1295
|
+
}
|
|
1296
|
+
async handleGameTerrain(args) {
|
|
1297
|
+
args = normalizeParameters(args || {});
|
|
1298
|
+
if (!args.action)
|
|
1299
|
+
return createErrorResponse('action is required.');
|
|
1300
|
+
if (args.action === 'create' && !args.parentPath)
|
|
1301
|
+
return createErrorResponse('parentPath is required for create.');
|
|
1302
|
+
if (args.action !== 'create' && !args.nodePath)
|
|
1303
|
+
return createErrorResponse('nodePath is required for this action.');
|
|
1304
|
+
if (args.action === 'get_height' && (args.x === undefined || args.z === undefined)) {
|
|
1305
|
+
return createErrorResponse('x and z are required for get_height.');
|
|
1306
|
+
}
|
|
1307
|
+
if (args.action === 'modify' && (args.x === undefined || args.z === undefined || args.radius === undefined || args.heightDelta === undefined)) {
|
|
1308
|
+
return createErrorResponse('x, z, radius, and heightDelta are required for modify.');
|
|
1309
|
+
}
|
|
1310
|
+
if (args.action === 'paint' && (args.x === undefined || args.z === undefined || args.radius === undefined || !args.color)) {
|
|
1311
|
+
return createErrorResponse('x, z, radius, and color are required for paint.');
|
|
1312
|
+
}
|
|
1313
|
+
return this.context.gameCommand('terrain', args, a => ({
|
|
1314
|
+
action: a.action,
|
|
1315
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
1316
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
1317
|
+
...(a.heightData ? { height_data: a.heightData } : {}),
|
|
1318
|
+
...(a.width !== undefined ? { width: a.width } : {}),
|
|
1319
|
+
...(a.depth !== undefined ? { depth: a.depth } : {}),
|
|
1320
|
+
...(a.maxHeight !== undefined ? { max_height: a.maxHeight } : {}),
|
|
1321
|
+
...(a.x !== undefined ? { x: a.x } : {}),
|
|
1322
|
+
...(a.z !== undefined ? { z: a.z } : {}),
|
|
1323
|
+
...(a.radius !== undefined ? { radius: a.radius } : {}),
|
|
1324
|
+
...(a.heightDelta !== undefined ? { height_delta: a.heightDelta } : {}),
|
|
1325
|
+
...(a.color ? { color: a.color } : {}),
|
|
1326
|
+
...(a.name ? { name: a.name } : {}),
|
|
1327
|
+
}));
|
|
1328
|
+
}
|
|
1329
|
+
async handleGameVideo(args) {
|
|
1330
|
+
args = normalizeParameters(args || {});
|
|
1331
|
+
if (!args.action)
|
|
1332
|
+
return createErrorResponse('action is required.');
|
|
1333
|
+
return this.context.gameCommand('video', args, a => ({
|
|
1334
|
+
action: a.action,
|
|
1335
|
+
...(a.nodePath ? { node_path: a.nodePath } : {}),
|
|
1336
|
+
...(a.parentPath ? { parent_path: a.parentPath } : {}),
|
|
1337
|
+
...(a.videoPath ? { video_path: a.videoPath } : {}),
|
|
1338
|
+
...(a.position !== undefined ? { position: a.position } : {}),
|
|
1339
|
+
...(a.volume !== undefined ? { volume: a.volume } : {}),
|
|
1340
|
+
...(a.loop !== undefined ? { loop: a.loop } : {}),
|
|
1341
|
+
...(a.autoplay !== undefined ? { autoplay: a.autoplay } : {}),
|
|
1342
|
+
...(a.name ? { name: a.name } : {}),
|
|
1343
|
+
}));
|
|
1344
|
+
}
|
|
1345
|
+
}
|