diff --git a/_Core/managers/ChatManager.gd b/_Core/managers/ChatManager.gd index b9e5da2..9ad2c19 100644 --- a/_Core/managers/ChatManager.gd +++ b/_Core/managers/ChatManager.gd @@ -62,9 +62,8 @@ signal chat_position_updated(stream: String, topic: String) # 常量定义 # ============================================================================ -# WebSocket 服务器 URL -# 添加 Socket.IO 握手参数(EIO=4 表示 Engine.IO 版本,transport=websocket) -const WEBSOCKET_URL: String = "wss://whaletownend.xinghangee.icu/game/socket.io/?EIO=4&transport=websocket" +# WebSocket 服务器 URL(原生 WebSocket) +const WEBSOCKET_URL: String = "wss://whaletownend.xinghangee.icu/game" # 重连配置 const RECONNECT_MAX_ATTEMPTS: int = 5 @@ -102,9 +101,6 @@ const CHAT_ERROR_MESSAGES: Dictionary = { # WebSocket 管理器 var _websocket_manager: WebSocketManager -# Socket.IO 客户端 -var _socket_client: SocketIOClient - # 是否已登录 var _is_logged_in: bool = false @@ -138,9 +134,6 @@ func _ready() -> void: _websocket_manager = WebSocketManager.new() add_child(_websocket_manager) - # 获取 Socket.IO 客户端引用 - _socket_client = _websocket_manager.get_socket_client() - # 连接信号 _connect_signals() @@ -191,7 +184,7 @@ func disconnect_from_chat_server() -> void: # 发送登出消息 if _is_logged_in: var logout_data := {"type": "logout"} - _socket_client.emit("logout", logout_data) + _websocket_manager.send_message(JSON.stringify(logout_data)) _is_logged_in = false # 断开连接 @@ -245,8 +238,9 @@ func send_chat_message(content: String, scope: String = "local") -> void: "scope": scope } - # 发送消息 - _socket_client.emit("chat", message_data) + # 发送消息(JSON 字符串) + var json_string := JSON.stringify(message_data) + _websocket_manager.send_message(json_string) # 记录发送时间 _record_message_timestamp() @@ -261,6 +255,21 @@ func send_chat_message(content: String, scope: String = "local") -> void: print("📤 发送聊天消息: ", content) +# 消息发送完成回调 +func _on_chat_message_sent(request_id: String, success: bool, data: Dictionary, error_info: Dictionary) -> void: + if success: + print("✅ 消息发送成功: ", data) + var message_id: String = str(data.get("data", {}).get("id", "")) + var timestamp: float = Time.get_unix_time_from_system() + chat_message_sent.emit(message_id, timestamp) + EventSystem.emit_event(EventNames.CHAT_MESSAGE_SENT, { + "message_id": message_id, + "timestamp": timestamp + }) + else: + print("❌ 消息发送失败: ", error_info) + _handle_error("SEND_FAILED", error_info.get("message", "发送失败")) + # 更新玩家位置 # # 参数: @@ -281,7 +290,10 @@ func update_player_position(x: float, y: float, map_id: String) -> void: "mapId": map_id } - _socket_client.emit("position", position_data) + # 发送消息(JSON 字符串) + var json_string := JSON.stringify(position_data) + _websocket_manager.send_message(json_string) + print("📍 更新位置: (%.2f, %.2f) in %s" % [x, y, map_id]) # ============================================================================ @@ -435,28 +447,21 @@ func _on_history_loaded(messages: Array) -> void: func _connect_signals() -> void: # WebSocket 管理器信号 _websocket_manager.connection_state_changed.connect(_on_connection_state_changed) + _websocket_manager.data_received.connect(_on_data_received) - # Socket.IO 客户端信号 - _socket_client.connected.connect(_on_socket_connected) - _socket_client.disconnected.connect(_on_socket_disconnected) - _socket_client.event_received.connect(_on_socket_event_received) - _socket_client.error_occurred.connect(_on_socket_error) - -# ============================================================================ -# 内部方法 - 连接状态处理 -# ============================================================================ - -# Socket 连接成功 -func _on_socket_connected() -> void: - print("✅ ChatManager: Socket 连接成功") - - # 发送登录消息 - _send_login_message() - -# Socket 连接断开 -func _on_socket_disconnected(_clean_close: bool) -> void: - print("🔌 ChatManager: Socket 连接断开") - _is_logged_in = false +# 发送登录消息 +func _send_login_message() -> void: + print("📤 发送登录消息...") + + var login_data := { + "type": "login", + "token": _game_token + } + + var json_string := JSON.stringify(login_data) + _websocket_manager.send_message(json_string) + + print(" Token: ", _game_token.left(20) + "..." if _game_token.length() > 20 else _game_token) # 连接状态变化 func _on_connection_state_changed(state: int) -> void: @@ -471,25 +476,45 @@ func _on_connection_state_changed(state: int) -> void: "state": state }) + # 如果连接成功,发送登录消息 + if state == 2: # CONNECTED + _send_login_message() + # ============================================================================ # 内部方法 - 消息处理 # ============================================================================ -# Socket 事件接收 -func _on_socket_event_received(event_name: String, data: Dictionary) -> void: - match event_name: +# WebSocket 数据接收 +func _on_data_received(message: String) -> void: + # 解析 JSON 消息 + var json := JSON.new() + var parse_result := json.parse(message) + + if parse_result != OK: + print("❌ ChatManager: JSON 解析失败 - ", message) + return + + var data: Dictionary = json.data + + # 检查消息类型字段 + var message_type: String = data.get("t", "") + + match message_type: "login_success": _handle_login_success(data) + "login_error": + _handle_login_error(data) "chat_sent": _handle_chat_sent(data) + "chat_error": + _handle_chat_error(data) "chat_render": _handle_chat_render(data) "position_updated": _handle_position_updated(data) - "error": - _handle_error_response(data) _: - print("⚠️ ChatManager: 未处理的事件 - ", event_name) + print("⚠️ ChatManager: 未处理的消息类型 - ", message_type) + print(" 消息内容: ", data) # 处理登录成功 func _handle_login_success(data: Dictionary) -> void: @@ -511,9 +536,23 @@ func _handle_login_success(data: Dictionary) -> void: "current_map": _current_map }) +# 处理登录失败 +func _handle_login_error(data: Dictionary) -> void: + var error_message: String = data.get("message", "登录失败") + + print("❌ ChatManager: 登录失败 - ", error_message) + + _is_logged_in = false + + # 通过 EventSystem 广播错误(Signal Up) + EventSystem.emit_event(EventNames.CHAT_LOGIN_FAILED, { + "error_code": "LOGIN_FAILED", + "message": error_message + }) + # 处理聊天消息发送成功 func _handle_chat_sent(data: Dictionary) -> void: - var message_id: String = data.get("messageId", "") + var message_id: String = str(data.get("messageId", "")) var timestamp: float = data.get("timestamp", 0.0) print("✅ 消息发送成功: ", message_id) @@ -527,6 +566,18 @@ func _handle_chat_sent(data: Dictionary) -> void: "timestamp": timestamp }) +# 处理聊天消息发送失败 +func _handle_chat_error(data: Dictionary) -> void: + var error_message: String = data.get("message", "消息发送失败") + + print("❌ ChatManager: 聊天错误 - ", error_message) + + # 通过 EventSystem 广播错误(Signal Up) + EventSystem.emit_event(EventNames.CHAT_ERROR_OCCURRED, { + "error_code": "CHAT_SEND_FAILED", + "message": error_message + }) + # 处理接收到的聊天消息 func _handle_chat_render(data: Dictionary) -> void: var from_user: String = data.get("from", "") @@ -571,14 +622,14 @@ func _handle_position_updated(data: Dictionary) -> void: "topic": topic }) -# 处理错误响应 +# 处理错误响应(如果需要) func _handle_error_response(data: Dictionary) -> void: var error_code: String = data.get("code", "") var error_message: String = data.get("message", "") _handle_error(error_code, error_message) -# 处理 Socket 错误 +# 处理 Socket 错误(如果需要) func _on_socket_error(error: String) -> void: _handle_error("SOCKET_ERROR", error) @@ -586,21 +637,6 @@ func _on_socket_error(error: String) -> void: # 内部方法 - 工具函数 # ============================================================================ -# 发送登录消息 -func _send_login_message() -> void: - if _game_token.is_empty(): - push_error("无法获取 access token,请确保已登录") - _handle_error("AUTH_FAILED", "无法获取 access token,请先登录") - return - - var login_data := { - "type": "login", - "token": _game_token - } - - _socket_client.emit("login", login_data) - print("📤 发送登录消息(使用 access token)") - # 处理错误 func _handle_error(error_code: String, error_message: String) -> void: print("❌ ChatManager 错误: [", error_code, "] ", error_message) diff --git a/_Core/managers/WebSocketManager.gd b/_Core/managers/WebSocketManager.gd index 67062fe..952b837 100644 --- a/_Core/managers/WebSocketManager.gd +++ b/_Core/managers/WebSocketManager.gd @@ -1,7 +1,7 @@ extends Node # ============================================================================ -# WebSocketManager.gd - WebSocket 连接生命周期管理 +# WebSocketManager.gd - WebSocket 连接生命周期管理(原生 WebSocket 版本) # ============================================================================ # 管理 WebSocket 连接状态、自动重连和错误恢复 # @@ -9,8 +9,8 @@ extends Node # - 连接状态管理(断开、连接中、已连接、重连中) # - 自动重连机制(指数退避) # - 连接错误恢复 -# - Socket.IO 客户端封装 -# +# - WebSocket 消息发送/接收 +# ============================================================================ # 使用方式: # WebSocketManager.connect_to_game_server() # WebSocketManager.connection_state_changed.connect(_on_state_changed) @@ -44,6 +44,11 @@ signal reconnection_succeeded() # max_attempts: int - 最大重连次数 signal reconnection_failed(attempt: int, max_attempts: int) +# WebSocket 消息接收信号 +# 参数: +# message: String - 接收到的消息内容(JSON 字符串) +signal data_received(message: String) + # ============================================================================ # 枚举定义 # ============================================================================ @@ -61,9 +66,8 @@ enum ConnectionState { # 常量定义 # ============================================================================ -# WebSocket 服务器 URL -# 添加 Socket.IO 握手参数(EIO=4 表示 Engine.IO 版本,transport=websocket) -const WEBSOCKET_URL: String = "wss://whaletownend.xinghangee.icu/game/socket.io/?EIO=4&transport=websocket" +# WebSocket 服务器 URL(原生 WebSocket) +const WEBSOCKET_URL: String = "wss://whaletownend.xinghangee.icu/game" # 默认最大重连次数 const DEFAULT_MAX_RECONNECT_ATTEMPTS: int = 5 @@ -78,8 +82,8 @@ const MAX_RECONNECT_DELAY: float = 30.0 # 成员变量 # ============================================================================ -# Socket.IO 客户端 -var _socket_client: SocketIOClient +# WebSocket peer +var _websocket_peer: WebSocketPeer = WebSocketPeer.new() # 当前连接状态 var _connection_state: ConnectionState = ConnectionState.DISCONNECTED @@ -102,6 +106,12 @@ var _reconnect_timer: Timer = Timer.new() # 是否为正常关闭(非异常断开) var _clean_close: bool = true +# 心跳定时器 +var _heartbeat_timer: Timer = Timer.new() + +# 心跳间隔(秒) +const HEARTBEAT_INTERVAL: float = 30.0 + # ============================================================================ # 生命周期方法 # ============================================================================ @@ -110,24 +120,54 @@ var _clean_close: bool = true func _ready() -> void: print("WebSocketManager 初始化完成") - # 创建 Socket.IO 客户端 - _socket_client = SocketIOClient.new() - add_child(_socket_client) - - # 连接信号 - _socket_client.connected.connect(_on_socket_connected) - _socket_client.disconnected.connect(_on_socket_disconnected) - _socket_client.error_occurred.connect(_on_socket_error) - # 设置重连定时器 _setup_reconnect_timer() + # 设置心跳定时器 + _setup_heartbeat_timer() + + # 启动处理循环 + set_process(true) + +# 处理每帧 +func _process(_delta: float) -> void: + # 检查 WebSocket 状态变化 + _check_websocket_state() + + var state: WebSocketPeer.State = _websocket_peer.get_ready_state() + + # 调试:打印状态变化 + if _connection_state == ConnectionState.CONNECTING: + var peer_state_name = ["DISCONNECTED", "CONNECTING", "OPEN", "CLOSING", "CLOSED"][state] + print("📡 WebSocket 状态: peer=%s, manager=%s" % [peer_state_name, ConnectionState.keys()[_connection_state]]) + + if state == WebSocketPeer.STATE_OPEN: + # 接收数据 + _websocket_peer.poll() + + # 处理收到的数据 + while _websocket_peer.get_available_packet_count() > 0: + var packet: PackedByteArray = _websocket_peer.get_packet() + var message: String = packet.get_string_from_utf8() + + # 发射消息接收信号 + data_received.emit(message) + + # 打印调试信息 + print("📨 WebSocket 收到消息: ", message) + # 清理 func _exit_tree() -> void: + _disconnect() + if is_instance_valid(_reconnect_timer): _reconnect_timer.stop() _reconnect_timer.queue_free() + if is_instance_valid(_heartbeat_timer): + _heartbeat_timer.stop() + _heartbeat_timer.queue_free() + # ============================================================================ # 公共 API - 连接管理 # ============================================================================ @@ -139,22 +179,41 @@ func connect_to_game_server() -> void: return print("=== WebSocketManager 开始连接 ===") + print("服务器 URL: ", WEBSOCKET_URL) + print("WebSocket 连接中...") + _set_connection_state(ConnectionState.CONNECTING) _clean_close = true _reconnect_attempt = 0 - _socket_client.connect_to_server(WEBSOCKET_URL) + var err: Error = _websocket_peer.connect_to_url(WEBSOCKET_URL) + if err != OK: + print("❌ WebSocket 连接失败: ", error_string(err)) + _set_connection_state(ConnectionState.ERROR) + return + + # 启动心跳 + _start_heartbeat() # 断开 WebSocket 连接 func disconnect_websocket() -> void: print("=== WebSocketManager 断开连接 ===") + _disconnect() + +# 断开连接(内部方法) +func _disconnect() -> void: _clean_close = true # 停止重连定时器 _reconnect_timer.stop() - # 断开客户端 - _socket_client.disconnect_from_server() + # 停止心跳 + _heartbeat_timer.stop() + + # 关闭 WebSocket + if _websocket_peer.get_ready_state() == WebSocketPeer.STATE_OPEN: + _websocket_peer.close() + _set_connection_state(ConnectionState.DISCONNECTED) # 检查 WebSocket 是否已连接 @@ -171,6 +230,30 @@ func is_websocket_connected() -> bool: func get_connection_state() -> ConnectionState: return _connection_state +# ============================================================================ +# 公共 API - 消息发送 +# ============================================================================ + +# 发送 WebSocket 消息 +# +# 参数: +# message: String - 要发送的消息内容(JSON 字符串) +# +# 返回值: +# Error - 错误码,OK 表示成功 +func send_message(message: String) -> Error: + if _websocket_peer.get_ready_state() != WebSocketPeer.STATE_OPEN: + print("❌ WebSocket 未连接,无法发送消息") + return ERR_UNCONFIGURED + + var err: Error = _websocket_peer.send_text(message) + if err != OK: + print("❌ WebSocket 发送消息失败: ", error_string(err)) + return err + + print("📤 发送 WebSocket 消息: ", message) + return OK + # ============================================================================ # 公共 API - 自动重连 # ============================================================================ @@ -193,16 +276,17 @@ func enable_auto_reconnect(enabled: bool, max_attempts: int = DEFAULT_MAX_RECONN print("最大重连次数: ", _max_reconnect_attempts) print("基础重连延迟: ", _reconnect_base_delay, " 秒") -# 获取 Socket.IO 客户端 +# 获取重连信息 # # 返回值: -# SocketIOClient - Socket.IO 客户端实例 -# -# 使用示例: -# var socket = WebSocketManager.get_socket_client() -# socket.emit("chat", {"t": "chat", "content": "Hello"}) -func get_socket_client() -> SocketIOClient: - return _socket_client +# Dictionary - 重连信息 {enabled, attempt, max_attempts, delay} +func get_reconnect_info() -> Dictionary: + return { + "enabled": _auto_reconnect_enabled, + "attempt": _reconnect_attempt, + "max_attempts": _max_reconnect_attempts, + "next_delay": _calculate_reconnect_delay() if _connection_state == ConnectionState.RECONNECTING else 0.0 + } # ============================================================================ # 内部方法 - 连接状态管理 @@ -220,12 +304,41 @@ func _set_connection_state(new_state: ConnectionState) -> void: connection_state_changed.emit(new_state) # ============================================================================ -# 内部方法 - Socket 事件处理 +# 内部方法 - WebSocket 状态监控 # ============================================================================ -# Socket 连接成功处理 -func _on_socket_connected() -> void: - print("✅ WebSocketManager: Socket 连接成功") +# 检查 WebSocket 状态变化 +func _check_websocket_state() -> void: + # 必须先 poll 才能获取最新状态 + _websocket_peer.poll() + + var state: WebSocketPeer.State = _websocket_peer.get_ready_state() + + match state: + WebSocketPeer.STATE_CONNECTING: + # 正在连接 + if _connection_state != ConnectionState.CONNECTING and _connection_state != ConnectionState.RECONNECTING: + _set_connection_state(ConnectionState.CONNECTING) + + WebSocketPeer.STATE_OPEN: + # 连接成功 + if _connection_state != ConnectionState.CONNECTED: + _on_websocket_connected() + + WebSocketPeer.STATE_CLOSING: + # 正在关闭 + pass + + WebSocketPeer.STATE_CLOSED: + # 连接关闭 + var code: int = _websocket_peer.get_close_code() + var reason: String = _websocket_peer.get_close_reason() + print("🔌 WebSocket 关闭: code=%d, reason=%s" % [code, reason]) + _on_websocket_closed(code != 0) # code=0 表示正常关闭 + +# WebSocket 连接成功处理 +func _on_websocket_connected() -> void: + print("✅ WebSocketManager: WebSocket 连接成功") # 如果是重连,发射重连成功信号 if _connection_state == ConnectionState.RECONNECTING: @@ -235,9 +348,9 @@ func _on_socket_connected() -> void: _set_connection_state(ConnectionState.CONNECTED) -# Socket 连接断开处理 -func _on_socket_disconnected(clean_close: bool) -> void: - print("🔌 WebSocketManager: Socket 连接断开") +# WebSocket 连接关闭处理 +func _on_websocket_closed(clean_close: bool) -> void: + print("🔌 WebSocketManager: WebSocket 连接断开") print(" 正常关闭: ", clean_close) _clean_close = clean_close @@ -249,11 +362,6 @@ func _on_socket_disconnected(clean_close: bool) -> void: else: _set_connection_state(ConnectionState.DISCONNECTED) -# Socket 错误处理 -func _on_socket_error(error: String) -> void: - print("❌ WebSocketManager: Socket 错误 - ", error) - _set_connection_state(ConnectionState.ERROR) - # ============================================================================ # 内部方法 - 重连机制 # ============================================================================ @@ -280,7 +388,7 @@ func _attempt_reconnect() -> void: _set_connection_state(ConnectionState.RECONNECTING) # 计算重连延迟(指数退避) - var delay := _calculate_reconnect_delay() + var delay: float = _calculate_reconnect_delay() print("🔄 尝试重连 (", _reconnect_attempt, "/", _max_reconnect_attempts, ")") print(" 延迟: ", delay, " 秒") @@ -298,7 +406,36 @@ func _calculate_reconnect_delay() -> float: # 重连定时器超时处理 func _on_reconnect_timeout() -> void: print("⏰ 重连定时器超时,开始重连...") - _socket_client.connect_to_server(WEBSOCKET_URL) + _clean_close = false + connect_to_game_server() + +# ============================================================================ +# 内部方法 - 心跳机制 +# ============================================================================ + +# 设置心跳定时器 +func _setup_heartbeat_timer() -> void: + _heartbeat_timer = Timer.new() + _heartbeat_timer.wait_time = HEARTBEAT_INTERVAL + _heartbeat_timer.one_shot = false + _heartbeat_timer.autostart = false + add_child(_heartbeat_timer) + + _heartbeat_timer.timeout.connect(_on_heartbeat) + +# 启动心跳 +func _start_heartbeat() -> void: + _heartbeat_timer.start() + +# 停止心跳 +func _stop_heartbeat() -> void: + _heartbeat_timer.stop() + +# 心跳超时处理 +func _on_heartbeat() -> void: + # 不发送心跳,避免服务器返回 "消息格式错误" + # 如果需要心跳,服务器应该支持特定格式 + pass # ============================================================================ # 工具方法 @@ -322,15 +459,3 @@ func get_state_description() -> String: return "错误" _: return "未知状态" - -# 获取重连信息 -# -# 返回值: -# Dictionary - 重连信息 {enabled, attempt, max_attempts, delay} -func get_reconnect_info() -> Dictionary: - return { - "enabled": _auto_reconnect_enabled, - "attempt": _reconnect_attempt, - "max_attempts": _max_reconnect_attempts, - "next_delay": _calculate_reconnect_delay() if _connection_state == ConnectionState.RECONNECTING else 0.0 - }