112 lines
3 KiB
GDScript
112 lines
3 KiB
GDScript
@tool
|
|
extends Node
|
|
## WebSocket server for AI Bridge
|
|
|
|
signal message_received(peer_id: int, message: String)
|
|
signal client_connected(peer_id: int)
|
|
signal client_disconnected(peer_id: int)
|
|
|
|
var port: int = 6550
|
|
var pairing_token: String = ""
|
|
const PROTOCOL_PREFIX := "godot-ai-bridge."
|
|
|
|
var _tcp_server: TCPServer
|
|
var _ws_peers: Dictionary = {} # peer_id -> {peer: WebSocketPeer, stream: StreamPeerTCP}
|
|
var _timer: Timer
|
|
|
|
|
|
func _ready() -> void:
|
|
pass
|
|
|
|
|
|
func start() -> Error:
|
|
if pairing_token.length() != 64:
|
|
return ERR_UNAUTHORIZED
|
|
_tcp_server = TCPServer.new()
|
|
var err = _tcp_server.listen(port, "127.0.0.1")
|
|
if err != OK:
|
|
return err
|
|
|
|
_timer = Timer.new()
|
|
_timer.wait_time = 0.016
|
|
_timer.timeout.connect(_poll)
|
|
add_child(_timer)
|
|
_timer.start()
|
|
|
|
print("[AI Bridge] Timer started for polling")
|
|
return OK
|
|
|
|
|
|
func stop() -> void:
|
|
if _timer:
|
|
_timer.stop()
|
|
_timer.queue_free()
|
|
_timer = null
|
|
|
|
for peer_id in _ws_peers.keys():
|
|
_ws_peers[peer_id]["peer"].close()
|
|
_ws_peers.clear()
|
|
|
|
if _tcp_server:
|
|
_tcp_server.stop()
|
|
_tcp_server = null
|
|
|
|
|
|
func _poll() -> void:
|
|
# Accept new TCP connections and immediately create WebSocketPeer
|
|
while _tcp_server and _tcp_server.is_connection_available():
|
|
var stream = _tcp_server.take_connection()
|
|
if stream:
|
|
print("[AI Bridge] New TCP connection!")
|
|
var ws_peer = WebSocketPeer.new()
|
|
ws_peer.supported_protocols = PackedStringArray([PROTOCOL_PREFIX + pairing_token])
|
|
# Let WebSocketPeer handle the handshake itself
|
|
var err = ws_peer.accept_stream(stream)
|
|
if err == OK:
|
|
var peer_id = stream.get_instance_id()
|
|
_ws_peers[peer_id] = {"peer": ws_peer, "stream": stream, "connected": false}
|
|
print("[AI Bridge] WebSocket peer created, waiting for handshake...")
|
|
else:
|
|
print("[AI Bridge] Failed to accept stream: ", err)
|
|
|
|
# Process WebSocket peers
|
|
var to_remove: Array = []
|
|
for peer_id in _ws_peers.keys():
|
|
var data = _ws_peers[peer_id]
|
|
var peer: WebSocketPeer = data["peer"]
|
|
|
|
peer.poll()
|
|
var state = peer.get_ready_state()
|
|
|
|
if state == WebSocketPeer.STATE_OPEN:
|
|
if peer.get_selected_protocol() != PROTOCOL_PREFIX + pairing_token:
|
|
peer.close(1008, "Pairing required")
|
|
continue
|
|
if not data["connected"]:
|
|
data["connected"] = true
|
|
print("[AI Bridge] Client connected: ", peer_id)
|
|
client_connected.emit(peer_id)
|
|
|
|
while peer.get_available_packet_count() > 0:
|
|
var packet = peer.get_packet()
|
|
var msg = packet.get_string_from_utf8()
|
|
message_received.emit(peer_id, msg)
|
|
|
|
elif state == WebSocketPeer.STATE_CLOSED:
|
|
print("[AI Bridge] Client disconnected: ", peer_id)
|
|
to_remove.append(peer_id)
|
|
client_disconnected.emit(peer_id)
|
|
|
|
for peer_id in to_remove:
|
|
_ws_peers.erase(peer_id)
|
|
|
|
|
|
func send_message(peer_id: int, message: String) -> Error:
|
|
if not _ws_peers.has(peer_id):
|
|
return ERR_DOES_NOT_EXIST
|
|
|
|
var peer: WebSocketPeer = _ws_peers[peer_id]["peer"]
|
|
if peer.get_ready_state() != WebSocketPeer.STATE_OPEN:
|
|
return ERR_CONNECTION_ERROR
|
|
|
|
return peer.send_text(message)
|