extends Node # ============================================================================ # LocationManager.gd - 位置同步管理器 # ============================================================================ # 负责与后端 WebSocket 服务进行位置同步和多人会话管理 # # 协议文档: new_docs/game_architecture_design.md # 后端地址: wss://whaletownend.xinghangee.icu/location-broadcast # ============================================================================ signal connected_to_server() signal connection_closed() signal connection_error() signal session_joined(data: Dictionary) signal user_joined(data: Dictionary) signal user_left(data: Dictionary) signal position_updated(data: Dictionary) const WS_URL = "wss://whaletownend.xinghangee.icu/location-broadcast" const PING_INTERVAL = 25.0 # 秒 var _socket: WebSocketPeer var _connected: bool = false var _ping_timer: float = 0.0 var _auth_token: String = "" func _ready(): _socket = WebSocketPeer.new() process_mode = Node.PROCESS_MODE_ALWAYS # 保证暂停时也能处理网络 func _process(delta): _socket.poll() var state = _socket.get_ready_state() if state == WebSocketPeer.STATE_OPEN: if not _connected: _on_connected() # 处理接收到的数据包 while _socket.get_available_packet_count() > 0: var packet = _socket.get_packet() _handle_packet(packet) # 心跳处理 _ping_timer += delta if _ping_timer >= PING_INTERVAL: _send_heartbeat() _ping_timer = 0.0 elif state == WebSocketPeer.STATE_CLOSED: if _connected: _on_disconnected() func connect_to_server(): if _socket.get_ready_state() == WebSocketPeer.STATE_OPEN: return var err = _socket.connect_to_url(WS_URL) if err != OK: push_error("LocationManager: WebSocket 连接请求失败,错误码: %d" % err) connection_error.emit() else: # Godot WebSocket connect is non-blocking, wait for state change in _process pass func close_connection(): _socket.close() func set_auth_token(token: String): _auth_token = token # ============ 协议发送 ============ func send_packet(event: String, data: Dictionary): if _socket.get_ready_state() != WebSocketPeer.STATE_OPEN: return var message = { "event": event, "data": data } var json_str = JSON.stringify(message) _socket.put_packet(json_str.to_utf8_buffer()) func join_session(map_id: String, initial_pos: Vector2): var data = { "sessionId": map_id, "initialPosition": { "x": initial_pos.x, "y": initial_pos.y, "mapId": map_id }, "token": _auth_token } send_packet("join_session", data) func leave_session(map_id: String): send_packet("leave_session", {"sessionId": map_id}) func send_position_update(map_id: String, pos: Vector2, anim_data: Dictionary = {}): var data = { "x": pos.x, "y": pos.y, "mapId": map_id, "metadata": anim_data } if map_id == "": push_warning("LocationManager: position_update 的 map_id 为空") send_packet("position_update", data) func _send_heartbeat(): send_packet("heartbeat", {"timestamp": Time.get_unix_time_from_system()}) # ============ 事件处理 ============ func _on_connected(): _connected = true connected_to_server.emit() func _on_disconnected(): _connected = false connection_closed.emit() func _handle_packet(packet: PackedByteArray): var json_str = packet.get_string_from_utf8() var json = JSON.new() var err = json.parse(json_str) if err != OK: push_error("LocationManager: JSON 解析失败 - %s" % json.get_error_message()) return var message = json.data if not message is Dictionary or not message.has("event"): return var event = message["event"] var data = message.get("data", {}) match event: "session_joined": session_joined.emit(data) "user_joined": user_joined.emit(data) "user_left": user_left.emit(data) "position_update": position_updated.emit(data) "heartbeat_response": pass # 静默处理 "error": push_error("LocationManager: WebSocket 错误事件 - %s" % JSON.stringify(data)) _: push_warning("LocationManager: 未处理的 WebSocket 事件 %s" % event)