refactor: harden client runtime and exports
This commit is contained in:
@@ -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", "")
|
||||
|
||||
Reference in New Issue
Block a user