Files
whale-town-front/_Core/managers/WebSocketManager.gd
王浩 136e1344a0 fix(chat): 修复WebSocket连接和消息格式
- 修复连接状态检测时机问题
- 修复聊天消息格式为 {t: chat, content, scope}
- 添加 _send_login_message 函数
- 移除空消息心跳避免服务器错误
2026-01-09 23:21:12 +08:00

462 lines
14 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Node
# ============================================================================
# WebSocketManager.gd - WebSocket 连接生命周期管理(原生 WebSocket 版本)
# ============================================================================
# 管理 WebSocket 连接状态、自动重连和错误恢复
#
# 核心职责:
# - 连接状态管理(断开、连接中、已连接、重连中)
# - 自动重连机制(指数退避)
# - 连接错误恢复
# - WebSocket 消息发送/接收
# ============================================================================
# 使用方式:
# WebSocketManager.connect_to_game_server()
# WebSocketManager.connection_state_changed.connect(_on_state_changed)
#
# 注意事项:
# - 作为自动加载单例,全局可访问
# - 自动处理连接断开和重连
# - 通过信号通知连接状态变化
# ============================================================================
class_name WebSocketManager
# ============================================================================
# 信号定义
# ============================================================================
# 连接状态变化信号
# 参数:
# new_state: ConnectionState - 新的连接状态
signal connection_state_changed(new_state: ConnectionState)
# 连接丢失信号
signal connection_lost()
# 重连成功信号
signal reconnection_succeeded()
# 重连失败信号
# 参数:
# attempt: int - 当前重连尝试次数
# max_attempts: int - 最大重连次数
signal reconnection_failed(attempt: int, max_attempts: int)
# WebSocket 消息接收信号
# 参数:
# message: String - 接收到的消息内容JSON 字符串)
signal data_received(message: String)
# ============================================================================
# 枚举定义
# ============================================================================
# 连接状态枚举
enum ConnectionState {
DISCONNECTED, # 未连接
CONNECTING, # 连接中
CONNECTED, # 已连接
RECONNECTING, # 重连中
ERROR # 错误状态
}
# ============================================================================
# 常量定义
# ============================================================================
# WebSocket 服务器 URL原生 WebSocket
const WEBSOCKET_URL: String = "wss://whaletownend.xinghangee.icu/game"
# 默认最大重连次数
const DEFAULT_MAX_RECONNECT_ATTEMPTS: int = 5
# 默认重连基础延迟(秒)
const DEFAULT_RECONNECT_BASE_DELAY: float = 3.0
# 最大重连延迟(秒)
const MAX_RECONNECT_DELAY: float = 30.0
# ============================================================================
# 成员变量
# ============================================================================
# WebSocket peer
var _websocket_peer: WebSocketPeer = WebSocketPeer.new()
# 当前连接状态
var _connection_state: ConnectionState = ConnectionState.DISCONNECTED
# 自动重连启用标志
var _auto_reconnect_enabled: bool = true
# 最大重连次数
var _max_reconnect_attempts: int = DEFAULT_MAX_RECONNECT_ATTEMPTS
# 重连基础延迟
var _reconnect_base_delay: float = DEFAULT_RECONNECT_BASE_DELAY
# 当前重连尝试次数
var _reconnect_attempt: int = 0
# 重连定时器
var _reconnect_timer: Timer = Timer.new()
# 是否为正常关闭(非异常断开)
var _clean_close: bool = true
# 心跳定时器
var _heartbeat_timer: Timer = Timer.new()
# 心跳间隔(秒)
const HEARTBEAT_INTERVAL: float = 30.0
# ============================================================================
# 生命周期方法
# ============================================================================
# 初始化
func _ready() -> void:
print("WebSocketManager 初始化完成")
# 设置重连定时器
_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 - 连接管理
# ============================================================================
# 连接到游戏服务器
func connect_to_game_server() -> void:
if _connection_state == ConnectionState.CONNECTED or _connection_state == ConnectionState.CONNECTING:
push_warning("已经在连接或已连接状态")
return
print("=== WebSocketManager 开始连接 ===")
print("服务器 URL: ", WEBSOCKET_URL)
print("WebSocket 连接中...")
_set_connection_state(ConnectionState.CONNECTING)
_clean_close = true
_reconnect_attempt = 0
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()
# 停止心跳
_heartbeat_timer.stop()
# 关闭 WebSocket
if _websocket_peer.get_ready_state() == WebSocketPeer.STATE_OPEN:
_websocket_peer.close()
_set_connection_state(ConnectionState.DISCONNECTED)
# 检查 WebSocket 是否已连接
#
# 返回值:
# bool - WebSocket 是否已连接
func is_websocket_connected() -> bool:
return _connection_state == ConnectionState.CONNECTED
# 获取当前连接状态
#
# 返回值:
# ConnectionState - 当前连接状态
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 - 自动重连
# ============================================================================
# 启用/禁用自动重连
#
# 参数:
# enabled: bool - 是否启用自动重连
# max_attempts: int - 最大重连次数(默认 5
# base_delay: float - 基础重连延迟,秒(默认 3.0
#
# 使用示例:
# WebSocketManager.enable_auto_reconnect(true, 5, 3.0)
func enable_auto_reconnect(enabled: bool, max_attempts: int = DEFAULT_MAX_RECONNECT_ATTEMPTS, base_delay: float = DEFAULT_RECONNECT_BASE_DELAY) -> void:
_auto_reconnect_enabled = enabled
_max_reconnect_attempts = max_attempts
_reconnect_base_delay = base_delay
print("自动重连: ", "启用" if enabled else "禁用")
print("最大重连次数: ", _max_reconnect_attempts)
print("基础重连延迟: ", _reconnect_base_delay, "")
# 获取重连信息
#
# 返回值:
# 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
}
# ============================================================================
# 内部方法 - 连接状态管理
# ============================================================================
# 设置连接状态
func _set_connection_state(new_state: ConnectionState) -> void:
if _connection_state == new_state:
return
_connection_state = new_state
print("📡 连接状态变更: ", ConnectionState.keys()[new_state])
# 发射信号
connection_state_changed.emit(new_state)
# ============================================================================
# 内部方法 - WebSocket 状态监控
# ============================================================================
# 检查 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:
_reconnect_attempt = 0
reconnection_succeeded.emit()
print("🔄 重连成功")
_set_connection_state(ConnectionState.CONNECTED)
# WebSocket 连接关闭处理
func _on_websocket_closed(clean_close: bool) -> void:
print("🔌 WebSocketManager: WebSocket 连接断开")
print(" 正常关闭: ", clean_close)
_clean_close = clean_close
# 如果是异常断开且启用了自动重连
if not clean_close and _auto_reconnect_enabled:
connection_lost.emit()
_attempt_reconnect()
else:
_set_connection_state(ConnectionState.DISCONNECTED)
# ============================================================================
# 内部方法 - 重连机制
# ============================================================================
# 设置重连定时器
func _setup_reconnect_timer() -> void:
_reconnect_timer = Timer.new()
_reconnect_timer.one_shot = true
_reconnect_timer.autostart = false
add_child(_reconnect_timer)
_reconnect_timer.timeout.connect(_on_reconnect_timeout)
# 尝试重连
func _attempt_reconnect() -> void:
# 检查是否超过最大重连次数
if _reconnect_attempt >= _max_reconnect_attempts:
print("❌ 达到最大重连次数 (", _max_reconnect_attempts, "),停止重连")
reconnection_failed.emit(_reconnect_attempt, _max_reconnect_attempts)
_set_connection_state(ConnectionState.ERROR)
return
_reconnect_attempt += 1
_set_connection_state(ConnectionState.RECONNECTING)
# 计算重连延迟(指数退避)
var delay: float = _calculate_reconnect_delay()
print("🔄 尝试重连 (", _reconnect_attempt, "/", _max_reconnect_attempts, ")")
print(" 延迟: ", delay, "")
# 启动重连定时器
_reconnect_timer.start(delay)
# 计算重连延迟(指数退避)
func _calculate_reconnect_delay() -> float:
# 指数退避: base_delay * 2^(attempt-1)
var delay: float = _reconnect_base_delay * pow(2.0, _reconnect_attempt - 1)
# 限制最大延迟
return min(delay, MAX_RECONNECT_DELAY)
# 重连定时器超时处理
func _on_reconnect_timeout() -> void:
print("⏰ 重连定时器超时,开始重连...")
_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
# ============================================================================
# 工具方法
# ============================================================================
# 获取连接状态描述
#
# 返回值:
# String - 连接状态描述
func get_state_description() -> String:
match _connection_state:
ConnectionState.DISCONNECTED:
return "未连接"
ConnectionState.CONNECTING:
return "连接中"
ConnectionState.CONNECTED:
return "已连接"
ConnectionState.RECONNECTING:
return "重连中 (%d/%d)" % [_reconnect_attempt, _max_reconnect_attempts]
ConnectionState.ERROR:
return "错误"
_:
return "未知状态"