feat:增加多角色在线功能
- 增加远程登录角色精灵 - 基于后端接口完成位置同步 - 实现多人在线以及跳转 - 增加个人房间功能
This commit is contained in:
172
_Core/managers/WebSocketManager.gd
Normal file
172
_Core/managers/WebSocketManager.gd
Normal file
@@ -0,0 +1,172 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user