173 lines
4.7 KiB
GDScript
173 lines
4.7 KiB
GDScript
extends Node
|
|
|
|
# ============================================================================
|
|
# WebSocketManager.gd - WebSocket连接管理器
|
|
# ============================================================================
|
|
# 负责与后端 Native WebSocket 服务进行实时通信
|
|
#
|
|
# 协议文档: new_docs/game_architecture_design.md
|
|
# 后端地址: ws://localhost:3000/location-broadcast
|
|
# ============================================================================
|
|
|
|
signal connected_to_server()
|
|
signal connection_closed()
|
|
signal connection_error()
|
|
signal session_joined(data: Dictionary)
|
|
signal user_joined(data: Dictionary)
|
|
signal user_left(data: Dictionary)
|
|
signal position_updated(data: Dictionary)
|
|
|
|
const WS_URL = "wss://whaletownend.xinghangee.icu/location-broadcast"
|
|
const PING_INTERVAL = 25.0 # 秒
|
|
|
|
var _socket: WebSocketPeer
|
|
var _connected: bool = false
|
|
var _ping_timer: float = 0.0
|
|
var _auth_token: String = ""
|
|
|
|
func _ready():
|
|
_socket = WebSocketPeer.new()
|
|
process_mode = Node.PROCESS_MODE_ALWAYS # 保证暂停时也能处理网络
|
|
print("WebSocketManager Initialized")
|
|
|
|
func _process(delta):
|
|
_socket.poll()
|
|
var state = _socket.get_ready_state()
|
|
|
|
if state == WebSocketPeer.STATE_OPEN:
|
|
if not _connected:
|
|
_on_connected()
|
|
|
|
# 处理接收到的数据包
|
|
while _socket.get_available_packet_count() > 0:
|
|
var packet = _socket.get_packet()
|
|
_handle_packet(packet)
|
|
|
|
# 心跳处理
|
|
_ping_timer += delta
|
|
if _ping_timer >= PING_INTERVAL:
|
|
_send_heartbeat()
|
|
_ping_timer = 0.0
|
|
|
|
elif state == WebSocketPeer.STATE_CLOSED:
|
|
if _connected:
|
|
_on_disconnected()
|
|
|
|
func connect_to_server():
|
|
if _socket.get_ready_state() == WebSocketPeer.STATE_OPEN:
|
|
print("WebSocket 已经是连接状态")
|
|
return
|
|
|
|
print("正在连接 WebSocket: ", WS_URL)
|
|
var err = _socket.connect_to_url(WS_URL)
|
|
if err != OK:
|
|
print("WebSocket 连接请求失败: ", err)
|
|
connection_error.emit()
|
|
else:
|
|
# Godot WebSocket connect is non-blocking, wait for state change in _process
|
|
pass
|
|
|
|
func close_connection():
|
|
_socket.close()
|
|
|
|
func set_auth_token(token: String):
|
|
_auth_token = token
|
|
|
|
# ============ 协议发送 ============
|
|
|
|
func send_packet(event: String, data: Dictionary):
|
|
if _socket.get_ready_state() != WebSocketPeer.STATE_OPEN:
|
|
return
|
|
|
|
var message = {
|
|
"event": event,
|
|
"data": data
|
|
}
|
|
var json_str = JSON.stringify(message)
|
|
_socket.put_packet(json_str.to_utf8_buffer())
|
|
|
|
func join_session(map_id: String, initial_pos: Vector2):
|
|
var data = {
|
|
"sessionId": map_id,
|
|
"initialPosition": {
|
|
"x": initial_pos.x,
|
|
"y": initial_pos.y,
|
|
"mapId": map_id
|
|
},
|
|
"token": _auth_token
|
|
}
|
|
print("发送加入会话请求: ", map_id, " mapId Payload: ", data.initialPosition.mapId)
|
|
send_packet("join_session", data)
|
|
|
|
func leave_session(map_id: String):
|
|
print("发送离开会话请求: ", map_id)
|
|
send_packet("leave_session", {"sessionId": map_id})
|
|
|
|
func send_position_update(map_id: String, pos: Vector2, anim_data: Dictionary = {}):
|
|
var data = {
|
|
"x": pos.x,
|
|
"y": pos.y,
|
|
"mapId": map_id,
|
|
"metadata": anim_data
|
|
}
|
|
# print("发送位置更新: ", map_id)
|
|
if map_id == "":
|
|
print("WARNING: Sending position update with EMPTY mapId! Pos: ", pos)
|
|
send_packet("position_update", data)
|
|
|
|
func _send_heartbeat():
|
|
send_packet("heartbeat", {"timestamp": Time.get_unix_time_from_system()})
|
|
|
|
# ============ 事件处理 ============
|
|
|
|
func _on_connected():
|
|
_connected = true
|
|
print("WebSocket 连接成功!")
|
|
connected_to_server.emit()
|
|
|
|
func _on_disconnected():
|
|
_connected = false
|
|
var code = _socket.get_close_code()
|
|
var reason = _socket.get_close_reason()
|
|
print("WebSocket 连接断开. Code: %d, Reason: %s" % [code, reason])
|
|
connection_closed.emit()
|
|
|
|
func _handle_packet(packet: PackedByteArray):
|
|
var json_str = packet.get_string_from_utf8()
|
|
var json = JSON.new()
|
|
var err = json.parse(json_str)
|
|
|
|
if err != OK:
|
|
print("JSON 解析失败: ", json.get_error_message())
|
|
return
|
|
|
|
var message = json.data
|
|
if not message is Dictionary or not message.has("event"):
|
|
return
|
|
|
|
var event = message["event"]
|
|
var data = message.get("data", {})
|
|
|
|
if event != "heartbeat_response":
|
|
print("WebSocket Rx: ", event) # Debug logs for all events
|
|
|
|
match event:
|
|
"session_joined":
|
|
session_joined.emit(data)
|
|
print("加入会话成功,当前房间人数: ", data.get("users", []).size())
|
|
"user_joined":
|
|
user_joined.emit(data)
|
|
print("用户加入: ", data.get("userId"))
|
|
"user_left":
|
|
user_left.emit(data)
|
|
print("用户离开: ", data.get("userId"))
|
|
"position_update":
|
|
print("WebSocket Rx position_update: ", data.get("userId", "unknown"))
|
|
position_updated.emit(data)
|
|
"heartbeat_response":
|
|
pass # 静默处理
|
|
"error":
|
|
print("WebSocket 错误事件: ", JSON.stringify(data))
|
|
_:
|
|
print("未处理的 WebSocket 事件: ", event)
|