@satelliteoflove/godot-mcp 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +3 -4
  2. package/addon/command_router.gd +39 -0
  3. package/addon/command_router.gd.uid +1 -0
  4. package/addon/commands/animation_commands.gd +633 -0
  5. package/addon/commands/animation_commands.gd.uid +1 -0
  6. package/addon/commands/debug_commands.gd +109 -0
  7. package/addon/commands/debug_commands.gd.uid +1 -0
  8. package/addon/commands/file_commands.gd +95 -0
  9. package/addon/commands/file_commands.gd.uid +1 -0
  10. package/addon/commands/node_commands.gd +252 -0
  11. package/addon/commands/node_commands.gd.uid +1 -0
  12. package/addon/commands/project_commands.gd +114 -0
  13. package/addon/commands/project_commands.gd.uid +1 -0
  14. package/addon/commands/resource_commands.gd +293 -0
  15. package/addon/commands/resource_commands.gd.uid +1 -0
  16. package/addon/commands/scene3d_commands.gd +169 -0
  17. package/addon/commands/scene3d_commands.gd.uid +1 -0
  18. package/addon/commands/scene_commands.gd +131 -0
  19. package/addon/commands/scene_commands.gd.uid +1 -0
  20. package/addon/commands/screenshot_commands.gd +130 -0
  21. package/addon/commands/screenshot_commands.gd.uid +1 -0
  22. package/addon/commands/script_commands.gd +156 -0
  23. package/addon/commands/script_commands.gd.uid +1 -0
  24. package/addon/commands/selection_commands.gd +170 -0
  25. package/addon/commands/selection_commands.gd.uid +1 -0
  26. package/addon/commands/system_commands.gd +29 -0
  27. package/addon/commands/tilemap_commands.gd +657 -0
  28. package/addon/commands/tilemap_commands.gd.uid +1 -0
  29. package/addon/core/base_command.gd +29 -0
  30. package/addon/core/base_command.gd.uid +1 -0
  31. package/addon/core/mcp_debugger_plugin.gd +144 -0
  32. package/addon/core/mcp_debugger_plugin.gd.uid +1 -0
  33. package/addon/core/mcp_logger.gd +40 -0
  34. package/addon/core/mcp_logger.gd.uid +1 -0
  35. package/addon/core/mcp_utils.gd +125 -0
  36. package/addon/core/mcp_utils.gd.uid +1 -0
  37. package/addon/game_bridge/mcp_game_bridge.gd +195 -0
  38. package/addon/game_bridge/mcp_game_bridge.gd.uid +1 -0
  39. package/addon/plugin.cfg +8 -0
  40. package/addon/plugin.gd +89 -0
  41. package/addon/plugin.gd.uid +1 -0
  42. package/addon/ui/status_panel.gd +23 -0
  43. package/addon/ui/status_panel.gd.uid +1 -0
  44. package/addon/ui/status_panel.tscn +41 -0
  45. package/addon/websocket_server.gd +143 -0
  46. package/addon/websocket_server.gd.uid +1 -0
  47. package/dist/cli.d.ts +3 -0
  48. package/dist/cli.d.ts.map +1 -0
  49. package/dist/cli.js +60 -0
  50. package/dist/cli.js.map +1 -0
  51. package/dist/connection/websocket.d.ts +14 -0
  52. package/dist/connection/websocket.d.ts.map +1 -1
  53. package/dist/connection/websocket.js +69 -1
  54. package/dist/connection/websocket.js.map +1 -1
  55. package/dist/index.d.ts +1 -2
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -6
  58. package/dist/index.js.map +1 -1
  59. package/dist/installer/install.d.ts +13 -0
  60. package/dist/installer/install.d.ts.map +1 -0
  61. package/dist/installer/install.js +77 -0
  62. package/dist/installer/install.js.map +1 -0
  63. package/dist/tools/editor.d.ts +20 -5
  64. package/dist/tools/editor.d.ts.map +1 -1
  65. package/dist/tools/editor.js +27 -4
  66. package/dist/tools/editor.js.map +1 -1
  67. package/dist/tools/project.d.ts +3 -3
  68. package/dist/tools/project.d.ts.map +1 -1
  69. package/dist/tools/project.js +28 -2
  70. package/dist/tools/project.js.map +1 -1
  71. package/dist/version.d.ts +2 -0
  72. package/dist/version.d.ts.map +1 -0
  73. package/dist/version.js +20 -0
  74. package/dist/version.js.map +1 -0
  75. package/package.json +6 -4
@@ -0,0 +1,657 @@
1
+ @tool
2
+ extends MCPBaseCommand
3
+ class_name MCPTilemapCommands
4
+
5
+
6
+ func get_commands() -> Dictionary:
7
+ return {
8
+ "list_tilemap_layers": list_tilemap_layers,
9
+ "get_tilemap_layer_info": get_tilemap_layer_info,
10
+ "get_tileset_info": get_tileset_info,
11
+ "get_used_cells": get_used_cells,
12
+ "get_cell": get_cell,
13
+ "set_cell": set_cell,
14
+ "erase_cell": erase_cell,
15
+ "clear_layer": clear_layer,
16
+ "get_cells_in_region": get_cells_in_region,
17
+ "set_cells_batch": set_cells_batch,
18
+ "convert_coords": convert_coords,
19
+ "list_gridmaps": list_gridmaps,
20
+ "get_gridmap_info": get_gridmap_info,
21
+ "get_meshlib_info": get_meshlib_info,
22
+ "get_gridmap_used_cells": get_gridmap_used_cells,
23
+ "get_gridmap_cell": get_gridmap_cell,
24
+ "set_gridmap_cell": set_gridmap_cell,
25
+ "clear_gridmap_cell": clear_gridmap_cell,
26
+ "clear_gridmap": clear_gridmap,
27
+ "get_cells_by_item": get_cells_by_item,
28
+ "set_gridmap_cells_batch": set_gridmap_cells_batch,
29
+ }
30
+
31
+
32
+ func _get_tilemap_layer(node_path: String) -> TileMapLayer:
33
+ var node := _get_node(node_path)
34
+ if not node:
35
+ return null
36
+ if not node is TileMapLayer:
37
+ return null
38
+ return node as TileMapLayer
39
+
40
+
41
+ func _get_gridmap(node_path: String) -> GridMap:
42
+ var node := _get_node(node_path)
43
+ if not node:
44
+ return null
45
+ if not node is GridMap:
46
+ return null
47
+ return node as GridMap
48
+
49
+
50
+ func _find_tilemap_layers(node: Node, result: Array) -> void:
51
+ if node is TileMapLayer:
52
+ result.append({
53
+ "path": str(node.get_path()),
54
+ "name": node.name
55
+ })
56
+ for child in node.get_children():
57
+ _find_tilemap_layers(child, result)
58
+
59
+
60
+ func _find_gridmaps(node: Node, result: Array) -> void:
61
+ if node is GridMap:
62
+ result.append({
63
+ "path": str(node.get_path()),
64
+ "name": node.name
65
+ })
66
+ for child in node.get_children():
67
+ _find_gridmaps(child, result)
68
+
69
+
70
+ func _serialize_vector2i(v: Vector2i) -> Dictionary:
71
+ return {"x": v.x, "y": v.y}
72
+
73
+
74
+ func _serialize_vector3i(v: Vector3i) -> Dictionary:
75
+ return {"x": v.x, "y": v.y, "z": v.z}
76
+
77
+
78
+ func _deserialize_vector2i(d: Dictionary) -> Vector2i:
79
+ return Vector2i(int(d.get("x", 0)), int(d.get("y", 0)))
80
+
81
+
82
+ func _deserialize_vector3i(d: Dictionary) -> Vector3i:
83
+ return Vector3i(int(d.get("x", 0)), int(d.get("y", 0)), int(d.get("z", 0)))
84
+
85
+
86
+ func list_tilemap_layers(params: Dictionary) -> Dictionary:
87
+ var root_path: String = params.get("root_path", "")
88
+ var root: Node
89
+
90
+ if root_path.is_empty():
91
+ root = EditorInterface.get_edited_scene_root()
92
+ else:
93
+ root = _get_node(root_path)
94
+
95
+ if not root:
96
+ return _error("NODE_NOT_FOUND", "Root node not found")
97
+
98
+ var layers := []
99
+ _find_tilemap_layers(root, layers)
100
+
101
+ return _success({"tilemap_layers": layers})
102
+
103
+
104
+ func get_tilemap_layer_info(params: Dictionary) -> Dictionary:
105
+ var node_path: String = params.get("node_path", "")
106
+ if node_path.is_empty():
107
+ return _error("INVALID_PARAMS", "node_path is required")
108
+
109
+ var layer := _get_tilemap_layer(node_path)
110
+ if not layer:
111
+ var node := _get_node(node_path)
112
+ if not node:
113
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
114
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
115
+
116
+ var tileset_path := ""
117
+ if layer.tile_set:
118
+ tileset_path = layer.tile_set.resource_path
119
+
120
+ return _success({
121
+ "name": layer.name,
122
+ "enabled": layer.enabled,
123
+ "tileset_path": tileset_path,
124
+ "cell_quadrant_size": layer.rendering_quadrant_size,
125
+ "collision_enabled": layer.collision_enabled,
126
+ "used_cells_count": layer.get_used_cells().size()
127
+ })
128
+
129
+
130
+ func get_tileset_info(params: Dictionary) -> Dictionary:
131
+ var node_path: String = params.get("node_path", "")
132
+ if node_path.is_empty():
133
+ return _error("INVALID_PARAMS", "node_path is required")
134
+
135
+ var layer := _get_tilemap_layer(node_path)
136
+ if not layer:
137
+ var node := _get_node(node_path)
138
+ if not node:
139
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
140
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
141
+
142
+ if not layer.tile_set:
143
+ return _error("NO_TILESET", "TileMapLayer has no TileSet assigned")
144
+
145
+ var tileset := layer.tile_set
146
+ var sources := []
147
+
148
+ for i in range(tileset.get_source_count()):
149
+ var source_id := tileset.get_source_id(i)
150
+ var source := tileset.get_source(source_id)
151
+ var source_info := {
152
+ "source_id": source_id,
153
+ "source_type": "unknown"
154
+ }
155
+
156
+ if source is TileSetAtlasSource:
157
+ var atlas := source as TileSetAtlasSource
158
+ source_info["source_type"] = "atlas"
159
+ source_info["texture_path"] = atlas.texture.resource_path if atlas.texture else ""
160
+ source_info["texture_region_size"] = _serialize_vector2i(atlas.texture_region_size)
161
+ source_info["tile_count"] = atlas.get_tiles_count()
162
+ elif source is TileSetScenesCollectionSource:
163
+ source_info["source_type"] = "scenes_collection"
164
+ var scenes_source := source as TileSetScenesCollectionSource
165
+ source_info["scene_count"] = scenes_source.get_scene_tiles_count()
166
+
167
+ sources.append(source_info)
168
+
169
+ return _success({
170
+ "tileset_path": tileset.resource_path,
171
+ "tile_size": _serialize_vector2i(tileset.tile_size),
172
+ "source_count": tileset.get_source_count(),
173
+ "sources": sources
174
+ })
175
+
176
+
177
+ func get_used_cells(params: Dictionary) -> Dictionary:
178
+ var node_path: String = params.get("node_path", "")
179
+ if node_path.is_empty():
180
+ return _error("INVALID_PARAMS", "node_path is required")
181
+
182
+ var layer := _get_tilemap_layer(node_path)
183
+ if not layer:
184
+ var node := _get_node(node_path)
185
+ if not node:
186
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
187
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
188
+
189
+ var cells := layer.get_used_cells()
190
+ var result := []
191
+ for cell in cells:
192
+ result.append(_serialize_vector2i(cell))
193
+
194
+ return _success({"cells": result, "count": result.size()})
195
+
196
+
197
+ func get_cell(params: Dictionary) -> Dictionary:
198
+ var node_path: String = params.get("node_path", "")
199
+ if node_path.is_empty():
200
+ return _error("INVALID_PARAMS", "node_path is required")
201
+ if not params.has("coords"):
202
+ return _error("INVALID_PARAMS", "coords is required")
203
+
204
+ var layer := _get_tilemap_layer(node_path)
205
+ if not layer:
206
+ var node := _get_node(node_path)
207
+ if not node:
208
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
209
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
210
+
211
+ var coords := _deserialize_vector2i(params["coords"])
212
+ var source_id := layer.get_cell_source_id(coords)
213
+ var atlas_coords := layer.get_cell_atlas_coords(coords)
214
+ var alt_tile := layer.get_cell_alternative_tile(coords)
215
+
216
+ if source_id == -1:
217
+ return _success({
218
+ "coords": _serialize_vector2i(coords),
219
+ "empty": true
220
+ })
221
+
222
+ return _success({
223
+ "coords": _serialize_vector2i(coords),
224
+ "empty": false,
225
+ "source_id": source_id,
226
+ "atlas_coords": _serialize_vector2i(atlas_coords),
227
+ "alternative_tile": alt_tile
228
+ })
229
+
230
+
231
+ func set_cell(params: Dictionary) -> Dictionary:
232
+ var node_path: String = params.get("node_path", "")
233
+ if node_path.is_empty():
234
+ return _error("INVALID_PARAMS", "node_path is required")
235
+ if not params.has("coords"):
236
+ return _error("INVALID_PARAMS", "coords is required")
237
+
238
+ var layer := _get_tilemap_layer(node_path)
239
+ if not layer:
240
+ var node := _get_node(node_path)
241
+ if not node:
242
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
243
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
244
+
245
+ var coords := _deserialize_vector2i(params["coords"])
246
+ var source_id: int = params.get("source_id", 0)
247
+ var atlas_coords := Vector2i(0, 0)
248
+ if params.has("atlas_coords"):
249
+ atlas_coords = _deserialize_vector2i(params["atlas_coords"])
250
+ var alt_tile: int = params.get("alternative_tile", 0)
251
+
252
+ layer.set_cell(coords, source_id, atlas_coords, alt_tile)
253
+
254
+ return _success({
255
+ "coords": _serialize_vector2i(coords),
256
+ "source_id": source_id,
257
+ "atlas_coords": _serialize_vector2i(atlas_coords),
258
+ "alternative_tile": alt_tile
259
+ })
260
+
261
+
262
+ func erase_cell(params: Dictionary) -> Dictionary:
263
+ var node_path: String = params.get("node_path", "")
264
+ if node_path.is_empty():
265
+ return _error("INVALID_PARAMS", "node_path is required")
266
+ if not params.has("coords"):
267
+ return _error("INVALID_PARAMS", "coords is required")
268
+
269
+ var layer := _get_tilemap_layer(node_path)
270
+ if not layer:
271
+ var node := _get_node(node_path)
272
+ if not node:
273
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
274
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
275
+
276
+ var coords := _deserialize_vector2i(params["coords"])
277
+ layer.erase_cell(coords)
278
+
279
+ return _success({"erased": _serialize_vector2i(coords)})
280
+
281
+
282
+ func clear_layer(params: Dictionary) -> Dictionary:
283
+ var node_path: String = params.get("node_path", "")
284
+ if node_path.is_empty():
285
+ return _error("INVALID_PARAMS", "node_path is required")
286
+
287
+ var layer := _get_tilemap_layer(node_path)
288
+ if not layer:
289
+ var node := _get_node(node_path)
290
+ if not node:
291
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
292
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
293
+
294
+ var count := layer.get_used_cells().size()
295
+ layer.clear()
296
+
297
+ return _success({"cleared": true, "cells_removed": count})
298
+
299
+
300
+ func get_cells_in_region(params: Dictionary) -> Dictionary:
301
+ var node_path: String = params.get("node_path", "")
302
+ if node_path.is_empty():
303
+ return _error("INVALID_PARAMS", "node_path is required")
304
+ if not params.has("min_coords"):
305
+ return _error("INVALID_PARAMS", "min_coords is required")
306
+ if not params.has("max_coords"):
307
+ return _error("INVALID_PARAMS", "max_coords is required")
308
+
309
+ var layer := _get_tilemap_layer(node_path)
310
+ if not layer:
311
+ var node := _get_node(node_path)
312
+ if not node:
313
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
314
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
315
+
316
+ var min_coords := _deserialize_vector2i(params["min_coords"])
317
+ var max_coords := _deserialize_vector2i(params["max_coords"])
318
+
319
+ var cells := []
320
+ for cell in layer.get_used_cells():
321
+ if cell.x >= min_coords.x and cell.x <= max_coords.x and cell.y >= min_coords.y and cell.y <= max_coords.y:
322
+ var source_id := layer.get_cell_source_id(cell)
323
+ var atlas_coords := layer.get_cell_atlas_coords(cell)
324
+ var alt_tile := layer.get_cell_alternative_tile(cell)
325
+ cells.append({
326
+ "coords": _serialize_vector2i(cell),
327
+ "source_id": source_id,
328
+ "atlas_coords": _serialize_vector2i(atlas_coords),
329
+ "alternative_tile": alt_tile
330
+ })
331
+
332
+ return _success({"cells": cells, "count": cells.size()})
333
+
334
+
335
+ func set_cells_batch(params: Dictionary) -> Dictionary:
336
+ var node_path: String = params.get("node_path", "")
337
+ if node_path.is_empty():
338
+ return _error("INVALID_PARAMS", "node_path is required")
339
+ if not params.has("cells"):
340
+ return _error("INVALID_PARAMS", "cells is required")
341
+
342
+ var layer := _get_tilemap_layer(node_path)
343
+ if not layer:
344
+ var node := _get_node(node_path)
345
+ if not node:
346
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
347
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
348
+
349
+ var cells_data: Array = params["cells"]
350
+ var count := 0
351
+
352
+ for cell_data in cells_data:
353
+ if not cell_data.has("coords"):
354
+ continue
355
+ var coords := _deserialize_vector2i(cell_data["coords"])
356
+ var source_id: int = cell_data.get("source_id", 0)
357
+ var atlas_coords := Vector2i(0, 0)
358
+ if cell_data.has("atlas_coords"):
359
+ atlas_coords = _deserialize_vector2i(cell_data["atlas_coords"])
360
+ var alt_tile: int = cell_data.get("alternative_tile", 0)
361
+
362
+ layer.set_cell(coords, source_id, atlas_coords, alt_tile)
363
+ count += 1
364
+
365
+ return _success({"cells_set": count})
366
+
367
+
368
+ func convert_coords(params: Dictionary) -> Dictionary:
369
+ var node_path: String = params.get("node_path", "")
370
+ if node_path.is_empty():
371
+ return _error("INVALID_PARAMS", "node_path is required")
372
+
373
+ var layer := _get_tilemap_layer(node_path)
374
+ if not layer:
375
+ var node := _get_node(node_path)
376
+ if not node:
377
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
378
+ return _error("NOT_TILEMAP_LAYER", "Node is not a TileMapLayer: %s" % node_path)
379
+
380
+ if params.has("local_position"):
381
+ var local_pos_data: Dictionary = params["local_position"]
382
+ var local_pos := Vector2(local_pos_data.get("x", 0.0), local_pos_data.get("y", 0.0))
383
+ var map_coords := layer.local_to_map(local_pos)
384
+ return _success({
385
+ "direction": "local_to_map",
386
+ "local_position": {"x": local_pos.x, "y": local_pos.y},
387
+ "map_coords": _serialize_vector2i(map_coords)
388
+ })
389
+ elif params.has("map_coords"):
390
+ var map_coords := _deserialize_vector2i(params["map_coords"])
391
+ var local_pos := layer.map_to_local(map_coords)
392
+ return _success({
393
+ "direction": "map_to_local",
394
+ "map_coords": _serialize_vector2i(map_coords),
395
+ "local_position": {"x": local_pos.x, "y": local_pos.y}
396
+ })
397
+ else:
398
+ return _error("INVALID_PARAMS", "Either local_position or map_coords is required")
399
+
400
+
401
+ func list_gridmaps(params: Dictionary) -> Dictionary:
402
+ var root_path: String = params.get("root_path", "")
403
+ var root: Node
404
+
405
+ if root_path.is_empty():
406
+ root = EditorInterface.get_edited_scene_root()
407
+ else:
408
+ root = _get_node(root_path)
409
+
410
+ if not root:
411
+ return _error("NODE_NOT_FOUND", "Root node not found")
412
+
413
+ var gridmaps := []
414
+ _find_gridmaps(root, gridmaps)
415
+
416
+ return _success({"gridmaps": gridmaps})
417
+
418
+
419
+ func get_gridmap_info(params: Dictionary) -> Dictionary:
420
+ var node_path: String = params.get("node_path", "")
421
+ if node_path.is_empty():
422
+ return _error("INVALID_PARAMS", "node_path is required")
423
+
424
+ var gridmap := _get_gridmap(node_path)
425
+ if not gridmap:
426
+ var node := _get_node(node_path)
427
+ if not node:
428
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
429
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
430
+
431
+ var meshlib_path := ""
432
+ if gridmap.mesh_library:
433
+ meshlib_path = gridmap.mesh_library.resource_path
434
+
435
+ return _success({
436
+ "name": gridmap.name,
437
+ "mesh_library_path": meshlib_path,
438
+ "cell_size": _serialize_value(gridmap.cell_size),
439
+ "cell_center_x": gridmap.cell_center_x,
440
+ "cell_center_y": gridmap.cell_center_y,
441
+ "cell_center_z": gridmap.cell_center_z,
442
+ "used_cells_count": gridmap.get_used_cells().size()
443
+ })
444
+
445
+
446
+ func get_meshlib_info(params: Dictionary) -> Dictionary:
447
+ var node_path: String = params.get("node_path", "")
448
+ if node_path.is_empty():
449
+ return _error("INVALID_PARAMS", "node_path is required")
450
+
451
+ var gridmap := _get_gridmap(node_path)
452
+ if not gridmap:
453
+ var node := _get_node(node_path)
454
+ if not node:
455
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
456
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
457
+
458
+ if not gridmap.mesh_library:
459
+ return _error("NO_MESH_LIBRARY", "GridMap has no MeshLibrary assigned")
460
+
461
+ var meshlib := gridmap.mesh_library
462
+ var items := []
463
+
464
+ for i in range(meshlib.get_item_list().size()):
465
+ var item_id: int = meshlib.get_item_list()[i]
466
+ var item_name := meshlib.get_item_name(item_id)
467
+ var mesh := meshlib.get_item_mesh(item_id)
468
+ var mesh_path := mesh.resource_path if mesh else ""
469
+
470
+ items.append({
471
+ "index": item_id,
472
+ "name": item_name,
473
+ "mesh_path": mesh_path
474
+ })
475
+
476
+ return _success({
477
+ "mesh_library_path": meshlib.resource_path,
478
+ "item_count": meshlib.get_item_list().size(),
479
+ "items": items
480
+ })
481
+
482
+
483
+ func get_gridmap_used_cells(params: Dictionary) -> Dictionary:
484
+ var node_path: String = params.get("node_path", "")
485
+ if node_path.is_empty():
486
+ return _error("INVALID_PARAMS", "node_path is required")
487
+
488
+ var gridmap := _get_gridmap(node_path)
489
+ if not gridmap:
490
+ var node := _get_node(node_path)
491
+ if not node:
492
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
493
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
494
+
495
+ var cells := gridmap.get_used_cells()
496
+ var result := []
497
+ for cell in cells:
498
+ result.append(_serialize_vector3i(cell))
499
+
500
+ return _success({"cells": result, "count": result.size()})
501
+
502
+
503
+ func get_gridmap_cell(params: Dictionary) -> Dictionary:
504
+ var node_path: String = params.get("node_path", "")
505
+ if node_path.is_empty():
506
+ return _error("INVALID_PARAMS", "node_path is required")
507
+ if not params.has("coords"):
508
+ return _error("INVALID_PARAMS", "coords is required")
509
+
510
+ var gridmap := _get_gridmap(node_path)
511
+ if not gridmap:
512
+ var node := _get_node(node_path)
513
+ if not node:
514
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
515
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
516
+
517
+ var coords := _deserialize_vector3i(params["coords"])
518
+ var item := gridmap.get_cell_item(coords)
519
+ var orientation := gridmap.get_cell_item_orientation(coords)
520
+
521
+ if item == GridMap.INVALID_CELL_ITEM:
522
+ return _success({
523
+ "coords": _serialize_vector3i(coords),
524
+ "empty": true
525
+ })
526
+
527
+ var item_name := ""
528
+ if gridmap.mesh_library:
529
+ item_name = gridmap.mesh_library.get_item_name(item)
530
+
531
+ return _success({
532
+ "coords": _serialize_vector3i(coords),
533
+ "empty": false,
534
+ "item": item,
535
+ "item_name": item_name,
536
+ "orientation": orientation
537
+ })
538
+
539
+
540
+ func set_gridmap_cell(params: Dictionary) -> Dictionary:
541
+ var node_path: String = params.get("node_path", "")
542
+ if node_path.is_empty():
543
+ return _error("INVALID_PARAMS", "node_path is required")
544
+ if not params.has("coords"):
545
+ return _error("INVALID_PARAMS", "coords is required")
546
+ if not params.has("item"):
547
+ return _error("INVALID_PARAMS", "item is required")
548
+
549
+ var gridmap := _get_gridmap(node_path)
550
+ if not gridmap:
551
+ var node := _get_node(node_path)
552
+ if not node:
553
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
554
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
555
+
556
+ var coords := _deserialize_vector3i(params["coords"])
557
+ var item: int = params["item"]
558
+ var orientation: int = params.get("orientation", 0)
559
+
560
+ gridmap.set_cell_item(coords, item, orientation)
561
+
562
+ return _success({
563
+ "coords": _serialize_vector3i(coords),
564
+ "item": item,
565
+ "orientation": orientation
566
+ })
567
+
568
+
569
+ func clear_gridmap_cell(params: Dictionary) -> Dictionary:
570
+ var node_path: String = params.get("node_path", "")
571
+ if node_path.is_empty():
572
+ return _error("INVALID_PARAMS", "node_path is required")
573
+ if not params.has("coords"):
574
+ return _error("INVALID_PARAMS", "coords is required")
575
+
576
+ var gridmap := _get_gridmap(node_path)
577
+ if not gridmap:
578
+ var node := _get_node(node_path)
579
+ if not node:
580
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
581
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
582
+
583
+ var coords := _deserialize_vector3i(params["coords"])
584
+ gridmap.set_cell_item(coords, GridMap.INVALID_CELL_ITEM)
585
+
586
+ return _success({"cleared": _serialize_vector3i(coords)})
587
+
588
+
589
+ func clear_gridmap(params: Dictionary) -> Dictionary:
590
+ var node_path: String = params.get("node_path", "")
591
+ if node_path.is_empty():
592
+ return _error("INVALID_PARAMS", "node_path is required")
593
+
594
+ var gridmap := _get_gridmap(node_path)
595
+ if not gridmap:
596
+ var node := _get_node(node_path)
597
+ if not node:
598
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
599
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
600
+
601
+ var count := gridmap.get_used_cells().size()
602
+ gridmap.clear()
603
+
604
+ return _success({"cleared": true, "cells_removed": count})
605
+
606
+
607
+ func get_cells_by_item(params: Dictionary) -> Dictionary:
608
+ var node_path: String = params.get("node_path", "")
609
+ if node_path.is_empty():
610
+ return _error("INVALID_PARAMS", "node_path is required")
611
+ if not params.has("item"):
612
+ return _error("INVALID_PARAMS", "item is required")
613
+
614
+ var gridmap := _get_gridmap(node_path)
615
+ if not gridmap:
616
+ var node := _get_node(node_path)
617
+ if not node:
618
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
619
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
620
+
621
+ var item: int = params["item"]
622
+ var cells := gridmap.get_used_cells_by_item(item)
623
+ var result := []
624
+ for cell in cells:
625
+ result.append(_serialize_vector3i(cell))
626
+
627
+ return _success({"item": item, "cells": result, "count": result.size()})
628
+
629
+
630
+ func set_gridmap_cells_batch(params: Dictionary) -> Dictionary:
631
+ var node_path: String = params.get("node_path", "")
632
+ if node_path.is_empty():
633
+ return _error("INVALID_PARAMS", "node_path is required")
634
+ if not params.has("cells"):
635
+ return _error("INVALID_PARAMS", "cells is required")
636
+
637
+ var gridmap := _get_gridmap(node_path)
638
+ if not gridmap:
639
+ var node := _get_node(node_path)
640
+ if not node:
641
+ return _error("NODE_NOT_FOUND", "Node not found: %s" % node_path)
642
+ return _error("NOT_GRIDMAP", "Node is not a GridMap: %s" % node_path)
643
+
644
+ var cells_data: Array = params["cells"]
645
+ var count := 0
646
+
647
+ for cell_data in cells_data:
648
+ if not cell_data.has("coords") or not cell_data.has("item"):
649
+ continue
650
+ var coords := _deserialize_vector3i(cell_data["coords"])
651
+ var item: int = cell_data["item"]
652
+ var orientation: int = cell_data.get("orientation", 0)
653
+
654
+ gridmap.set_cell_item(coords, item, orientation)
655
+ count += 1
656
+
657
+ return _success({"cells_set": count})
@@ -0,0 +1 @@
1
+ uid://0hpst23ps31k
@@ -0,0 +1,29 @@
1
+ @tool
2
+ class_name MCPBaseCommand
3
+ extends RefCounted
4
+
5
+ var _plugin: EditorPlugin
6
+
7
+
8
+ func setup(plugin: EditorPlugin) -> void:
9
+ _plugin = plugin
10
+
11
+
12
+ func get_commands() -> Dictionary:
13
+ return {}
14
+
15
+
16
+ func _success(result: Dictionary) -> Dictionary:
17
+ return MCPUtils.success(result)
18
+
19
+
20
+ func _error(code: String, message: String) -> Dictionary:
21
+ return MCPUtils.error(code, message)
22
+
23
+
24
+ func _get_node(path: String) -> Node:
25
+ return MCPUtils.get_node_from_path(path)
26
+
27
+
28
+ func _serialize_value(value: Variant) -> Variant:
29
+ return MCPUtils.serialize_value(value)
@@ -0,0 +1 @@
1
+ uid://nr8o2h4rehka