feature/auth-system-refactor #8

Merged
moyin merged 6 commits from feature/auth-system-refactor into main 2026-01-02 21:25:30 +08:00
4 changed files with 708 additions and 59 deletions
Showing only changes of commit 2f1ccbc2cd - Show all commits

View File

@@ -1,50 +1,142 @@
extends Node extends Node
# 游戏管理器 - 全局游戏状态管理 # ============================================================================
# 单例模式,管理游戏的整体状态和生命周期 # GameManager.gd - 游戏管理器
# ============================================================================
# 全局单例管理器,负责游戏状态管理和生命周期控制
#
# 核心职责:
# - 游戏状态切换 (加载、认证、游戏中、暂停等)
# - 用户信息管理
# - 全局配置访问
# - 系统初始化和清理
#
# 使用方式:
# GameManager.change_state(GameManager.GameState.IN_GAME)
# GameManager.set_current_user("player123")
#
# 注意事项:
# - 作为自动加载单例,全局可访问
# - 状态变更会触发 game_state_changed 信号
# - 状态切换应该通过 change_state() 方法进行
# ============================================================================
# ============ 信号定义 ============
# 游戏状态变更信号
# 参数: new_state - 新的游戏状态
signal game_state_changed(new_state: GameState) signal game_state_changed(new_state: GameState)
# ============ 枚举定义 ============
# 游戏状态枚举
# 定义了游戏的各种运行状态
enum GameState { enum GameState {
LOADING, # 加载中 LOADING, # 加载中 - 游戏启动时的初始化状态
AUTH, # 认证状态 AUTH, # 认证状态 - 用户登录/注册界面
MAIN_MENU, # 主菜单 MAIN_MENU, # 主菜单 - 游戏主界面
IN_GAME, # 游戏中 IN_GAME, # 游戏中 - 正在进行游戏
PAUSED, # 暂停 PAUSED, # 暂停 - 游戏暂停状态
SETTINGS # 设置 SETTINGS # 设置 - 设置界面
} }
var current_state: GameState = GameState.LOADING # ============ 成员变量 ============
var previous_state: GameState = GameState.LOADING
var current_user: String = ""
var game_version: String = "1.0.0"
# 状态管理
var current_state: GameState = GameState.LOADING # 当前游戏状态
var previous_state: GameState = GameState.LOADING # 上一个游戏状态
# 用户信息
var current_user: String = "" # 当前登录用户名
# 游戏配置
var game_version: String = "1.0.0" # 游戏版本号
# ============ 生命周期方法 ============
# 初始化游戏管理器
# 在节点准备就绪时调用,设置初始状态
func _ready(): func _ready():
print("GameManager 初始化完成") print("GameManager 初始化完成")
change_state(GameState.AUTH) change_state(GameState.AUTH) # 启动时进入认证状态
# ============ 状态管理方法 ============
# 切换游戏状态
#
# 参数:
# new_state: GameState - 要切换到的新状态
#
# 功能:
# - 检查状态是否需要切换
# - 记录状态变更历史
# - 发送状态变更信号
# - 输出状态变更日志
func change_state(new_state: GameState): func change_state(new_state: GameState):
# 避免重复切换到相同状态
if current_state == new_state: if current_state == new_state:
return return
# 记录状态变更
previous_state = current_state previous_state = current_state
current_state = new_state current_state = new_state
# 输出状态变更日志
print("游戏状态变更: ", GameState.keys()[previous_state], " -> ", GameState.keys()[current_state]) print("游戏状态变更: ", GameState.keys()[previous_state], " -> ", GameState.keys()[current_state])
# 发送状态变更信号
game_state_changed.emit(new_state) game_state_changed.emit(new_state)
# 获取当前游戏状态
#
# 返回值:
# GameState - 当前的游戏状态
func get_current_state() -> GameState: func get_current_state() -> GameState:
return current_state return current_state
# 获取上一个游戏状态
#
# 返回值:
# GameState - 上一个游戏状态
#
# 使用场景:
# - 从暂停状态恢复时,返回到之前的状态
# - 错误处理时回退到安全状态
func get_previous_state() -> GameState: func get_previous_state() -> GameState:
return previous_state return previous_state
# ============ 用户管理方法 ============
# 设置当前登录用户
#
# 参数:
# username: String - 用户名
#
# 功能:
# - 存储当前登录用户信息
# - 输出用户设置日志
#
# 注意事项:
# - 用户登录成功后调用此方法
# - 用户登出时应传入空字符串
func set_current_user(username: String): func set_current_user(username: String):
current_user = username current_user = username
print("当前用户设置为: ", username) print("当前用户设置为: ", username)
# 获取当前登录用户
#
# 返回值:
# String - 当前登录的用户名,未登录时为空字符串
func get_current_user() -> String: func get_current_user() -> String:
return current_user return current_user
# 检查用户是否已登录
#
# 返回值:
# bool - true表示已登录false表示未登录
#
# 使用场景:
# - 进入需要登录的功能前检查
# - UI显示逻辑判断
func is_user_logged_in() -> bool: func is_user_logged_in() -> bool:
return not current_user.is_empty() return not current_user.is_empty()

View File

@@ -1,45 +1,96 @@
extends Node extends Node
# 网络请求管理器 - 统一处理所有HTTP请求 # ============================================================================
# NetworkManager.gd - 网络请求管理器
# ============================================================================
# 全局单例管理器统一处理所有HTTP请求
#
# 核心职责:
# - 统一的HTTP请求接口 (GET, POST, PUT, DELETE, PATCH)
# - 认证相关API封装 (登录、注册、验证码等)
# - 请求状态管理和错误处理
# - 支持API v1.1.1规范的响应处理
#
# 使用方式:
# NetworkManager.login("user@example.com", "password", callback)
# var request_id = NetworkManager.get_request("/api/data", callback)
#
# 注意事项:
# - 作为自动加载单例,全局可访问
# - 所有请求都是异步的,通过回调函数或信号处理结果
# - 支持请求超时和取消功能
# - 自动处理JSON序列化和反序列化
# ============================================================================
# 信号定义 # ============ 信号定义 ============
# 请求完成信号
# 参数:
# request_id: String - 请求唯一标识符
# success: bool - 请求是否成功
# data: Dictionary - 响应数据
signal request_completed(request_id: String, success: bool, data: Dictionary) signal request_completed(request_id: String, success: bool, data: Dictionary)
# 请求失败信号
# 参数:
# request_id: String - 请求唯一标识符
# error_type: String - 错误类型名称
# message: String - 错误消息
signal request_failed(request_id: String, error_type: String, message: String) signal request_failed(request_id: String, error_type: String, message: String)
# API配置 # ============ 常量定义 ============
# API基础URL - 所有请求的根地址
const API_BASE_URL = "https://whaletownend.xinghangee.icu" const API_BASE_URL = "https://whaletownend.xinghangee.icu"
# 默认请求超时时间(秒)
const DEFAULT_TIMEOUT = 30.0 const DEFAULT_TIMEOUT = 30.0
# 请求类型枚举 # ============ 枚举定义 ============
# HTTP请求方法枚举
enum RequestType { enum RequestType {
GET, GET, # 获取数据
POST, POST, # 创建数据
PUT, PUT, # 更新数据
DELETE, DELETE, # 删除数据
PATCH PATCH # 部分更新数据
} }
# 错误类型枚举 # 错误类型枚举
# 用于分类不同类型的网络错误
enum ErrorType { enum ErrorType {
NETWORK_ERROR, # 网络连接错误 NETWORK_ERROR, # 网络连接错误 - 无法连接到服务器
TIMEOUT_ERROR, # 请求超时 TIMEOUT_ERROR, # 请求超时 - 服务器响应时间过长
PARSE_ERROR, # JSON解析错误 PARSE_ERROR, # JSON解析错误 - 服务器返回格式错误
HTTP_ERROR, # HTTP状态码错误 HTTP_ERROR, # HTTP状态码错误 - 4xx, 5xx状态码
BUSINESS_ERROR # 业务逻辑错误 BUSINESS_ERROR # 业务逻辑错误 - API返回的业务错误
} }
# 请求状态 # ============ 请求信息类 ============
# 请求信息封装类
# 存储单个HTTP请求的所有相关信息
class RequestInfo: class RequestInfo:
var id: String var id: String # 请求唯一标识符
var url: String var url: String # 完整的请求URL
var method: RequestType var method: RequestType # HTTP请求方法
var headers: PackedStringArray var headers: PackedStringArray # 请求头数组
var body: String var body: String # 请求体内容
var timeout: float var timeout: float # 超时时间(秒)
var start_time: float var start_time: float # 请求开始时间戳
var http_request: HTTPRequest var http_request: HTTPRequest # Godot HTTPRequest节点引用
var callback: Callable var callback: Callable # 完成时的回调函数
# 构造函数
#
# 参数:
# request_id: String - 请求唯一标识符
# request_url: String - 请求URL
# request_method: RequestType - HTTP方法
# request_headers: PackedStringArray - 请求头(可选)
# request_body: String - 请求体(可选)
# request_timeout: float - 超时时间可选默认使用DEFAULT_TIMEOUT
func _init(request_id: String, request_url: String, request_method: RequestType, func _init(request_id: String, request_url: String, request_method: RequestType,
request_headers: PackedStringArray = [], request_body: String = "", request_headers: PackedStringArray = [], request_body: String = "",
request_timeout: float = DEFAULT_TIMEOUT): request_timeout: float = DEFAULT_TIMEOUT):
@@ -49,40 +100,107 @@ class RequestInfo:
headers = request_headers headers = request_headers
body = request_body body = request_body
timeout = request_timeout timeout = request_timeout
# 记录请求开始时间(简化版时间戳)
start_time = Time.get_time_dict_from_system().hour * 3600 + Time.get_time_dict_from_system().minute * 60 + Time.get_time_dict_from_system().second start_time = Time.get_time_dict_from_system().hour * 3600 + Time.get_time_dict_from_system().minute * 60 + Time.get_time_dict_from_system().second
# 活动请求管理 # ============ 成员变量 ============
var active_requests: Dictionary = {}
var request_counter: int = 0
# 活动请求管理
var active_requests: Dictionary = {} # 存储所有活动请求 {request_id: RequestInfo}
var request_counter: int = 0 # 请求计数器用于生成唯一ID
# ============ 生命周期方法 ============
# 初始化网络管理器
# 在节点准备就绪时调用
func _ready(): func _ready():
print("NetworkManager 已初始化") print("NetworkManager 已初始化")
# ============ 公共API接口 ============ # ============ 公共API接口 ============
# 发送GET请求 # 发送GET请求
#
# 参数:
# endpoint: String - API端点路径如: "/api/users"
# callback: Callable - 完成时的回调函数(可选)
# timeout: float - 超时时间可选默认30秒
#
# 返回值:
# String - 请求ID可用于取消请求或跟踪状态
#
# 使用示例:
# var request_id = NetworkManager.get_request("/api/users", my_callback)
func get_request(endpoint: String, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String: func get_request(endpoint: String, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String:
return send_request(endpoint, RequestType.GET, [], "", callback, timeout) return send_request(endpoint, RequestType.GET, [], "", callback, timeout)
# 发送POST请求 # 发送POST请求
#
# 参数:
# endpoint: String - API端点路径
# data: Dictionary - 要发送的数据将自动转换为JSON
# callback: Callable - 完成时的回调函数(可选)
# timeout: float - 超时时间(可选)
#
# 返回值:
# String - 请求ID
#
# 使用示例:
# var data = {"name": "张三", "age": 25}
# var request_id = NetworkManager.post_request("/api/users", data, my_callback)
func post_request(endpoint: String, data: Dictionary, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String: func post_request(endpoint: String, data: Dictionary, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String:
var body = JSON.stringify(data) var body = JSON.stringify(data)
var headers = ["Content-Type: application/json"] var headers = ["Content-Type: application/json"]
return send_request(endpoint, RequestType.POST, headers, body, callback, timeout) return send_request(endpoint, RequestType.POST, headers, body, callback, timeout)
# 发送PUT请求 # 发送PUT请求
#
# 参数:
# endpoint: String - API端点路径
# data: Dictionary - 要更新的数据
# callback: Callable - 完成时的回调函数(可选)
# timeout: float - 超时时间(可选)
#
# 返回值:
# String - 请求ID
func put_request(endpoint: String, data: Dictionary, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String: func put_request(endpoint: String, data: Dictionary, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String:
var body = JSON.stringify(data) var body = JSON.stringify(data)
var headers = ["Content-Type: application/json"] var headers = ["Content-Type: application/json"]
return send_request(endpoint, RequestType.PUT, headers, body, callback, timeout) return send_request(endpoint, RequestType.PUT, headers, body, callback, timeout)
# 发送DELETE请求 # 发送DELETE请求
#
# 参数:
# endpoint: String - API端点路径
# callback: Callable - 完成时的回调函数(可选)
# timeout: float - 超时时间(可选)
#
# 返回值:
# String - 请求ID
func delete_request(endpoint: String, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String: func delete_request(endpoint: String, callback: Callable = Callable(), timeout: float = DEFAULT_TIMEOUT) -> String:
return send_request(endpoint, RequestType.DELETE, [], "", callback, timeout) return send_request(endpoint, RequestType.DELETE, [], "", callback, timeout)
# ============ 认证相关API ============ # ============ 认证相关API ============
# 用户登录 # 用户登录
#
# 参数:
# identifier: String - 用户标识符(邮箱或手机号)
# password: String - 用户密码
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 回调函数签名:
# func callback(success: bool, data: Dictionary, error_info: Dictionary)
#
# 使用示例:
# NetworkManager.login("user@example.com", "password123", func(success, data, error):
# if success:
# print("登录成功: ", data)
# else:
# print("登录失败: ", error.message)
# )
func login(identifier: String, password: String, callback: Callable = Callable()) -> String: func login(identifier: String, password: String, callback: Callable = Callable()) -> String:
var data = { var data = {
"identifier": identifier, "identifier": identifier,
@@ -91,6 +209,18 @@ func login(identifier: String, password: String, callback: Callable = Callable()
return post_request("/auth/login", data, callback) return post_request("/auth/login", data, callback)
# 验证码登录 # 验证码登录
#
# 参数:
# identifier: String - 用户标识符(邮箱或手机号)
# verification_code: String - 验证码
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 使用场景:
# - 用户忘记密码时的替代登录方式
# - 提供更安全的登录选项
func verification_code_login(identifier: String, verification_code: String, callback: Callable = Callable()) -> String: func verification_code_login(identifier: String, verification_code: String, callback: Callable = Callable()) -> String:
var data = { var data = {
"identifier": identifier, "identifier": identifier,
@@ -99,11 +229,39 @@ func verification_code_login(identifier: String, verification_code: String, call
return post_request("/auth/verification-code-login", data, callback) return post_request("/auth/verification-code-login", data, callback)
# 发送登录验证码 # 发送登录验证码
#
# 参数:
# identifier: String - 用户标识符(邮箱或手机号)
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 向已注册用户发送登录验证码
# - 支持邮箱和手机号
# - 有频率限制保护
func send_login_verification_code(identifier: String, callback: Callable = Callable()) -> String: func send_login_verification_code(identifier: String, callback: Callable = Callable()) -> String:
var data = {"identifier": identifier} var data = {"identifier": identifier}
return post_request("/auth/send-login-verification-code", data, callback) return post_request("/auth/send-login-verification-code", data, callback)
# 用户注册 # 用户注册
#
# 参数:
# username: String - 用户名
# password: String - 密码
# nickname: String - 昵称
# email: String - 邮箱地址(可选)
# email_verification_code: String - 邮箱验证码(可选)
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 注意事项:
# - 如果提供邮箱,建议同时提供验证码
# - 用户名和邮箱必须唯一
# - 密码需要符合安全要求
func register(username: String, password: String, nickname: String, email: String = "", func register(username: String, password: String, nickname: String, email: String = "",
email_verification_code: String = "", callback: Callable = Callable()) -> String: email_verification_code: String = "", callback: Callable = Callable()) -> String:
var data = { var data = {
@@ -112,6 +270,7 @@ func register(username: String, password: String, nickname: String, email: Strin
"nickname": nickname "nickname": nickname
} }
# 可选参数处理
if email != "": if email != "":
data["email"] = email data["email"] = email
if email_verification_code != "": if email_verification_code != "":
@@ -120,11 +279,35 @@ func register(username: String, password: String, nickname: String, email: Strin
return post_request("/auth/register", data, callback) return post_request("/auth/register", data, callback)
# 发送邮箱验证码 # 发送邮箱验证码
#
# 参数:
# email: String - 邮箱地址
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 向指定邮箱发送验证码
# - 用于注册时的邮箱验证
# - 支持测试模式(开发环境)
func send_email_verification(email: String, callback: Callable = Callable()) -> String: func send_email_verification(email: String, callback: Callable = Callable()) -> String:
var data = {"email": email} var data = {"email": email}
return post_request("/auth/send-email-verification", data, callback) return post_request("/auth/send-email-verification", data, callback)
# 验证邮箱 # 验证邮箱
#
# 参数:
# email: String - 邮箱地址
# verification_code: String - 验证码
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 验证邮箱验证码的有效性
# - 通常在注册流程中使用
func verify_email(email: String, verification_code: String, callback: Callable = Callable()) -> String: func verify_email(email: String, verification_code: String, callback: Callable = Callable()) -> String:
var data = { var data = {
"email": email, "email": email,
@@ -133,20 +316,66 @@ func verify_email(email: String, verification_code: String, callback: Callable =
return post_request("/auth/verify-email", data, callback) return post_request("/auth/verify-email", data, callback)
# 获取应用状态 # 获取应用状态
#
# 参数:
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 检查API服务器状态
# - 获取应用基本信息
# - 用于网络连接测试
func get_app_status(callback: Callable = Callable()) -> String: func get_app_status(callback: Callable = Callable()) -> String:
return get_request("/", callback) return get_request("/", callback)
# 重新发送邮箱验证码 # 重新发送邮箱验证码
#
# 参数:
# email: String - 邮箱地址
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 使用场景:
# - 用户未收到验证码时重新发送
# - 验证码过期后重新获取
func resend_email_verification(email: String, callback: Callable = Callable()) -> String: func resend_email_verification(email: String, callback: Callable = Callable()) -> String:
var data = {"email": email} var data = {"email": email}
return post_request("/auth/resend-email-verification", data, callback) return post_request("/auth/resend-email-verification", data, callback)
# 忘记密码 - 发送重置验证码 # 忘记密码 - 发送重置验证码
#
# 参数:
# identifier: String - 用户标识符(邮箱或手机号)
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 向用户发送密码重置验证码
# - 用于密码找回流程的第一步
func forgot_password(identifier: String, callback: Callable = Callable()) -> String: func forgot_password(identifier: String, callback: Callable = Callable()) -> String:
var data = {"identifier": identifier} var data = {"identifier": identifier}
return post_request("/auth/forgot-password", data, callback) return post_request("/auth/forgot-password", data, callback)
# 重置密码 # 重置密码
#
# 参数:
# identifier: String - 用户标识符
# verification_code: String - 重置验证码
# new_password: String - 新密码
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 使用验证码重置用户密码
# - 密码找回流程的第二步
func reset_password(identifier: String, verification_code: String, new_password: String, callback: Callable = Callable()) -> String: func reset_password(identifier: String, verification_code: String, new_password: String, callback: Callable = Callable()) -> String:
var data = { var data = {
"identifier": identifier, "identifier": identifier,
@@ -156,6 +385,19 @@ func reset_password(identifier: String, verification_code: String, new_password:
return post_request("/auth/reset-password", data, callback) return post_request("/auth/reset-password", data, callback)
# 修改密码 # 修改密码
#
# 参数:
# user_id: String - 用户ID
# old_password: String - 旧密码
# new_password: String - 新密码
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 已登录用户修改密码
# - 需要验证旧密码
func change_password(user_id: String, old_password: String, new_password: String, callback: Callable = Callable()) -> String: func change_password(user_id: String, old_password: String, new_password: String, callback: Callable = Callable()) -> String:
var data = { var data = {
"user_id": user_id, "user_id": user_id,
@@ -165,6 +407,21 @@ func change_password(user_id: String, old_password: String, new_password: String
return put_request("/auth/change-password", data, callback) return put_request("/auth/change-password", data, callback)
# GitHub OAuth登录 # GitHub OAuth登录
#
# 参数:
# github_id: String - GitHub用户ID
# username: String - GitHub用户名
# nickname: String - 显示昵称
# email: String - GitHub邮箱
# avatar_url: String - 头像URL可选
# callback: Callable - 完成时的回调函数(可选)
#
# 返回值:
# String - 请求ID
#
# 功能:
# - 通过GitHub账号登录或注册
# - 支持第三方OAuth认证
func github_login(github_id: String, username: String, nickname: String, email: String, avatar_url: String = "", callback: Callable = Callable()) -> String: func github_login(github_id: String, username: String, nickname: String, email: String, avatar_url: String = "", callback: Callable = Callable()) -> String:
var data = { var data = {
"github_id": github_id, "github_id": github_id,
@@ -173,6 +430,7 @@ func github_login(github_id: String, username: String, nickname: String, email:
"email": email "email": email
} }
# 可选头像URL
if avatar_url != "": if avatar_url != "":
data["avatar_url"] = avatar_url data["avatar_url"] = avatar_url

View File

@@ -1,33 +1,96 @@
extends Node extends Node
# 场景管理器 - 负责场景切换和管理 # ============================================================================
# 提供场景切换的统一接口 # SceneManager.gd - 场景管理器
# ============================================================================
# 全局单例管理器,负责场景切换和管理
#
# 核心职责:
# - 场景切换的统一接口
# - 场景路径映射管理
# - 场景切换过渡效果
# - 场景状态跟踪
#
# 使用方式:
# SceneManager.change_scene("main")
# SceneManager.register_scene("custom", "res://scenes/custom.tscn")
#
# 注意事项:
# - 作为自动加载单例,全局可访问
# - 场景切换是异步操作,支持过渡效果
# - 场景名称必须在 scene_paths 中注册
# ============================================================================
# ============ 信号定义 ============
# 场景切换完成信号
# 参数: scene_name - 切换到的场景名称
signal scene_changed(scene_name: String) signal scene_changed(scene_name: String)
# 场景切换开始信号
# 参数: scene_name - 即将切换到的场景名称
signal scene_change_started(scene_name: String) signal scene_change_started(scene_name: String)
var current_scene_name: String = "" # ============ 成员变量 ============
var is_changing_scene: bool = false
# 场景路径映射 # 场景状态
var current_scene_name: String = "" # 当前场景名称
var is_changing_scene: bool = false # 是否正在切换场景
# 场景路径映射表
# 将场景名称映射到实际的文件路径
# 便于统一管理和修改场景路径
var scene_paths: Dictionary = { var scene_paths: Dictionary = {
"main": "res://scenes/maps/main_scene.tscn", "main": "res://scenes/MainScene.tscn", # 主场景 - 游戏入口
"auth": "res://scenes/ui/LoginWindow.tscn", "auth": "res://scenes/ui/LoginWindow.tscn", # 认证场景 - 登录窗口
"game": "res://scenes/maps/game_scene.tscn", "game": "res://scenes/maps/game_scene.tscn", # 游戏场景 - 主要游戏内容
"battle": "res://scenes/maps/battle_scene.tscn", "battle": "res://scenes/maps/battle_scene.tscn", # 战斗场景 - 战斗系统
"inventory": "res://scenes/ui/InventoryWindow.tscn", "inventory": "res://scenes/ui/InventoryWindow.tscn", # 背包界面
"shop": "res://scenes/ui/ShopWindow.tscn", "shop": "res://scenes/ui/ShopWindow.tscn", # 商店界面
"settings": "res://scenes/ui/SettingsWindow.tscn" "settings": "res://scenes/ui/SettingsWindow.tscn" # 设置界面
} }
# ============ 生命周期方法 ============
# 初始化场景管理器
# 在节点准备就绪时调用
func _ready(): func _ready():
print("SceneManager 初始化完成") print("SceneManager 初始化完成")
# ============ 场景切换方法 ============
# 切换到指定场景
#
# 参数:
# scene_name: String - 要切换到的场景名称必须在scene_paths中注册
# use_transition: bool - 是否使用过渡效果默认为true
#
# 返回值:
# bool - 切换是否成功
#
# 功能:
# - 检查场景切换状态和场景是否存在
# - 显示过渡效果(可选)
# - 执行场景切换
# - 更新当前场景状态
# - 发送相关信号
#
# 使用示例:
# var success = SceneManager.change_scene("main", true)
# if success:
# print("场景切换成功")
#
# 注意事项:
# - 场景切换是异步操作
# - 切换过程中会阻止新的切换请求
# - 场景名称必须预先注册
func change_scene(scene_name: String, use_transition: bool = true): func change_scene(scene_name: String, use_transition: bool = true):
# 防止重复切换
if is_changing_scene: if is_changing_scene:
print("场景切换中,忽略新的切换请求") print("场景切换中,忽略新的切换请求")
return false return false
# 检查场景是否存在
if not scene_paths.has(scene_name): if not scene_paths.has(scene_name):
print("错误: 未找到场景 ", scene_name) print("错误: 未找到场景 ", scene_name)
return false return false
@@ -35,40 +98,89 @@ func change_scene(scene_name: String, use_transition: bool = true):
var scene_path = scene_paths[scene_name] var scene_path = scene_paths[scene_name]
print("开始切换场景: ", current_scene_name, " -> ", scene_name) print("开始切换场景: ", current_scene_name, " -> ", scene_name)
# 设置切换状态
is_changing_scene = true is_changing_scene = true
scene_change_started.emit(scene_name) scene_change_started.emit(scene_name)
# 显示过渡效果
if use_transition: if use_transition:
await show_transition() await show_transition()
# 执行场景切换
var error = get_tree().change_scene_to_file(scene_path) var error = get_tree().change_scene_to_file(scene_path)
if error != OK: if error != OK:
print("场景切换失败: ", error) print("场景切换失败: ", error)
is_changing_scene = false is_changing_scene = false
return false return false
# 更新状态
current_scene_name = scene_name current_scene_name = scene_name
is_changing_scene = false is_changing_scene = false
scene_changed.emit(scene_name) scene_changed.emit(scene_name)
# 隐藏过渡效果
if use_transition: if use_transition:
await hide_transition() await hide_transition()
print("场景切换完成: ", scene_name) print("场景切换完成: ", scene_name)
return true return true
# ============ 查询方法 ============
# 获取当前场景名称
#
# 返回值:
# String - 当前场景的名称
func get_current_scene_name() -> String: func get_current_scene_name() -> String:
return current_scene_name return current_scene_name
# ============ 场景注册方法 ============
# 注册新场景
#
# 参数:
# scene_name: String - 场景名称(用于切换时引用)
# scene_path: String - 场景文件路径
#
# 功能:
# - 将场景名称和路径添加到映射表
# - 支持运行时动态注册场景
#
# 使用示例:
# SceneManager.register_scene("boss_battle", "res://scenes/boss/boss_battle.tscn")
func register_scene(scene_name: String, scene_path: String): func register_scene(scene_name: String, scene_path: String):
scene_paths[scene_name] = scene_path scene_paths[scene_name] = scene_path
print("注册场景: ", scene_name, " -> ", scene_path) print("注册场景: ", scene_name, " -> ", scene_path)
# ============ 过渡效果方法 ============
# 显示场景切换过渡效果
#
# 功能:
# - 显示场景切换时的过渡动画
# - 为用户提供视觉反馈
#
# 注意事项:
# - 这是异步方法需要await等待完成
# - 当前实现为简单的延时,可扩展为复杂动画
#
# TODO: 实现淡入淡出、滑动等过渡效果
func show_transition(): func show_transition():
# TODO: 实现场景切换过渡效果 # TODO: 实现场景切换过渡效果
print("显示场景切换过渡效果") print("显示场景切换过渡效果")
await get_tree().create_timer(0.2).timeout await get_tree().create_timer(0.2).timeout
# 隐藏场景切换过渡效果
#
# 功能:
# - 隐藏场景切换完成后的过渡动画
# - 恢复正常的游戏显示
#
# 注意事项:
# - 这是异步方法需要await等待完成
# - 与show_transition()配对使用
#
# TODO: 实现与show_transition()对应的隐藏效果
func hide_transition(): func hide_transition():
# TODO: 隐藏场景切换过渡效果 # TODO: 隐藏场景切换过渡效果
print("隐藏场景切换过渡效果") print("隐藏场景切换过渡效果")

View File

@@ -1,26 +1,102 @@
class_name StringUtils class_name StringUtils
# 字符串工具类 - 提供常用的字符串处理功能 # ============================================================================
# StringUtils.gd - 字符串工具类
# ============================================================================
# 静态工具类,提供常用的字符串处理功能
#
# 核心功能:
# - 输入验证(邮箱、用户名、密码)
# - 字符串格式化和转换
# - 时间格式化和相对时间计算
# - 文件大小格式化
#
# 使用方式:
# var is_valid = StringUtils.is_valid_email("user@example.com")
# var formatted_time = StringUtils.format_utc_to_local_time(utc_string)
#
# 注意事项:
# - 所有方法都是静态的,无需实例化
# - 验证方法返回布尔值或包含详细信息的字典
# - 时间处理方法支持UTC到本地时间的转换
# ============================================================================
# ============ 输入验证方法 ============
# 验证邮箱格式 # 验证邮箱格式
#
# 参数:
# email: String - 待验证的邮箱地址
#
# 返回值:
# bool - true表示格式正确false表示格式错误
#
# 验证规则:
# - 必须包含@符号
# - @前后都必须有内容
# - 域名部分必须包含至少一个点
# - 顶级域名至少2个字符
#
# 使用示例:
# if StringUtils.is_valid_email("user@example.com"):
# print("邮箱格式正确")
static func is_valid_email(email: String) -> bool: static func is_valid_email(email: String) -> bool:
var regex = RegEx.new() var regex = RegEx.new()
regex.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") regex.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")
return regex.search(email) != null return regex.search(email) != null
# 验证用户名格式(字母、数字、下划线) # 验证用户名格式
#
# 参数:
# username: String - 待验证的用户名
#
# 返回值:
# bool - true表示格式正确false表示格式错误
#
# 验证规则:
# - 只能包含字母、数字、下划线
# - 长度不能为空且不超过50个字符
# - 不能包含空格或特殊字符
#
# 使用示例:
# if StringUtils.is_valid_username("user_123"):
# print("用户名格式正确")
static func is_valid_username(username: String) -> bool: static func is_valid_username(username: String) -> bool:
# 检查长度
if username.is_empty() or username.length() > 50: if username.is_empty() or username.length() > 50:
return false return false
# 检查字符组成
var regex = RegEx.new() var regex = RegEx.new()
regex.compile("^[a-zA-Z0-9_]+$") regex.compile("^[a-zA-Z0-9_]+$")
return regex.search(username) != null return regex.search(username) != null
# 验证密码强度 # 验证密码强度
#
# 参数:
# password: String - 待验证的密码
#
# 返回值:
# Dictionary - 包含验证结果的详细信息
# {
# "valid": bool, # 是否符合最低要求
# "message": String, # 验证结果消息
# "strength": int # 强度等级 (1-4)
# }
#
# 验证规则:
# - 最少8位最多128位
# - 必须包含字母和数字
# - 强度评级:包含字母(+1)、数字(+1)、特殊字符(+1)、长度>=12(+1)
#
# 使用示例:
# var result = StringUtils.validate_password_strength("MyPass123!")
# if result.valid:
# print("密码强度: ", result.message)
static func validate_password_strength(password: String) -> Dictionary: static func validate_password_strength(password: String) -> Dictionary:
var result = {"valid": false, "message": "", "strength": 0} var result = {"valid": false, "message": "", "strength": 0}
# 检查长度限制
if password.length() < 8: if password.length() < 8:
result.message = "密码长度至少8位" result.message = "密码长度至少8位"
return result return result
@@ -29,9 +105,10 @@ static func validate_password_strength(password: String) -> Dictionary:
result.message = "密码长度不能超过128位" result.message = "密码长度不能超过128位"
return result return result
var has_letter = false # 检查字符类型
var has_digit = false var has_letter = false # 是否包含字母
var has_special = false var has_digit = false # 是否包含数字
var has_special = false # 是否包含特殊字符
for i in range(password.length()): for i in range(password.length()):
var character = password[i] var character = password[i]
@@ -42,6 +119,7 @@ static func validate_password_strength(password: String) -> Dictionary:
elif character in "!@#$%^&*()_+-=[]{}|;:,.<>?": elif character in "!@#$%^&*()_+-=[]{}|;:,.<>?":
has_special = true has_special = true
# 计算强度等级
var strength = 0 var strength = 0
if has_letter: if has_letter:
strength += 1 strength += 1
@@ -54,27 +132,72 @@ static func validate_password_strength(password: String) -> Dictionary:
result.strength = strength result.strength = strength
# 检查最低要求
if not (has_letter and has_digit): if not (has_letter and has_digit):
result.message = "密码必须包含字母和数字" result.message = "密码必须包含字母和数字"
return result return result
# 密码符合要求
result.valid = true result.valid = true
result.message = "密码强度: " + ["", "", "", "很强"][min(strength - 1, 3)] result.message = "密码强度: " + ["", "", "", "很强"][min(strength - 1, 3)]
return result return result
# ============ 字符串格式化方法 ============
# 截断字符串 # 截断字符串
#
# 参数:
# text: String - 原始字符串
# max_length: int - 最大长度
# suffix: String - 截断后缀(默认为"..."
#
# 返回值:
# String - 截断后的字符串
#
# 功能:
# - 如果字符串长度超过限制,截断并添加后缀
# - 如果字符串长度未超过限制,返回原字符串
#
# 使用示例:
# var short_text = StringUtils.truncate("这是一个很长的文本", 10, "...")
# # 结果: "这是一个很长..."
static func truncate(text: String, max_length: int, suffix: String = "...") -> String: static func truncate(text: String, max_length: int, suffix: String = "...") -> String:
if text.length() <= max_length: if text.length() <= max_length:
return text return text
return text.substr(0, max_length - suffix.length()) + suffix return text.substr(0, max_length - suffix.length()) + suffix
# 首字母大写 # 首字母大写
#
# 参数:
# text: String - 原始字符串
#
# 返回值:
# String - 首字母大写的字符串
#
# 使用示例:
# var capitalized = StringUtils.capitalize_first("hello world")
# # 结果: "Hello world"
static func capitalize_first(text: String) -> String: static func capitalize_first(text: String) -> String:
if text.is_empty(): if text.is_empty():
return text return text
return text[0].to_upper() + text.substr(1) return text[0].to_upper() + text.substr(1)
# 转换为标题格式(每个单词首字母大写) # 转换为标题格式
#
# 参数:
# text: String - 原始字符串
#
# 返回值:
# String - 每个单词首字母大写的字符串
#
# 功能:
# - 将每个单词的首字母转换为大写
# - 其余字母转换为小写
# - 以空格分隔单词
#
# 使用示例:
# var title = StringUtils.to_title_case("hello world game")
# # 结果: "Hello World Game"
static func to_title_case(text: String) -> String: static func to_title_case(text: String) -> String:
var words = text.split(" ") var words = text.split(" ")
var result = [] var result = []
@@ -84,27 +207,75 @@ static func to_title_case(text: String) -> String:
return " ".join(result) return " ".join(result)
# 移除HTML标签 # 移除HTML标签
#
# 参数:
# html: String - 包含HTML标签的字符串
#
# 返回值:
# String - 移除HTML标签后的纯文本
#
# 功能:
# - 使用正则表达式移除所有HTML标签
# - 保留标签之间的文本内容
#
# 使用示例:
# var plain_text = StringUtils.strip_html_tags("<p>Hello <b>World</b></p>")
# # 结果: "Hello World"
static func strip_html_tags(html: String) -> String: static func strip_html_tags(html: String) -> String:
var regex = RegEx.new() var regex = RegEx.new()
regex.compile("<[^>]*>") regex.compile("<[^>]*>")
return regex.sub(html, "", true) return regex.sub(html, "", true)
# 格式化文件大小 # 格式化文件大小
#
# 参数:
# bytes: int - 文件大小(字节)
#
# 返回值:
# String - 格式化后的文件大小字符串
#
# 功能:
# - 自动选择合适的单位B, KB, MB, GB, TB
# - 保留一位小数(除了字节)
# - 使用1024作为换算基数
#
# 使用示例:
# var size_text = StringUtils.format_file_size(1536)
# # 结果: "1.5 KB"
static func format_file_size(bytes: int) -> String: static func format_file_size(bytes: int) -> String:
var units = ["B", "KB", "MB", "GB", "TB"] var units = ["B", "KB", "MB", "GB", "TB"]
var size = float(bytes) var size = float(bytes)
var unit_index = 0 var unit_index = 0
# 自动选择合适的单位
while size >= 1024.0 and unit_index < units.size() - 1: while size >= 1024.0 and unit_index < units.size() - 1:
size /= 1024.0 size /= 1024.0
unit_index += 1 unit_index += 1
# 格式化输出
if unit_index == 0: if unit_index == 0:
return str(int(size)) + " " + units[unit_index] return str(int(size)) + " " + units[unit_index]
else: else:
return "%.1f %s" % [size, units[unit_index]] return "%.1f %s" % [size, units[unit_index]]
# ============ 时间处理方法 ============
# 将UTC时间字符串转换为本地时间显示 # 将UTC时间字符串转换为本地时间显示
#
# 参数:
# utc_time_str: String - UTC时间字符串格式: 2025-12-25T11:23:52.175Z
#
# 返回值:
# String - 格式化的本地时间字符串
#
# 功能:
# - 解析ISO 8601格式的UTC时间
# - 转换为本地时区时间
# - 格式化为易读的中文时间格式
#
# 使用示例:
# var local_time = StringUtils.format_utc_to_local_time("2025-12-25T11:23:52.175Z")
# # 结果: "2025年12月25日 19:23:52" (假设本地时区为UTC+8)
static func format_utc_to_local_time(utc_time_str: String) -> String: static func format_utc_to_local_time(utc_time_str: String) -> String:
# 解析UTC时间字符串 (格式: 2025-12-25T11:23:52.175Z) # 解析UTC时间字符串 (格式: 2025-12-25T11:23:52.175Z)
var regex = RegEx.new() var regex = RegEx.new()
@@ -148,7 +319,22 @@ static func format_utc_to_local_time(utc_time_str: String) -> String:
local_dict.second local_dict.second
] ]
# 获取相对时间描述(多少分钟后) # 获取相对时间描述
#
# 参数:
# utc_time_str: String - UTC时间字符串
#
# 返回值:
# String - 相对时间描述(如"5分钟后"、"2小时30分钟后"
#
# 功能:
# - 计算指定时间与当前时间的差值
# - 返回人性化的相对时间描述
# - 支持秒、分钟、小时的组合显示
#
# 使用示例:
# var relative_time = StringUtils.get_relative_time_until("2025-12-25T12:00:00Z")
# # 结果: "30分钟后" 或 "现在可以重试"
static func get_relative_time_until(utc_time_str: String) -> String: static func get_relative_time_until(utc_time_str: String) -> String:
# 解析UTC时间字符串 # 解析UTC时间字符串
var regex = RegEx.new() var regex = RegEx.new()
@@ -183,6 +369,7 @@ static func get_relative_time_until(utc_time_str: String) -> String:
# 计算时间差(秒) # 计算时间差(秒)
var diff_seconds = target_timestamp - current_timestamp var diff_seconds = target_timestamp - current_timestamp
# 格式化相对时间
if diff_seconds <= 0: if diff_seconds <= 0:
return "现在可以重试" return "现在可以重试"
elif diff_seconds < 60: elif diff_seconds < 60: