@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,81 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name ResourceUtils
|
|
3
|
+
extends RefCounted
|
|
4
|
+
|
|
5
|
+
# Load a resource safely, returning null if it fails
|
|
6
|
+
static func safe_load(path: String) -> Resource:
|
|
7
|
+
if not ResourceLoader.exists(path):
|
|
8
|
+
push_error("Resource does not exist: " + path)
|
|
9
|
+
return null
|
|
10
|
+
|
|
11
|
+
var res = ResourceLoader.load(path)
|
|
12
|
+
if not res:
|
|
13
|
+
push_error("Failed to load resource: " + path)
|
|
14
|
+
return null
|
|
15
|
+
|
|
16
|
+
return res
|
|
17
|
+
|
|
18
|
+
# Save a resource safely, returning true if successful
|
|
19
|
+
static func safe_save(resource: Resource, path: String) -> bool:
|
|
20
|
+
if not resource:
|
|
21
|
+
push_error("Cannot save null resource")
|
|
22
|
+
return false
|
|
23
|
+
|
|
24
|
+
# Make sure directory exists
|
|
25
|
+
var dir_path = path.get_base_dir()
|
|
26
|
+
if not DirAccess.dir_exists_absolute(dir_path):
|
|
27
|
+
var err = DirAccess.make_dir_recursive_absolute(dir_path)
|
|
28
|
+
if err != OK:
|
|
29
|
+
push_error("Failed to create directory: " + dir_path)
|
|
30
|
+
return false
|
|
31
|
+
|
|
32
|
+
var result = ResourceSaver.save(resource, path)
|
|
33
|
+
if result != OK:
|
|
34
|
+
push_error("Failed to save resource: " + path)
|
|
35
|
+
return false
|
|
36
|
+
|
|
37
|
+
return true
|
|
38
|
+
|
|
39
|
+
# Create a new resource instance by type name
|
|
40
|
+
static func create_resource(type_name: String) -> Resource:
|
|
41
|
+
if not ClassDB.class_exists(type_name):
|
|
42
|
+
push_error("Class does not exist: " + type_name)
|
|
43
|
+
return null
|
|
44
|
+
|
|
45
|
+
if not ClassDB.is_parent_class(type_name, "Resource"):
|
|
46
|
+
push_error("Class is not a Resource: " + type_name)
|
|
47
|
+
return null
|
|
48
|
+
|
|
49
|
+
if not ClassDB.can_instantiate(type_name):
|
|
50
|
+
push_error("Cannot instantiate Resource class: " + type_name)
|
|
51
|
+
return null
|
|
52
|
+
|
|
53
|
+
return ClassDB.instantiate(type_name)
|
|
54
|
+
|
|
55
|
+
# Get a list of all resource types that inherit from a base class
|
|
56
|
+
static func get_resource_types(base_class: String = "Resource") -> Array[String]:
|
|
57
|
+
var result: Array[String] = []
|
|
58
|
+
|
|
59
|
+
for class_type in ClassDB.get_class_list():
|
|
60
|
+
if ClassDB.is_parent_class(class_type, base_class) and ClassDB.can_instantiate(class_type):
|
|
61
|
+
result.append(class_type)
|
|
62
|
+
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
# Convert a resource to a JSON-compatible dictionary
|
|
66
|
+
static func resource_to_dict(resource: Resource) -> Dictionary:
|
|
67
|
+
var result = {
|
|
68
|
+
"resource_path": resource.resource_path,
|
|
69
|
+
"resource_name": resource.resource_name,
|
|
70
|
+
"type": resource.get_class(),
|
|
71
|
+
"properties": {}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Get properties
|
|
75
|
+
var property_list = resource.get_property_list()
|
|
76
|
+
for prop in property_list:
|
|
77
|
+
var prop_name = prop["name"]
|
|
78
|
+
if not prop_name.begins_with("_") and prop_name != "resource_path" and prop_name != "resource_name":
|
|
79
|
+
result["properties"][prop_name] = resource.get(prop_name)
|
|
80
|
+
|
|
81
|
+
return result
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://3jnu2dy6wa8a
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name ScriptUtils
|
|
3
|
+
extends RefCounted
|
|
4
|
+
|
|
5
|
+
# Create a new GDScript with basic template content
|
|
6
|
+
static func create_new_script(class_name_str: String = "", extends_type: String = "Node") -> GDScript:
|
|
7
|
+
var script = GDScript.new()
|
|
8
|
+
var content = ""
|
|
9
|
+
|
|
10
|
+
if not class_name_str.is_empty():
|
|
11
|
+
content += "class_name " + class_name_str + "\n"
|
|
12
|
+
|
|
13
|
+
content += "extends " + extends_type + "\n\n"
|
|
14
|
+
content += "func _ready():\n"
|
|
15
|
+
content += "\tpass\n"
|
|
16
|
+
|
|
17
|
+
script.source_code = content
|
|
18
|
+
return script
|
|
19
|
+
|
|
20
|
+
# Create a new script file with basic template content
|
|
21
|
+
static func create_script_file(path: String, class_name_str: String = "", extends_type: String = "Node") -> bool:
|
|
22
|
+
# Make sure directory exists
|
|
23
|
+
var dir_path = path.get_base_dir()
|
|
24
|
+
if not DirAccess.dir_exists_absolute(dir_path):
|
|
25
|
+
var err = DirAccess.make_dir_recursive_absolute(dir_path)
|
|
26
|
+
if err != OK:
|
|
27
|
+
push_error("Failed to create directory: " + dir_path)
|
|
28
|
+
return false
|
|
29
|
+
|
|
30
|
+
var content = ""
|
|
31
|
+
|
|
32
|
+
if not class_name_str.is_empty():
|
|
33
|
+
content += "class_name " + class_name_str + "\n"
|
|
34
|
+
|
|
35
|
+
content += "extends " + extends_type + "\n\n"
|
|
36
|
+
content += "func _ready():\n"
|
|
37
|
+
content += "\tpass\n"
|
|
38
|
+
|
|
39
|
+
var file = FileAccess.open(path, FileAccess.WRITE)
|
|
40
|
+
if file == null:
|
|
41
|
+
push_error("Failed to open file for writing: " + path)
|
|
42
|
+
return false
|
|
43
|
+
|
|
44
|
+
file.store_string(content)
|
|
45
|
+
file = null # Close the file
|
|
46
|
+
|
|
47
|
+
return true
|
|
48
|
+
|
|
49
|
+
# Parse a script file and extract its class name and base class
|
|
50
|
+
static func get_script_info(path: String) -> Dictionary:
|
|
51
|
+
var result = {
|
|
52
|
+
"class_name": "",
|
|
53
|
+
"extends": "",
|
|
54
|
+
"path": path
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
var file = FileAccess.open(path, FileAccess.READ)
|
|
58
|
+
if file == null:
|
|
59
|
+
push_error("Failed to open file for reading: " + path)
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
var content = file.get_as_text()
|
|
63
|
+
file = null # Close the file
|
|
64
|
+
|
|
65
|
+
# Find class_name
|
|
66
|
+
var class_name_regex = RegEx.new()
|
|
67
|
+
class_name_regex.compile("class_name\\s+([A-Za-z0-9_]+)")
|
|
68
|
+
var matches = class_name_regex.search(content)
|
|
69
|
+
if matches:
|
|
70
|
+
result["class_name"] = matches.get_string(1)
|
|
71
|
+
|
|
72
|
+
# Find extends
|
|
73
|
+
var extends_regex = RegEx.new()
|
|
74
|
+
extends_regex.compile("extends\\s+([A-Za-z0-9_]+)")
|
|
75
|
+
matches = extends_regex.search(content)
|
|
76
|
+
if matches:
|
|
77
|
+
result["extends"] = matches.get_string(1)
|
|
78
|
+
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
# Extract all method names from a script
|
|
82
|
+
static func get_script_methods(path: String) -> Array:
|
|
83
|
+
var methods = []
|
|
84
|
+
|
|
85
|
+
var file = FileAccess.open(path, FileAccess.READ)
|
|
86
|
+
if file == null:
|
|
87
|
+
push_error("Failed to open file for reading: " + path)
|
|
88
|
+
return methods
|
|
89
|
+
|
|
90
|
+
var content = file.get_as_text()
|
|
91
|
+
file = null # Close the file
|
|
92
|
+
|
|
93
|
+
var method_regex = RegEx.new()
|
|
94
|
+
method_regex.compile("func\\s+([A-Za-z0-9_]+)\\s*\\(")
|
|
95
|
+
|
|
96
|
+
var matches = method_regex.search_all(content)
|
|
97
|
+
for match_idx in range(matches.size()):
|
|
98
|
+
methods.append(matches[match_idx].get_string(1))
|
|
99
|
+
|
|
100
|
+
return methods
|
|
101
|
+
|
|
102
|
+
# Apply a script to a node
|
|
103
|
+
static func apply_script_to_node(node: Node, script_path: String) -> bool:
|
|
104
|
+
if not node:
|
|
105
|
+
push_error("Node is null")
|
|
106
|
+
return false
|
|
107
|
+
|
|
108
|
+
var script = ResourceLoader.load(script_path)
|
|
109
|
+
if not script:
|
|
110
|
+
push_error("Failed to load script: " + script_path)
|
|
111
|
+
return false
|
|
112
|
+
|
|
113
|
+
node.set_script(script)
|
|
114
|
+
return true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://dnj1wd7yiap7t
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPWebSocketServer
|
|
3
|
+
extends Node
|
|
4
|
+
|
|
5
|
+
signal client_connected(id)
|
|
6
|
+
signal client_disconnected(id)
|
|
7
|
+
signal command_received(client_id, command)
|
|
8
|
+
|
|
9
|
+
var tcp_server = TCPServer.new()
|
|
10
|
+
var peers = {}
|
|
11
|
+
var _port = 9080
|
|
12
|
+
|
|
13
|
+
func _ready():
|
|
14
|
+
set_process(false)
|
|
15
|
+
|
|
16
|
+
func _process(_delta):
|
|
17
|
+
poll()
|
|
18
|
+
|
|
19
|
+
func is_server_active() -> bool:
|
|
20
|
+
return tcp_server.is_listening()
|
|
21
|
+
|
|
22
|
+
func start_server() -> int:
|
|
23
|
+
if is_server_active():
|
|
24
|
+
return ERR_ALREADY_IN_USE
|
|
25
|
+
|
|
26
|
+
# Configure TCP server
|
|
27
|
+
var err = tcp_server.listen(_port, "127.0.0.1")
|
|
28
|
+
if err == OK:
|
|
29
|
+
set_process(true)
|
|
30
|
+
print("MCP WebSocket server started on port %d" % _port)
|
|
31
|
+
else:
|
|
32
|
+
print("Failed to start MCP WebSocket server: %d" % err)
|
|
33
|
+
|
|
34
|
+
return err
|
|
35
|
+
|
|
36
|
+
func stop_server() -> void:
|
|
37
|
+
if is_server_active():
|
|
38
|
+
# Close all client connections properly
|
|
39
|
+
for client_id in peers.keys():
|
|
40
|
+
if peers[client_id] != null:
|
|
41
|
+
peers[client_id].close()
|
|
42
|
+
peers.clear()
|
|
43
|
+
|
|
44
|
+
# Stop TCP server
|
|
45
|
+
tcp_server.stop()
|
|
46
|
+
set_process(false)
|
|
47
|
+
print("MCP WebSocket server stopped")
|
|
48
|
+
|
|
49
|
+
func poll() -> void:
|
|
50
|
+
if not tcp_server.is_listening():
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
# Handle new connections
|
|
54
|
+
if tcp_server.is_connection_available():
|
|
55
|
+
var tcp = tcp_server.take_connection()
|
|
56
|
+
if tcp == null:
|
|
57
|
+
print("Failed to take TCP connection")
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
tcp.set_no_delay(true) # Important for WebSocket
|
|
61
|
+
|
|
62
|
+
print("New TCP connection accepted")
|
|
63
|
+
var ws = WebSocketPeer.new()
|
|
64
|
+
|
|
65
|
+
# Configure WebSocket peer
|
|
66
|
+
ws.inbound_buffer_size = 64 * 1024 * 1024 # 64MB buffer
|
|
67
|
+
ws.outbound_buffer_size = 64 * 1024 * 1024 # 64MB buffer
|
|
68
|
+
ws.max_queued_packets = 4096
|
|
69
|
+
|
|
70
|
+
# Accept the stream
|
|
71
|
+
var err = ws.accept_stream(tcp)
|
|
72
|
+
if err != OK:
|
|
73
|
+
print("Failed to accept WebSocket stream: ", err)
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# Generate client ID and store peer
|
|
77
|
+
var client_id = randi() % (1 << 30) + 1
|
|
78
|
+
peers[client_id] = ws
|
|
79
|
+
print("WebSocket connection setup for client: ", client_id)
|
|
80
|
+
|
|
81
|
+
# Process existing connections
|
|
82
|
+
var to_remove = []
|
|
83
|
+
|
|
84
|
+
for client_id in peers:
|
|
85
|
+
var peer = peers[client_id]
|
|
86
|
+
if peer == null:
|
|
87
|
+
to_remove.append(client_id)
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
peer.poll()
|
|
91
|
+
var state = peer.get_ready_state()
|
|
92
|
+
|
|
93
|
+
match state:
|
|
94
|
+
WebSocketPeer.STATE_OPEN:
|
|
95
|
+
# Process any available packets
|
|
96
|
+
while peer.get_available_packet_count() > 0:
|
|
97
|
+
var packet = peer.get_packet()
|
|
98
|
+
_handle_packet(client_id, packet)
|
|
99
|
+
|
|
100
|
+
WebSocketPeer.STATE_CONNECTING:
|
|
101
|
+
print("Client %d still connecting..." % client_id)
|
|
102
|
+
|
|
103
|
+
WebSocketPeer.STATE_CLOSING:
|
|
104
|
+
print("Client %d closing connection..." % client_id)
|
|
105
|
+
|
|
106
|
+
WebSocketPeer.STATE_CLOSED:
|
|
107
|
+
print("Client %d connection closed. Code: %d, Reason: %s" % [
|
|
108
|
+
client_id,
|
|
109
|
+
peer.get_close_code(),
|
|
110
|
+
peer.get_close_reason()
|
|
111
|
+
])
|
|
112
|
+
emit_signal("client_disconnected", client_id)
|
|
113
|
+
to_remove.append(client_id)
|
|
114
|
+
|
|
115
|
+
# Remove disconnected clients
|
|
116
|
+
for client_id in to_remove:
|
|
117
|
+
var peer = peers[client_id]
|
|
118
|
+
if peer != null:
|
|
119
|
+
peer.close()
|
|
120
|
+
peers.erase(client_id)
|
|
121
|
+
|
|
122
|
+
func _handle_packet(client_id: int, packet: PackedByteArray) -> void:
|
|
123
|
+
var text = packet.get_string_from_utf8()
|
|
124
|
+
var json = JSON.new()
|
|
125
|
+
var parse_result = json.parse(text)
|
|
126
|
+
|
|
127
|
+
if parse_result == OK:
|
|
128
|
+
var data = json.get_data()
|
|
129
|
+
|
|
130
|
+
# Handle ping-pong for FastMCP
|
|
131
|
+
if data.has("method") and data["method"] == "ping":
|
|
132
|
+
var response = {
|
|
133
|
+
"jsonrpc": "2.0",
|
|
134
|
+
"id": data.get("id", 0),
|
|
135
|
+
"result": "pong"
|
|
136
|
+
}
|
|
137
|
+
send_response(client_id, response)
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
print("Received command from client %d: %s" % [client_id, data])
|
|
141
|
+
emit_signal("command_received", client_id, data)
|
|
142
|
+
else:
|
|
143
|
+
print("Error parsing JSON from client %d: %s at line %d" %
|
|
144
|
+
[client_id, json.get_error_message(), json.get_error_line()])
|
|
145
|
+
|
|
146
|
+
func send_response(client_id: int, response: Dictionary) -> int:
|
|
147
|
+
if not peers.has(client_id):
|
|
148
|
+
print("Error: Client %d not found" % client_id)
|
|
149
|
+
return ERR_DOES_NOT_EXIST
|
|
150
|
+
|
|
151
|
+
var peer = peers[client_id]
|
|
152
|
+
if peer == null:
|
|
153
|
+
print("Error: Peer is null for client %d" % client_id)
|
|
154
|
+
return ERR_INVALID_PARAMETER
|
|
155
|
+
|
|
156
|
+
if peer.get_ready_state() != WebSocketPeer.STATE_OPEN:
|
|
157
|
+
print("Error: Client %d connection not open" % client_id)
|
|
158
|
+
return ERR_UNAVAILABLE
|
|
159
|
+
|
|
160
|
+
var json_text = JSON.stringify(response)
|
|
161
|
+
var result = peer.send_text(json_text)
|
|
162
|
+
|
|
163
|
+
if result != OK:
|
|
164
|
+
print("Error sending response to client %d: %d" % [client_id, result])
|
|
165
|
+
|
|
166
|
+
return result
|
|
167
|
+
|
|
168
|
+
func send_event(client_id: int, event: Dictionary) -> int:
|
|
169
|
+
if not peers.has(client_id):
|
|
170
|
+
return ERR_DOES_NOT_EXIST
|
|
171
|
+
|
|
172
|
+
var peer = peers[client_id]
|
|
173
|
+
if peer == null or peer.get_ready_state() != WebSocketPeer.STATE_OPEN:
|
|
174
|
+
return ERR_UNAVAILABLE
|
|
175
|
+
|
|
176
|
+
var payload := event.duplicate(true)
|
|
177
|
+
if not payload.has("event"):
|
|
178
|
+
payload["event"] = "unknown"
|
|
179
|
+
|
|
180
|
+
var json_text = JSON.stringify(payload)
|
|
181
|
+
return peer.send_text(json_text)
|
|
182
|
+
|
|
183
|
+
func broadcast_event(event: Dictionary) -> void:
|
|
184
|
+
for client_id in peers.keys():
|
|
185
|
+
send_event(client_id, event)
|
|
186
|
+
|
|
187
|
+
func set_port(new_port: int) -> void:
|
|
188
|
+
if is_server_active():
|
|
189
|
+
push_error("Cannot change port while server is active")
|
|
190
|
+
return
|
|
191
|
+
_port = new_port
|
|
192
|
+
|
|
193
|
+
func get_port() -> int:
|
|
194
|
+
return _port
|
|
195
|
+
|
|
196
|
+
func get_client_count() -> int:
|
|
197
|
+
return peers.size()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://5yoxi2gkygu4
|
package/dist/cli.d.ts
ADDED