@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.
- package/README.md +187 -0
- package/addons/godot_mcp/command_handler.gd +161 -0
- package/addons/godot_mcp/command_handler.gd.uid +1 -0
- package/addons/godot_mcp/commands/base_command_processor.gd +221 -0
- package/addons/godot_mcp/commands/base_command_processor.gd.uid +1 -0
- package/addons/godot_mcp/commands/debugger_commands.gd +221 -0
- package/addons/godot_mcp/commands/debugger_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/editor_commands.gd +237 -0
- package/addons/godot_mcp/commands/editor_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/editor_script_commands.gd +365 -0
- package/addons/godot_mcp/commands/editor_script_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/input_commands.gd +337 -0
- package/addons/godot_mcp/commands/input_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/node_commands.gd +222 -0
- package/addons/godot_mcp/commands/node_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/project_commands.gd +298 -0
- package/addons/godot_mcp/commands/project_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/scene_commands.gd +337 -0
- package/addons/godot_mcp/commands/scene_commands.gd.uid +1 -0
- package/addons/godot_mcp/commands/script_commands.gd +349 -0
- package/addons/godot_mcp/commands/script_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_asset_commands.gd +153 -0
- package/addons/godot_mcp/mcp_asset_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_debug_output_publisher.gd +1669 -0
- package/addons/godot_mcp/mcp_debug_output_publisher.gd.uid +1 -0
- package/addons/godot_mcp/mcp_debugger_bridge.gd +1455 -0
- package/addons/godot_mcp/mcp_debugger_bridge.gd.uid +1 -0
- package/addons/godot_mcp/mcp_enhanced_commands.gd +1083 -0
- package/addons/godot_mcp/mcp_enhanced_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_input_handler.gd +545 -0
- package/addons/godot_mcp/mcp_input_handler.gd.uid +1 -0
- package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd +464 -0
- package/addons/godot_mcp/mcp_runtime_debugger_bridge.gd.uid +1 -0
- package/addons/godot_mcp/mcp_script_resource_commands.gd +165 -0
- package/addons/godot_mcp/mcp_script_resource_commands.gd.uid +1 -0
- package/addons/godot_mcp/mcp_server.gd +260 -0
- package/addons/godot_mcp/mcp_server.gd.uid +1 -0
- package/addons/godot_mcp/plugin.cfg +7 -0
- package/addons/godot_mcp/runtime_debugger.gd +81 -0
- package/addons/godot_mcp/runtime_debugger.gd.uid +1 -0
- package/addons/godot_mcp/ui/mcp_panel.gd +94 -0
- package/addons/godot_mcp/ui/mcp_panel.gd.uid +1 -0
- package/addons/godot_mcp/ui/mcp_panel.tscn +96 -0
- package/addons/godot_mcp/utils/node_utils.gd +82 -0
- package/addons/godot_mcp/utils/node_utils.gd.uid +1 -0
- package/addons/godot_mcp/utils/resource_utils.gd +81 -0
- package/addons/godot_mcp/utils/resource_utils.gd.uid +1 -0
- package/addons/godot_mcp/utils/script_utils.gd +114 -0
- package/addons/godot_mcp/utils/script_utils.gd.uid +1 -0
- package/addons/godot_mcp/websocket_server.gd +197 -0
- package/addons/godot_mcp/websocket_server.gd.uid +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +561 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +156 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/asset_resources.d.ts +29 -0
- package/dist/resources/asset_resources.js +145 -0
- package/dist/resources/asset_resources.js.map +1 -0
- package/dist/resources/debug_resources.d.ts +11 -0
- package/dist/resources/debug_resources.js +106 -0
- package/dist/resources/debug_resources.js.map +1 -0
- package/dist/resources/debugger_resources.d.ts +62 -0
- package/dist/resources/debugger_resources.js +201 -0
- package/dist/resources/debugger_resources.js.map +1 -0
- package/dist/resources/editor_resources.d.ts +47 -0
- package/dist/resources/editor_resources.js +155 -0
- package/dist/resources/editor_resources.js.map +1 -0
- package/dist/resources/project_resources.d.ts +33 -0
- package/dist/resources/project_resources.js +137 -0
- package/dist/resources/project_resources.js.map +1 -0
- package/dist/resources/scene_resources.d.ts +33 -0
- package/dist/resources/scene_resources.js +160 -0
- package/dist/resources/scene_resources.js.map +1 -0
- package/dist/resources/script_resources.d.ts +51 -0
- package/dist/resources/script_resources.js +203 -0
- package/dist/resources/script_resources.js.map +1 -0
- package/dist/tools/asset_tools.d.ts +5 -0
- package/dist/tools/asset_tools.js +125 -0
- package/dist/tools/asset_tools.js.map +1 -0
- package/dist/tools/debugger_tools.d.ts +2 -0
- package/dist/tools/debugger_tools.js +342 -0
- package/dist/tools/debugger_tools.js.map +1 -0
- package/dist/tools/editor_tools.d.ts +2 -0
- package/dist/tools/editor_tools.js +165 -0
- package/dist/tools/editor_tools.js.map +1 -0
- package/dist/tools/enhanced_tools.d.ts +5 -0
- package/dist/tools/enhanced_tools.js +706 -0
- package/dist/tools/enhanced_tools.js.map +1 -0
- package/dist/tools/input_tools.d.ts +2 -0
- package/dist/tools/input_tools.js +408 -0
- package/dist/tools/input_tools.js.map +1 -0
- package/dist/tools/node_tools.d.ts +5 -0
- package/dist/tools/node_tools.js +217 -0
- package/dist/tools/node_tools.js.map +1 -0
- package/dist/tools/project_tools.d.ts +5 -0
- package/dist/tools/project_tools.js +162 -0
- package/dist/tools/project_tools.js.map +1 -0
- package/dist/tools/scene_tools.d.ts +5 -0
- package/dist/tools/scene_tools.js +260 -0
- package/dist/tools/scene_tools.js.map +1 -0
- package/dist/tools/script_resource_tools.d.ts +5 -0
- package/dist/tools/script_resource_tools.js +5 -0
- package/dist/tools/script_resource_tools.js.map +1 -0
- package/dist/tools/script_tools.d.ts +5 -0
- package/dist/tools/script_tools.js +154 -0
- package/dist/tools/script_tools.js.map +1 -0
- package/dist/utils/godot_connection.d.ts +30 -0
- package/dist/utils/godot_connection.js +285 -0
- package/dist/utils/godot_connection.js.map +1 -0
- package/dist/utils/types.d.ts +16 -0
- package/dist/utils/types.js +2 -0
- package/dist/utils/types.js.map +1 -0
- 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
|