@baixfeng/godot-mcp-cli 1.0.11

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.
Files changed (115) hide show
  1. package/README.md +187 -0
  2. package/addons/godot_mcp/command_handler.gd +161 -0
  3. package/addons/godot_mcp/command_handler.gd.uid +1 -0
  4. package/addons/godot_mcp/commands/base_command_processor.gd +221 -0
  5. package/addons/godot_mcp/commands/base_command_processor.gd.uid +1 -0
  6. package/addons/godot_mcp/commands/debugger_commands.gd +221 -0
  7. package/addons/godot_mcp/commands/debugger_commands.gd.uid +1 -0
  8. package/addons/godot_mcp/commands/editor_commands.gd +237 -0
  9. package/addons/godot_mcp/commands/editor_commands.gd.uid +1 -0
  10. package/addons/godot_mcp/commands/editor_script_commands.gd +365 -0
  11. package/addons/godot_mcp/commands/editor_script_commands.gd.uid +1 -0
  12. package/addons/godot_mcp/commands/input_commands.gd +337 -0
  13. package/addons/godot_mcp/commands/input_commands.gd.uid +1 -0
  14. package/addons/godot_mcp/commands/node_commands.gd +222 -0
  15. package/addons/godot_mcp/commands/node_commands.gd.uid +1 -0
  16. package/addons/godot_mcp/commands/project_commands.gd +298 -0
  17. package/addons/godot_mcp/commands/project_commands.gd.uid +1 -0
  18. package/addons/godot_mcp/commands/scene_commands.gd +337 -0
  19. package/addons/godot_mcp/commands/scene_commands.gd.uid +1 -0
  20. package/addons/godot_mcp/commands/script_commands.gd +349 -0
  21. package/addons/godot_mcp/commands/script_commands.gd.uid +1 -0
  22. package/addons/godot_mcp/mcp_asset_commands.gd +153 -0
  23. package/addons/godot_mcp/mcp_asset_commands.gd.uid +1 -0
  24. package/addons/godot_mcp/mcp_debug_output_publisher.gd +1669 -0
  25. package/addons/godot_mcp/mcp_debug_output_publisher.gd.uid +1 -0
  26. package/addons/godot_mcp/mcp_debugger_bridge.gd +1455 -0
  27. package/addons/godot_mcp/mcp_debugger_bridge.gd.uid +1 -0
  28. package/addons/godot_mcp/mcp_enhanced_commands.gd +1083 -0
  29. package/addons/godot_mcp/mcp_enhanced_commands.gd.uid +1 -0
  30. package/addons/godot_mcp/mcp_input_handler.gd +545 -0
  31. package/addons/godot_mcp/mcp_input_handler.gd.uid +1 -0
  32. package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd +464 -0
  33. package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd.uid +1 -0
  34. package/addons/godot_mcp/mcp_script_resource_commands.gd +165 -0
  35. package/addons/godot_mcp/mcp_script_resource_commands.gd.uid +1 -0
  36. package/addons/godot_mcp/mcp_server.gd +260 -0
  37. package/addons/godot_mcp/mcp_server.gd.uid +1 -0
  38. package/addons/godot_mcp/plugin.cfg +7 -0
  39. package/addons/godot_mcp/runtime_debugger.gd +81 -0
  40. package/addons/godot_mcp/runtime_debugger.gd.uid +1 -0
  41. package/addons/godot_mcp/ui/mcp_panel.gd +94 -0
  42. package/addons/godot_mcp/ui/mcp_panel.gd.uid +1 -0
  43. package/addons/godot_mcp/ui/mcp_panel.tscn +96 -0
  44. package/addons/godot_mcp/utils/node_utils.gd +82 -0
  45. package/addons/godot_mcp/utils/node_utils.gd.uid +1 -0
  46. package/addons/godot_mcp/utils/resource_utils.gd +81 -0
  47. package/addons/godot_mcp/utils/resource_utils.gd.uid +1 -0
  48. package/addons/godot_mcp/utils/script_utils.gd +114 -0
  49. package/addons/godot_mcp/utils/script_utils.gd.uid +1 -0
  50. package/addons/godot_mcp/websocket_server.gd +197 -0
  51. package/addons/godot_mcp/websocket_server.gd.uid +1 -0
  52. package/dist/cli.d.ts +2 -0
  53. package/dist/cli.js +561 -0
  54. package/dist/cli.js.map +1 -0
  55. package/dist/index.d.ts +1 -0
  56. package/dist/index.js +156 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/resources/asset_resources.d.ts +29 -0
  59. package/dist/resources/asset_resources.js +145 -0
  60. package/dist/resources/asset_resources.js.map +1 -0
  61. package/dist/resources/debug_resources.d.ts +11 -0
  62. package/dist/resources/debug_resources.js +106 -0
  63. package/dist/resources/debug_resources.js.map +1 -0
  64. package/dist/resources/debugger_resources.d.ts +62 -0
  65. package/dist/resources/debugger_resources.js +201 -0
  66. package/dist/resources/debugger_resources.js.map +1 -0
  67. package/dist/resources/editor_resources.d.ts +47 -0
  68. package/dist/resources/editor_resources.js +155 -0
  69. package/dist/resources/editor_resources.js.map +1 -0
  70. package/dist/resources/project_resources.d.ts +33 -0
  71. package/dist/resources/project_resources.js +137 -0
  72. package/dist/resources/project_resources.js.map +1 -0
  73. package/dist/resources/scene_resources.d.ts +33 -0
  74. package/dist/resources/scene_resources.js +160 -0
  75. package/dist/resources/scene_resources.js.map +1 -0
  76. package/dist/resources/script_resources.d.ts +51 -0
  77. package/dist/resources/script_resources.js +203 -0
  78. package/dist/resources/script_resources.js.map +1 -0
  79. package/dist/tools/asset_tools.d.ts +5 -0
  80. package/dist/tools/asset_tools.js +125 -0
  81. package/dist/tools/asset_tools.js.map +1 -0
  82. package/dist/tools/debugger_tools.d.ts +2 -0
  83. package/dist/tools/debugger_tools.js +342 -0
  84. package/dist/tools/debugger_tools.js.map +1 -0
  85. package/dist/tools/editor_tools.d.ts +2 -0
  86. package/dist/tools/editor_tools.js +165 -0
  87. package/dist/tools/editor_tools.js.map +1 -0
  88. package/dist/tools/enhanced_tools.d.ts +5 -0
  89. package/dist/tools/enhanced_tools.js +706 -0
  90. package/dist/tools/enhanced_tools.js.map +1 -0
  91. package/dist/tools/input_tools.d.ts +2 -0
  92. package/dist/tools/input_tools.js +408 -0
  93. package/dist/tools/input_tools.js.map +1 -0
  94. package/dist/tools/node_tools.d.ts +5 -0
  95. package/dist/tools/node_tools.js +217 -0
  96. package/dist/tools/node_tools.js.map +1 -0
  97. package/dist/tools/project_tools.d.ts +5 -0
  98. package/dist/tools/project_tools.js +162 -0
  99. package/dist/tools/project_tools.js.map +1 -0
  100. package/dist/tools/scene_tools.d.ts +5 -0
  101. package/dist/tools/scene_tools.js +260 -0
  102. package/dist/tools/scene_tools.js.map +1 -0
  103. package/dist/tools/script_resource_tools.d.ts +5 -0
  104. package/dist/tools/script_resource_tools.js +5 -0
  105. package/dist/tools/script_resource_tools.js.map +1 -0
  106. package/dist/tools/script_tools.d.ts +5 -0
  107. package/dist/tools/script_tools.js +154 -0
  108. package/dist/tools/script_tools.js.map +1 -0
  109. package/dist/utils/godot_connection.d.ts +30 -0
  110. package/dist/utils/godot_connection.js +285 -0
  111. package/dist/utils/godot_connection.js.map +1 -0
  112. package/dist/utils/types.d.ts +16 -0
  113. package/dist/utils/types.js +2 -0
  114. package/dist/utils/types.js.map +1 -0
  115. package/package.json +58 -0
@@ -0,0 +1 @@
1
+ uid://c3mjslnwtixun
@@ -0,0 +1,545 @@
1
+ extends Node
2
+ ## Runtime input handler for MCP input simulation.
3
+ ## This script runs inside the game (not the editor) and handles input injection
4
+ ## via the debugger message system.
5
+
6
+ const CAPTURE_NAME := "mcp_input"
7
+
8
+ var _pending_drags: Dictionary = {}
9
+
10
+ func _ready() -> void:
11
+ # Only register in running game, not in editor
12
+ if Engine.is_editor_hint():
13
+ return
14
+
15
+ if not EngineDebugger.is_active():
16
+ print("[MCP Input Handler] Debugger not active, input simulation unavailable")
17
+ return
18
+
19
+ EngineDebugger.register_message_capture(CAPTURE_NAME, _on_capture)
20
+ print("[MCP Input Handler] Input simulation ready")
21
+
22
+
23
+ func _on_capture(message: String, data: Array) -> bool:
24
+ var action := message.substr(CAPTURE_NAME.length() + 1) if message.begins_with(CAPTURE_NAME + ":") else message
25
+
26
+ match action:
27
+ "action_press":
28
+ return _handle_action_press(data)
29
+ "action_release":
30
+ return _handle_action_release(data)
31
+ "action_tap":
32
+ return _handle_action_tap(data)
33
+ "mouse_click":
34
+ return _handle_mouse_click(data)
35
+ "mouse_move":
36
+ return _handle_mouse_move(data)
37
+ "drag":
38
+ return _handle_drag(data)
39
+ "key_press":
40
+ return _handle_key_press(data)
41
+ "input_sequence":
42
+ return _handle_input_sequence(data)
43
+ "get_input_actions":
44
+ return _handle_get_input_actions(data)
45
+
46
+ return false
47
+
48
+
49
+ func _handle_action_press(data: Array) -> bool:
50
+ if data.size() < 2:
51
+ return false
52
+
53
+ var request_id := int(data[0])
54
+ var action_name := str(data[1])
55
+ var strength := float(data[2]) if data.size() > 2 else 1.0
56
+
57
+ if not InputMap.has_action(action_name):
58
+ _send_result(request_id, {
59
+ "success": false,
60
+ "error": "Unknown action: %s" % action_name
61
+ })
62
+ return true
63
+
64
+ Input.action_press(action_name, strength)
65
+ _send_result(request_id, {
66
+ "success": true,
67
+ "action": action_name,
68
+ "type": "press",
69
+ "strength": strength
70
+ })
71
+ return true
72
+
73
+
74
+ func _handle_action_release(data: Array) -> bool:
75
+ if data.size() < 2:
76
+ return false
77
+
78
+ var request_id := int(data[0])
79
+ var action_name := str(data[1])
80
+
81
+ if not InputMap.has_action(action_name):
82
+ _send_result(request_id, {
83
+ "success": false,
84
+ "error": "Unknown action: %s" % action_name
85
+ })
86
+ return true
87
+
88
+ Input.action_release(action_name)
89
+ _send_result(request_id, {
90
+ "success": true,
91
+ "action": action_name,
92
+ "type": "release"
93
+ })
94
+ return true
95
+
96
+
97
+ func _handle_action_tap(data: Array) -> bool:
98
+ if data.size() < 2:
99
+ return false
100
+
101
+ var request_id := int(data[0])
102
+ var action_name := str(data[1])
103
+ var duration_ms := int(data[2]) if data.size() > 2 else 100
104
+
105
+ if not InputMap.has_action(action_name):
106
+ _send_result(request_id, {
107
+ "success": false,
108
+ "error": "Unknown action: %s" % action_name
109
+ })
110
+ return true
111
+
112
+ # Execute tap asynchronously
113
+ _execute_tap(request_id, action_name, duration_ms)
114
+ return true
115
+
116
+
117
+ func _execute_tap(request_id: int, action_name: String, duration_ms: int) -> void:
118
+ Input.action_press(action_name)
119
+ var tree := get_tree()
120
+ if tree:
121
+ await tree.create_timer(float(duration_ms) / 1000.0).timeout
122
+ Input.action_release(action_name)
123
+
124
+ _send_result(request_id, {
125
+ "success": true,
126
+ "action": action_name,
127
+ "type": "tap",
128
+ "duration_ms": duration_ms
129
+ })
130
+
131
+
132
+ func _handle_mouse_click(data: Array) -> bool:
133
+ if data.size() < 2:
134
+ return false
135
+
136
+ var request_id := int(data[0])
137
+ var options := data[1] as Dictionary if typeof(data[1]) == TYPE_DICTIONARY else {}
138
+
139
+ var position := Vector2(
140
+ float(options.get("x", 0)),
141
+ float(options.get("y", 0))
142
+ )
143
+ var button := int(options.get("button", MOUSE_BUTTON_LEFT))
144
+ var double_click := bool(options.get("double_click", false))
145
+
146
+ # Execute click asynchronously
147
+ _execute_mouse_click(request_id, position, button, double_click)
148
+ return true
149
+
150
+
151
+ func _execute_mouse_click(request_id: int, position: Vector2, button: int, double_click: bool) -> void:
152
+ var event := InputEventMouseButton.new()
153
+ event.position = position
154
+ event.global_position = position
155
+ event.button_index = button
156
+ event.pressed = true
157
+ event.double_click = double_click
158
+
159
+ Input.parse_input_event(event)
160
+
161
+ # Release after a frame
162
+ var tree := get_tree()
163
+ if tree:
164
+ await tree.process_frame
165
+
166
+ event = InputEventMouseButton.new()
167
+ event.position = position
168
+ event.global_position = position
169
+ event.button_index = button
170
+ event.pressed = false
171
+ Input.parse_input_event(event)
172
+
173
+ _send_result(request_id, {
174
+ "success": true,
175
+ "type": "mouse_click",
176
+ "position": [position.x, position.y],
177
+ "button": button,
178
+ "double_click": double_click
179
+ })
180
+
181
+
182
+ func _handle_mouse_move(data: Array) -> bool:
183
+ if data.size() < 2:
184
+ return false
185
+
186
+ var request_id := int(data[0])
187
+ var options := data[1] as Dictionary if typeof(data[1]) == TYPE_DICTIONARY else {}
188
+
189
+ var position := Vector2(
190
+ float(options.get("x", 0)),
191
+ float(options.get("y", 0))
192
+ )
193
+
194
+ var event := InputEventMouseMotion.new()
195
+ event.position = position
196
+ event.global_position = position
197
+ Input.parse_input_event(event)
198
+
199
+ # Also warp mouse to ensure cursor position updates
200
+ Input.warp_mouse(position)
201
+
202
+ _send_result(request_id, {
203
+ "success": true,
204
+ "type": "mouse_move",
205
+ "position": [position.x, position.y]
206
+ })
207
+ return true
208
+
209
+
210
+ func _handle_drag(data: Array) -> bool:
211
+ if data.size() < 2:
212
+ return false
213
+
214
+ var request_id := int(data[0])
215
+ var options := data[1] as Dictionary if typeof(data[1]) == TYPE_DICTIONARY else {}
216
+
217
+ var start := Vector2(
218
+ float(options.get("start_x", 0)),
219
+ float(options.get("start_y", 0))
220
+ )
221
+ var end_pos := Vector2(
222
+ float(options.get("end_x", 0)),
223
+ float(options.get("end_y", 0))
224
+ )
225
+ var duration_ms := int(options.get("duration_ms", 200))
226
+ var steps := int(options.get("steps", 10))
227
+ var button := int(options.get("button", MOUSE_BUTTON_LEFT))
228
+
229
+ # Execute drag asynchronously
230
+ _execute_drag(request_id, start, end_pos, duration_ms, steps, button)
231
+ return true
232
+
233
+
234
+ func _execute_drag(request_id: int, start: Vector2, end_pos: Vector2, duration_ms: int, steps: int, button: int) -> void:
235
+ var duration := float(duration_ms) / 1000.0
236
+ var step_delay := duration / float(steps)
237
+ var tree := get_tree()
238
+
239
+ # Move to start position first
240
+ Input.warp_mouse(start)
241
+ if tree:
242
+ await tree.process_frame
243
+
244
+ # Press at start position
245
+ var press_event := InputEventMouseButton.new()
246
+ press_event.position = start
247
+ press_event.global_position = start
248
+ press_event.button_index = button
249
+ press_event.pressed = true
250
+ Input.parse_input_event(press_event)
251
+
252
+ # Move in interpolated steps
253
+ var prev_pos := start
254
+ for i in range(steps):
255
+ var t := float(i + 1) / float(steps)
256
+ var pos := start.lerp(end_pos, t)
257
+
258
+ var motion := InputEventMouseMotion.new()
259
+ motion.position = pos
260
+ motion.global_position = pos
261
+ motion.relative = pos - prev_pos
262
+ Input.parse_input_event(motion)
263
+ Input.warp_mouse(pos)
264
+
265
+ prev_pos = pos
266
+ if tree:
267
+ await tree.create_timer(step_delay).timeout
268
+
269
+ # Release at end position
270
+ var release_event := InputEventMouseButton.new()
271
+ release_event.position = end_pos
272
+ release_event.global_position = end_pos
273
+ release_event.button_index = button
274
+ release_event.pressed = false
275
+ Input.parse_input_event(release_event)
276
+
277
+ _send_result(request_id, {
278
+ "success": true,
279
+ "type": "drag",
280
+ "start": [start.x, start.y],
281
+ "end": [end_pos.x, end_pos.y],
282
+ "duration_ms": duration_ms,
283
+ "steps": steps
284
+ })
285
+
286
+
287
+ func _handle_key_press(data: Array) -> bool:
288
+ if data.size() < 2:
289
+ return false
290
+
291
+ var request_id := int(data[0])
292
+ var options := data[1] as Dictionary if typeof(data[1]) == TYPE_DICTIONARY else {}
293
+
294
+ var key_string := str(options.get("key", ""))
295
+ var duration_ms := int(options.get("duration_ms", 100))
296
+ var modifiers := options.get("modifiers", {}) as Dictionary
297
+
298
+ # Convert key string to keycode
299
+ var keycode := _string_to_keycode(key_string)
300
+ if keycode == KEY_NONE:
301
+ _send_result(request_id, {
302
+ "success": false,
303
+ "error": "Unknown key: %s" % key_string
304
+ })
305
+ return true
306
+
307
+ # Execute key press asynchronously
308
+ _execute_key_press(request_id, keycode, key_string, duration_ms, modifiers)
309
+ return true
310
+
311
+
312
+ func _execute_key_press(request_id: int, keycode: int, key_string: String, duration_ms: int, modifiers: Dictionary) -> void:
313
+ var event := InputEventKey.new()
314
+ event.keycode = keycode
315
+ event.physical_keycode = keycode
316
+ event.pressed = true
317
+ event.shift_pressed = bool(modifiers.get("shift", false))
318
+ event.ctrl_pressed = bool(modifiers.get("ctrl", false))
319
+ event.alt_pressed = bool(modifiers.get("alt", false))
320
+ event.meta_pressed = bool(modifiers.get("meta", false))
321
+
322
+ Input.parse_input_event(event)
323
+
324
+ var tree := get_tree()
325
+ if tree:
326
+ await tree.create_timer(float(duration_ms) / 1000.0).timeout
327
+
328
+ event = InputEventKey.new()
329
+ event.keycode = keycode
330
+ event.physical_keycode = keycode
331
+ event.pressed = false
332
+ event.shift_pressed = bool(modifiers.get("shift", false))
333
+ event.ctrl_pressed = bool(modifiers.get("ctrl", false))
334
+ event.alt_pressed = bool(modifiers.get("alt", false))
335
+ event.meta_pressed = bool(modifiers.get("meta", false))
336
+
337
+ Input.parse_input_event(event)
338
+
339
+ _send_result(request_id, {
340
+ "success": true,
341
+ "type": "key_press",
342
+ "key": key_string,
343
+ "duration_ms": duration_ms
344
+ })
345
+
346
+
347
+ func _handle_input_sequence(data: Array) -> bool:
348
+ if data.size() < 2:
349
+ return false
350
+
351
+ var request_id := int(data[0])
352
+ var sequence := data[1] as Array if typeof(data[1]) == TYPE_ARRAY else []
353
+
354
+ # Execute sequence asynchronously
355
+ _execute_input_sequence(request_id, sequence)
356
+ return true
357
+
358
+
359
+ func _execute_input_sequence(request_id: int, sequence: Array) -> void:
360
+ var results := []
361
+ var errors := []
362
+ var tree := get_tree()
363
+
364
+ for step in sequence:
365
+ if typeof(step) != TYPE_DICTIONARY:
366
+ continue
367
+
368
+ var step_dict := step as Dictionary
369
+ var step_type := str(step_dict.get("type", ""))
370
+ var step_result := {}
371
+
372
+ match step_type:
373
+ "press":
374
+ var action := str(step_dict.get("action", ""))
375
+ var strength := float(step_dict.get("strength", 1.0))
376
+ if InputMap.has_action(action):
377
+ Input.action_press(action, strength)
378
+ step_result = { "type": "press", "action": action, "success": true }
379
+ else:
380
+ step_result = { "type": "press", "action": action, "success": false, "error": "Unknown action" }
381
+ errors.append("Unknown action: %s" % action)
382
+
383
+ "release":
384
+ var action := str(step_dict.get("action", ""))
385
+ if InputMap.has_action(action):
386
+ Input.action_release(action)
387
+ step_result = { "type": "release", "action": action, "success": true }
388
+ else:
389
+ step_result = { "type": "release", "action": action, "success": false, "error": "Unknown action" }
390
+ errors.append("Unknown action: %s" % action)
391
+
392
+ "tap":
393
+ var action := str(step_dict.get("action", ""))
394
+ var duration := float(step_dict.get("duration_ms", 100)) / 1000.0
395
+ if InputMap.has_action(action):
396
+ Input.action_press(action)
397
+ if tree:
398
+ await tree.create_timer(duration).timeout
399
+ Input.action_release(action)
400
+ step_result = { "type": "tap", "action": action, "success": true }
401
+ else:
402
+ step_result = { "type": "tap", "action": action, "success": false, "error": "Unknown action" }
403
+ errors.append("Unknown action: %s" % action)
404
+
405
+ "wait":
406
+ var duration := float(step_dict.get("duration_ms", 100)) / 1000.0
407
+ if tree:
408
+ await tree.create_timer(duration).timeout
409
+ step_result = { "type": "wait", "duration_ms": step_dict.get("duration_ms", 100), "success": true }
410
+
411
+ "click":
412
+ var pos := Vector2(
413
+ float(step_dict.get("x", 0)),
414
+ float(step_dict.get("y", 0))
415
+ )
416
+ var btn := int(step_dict.get("button", MOUSE_BUTTON_LEFT))
417
+
418
+ var click_event := InputEventMouseButton.new()
419
+ click_event.position = pos
420
+ click_event.global_position = pos
421
+ click_event.button_index = btn
422
+ click_event.pressed = true
423
+ Input.parse_input_event(click_event)
424
+
425
+ if tree:
426
+ await tree.process_frame
427
+
428
+ click_event = InputEventMouseButton.new()
429
+ click_event.position = pos
430
+ click_event.global_position = pos
431
+ click_event.button_index = btn
432
+ click_event.pressed = false
433
+ Input.parse_input_event(click_event)
434
+
435
+ step_result = { "type": "click", "position": [pos.x, pos.y], "success": true }
436
+
437
+ _:
438
+ step_result = { "type": step_type, "success": false, "error": "Unknown step type" }
439
+ errors.append("Unknown step type: %s" % step_type)
440
+
441
+ results.append(step_result)
442
+
443
+ _send_result(request_id, {
444
+ "success": errors.is_empty(),
445
+ "type": "sequence",
446
+ "steps_executed": results.size(),
447
+ "results": results,
448
+ "errors": errors
449
+ })
450
+
451
+
452
+ func _handle_get_input_actions(data: Array) -> bool:
453
+ var request_id := int(data[0]) if data.size() > 0 else 0
454
+
455
+ var actions := InputMap.get_actions()
456
+ var action_list := []
457
+
458
+ for action in actions:
459
+ var action_name := str(action)
460
+ # Skip built-in UI actions if they start with "ui_" for cleaner output
461
+ var events := InputMap.action_get_events(action_name)
462
+ var event_strings := []
463
+
464
+ for event in events:
465
+ if event is InputEventKey:
466
+ event_strings.append("Key: %s" % OS.get_keycode_string(event.keycode))
467
+ elif event is InputEventMouseButton:
468
+ event_strings.append("Mouse: Button %d" % event.button_index)
469
+ elif event is InputEventJoypadButton:
470
+ event_strings.append("Joypad: Button %d" % event.button_index)
471
+ elif event is InputEventJoypadMotion:
472
+ event_strings.append("Joypad: Axis %d" % event.axis)
473
+
474
+ action_list.append({
475
+ "name": action_name,
476
+ "events": event_strings,
477
+ "deadzone": InputMap.action_get_deadzone(action_name)
478
+ })
479
+
480
+ _send_result(request_id, {
481
+ "success": true,
482
+ "type": "input_actions",
483
+ "actions": action_list,
484
+ "count": action_list.size()
485
+ })
486
+ return true
487
+
488
+
489
+ func _string_to_keycode(key_string: String) -> int:
490
+ var upper := key_string.to_upper()
491
+
492
+ # Common key mappings
493
+ var key_map := {
494
+ "SPACE": KEY_SPACE,
495
+ "ENTER": KEY_ENTER,
496
+ "RETURN": KEY_ENTER,
497
+ "ESCAPE": KEY_ESCAPE,
498
+ "ESC": KEY_ESCAPE,
499
+ "TAB": KEY_TAB,
500
+ "BACKSPACE": KEY_BACKSPACE,
501
+ "DELETE": KEY_DELETE,
502
+ "INSERT": KEY_INSERT,
503
+ "HOME": KEY_HOME,
504
+ "END": KEY_END,
505
+ "PAGEUP": KEY_PAGEUP,
506
+ "PAGEDOWN": KEY_PAGEDOWN,
507
+ "UP": KEY_UP,
508
+ "DOWN": KEY_DOWN,
509
+ "LEFT": KEY_LEFT,
510
+ "RIGHT": KEY_RIGHT,
511
+ "SHIFT": KEY_SHIFT,
512
+ "CTRL": KEY_CTRL,
513
+ "CONTROL": KEY_CTRL,
514
+ "ALT": KEY_ALT,
515
+ "F1": KEY_F1,
516
+ "F2": KEY_F2,
517
+ "F3": KEY_F3,
518
+ "F4": KEY_F4,
519
+ "F5": KEY_F5,
520
+ "F6": KEY_F6,
521
+ "F7": KEY_F7,
522
+ "F8": KEY_F8,
523
+ "F9": KEY_F9,
524
+ "F10": KEY_F10,
525
+ "F11": KEY_F11,
526
+ "F12": KEY_F12,
527
+ }
528
+
529
+ if key_map.has(upper):
530
+ return key_map[upper]
531
+
532
+ # Single character keys (A-Z, 0-9)
533
+ if upper.length() == 1:
534
+ var code := upper.unicode_at(0)
535
+ if code >= 65 and code <= 90: # A-Z
536
+ return code
537
+ if code >= 48 and code <= 57: # 0-9
538
+ return code
539
+
540
+ return KEY_NONE
541
+
542
+
543
+ func _send_result(request_id: int, result: Dictionary) -> void:
544
+ result["request_id"] = request_id
545
+ EngineDebugger.send_message("%s:result" % CAPTURE_NAME, [result])
@@ -0,0 +1 @@
1
+ uid://corwv3fq3dfwp