153 lines
3.4 KiB
GDScript
153 lines
3.4 KiB
GDScript
extends Node
|
|
|
|
# Bridge 客户端 - 连接 Godot 和 OpenClaw
|
|
|
|
signal action_received(action: Dictionary)
|
|
signal state_synced(state: Dictionary)
|
|
signal connected_to_bridge()
|
|
signal disconnected_from_bridge()
|
|
|
|
@export var bridge_url: String = "ws://127.0.0.1:8765"
|
|
@export var reconnect_interval: float = 5.0
|
|
@export var auto_connect: bool = false # 默认不自动连接
|
|
|
|
var socket: WebSocketPeer
|
|
var is_connected: bool = false
|
|
var reconnect_timer: float = 0.0
|
|
|
|
func _ready():
|
|
# 作为全局单例
|
|
name = "BridgeClient"
|
|
print("BridgeClient initialized, auto_connect = ", auto_connect)
|
|
|
|
if auto_connect:
|
|
_connect_to_bridge()
|
|
|
|
func _process(delta):
|
|
if socket:
|
|
_socket_poll()
|
|
elif reconnect_timer > 0:
|
|
reconnect_timer -= delta
|
|
if reconnect_timer <= 0:
|
|
_connect_to_bridge()
|
|
|
|
func _connect_to_bridge():
|
|
print("Connecting to OpenClaw Bridge at ", bridge_url)
|
|
|
|
socket = WebSocketPeer.new()
|
|
var err = socket.connect_to_url(bridge_url)
|
|
|
|
if err != OK:
|
|
print("Failed to connect: ", err)
|
|
_schedule_reconnect()
|
|
return
|
|
|
|
# 设置状态
|
|
is_connected = false
|
|
|
|
func _socket_poll():
|
|
socket.poll()
|
|
var state = socket.get_ready_state()
|
|
|
|
match state:
|
|
WebSocketPeer.STATE_CONNECTING:
|
|
pass # 正在连接
|
|
|
|
WebSocketPeer.STATE_OPEN:
|
|
if not is_connected:
|
|
is_connected = true
|
|
print("Connected to OpenClaw Bridge!")
|
|
emit_signal("connected_to_bridge")
|
|
_send_hello()
|
|
|
|
# 处理收到的消息
|
|
while socket.get_available_packets() > 0:
|
|
var packet = socket.get_packet()
|
|
var message_str = packet.get_string_from_utf8()
|
|
_parse_message(message_str)
|
|
|
|
WebSocketPeer.STATE_CLOSING:
|
|
pass # 正在关闭
|
|
|
|
WebSocketPeer.STATE_CLOSED:
|
|
if is_connected:
|
|
is_connected = false
|
|
print("Disconnected from Bridge")
|
|
emit_signal("disconnected_from_bridge")
|
|
socket = null
|
|
_schedule_reconnect()
|
|
|
|
func _schedule_reconnect():
|
|
reconnect_timer = reconnect_interval
|
|
print("Will reconnect in ", reconnect_interval, " seconds...")
|
|
|
|
func _send_hello():
|
|
send_command({
|
|
"type": "hello",
|
|
"client": "godot",
|
|
"agent_id": "val",
|
|
"timestamp": Time.get_unix_time_from_system()
|
|
})
|
|
|
|
func _parse_message(message_str: String):
|
|
var json = JSON.new()
|
|
var err = json.parse(message_str)
|
|
|
|
if err != OK:
|
|
print("Failed to parse JSON: ", message_str)
|
|
return
|
|
|
|
var message = json.get_data()
|
|
|
|
if typeof(message) != TYPE_DICTIONARY:
|
|
print("Invalid message format")
|
|
return
|
|
|
|
var msg_type = message.get("type", "")
|
|
|
|
match msg_type:
|
|
"action_sequence":
|
|
# 解析动作序列
|
|
var actions = message.get("actions", [])
|
|
for action in actions:
|
|
emit_signal("action_received", action)
|
|
|
|
"state_sync":
|
|
# 同步Agent状态
|
|
emit_signal("state_synced", message.get("state", {}))
|
|
|
|
"heartbeat":
|
|
# 响应心跳
|
|
send_command({"type": "heartbeat_ack", "timestamp": Time.get_unix_time_from_system()})
|
|
|
|
"error":
|
|
print("Bridge error: ", message.get("error", "Unknown error"))
|
|
|
|
_:
|
|
print("Unknown message type: ", msg_type)
|
|
|
|
func send_command(command: Dictionary) -> bool:
|
|
if not is_connected or not socket:
|
|
# 静默处理,不打印警告
|
|
return false
|
|
|
|
var json_str = JSON.stringify(command)
|
|
var err = socket.send_text(json_str)
|
|
|
|
if err != OK:
|
|
print("Failed to send command: ", err)
|
|
return false
|
|
|
|
return true
|
|
|
|
func request_state_sync():
|
|
send_command({
|
|
"type": "request_state_sync",
|
|
"agent_id": "val"
|
|
})
|
|
|
|
func close_connection():
|
|
if socket:
|
|
socket.close()
|
|
is_connected = false
|