fix: 修复聊天系统编译错误

- 修复 WebSocketManager/SocketIOClient 函数缩进错误
- 重命名 is_connected() 避免与 Object 基类冲突
- 修复 tscn 文件多余前导空格
- 修复测试文件 GUT 断言函数调用
- 添加 GUT 测试框架
This commit is contained in:
WhaleTown Developer
2026-01-08 00:11:12 +08:00
parent 16f24ab26f
commit c8e73bec59
255 changed files with 21876 additions and 91 deletions

View File

@@ -80,25 +80,146 @@ var current_email: String = ""
# 网络请求管理
var active_request_ids: Array = []
# 游戏 token 管理(用于 WebSocket 聊天认证)
var _game_token: String = ""
# ============ Token 管理 ============
# 本地存储路径常量
const AUTH_CONFIG_PATH: String = "user://auth.cfg"
# Token 存储(内存中,用于快速访问)
var _access_token: String = "" # JWT访问令牌短期用于API和WebSocket
var _refresh_token: String = "" # JWT刷新令牌长期用于获取新access_token
var _user_info: Dictionary = {} # 用户信息
var _token_expiry: float = 0.0 # access_token过期时间Unix时间戳
# 游戏 token兼容旧代码保留但标记为废弃
var _game_token: String = "" # @deprecated 使用 _access_token 替代
# ============ 生命周期方法 ============
# 初始化管理器
func _init():
func _init() -> void:
print("AuthManager 初始化完成")
_load_auth_data()
# 清理资源
func cleanup():
func cleanup() -> void:
# 取消所有活动的网络请求
for request_id in active_request_ids:
NetworkManager.cancel_request(request_id)
active_request_ids.clear()
# ============ 游戏 Token 管理 ============
# ============ Token 管理 ============
# 设置游戏 token
# 保存 Token 到内存
#
# 参数:
# data: Dictionary - 登录响应数据
#
# 功能:
# - 从登录响应中提取 access_token 和 refresh_token
# - 保存到内存变量中
# - 保存用户信息
func _save_tokens_to_memory(data: Dictionary) -> void:
if not data.has("data"):
print("⚠️ 登录响应中没有 data 字段")
return
var token_data: Dictionary = data.data
_access_token = token_data.get("access_token", "")
_refresh_token = token_data.get("refresh_token", "")
_user_info = token_data.get("user", {})
_token_expiry = Time.get_unix_time_from_system() + float(token_data.get("expires_in", 0))
# 保持兼容性:设置 _game_token
_game_token = _access_token
print("✅ Token已保存到内存")
print(" Access Token: ", _access_token.substr(0, 20) + "...")
print(" 用户: ", _user_info.get("username", "未知"))
# 保存 Token 到本地ConfigFile
#
# 参数:
# data: Dictionary - 登录响应数据
#
# 功能:
# - 将 refresh_token 和用户信息保存到 ConfigFile
# - access_token 不保存到本地,仅保存在内存中
func _save_tokens_to_local(data: Dictionary) -> void:
if not data.has("data"):
return
var token_data: Dictionary = data.data
var auth_data: Dictionary = {
"refresh_token": token_data.get("refresh_token", ""),
"user_id": token_data.get("user", {}).get("id", ""),
"username": token_data.get("user", {}).get("username", ""),
"saved_at": Time.get_unix_time_from_system()
}
var config: ConfigFile = ConfigFile.new()
config.load(AUTH_CONFIG_PATH)
config.set_value("auth", "refresh_token", auth_data["refresh_token"])
config.set_value("auth", "user_id", auth_data["user_id"])
config.set_value("auth", "username", auth_data["username"])
config.set_value("auth", "saved_at", auth_data["saved_at"])
var error: Error = config.save(AUTH_CONFIG_PATH)
if error == OK:
print("✅ Token已保存到本地: ", AUTH_CONFIG_PATH)
else:
print("❌ 保存Token到本地失败错误码: ", error)
# 从本地加载 Token游戏启动时调用
#
# 功能:
# - 从 ConfigFile 加载 refresh_token 和用户信息
# - access_token 需要通过 refresh_token 刷新获取
func _load_auth_data() -> void:
if not FileAccess.file_exists(AUTH_CONFIG_PATH):
print(" 本地不存在认证数据")
return
var config: ConfigFile = ConfigFile.new()
var error: Error = config.load(AUTH_CONFIG_PATH)
if error != OK:
print("❌ 加载本地认证数据失败,错误码: ", error)
return
_refresh_token = config.get_value("auth", "refresh_token", "")
var user_id: String = config.get_value("auth", "user_id", "")
var username: String = config.get_value("auth", "username", "")
if not _refresh_token.is_empty():
_user_info = {
"id": user_id,
"username": username
}
print("✅ 已从本地加载认证数据")
print(" 用户: ", username)
else:
print("⚠️ 本地认证数据无效(没有 refresh_token")
# 清除本地认证数据(登出时调用)
#
# 功能:
# - 清除内存中的 Token
# - 删除本地 ConfigFile
func _clear_auth_data() -> void:
_access_token = ""
_refresh_token = ""
_user_info = {}
_token_expiry = 0.0
_game_token = ""
if FileAccess.file_exists(AUTH_CONFIG_PATH):
DirAccess.remove_absolute(AUTH_CONFIG_PATH)
print("✅ 已清除本地认证数据")
# ============ Token 访问方法 ============
# 设置游戏 token兼容旧代码推荐使用 _save_tokens_to_memory
#
# 参数:
# token: String - 游戏认证 token
@@ -108,18 +229,47 @@ func cleanup():
# - 从服务器响应中获取 token
func set_game_token(token: String) -> void:
_game_token = token
_access_token = token # 同步更新 access_token
print("AuthManager: 游戏 token 已设置")
# 获取游戏 token
#
# 返回值:
# String - 游戏 token如果未设置则返回空字符串
# String - access_token如果未设置则返回空字符串
#
# 使用场景:
# - ChatManager 连接 WebSocket 时需要 token
# - 其他需要游戏认证的场景
func get_game_token() -> String:
return _game_token
return _access_token
# 获取 access token
#
# 返回值:
# String - JWT访问令牌
#
# 使用场景:
# - API请求认证
# - WebSocket聊天认证
func get_access_token() -> String:
return _access_token
# 获取 refresh token
#
# 返回值:
# String - JWT刷新令牌
#
# 使用场景:
# - 刷新过期的 access token
func get_refresh_token() -> String:
return _refresh_token
# 获取用户信息
#
# 返回值:
# Dictionary - 用户信息字典
func get_user_info() -> Dictionary:
return _user_info
# ============ 登录相关方法 ============
@@ -449,19 +599,21 @@ func validate_verification_code(code: String) -> Dictionary:
# ============ 网络响应处理 ============
# 处理登录响应
func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary):
func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary) -> void:
_reset_login_state()
var result = ResponseHandler.handle_login_response(success, data, error_info)
if result.should_show_toast:
show_toast_message.emit(result.message, result.success)
if result.success:
var username = ""
if data.has("data") and data.data.has("user") and data.data.user.has("username"):
username = data.data.user.username
# 保存 Token 到内存和本地
_save_tokens_to_memory(data)
_save_tokens_to_local(data)
var username: String = _user_info.get("username", "")
# 延迟发送登录成功信号
await Engine.get_main_loop().create_timer(1.0).timeout
login_success.emit(username)
@@ -469,19 +621,21 @@ func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary)
login_failed.emit(result.message)
# 处理验证码登录响应
func _on_verification_login_response(success: bool, data: Dictionary, error_info: Dictionary):
func _on_verification_login_response(success: bool, data: Dictionary, error_info: Dictionary) -> void:
_reset_login_state()
var result = ResponseHandler.handle_verification_code_login_response(success, data, error_info)
if result.should_show_toast:
show_toast_message.emit(result.message, result.success)
if result.success:
var username = ""
if data.has("data") and data.data.has("user") and data.data.user.has("username"):
username = data.data.user.username
# 保存 Token 到内存和本地
_save_tokens_to_memory(data)
_save_tokens_to_local(data)
var username: String = _user_info.get("username", "")
await Engine.get_main_loop().create_timer(1.0).timeout
login_success.emit(username)
else:

View File

@@ -23,8 +23,6 @@ extends Node
# - 所有聊天事件通过 EventSystem 广播
# ============================================================================
class_name ChatManager
# ============================================================================
# 信号定义 (Signal Up)
# ============================================================================
@@ -51,8 +49,8 @@ signal chat_error_occurred(error_code: String, message: String)
# 聊天连接状态变化信号
# 参数:
# state: WebSocketManager.ConnectionState - 连接状态
signal chat_connection_state_changed(state: WebSocketManager.ConnectionState)
# state: int - 连接状态0=DISCONNECTED, 1=CONNECTING, 2=CONNECTED, 3=RECONNECTING, 4=ERROR
signal chat_connection_state_changed(state: int)
# 位置更新成功信号
# 参数:
@@ -178,7 +176,7 @@ func get_game_token() -> String:
# 连接到聊天服务器
func connect_to_chat_server() -> void:
if _websocket_manager.is_connected():
if _websocket_manager.is_websocket_connected():
push_warning("聊天服务器已连接")
return
@@ -193,17 +191,17 @@ func disconnect_from_chat_server() -> void:
if _is_logged_in:
var logout_data := {"type": "logout"}
_socket_client.emit("logout", logout_data)
_is_logged_in = False
_is_logged_in = false
# 断开连接
_websocket_manager.disconnect()
_websocket_manager.disconnect_websocket()
# 检查是否已连接
#
# 返回值:
# bool - 是否已连接
func is_connected() -> bool:
return _websocket_manager.is_connected()
func is_chat_connected() -> bool:
return _websocket_manager.is_websocket_connected()
# ============================================================================
# 公共 API - 聊天操作
@@ -219,7 +217,7 @@ func is_connected() -> bool:
# ChatManager.send_chat_message("Hello, world!", "local")
func send_chat_message(content: String, scope: String = "local") -> void:
# 检查连接状态
if not _websocket_manager.is_connected():
if not _websocket_manager.is_websocket_connected():
_handle_error("NOT_CONNECTED", "未连接到聊天服务器")
return
@@ -272,7 +270,7 @@ func send_chat_message(content: String, scope: String = "local") -> void:
# 使用示例:
# ChatManager.update_player_position(150.0, 200.0, "novice_village")
func update_player_position(x: float, y: float, map_id: String) -> void:
if not _websocket_manager.is_connected():
if not _websocket_manager.is_websocket_connected():
return
var position_data := {
@@ -297,10 +295,9 @@ func can_send_message() -> bool:
var current_time := Time.get_unix_time_from_system()
# 清理过期的时间戳
_message_timestamps = _message_timestamps.filter(
func(timestamp: float) -> bool:
return current_time - timestamp < RATE_LIMIT_WINDOW
)
var filter_func := func(timestamp: float) -> bool:
return current_time - timestamp < RATE_LIMIT_WINDOW
_message_timestamps = _message_timestamps.filter(filter_func)
# 检查数量
return _message_timestamps.size() < RATE_LIMIT_MESSAGES
@@ -458,11 +455,12 @@ func _on_socket_connected() -> void:
# Socket 连接断开
func _on_socket_disconnected(_clean_close: bool) -> void:
print("🔌 ChatManager: Socket 连接断开")
_is_logged_in = False
_is_logged_in = false
# 连接状态变化
func _on_connection_state_changed(state: WebSocketManager.ConnectionState) -> void:
print("📡 ChatManager: 连接状态变化 - ", WebSocketManager.ConnectionState.keys()[state])
func _on_connection_state_changed(state: int) -> void:
var state_names := ["DISCONNECTED", "CONNECTING", "CONNECTED", "RECONNECTING", "ERROR"]
print("📡 ChatManager: 连接状态变化 - ", state_names[state])
# 发射信号
chat_connection_state_changed.emit(state)
@@ -496,7 +494,7 @@ func _on_socket_event_received(event_name: String, data: Dictionary) -> void:
func _handle_login_success(data: Dictionary) -> void:
print("✅ ChatManager: 登录成功")
_is_logged_in = True
_is_logged_in = true
_current_username = data.get("username", "")
_current_map = data.get("currentMap", "")
@@ -590,8 +588,8 @@ func _on_socket_error(error: String) -> void:
# 发送登录消息
func _send_login_message() -> void:
if _game_token.is_empty():
push_error("无法获取游戏 token请先调用 set_game_token() 设置 token")
_handle_error("AUTH_FAILED", "无法获取游戏 token请先设置 token")
push_error("无法获取 access token请确保已登录")
_handle_error("AUTH_FAILED", "无法获取 access token请先登录")
return
var login_data := {
@@ -600,14 +598,14 @@ func _send_login_message() -> void:
}
_socket_client.emit("login", login_data)
print("📤 发送登录消息")
print("📤 发送登录消息(使用 access token")
# 处理错误
func _handle_error(error_code: String, error_message: String) -> void:
print("❌ ChatManager 错误: [", error_code, "] ", error_message)
# 获取用户友好的错误消息
var user_message := CHAT_ERROR_MESSAGES.get(error_code, error_message)
var user_message: String = CHAT_ERROR_MESSAGES.get(error_code, error_message) as String
# 发射信号
chat_error_occurred.emit(error_code, user_message)
@@ -620,7 +618,7 @@ func _handle_error(error_code: String, error_message: String) -> void:
# 特殊处理认证失败
if error_code == "AUTH_FAILED" or error_code == "SESSION_EXPIRED":
_is_logged_in = False
_is_logged_in = false
EventSystem.emit_event(EventNames.CHAT_LOGIN_FAILED, {
"error_code": error_code
})
@@ -633,11 +631,11 @@ func _record_message_timestamp() -> void:
# 添加消息到当前会话历史
func _add_message_to_history(message: Dictionary) -> void:
_message_history.append(message)
# 更新最旧消息时间戳(用于历史消息加载)
if _oldest_message_timestamp == 0.0 or message.timestamp < _oldest_message_timestamp:
_oldest_message_timestamp = message.timestamp
# 限制当前会话消息数量(超过后删除最旧的)
if _message_history.size() > MAX_SESSION_MESSAGES:
_message_history.pop_front()

View File

@@ -144,8 +144,8 @@ func connect_to_game_server() -> void:
_socket_client.connect_to_server(WEBSOCKET_URL)
# 断开连接
func disconnect() -> void:
# 断开 WebSocket 连接
func disconnect_websocket() -> void:
print("=== WebSocketManager 断开连接 ===")
_clean_close = true
@@ -156,11 +156,11 @@ func disconnect() -> void:
_socket_client.disconnect_from_server()
_set_connection_state(ConnectionState.DISCONNECTED)
# 检查是否已连接
# 检查 WebSocket 是否已连接
#
# 返回值:
# bool - 是否已连接
func is_connected() -> bool:
# bool - WebSocket 是否已连接
func is_websocket_connected() -> bool:
return _connection_state == ConnectionState.CONNECTED
# 获取当前连接状态