refactor: harden client runtime and exports

This commit is contained in:
ANG-Server
2026-07-23 00:59:00 +08:00
parent 5d2339a29b
commit fc872fe8af
27 changed files with 902 additions and 856 deletions

View File

@@ -63,10 +63,7 @@ signal chat_position_updated(stream: String, topic: String)
# ============================================================================
const CHAT_WEBSOCKET_MANAGER_SCRIPT: Script = preload("res://_Core/managers/WebSocketManager.gd")
const NetworkConfig = preload("res://_Core/utils/NetworkConfig.gd")
# WebSocket 服务器 URL原生 WebSocket
const WEBSOCKET_URL: String = "wss://whaletownend.xinghangee.icu/game"
const ChatMessageCodec = preload("res://_Core/chat/ChatMessageCodec.gd")
# 重连配置
const RECONNECT_MAX_ATTEMPTS: int = 5
@@ -107,7 +104,6 @@ const CHAT_ERROR_MESSAGES: Dictionary = {
# WebSocket 管理器
var _websocket_manager: Node
var _history_request: HTTPRequest
# 是否已登录
var _is_logged_in: bool = false
@@ -119,6 +115,7 @@ var _message_history: Array[Dictionary] = []
var _history_loading: bool = false
var _has_more_history: bool = true
var _oldest_message_timestamp: float = 0.0
var _history_request_generation: int = 0
# 消息发送时间戳(用于频率限制)
var _message_timestamps: Array[float] = []
@@ -160,10 +157,6 @@ func _ready() -> void:
# 创建 WebSocket 管理器
_websocket_manager = CHAT_WEBSOCKET_MANAGER_SCRIPT.new()
add_child(_websocket_manager)
_history_request = HTTPRequest.new()
_history_request.timeout = 12.0
_history_request.request_completed.connect(_on_history_request_completed)
add_child(_history_request)
# 连接信号
_connect_signals()
@@ -661,6 +654,7 @@ func clear_message_history() -> void:
# - 用户登录成功后
# - 重新连接到聊天服务器后
func reset_session() -> void:
_history_request_generation += 1
_message_history.clear()
_history_loading = false
_has_more_history = true
@@ -695,59 +689,31 @@ func load_history(count: int = HISTORY_PAGE_SIZE) -> void:
var mapId := _current_map.strip_edges()
if mapId.is_empty():
mapId = "whale_port"
var url := "%s/chat/history?mapId=%s&limit=%d&offset=%d" % [
NetworkConfig.get_api_base_url(),
var endpoint := "/chat/history?mapId=%s&limit=%d&offset=%d" % [
mapId.uri_encode(),
max(1, count),
_message_history.size(),
]
var err := _history_request.request(url, PackedStringArray([
"Accept: application/json",
"Authorization: Bearer %s" % token,
]), HTTPClient.METHOD_GET)
if err != OK:
_history_loading = false
_handle_error("INTERNAL_ERROR", "聊天历史请求发送失败")
var request_generation := _history_request_generation
ApiClient.get_json(endpoint, _on_history_request_completed.bind(request_generation))
func _on_history_request_completed(result: int, responseCode: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
if result != HTTPRequest.RESULT_SUCCESS or responseCode < 200 or responseCode >= 300:
_history_loading = false
_handle_error("INTERNAL_ERROR", "聊天历史读取失败")
func _on_history_request_completed(success: bool, response: Dictionary, error_info: Dictionary, request_generation: int) -> void:
if request_generation != _history_request_generation:
return
var json := JSON.new()
if json.parse(body.get_string_from_utf8()) != OK or not (json.data is Dictionary):
if not success:
_history_loading = false
_handle_error("INTERNAL_ERROR", "聊天历史响应解析失败")
_handle_error("INTERNAL_ERROR", str(error_info.get("message", "聊天历史读取失败")))
return
var response: Dictionary = json.data
var messagesVariant: Variant = response.get("messages", [])
var dataVariant: Variant = response.get("data", {})
if messagesVariant is Array and (messagesVariant as Array).is_empty() and dataVariant is Dictionary:
messagesVariant = (dataVariant as Dictionary).get("messages", [])
if messagesVariant is Array:
_on_history_loaded(_normalize_history_messages(messagesVariant as Array))
_on_history_loaded(ChatMessageCodec.normalize_history(messagesVariant as Array, _current_username))
return
_history_loading = false
_has_more_history = false
func _normalize_history_messages(messages: Array) -> Array:
var normalized: Array = []
for messageVariant in messages:
if not (messageVariant is Dictionary):
continue
var message: Dictionary = messageVariant
normalized.append({
"from_user": str(message.get("sender", message.get("from_user", ""))),
"from_user_id": str(message.get("fromUserId", message.get("from_user_id", ""))),
"content": str(message.get("content", "")),
"timestamp": _parse_chat_timestamp_to_unix(message.get("timestamp", 0.0)),
"is_self": str(message.get("sender", "")) == _current_username,
"scope": str(message.get("scope", "local")),
"show_bubble": bool(message.get("bubble", false)),
"is_history": true,
})
return normalized
# 历史消息加载完成回调
func _on_history_loaded(messages: Array) -> void:
_history_loading = false
@@ -1061,7 +1027,7 @@ func _handle_system_presence(data: Dictionary) -> void:
var content := str(data.get("content", "")).strip_edges()
if content.is_empty():
return
var timestamp := _parse_chat_timestamp_to_unix(data.get("timestamp", 0.0))
var timestamp := ChatMessageCodec.parse_timestamp(data.get("timestamp", 0.0))
var message := {
"from_user": "系统",
"from_user_id": "",
@@ -1265,7 +1231,7 @@ func _handle_chat_render(data: Dictionary) -> void:
var private_context: String = str(data.get("privateContext", data.get("private_context", ""))).strip_edges()
var is_private: bool = scope == "private"
var timestamp: float = _parse_chat_timestamp_to_unix(data.get("timestamp", 0.0))
var timestamp: float = ChatMessageCodec.parse_timestamp(data.get("timestamp", 0.0))
var is_self: bool = (not _current_username.is_empty() and from_user == _current_username)
if is_self and _consume_pending_self_message(content, scope, to_user_id):
@@ -1344,40 +1310,6 @@ func _current_user_id() -> String:
return str((user_variant as Dictionary).get("id", ""))
return ""
# 解析聊天消息时间戳(兼容 unix 秒 / ISO 8601 字符串)
func _parse_chat_timestamp_to_unix(timestamp_raw: Variant) -> float:
if typeof(timestamp_raw) == TYPE_INT or typeof(timestamp_raw) == TYPE_FLOAT:
var ts := float(timestamp_raw)
return ts if ts > 0.0 else Time.get_unix_time_from_system()
var ts_str := str(timestamp_raw)
if ts_str.strip_edges().is_empty():
return Time.get_unix_time_from_system()
# 纯数字字符串(必须整串都是数字/小数点,避免把 ISO 字符串前缀 "2026" 误判成时间戳)
var numeric_regex := RegEx.new()
numeric_regex.compile("^\\s*-?\\d+(?:\\.\\d+)?\\s*$")
if numeric_regex.search(ts_str) != null:
var ts_num := float(ts_str)
return ts_num if ts_num > 0.0 else Time.get_unix_time_from_system()
# ISO 8601: 2026-01-19T15:15:43.930Z
var regex := RegEx.new()
regex.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})")
var result := regex.search(ts_str)
if result == null:
return Time.get_unix_time_from_system()
var utc_dict := {
"year": int(result.get_string(1)),
"month": int(result.get_string(2)),
"day": int(result.get_string(3)),
"hour": int(result.get_string(4)),
"minute": int(result.get_string(5)),
"second": int(result.get_string(6))
}
return Time.get_unix_time_from_datetime_dict(utc_dict)
# 处理位置更新成功
func _handle_position_updated(data: Dictionary) -> void:
var stream: String = data.get("stream", "")