forked from datawhale/whale-town-front
- 修复 WebSocketManager/SocketIOClient 函数缩进错误 - 重命名 is_connected() 避免与 Object 基类冲突 - 修复 tscn 文件多余前导空格 - 修复测试文件 GUT 断言函数调用 - 添加 GUT 测试框架
305 lines
9.1 KiB
GDScript
305 lines
9.1 KiB
GDScript
extends Node
|
||
|
||
# ============================================================================
|
||
# SocketIOClient.gd - Socket.IO 协议封装
|
||
# ============================================================================
|
||
# 封装 Godot 的 WebSocketPeer,实现简化的 Socket.IO 协议
|
||
#
|
||
# 核心职责:
|
||
# - WebSocket 连接管理
|
||
# - Socket.IO 消息协议(简化版 JSON 格式)
|
||
# - 事件监听器管理
|
||
# - 消息发送/接收
|
||
#
|
||
# 注意事项:
|
||
# - 后端使用简化版 Socket.IO(纯 JSON,无二进制协议)
|
||
# - 发送消息使用 "t" 字段标识事件类型
|
||
# - 所有消息通过 JSON 序列化
|
||
# ============================================================================
|
||
|
||
class_name SocketIOClient
|
||
|
||
# ============================================================================
|
||
# 信号定义
|
||
# ============================================================================
|
||
|
||
# 连接成功信号
|
||
signal connected()
|
||
|
||
# 连接断开信号
|
||
# 参数:
|
||
# clean_close: bool - 是否为正常关闭
|
||
signal disconnected(clean_close: bool)
|
||
|
||
# 事件接收信号
|
||
# 参数:
|
||
# event_name: String - 事件名称(从 "t" 字段提取)
|
||
# data: Dictionary - 事件数据
|
||
signal event_received(event_name: String, data: Dictionary)
|
||
|
||
# 错误发生信号
|
||
# 参数:
|
||
# error: String - 错误信息
|
||
signal error_occurred(error: String)
|
||
|
||
# ============================================================================
|
||
# 常量定义
|
||
# ============================================================================
|
||
|
||
# 连接状态枚举
|
||
enum ConnectionState {
|
||
DISCONNECTED, # 未连接
|
||
CONNECTING, # 连接中
|
||
CONNECTED # 已连接
|
||
}
|
||
|
||
# ============================================================================
|
||
# 成员变量
|
||
# ============================================================================
|
||
|
||
# WebSocket 客户端
|
||
var _websocket_peer: WebSocketPeer = WebSocketPeer.new()
|
||
|
||
# 连接状态
|
||
var _connection_state: ConnectionState = ConnectionState.DISCONNECTED
|
||
|
||
# 服务器 URL
|
||
var _server_url: String = ""
|
||
|
||
# 事件监听器: {event_name: [Callable, ...]}
|
||
var _event_listeners: Dictionary = {}
|
||
|
||
# ============================================================================
|
||
# 生命周期方法
|
||
# ============================================================================
|
||
|
||
# 初始化
|
||
func _ready() -> void:
|
||
print("SocketIOClient 初始化完成")
|
||
|
||
# 处理进程 - 轮询 WebSocket 消息
|
||
func _process(_delta: float) -> void:
|
||
# 轮询 WebSocket 状态
|
||
_websocket_peer.poll()
|
||
|
||
# 检查连接状态变化
|
||
var new_state: ConnectionState = _get_connection_state()
|
||
if new_state != _connection_state:
|
||
_connection_state = new_state
|
||
_on_state_changed(_connection_state)
|
||
|
||
# 处理接收到的消息
|
||
_process_incoming_messages()
|
||
|
||
# ============================================================================
|
||
# 公共 API - 连接管理
|
||
# ============================================================================
|
||
|
||
# 连接到服务器
|
||
#
|
||
# 参数:
|
||
# url: String - WebSocket 服务器 URL (ws:// 或 wss://)
|
||
#
|
||
# 使用示例:
|
||
# socket_client.connect_to_server("wss://example.com/game")
|
||
func connect_to_server(url: String) -> void:
|
||
if _connection_state == ConnectionState.CONNECTED:
|
||
push_warning("已经连接到服务器,无需重复连接")
|
||
return
|
||
|
||
_server_url = url
|
||
print("=== SocketIOClient 开始连接 ===")
|
||
print("服务器 URL: ", _server_url)
|
||
|
||
# 创建 WebSocket 客户端
|
||
_websocket_peer = WebSocketPeer.new()
|
||
|
||
# 发起连接
|
||
var error := _websocket_peer.connect_to_url(url)
|
||
if error != OK:
|
||
push_error("WebSocket 连接失败: %s" % error)
|
||
error_occurred.emit("WebSocket 连接失败")
|
||
return
|
||
|
||
_connection_state = ConnectionState.CONNECTING
|
||
print("WebSocket 连接中...")
|
||
|
||
# 断开连接
|
||
func disconnect_from_server() -> void:
|
||
if _connection_state == ConnectionState.DISCONNECTED:
|
||
return
|
||
|
||
print("=== SocketIOClient 断开连接 ===")
|
||
_websocket_peer.close()
|
||
_connection_state = ConnectionState.DISCONNECTED
|
||
disconnected.emit(true)
|
||
|
||
# 检查 Socket 是否已连接
|
||
#
|
||
# 返回值:
|
||
# bool - 是否已连接
|
||
func is_socket_connected() -> bool:
|
||
return _connection_state == ConnectionState.CONNECTED
|
||
|
||
# ============================================================================
|
||
# 公共 API - 事件发送
|
||
# ============================================================================
|
||
|
||
# 发送事件(对应 socket.emit)
|
||
#
|
||
# 参数:
|
||
# event_name: String - 事件名称(如 "login", "chat")
|
||
# data: Dictionary - 事件数据
|
||
#
|
||
# 使用示例:
|
||
# socket_client.emit("login", {"type": "login", "token": "abc123"})
|
||
# socket_client.emit("chat", {"t": "chat", "content": "Hello", "scope": "local"})
|
||
func emit(event_name: String, data: Dictionary) -> void:
|
||
if not is_socket_connected():
|
||
push_error("无法发送事件: 未连接到服务器")
|
||
error_occurred.emit("未连接到服务器")
|
||
return
|
||
|
||
# 序列化为 JSON
|
||
var json_string := JSON.stringify(data)
|
||
if json_string.is_empty():
|
||
push_error("JSON 序列化失败")
|
||
error_occurred.emit("JSON 序列化失败")
|
||
return
|
||
|
||
# 发送数据包
|
||
var packet := json_string.to_utf8_buffer()
|
||
var error := _websocket_peer.send(packet)
|
||
|
||
if error != OK:
|
||
push_error("发送数据包失败: %s" % error)
|
||
error_occurred.emit("发送数据包失败")
|
||
return
|
||
|
||
print("📤 发送事件: ", event_name)
|
||
print(" 数据: ", json_string if json_string.length() < 200 else json_string.substr(0, 200) + "...")
|
||
|
||
# ============================================================================
|
||
# 公共 API - 事件监听
|
||
# ============================================================================
|
||
|
||
# 添加事件监听器(对应 socket.on)
|
||
#
|
||
# 参数:
|
||
# event_name: String - 事件名称
|
||
# callback: Callable - 回调函数,接收 data: Dictionary 参数
|
||
#
|
||
# 使用示例:
|
||
# socket_client.add_event_listener("chat_render", func(data):
|
||
# print("收到消息: ", data.txt)
|
||
# )
|
||
func add_event_listener(event_name: String, callback: Callable) -> void:
|
||
if not _event_listeners.has(event_name):
|
||
_event_listeners[event_name] = []
|
||
|
||
_event_listeners[event_name].append(callback)
|
||
print("注册事件监听器: ", event_name, " -> ", callback)
|
||
|
||
# 移除事件监听器
|
||
#
|
||
# 参数:
|
||
# event_name: String - 事件名称
|
||
# callback: Callable - 要移除的回调函数
|
||
func remove_event_listener(event_name: String, callback: Callable) -> void:
|
||
if not _event_listeners.has(event_name):
|
||
return
|
||
|
||
var listeners: Array = _event_listeners[event_name]
|
||
listeners.erase(callback)
|
||
|
||
if listeners.is_empty():
|
||
_event_listeners.erase(event_name)
|
||
|
||
print("移除事件监听器: ", event_name, " -> ", callback)
|
||
|
||
# ============================================================================
|
||
# 内部方法 - 消息处理
|
||
# ============================================================================
|
||
|
||
# 处理接收到的消息
|
||
func _process_incoming_messages() -> void:
|
||
# 检查是否有可用数据包
|
||
while _websocket_peer.get_available_packet_count() > 0:
|
||
# 接收数据包
|
||
var packet: PackedByteArray = _websocket_peer.get_packet()
|
||
|
||
# 解析为字符串
|
||
var json_string: String = packet.get_string_from_utf8()
|
||
|
||
# 解析 JSON
|
||
var json := JSON.new()
|
||
var parse_result := json.parse(json_string)
|
||
|
||
if parse_result != OK:
|
||
push_error("JSON 解析失败: " + json_string)
|
||
error_occurred.emit("JSON 解析失败")
|
||
continue
|
||
|
||
var data: Dictionary = json.data
|
||
|
||
# 提取事件类型(从 "t" 字段)
|
||
var event_name: String = data.get("t", "")
|
||
if event_name.is_empty():
|
||
# 如果没有 "t" 字段,尝试其他方式识别
|
||
if data.has("type"):
|
||
event_name = data["type"]
|
||
elif data.has("code"):
|
||
event_name = "error"
|
||
else:
|
||
push_warning("收到未知格式消息: " + json_string)
|
||
continue
|
||
|
||
print("📨 收到事件: ", event_name)
|
||
print(" 数据: ", json_string if json_string.length() < 200 else json_string.substr(0, 200) + "...")
|
||
|
||
# 调用事件监听器
|
||
_notify_event_listeners(event_name, data)
|
||
|
||
# 发射通用信号
|
||
event_received.emit(event_name, data)
|
||
|
||
# 通知事件监听器
|
||
func _notify_event_listeners(event_name: String, data: Dictionary) -> void:
|
||
if not _event_listeners.has(event_name):
|
||
return
|
||
|
||
var listeners: Array = _event_listeners[event_name]
|
||
for callback in listeners:
|
||
if callback.is_valid():
|
||
callback.call(data)
|
||
|
||
# ============================================================================
|
||
# 内部方法 - 状态管理
|
||
# ============================================================================
|
||
|
||
# 获取当前连接状态
|
||
func _get_connection_state() -> ConnectionState:
|
||
match _websocket_peer.get_ready_state():
|
||
WebSocketPeer.STATE_CONNECTING:
|
||
return ConnectionState.CONNECTING
|
||
WebSocketPeer.STATE_OPEN:
|
||
return ConnectionState.CONNECTED
|
||
WebSocketPeer.STATE_CLOSING:
|
||
return ConnectionState.CONNECTING
|
||
WebSocketPeer.STATE_CLOSED:
|
||
return ConnectionState.DISCONNECTED
|
||
_:
|
||
return ConnectionState.DISCONNECTED
|
||
|
||
# 连接状态变化处理
|
||
func _on_state_changed(new_state: ConnectionState) -> void:
|
||
match new_state:
|
||
ConnectionState.CONNECTED:
|
||
print("✅ WebSocket 连接成功")
|
||
connected.emit()
|
||
ConnectionState.DISCONNECTED:
|
||
print("🔌 WebSocket 连接断开")
|
||
disconnected.emit(false)
|
||
ConnectionState.CONNECTING:
|
||
print("⏳ WebSocket 连接中...")
|