2026-06-19 18:18:04 +08:00
|
|
|
|
using Assets.MyGame.Scripts.Net.WebResponses;
|
2026-02-03 11:35:19 +08:00
|
|
|
|
using Best.WebSockets;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2026-03-09 09:25:01 +08:00
|
|
|
|
using Unity.VisualScripting;
|
2026-02-03 11:35:19 +08:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.SceneManagement;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Assets.MyGame.Scripts.Net
|
|
|
|
|
|
{
|
|
|
|
|
|
public enum SocketType
|
|
|
|
|
|
{
|
|
|
|
|
|
login,
|
|
|
|
|
|
room,
|
|
|
|
|
|
game,
|
2026-04-15 18:30:37 +08:00
|
|
|
|
chess,
|
|
|
|
|
|
customer,
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public partial class WebSocketManager : MonoSingleton<WebSocketManager>
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly Dictionary<SocketType, WebSocket> sockets = new();
|
|
|
|
|
|
private readonly Dictionary<SocketType, string> socketPaths = new();
|
2026-07-17 17:50:31 +08:00
|
|
|
|
private readonly Dictionary<SocketType, string> socketUrls = new();
|
|
|
|
|
|
private readonly Dictionary<SocketType, Action> handshakeActions = new();
|
|
|
|
|
|
private readonly HashSet<SocketType> handshakeCompleted = new();
|
|
|
|
|
|
private readonly HashSet<SocketType> reconnecting = new();
|
|
|
|
|
|
private readonly HashSet<SocketType> manualClosing = new();
|
2026-08-01 15:27:12 +08:00
|
|
|
|
/// <summary>登出标志:true 时所有 socket 关闭后都不触发自动重连。
|
|
|
|
|
|
/// Logout 开始时设 true,切回登录界面后设 false,避免关连接时误触发重连访问已 null 的 user。</summary>
|
|
|
|
|
|
public bool isLoggingOut = false;
|
|
|
|
|
|
/// <summary>每个 socketType 正在运行的重连协程引用,用于主动 StopCoroutine</summary>
|
|
|
|
|
|
private readonly Dictionary<SocketType, Coroutine> reconnectCoroutines = new();
|
2026-10-05 01:02:55 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>重连状态变化事件(type=哪个 socket;true=开始自动重连 / false=重连结束-成功或取消)。
|
|
|
|
|
|
/// 界面(如游戏面板的 Hl_WaitTip"正在连接服务器"提示)据此显隐;与重连生命周期严格一致。</summary>
|
|
|
|
|
|
public static event Action<SocketType, bool> OnReconnectingChanged;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>统一维护重连集合并广播变化:reconnecting 的所有 Add/Remove 入口都走这里,避免漏发事件</summary>
|
|
|
|
|
|
private bool SetReconnecting(SocketType type, bool value)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool changed = value ? reconnecting.Add(type) : reconnecting.Remove(type);
|
|
|
|
|
|
if (changed) OnReconnectingChanged?.Invoke(type, value);
|
|
|
|
|
|
return changed;
|
|
|
|
|
|
}
|
2026-07-17 17:50:31 +08:00
|
|
|
|
private float nextHeartbeatTime;
|
|
|
|
|
|
private const float HeartbeatIntervalSeconds = 20f;
|
2026-08-04 16:57:05 +08:00
|
|
|
|
#if NewIP
|
2026-08-03 17:00:34 +08:00
|
|
|
|
private const string baseUrl = "ws://47.109.34.50";
|
|
|
|
|
|
//private const string baseUrl = "ws://47.109.132.12";
|
2026-05-27 19:47:06 +08:00
|
|
|
|
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#else
|
2026-08-03 17:00:34 +08:00
|
|
|
|
private const string baseUrl = "ws://47.109.132.12";
|
2026-07-26 22:00:38 +08:00
|
|
|
|
|
2026-08-03 17:00:34 +08:00
|
|
|
|
//private const string baseUrl = "ws://47.109.34.50";
|
2026-06-13 01:25:41 +08:00
|
|
|
|
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#endif
|
2026-07-17 17:50:31 +08:00
|
|
|
|
public bool isGame = false;
|
2026-02-03 11:35:19 +08:00
|
|
|
|
protected override void Awake()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.Awake();
|
|
|
|
|
|
|
2026-06-25 23:18:14 +08:00
|
|
|
|
socketPaths[SocketType.game] = "/fishinghub";
|
2026-02-09 10:44:38 +08:00
|
|
|
|
socketPaths[SocketType.room] = "/fishingroomhub";
|
|
|
|
|
|
socketPaths[SocketType.login] = "/loginhub";
|
2026-02-13 19:23:01 +08:00
|
|
|
|
socketPaths[SocketType.chess] = "/chessgamehub";
|
2026-04-15 18:30:37 +08:00
|
|
|
|
socketPaths[SocketType.customer] = "/customerHub";
|
2026-07-17 17:50:31 +08:00
|
|
|
|
nextHeartbeatTime = Time.realtimeSinceStartup + HeartbeatIntervalSeconds;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-06 01:14:13 +08:00
|
|
|
|
/// <summary>获取指定类型 Socket 的完整连接地址(含 baseUrl,供外部拼接查询参数,如象棋 Hub 的 ?userId=)</summary>
|
|
|
|
|
|
public string GetSocketPath(SocketType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
return baseUrl + socketPaths[type];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-17 17:50:31 +08:00
|
|
|
|
private void Update()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Time.realtimeSinceStartup < nextHeartbeatTime)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
nextHeartbeatTime = Time.realtimeSinceStartup + HeartbeatIntervalSeconds;
|
|
|
|
|
|
foreach (var pair in sockets)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pair.Value != null && pair.Value.IsOpen && handshakeCompleted.Contains(pair.Key))
|
|
|
|
|
|
pair.Value.Send("{\"type\":6}\u001e");
|
|
|
|
|
|
}
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建或返回已存在的 WebSocket
|
|
|
|
|
|
/// </summary>
|
2026-07-17 17:50:31 +08:00
|
|
|
|
public WebSocket Connect(SocketType type, Action action = null)
|
2026-07-10 23:26:42 +08:00
|
|
|
|
{
|
|
|
|
|
|
return Connect(type, null, action);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建或返回已存在的 WebSocket(可指定动态 URL,用于 /api/Room/Assign 返回的房间专属地址)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public WebSocket Connect(SocketType type, string url, Action action = null)
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 已存在则直接返回
|
|
|
|
|
|
if (sockets.TryGetValue(type, out var exist))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (exist.IsOpen)
|
|
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#if UNITY_EDITOR
|
2026-02-03 11:35:19 +08:00
|
|
|
|
Debug.Log($"[WebSocket] {type} 已连接,直接返回");
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
2026-02-13 19:23:01 +08:00
|
|
|
|
switch (type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SocketType.login:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SocketType.room:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SocketType.game:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SocketType.chess:
|
2026-07-08 20:49:22 +08:00
|
|
|
|
UIManager.Instance.Get<ChineseChessMainPanel>(UIPanel.MainGamePanelZGXQ).subUIManager.ShowAsync(UIPanel.ZGXQDistrict);
|
2026-02-13 19:23:01 +08:00
|
|
|
|
break;
|
2026-04-15 18:30:37 +08:00
|
|
|
|
case SocketType.customer:
|
|
|
|
|
|
break;
|
2026-02-13 19:23:01 +08:00
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2026-07-05 16:07:22 +08:00
|
|
|
|
if (action != null)
|
|
|
|
|
|
action?.Invoke();
|
2026-02-03 11:35:19 +08:00
|
|
|
|
return exist;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 23:26:42 +08:00
|
|
|
|
// 未指定 url 时用默认 baseUrl + socketPaths
|
|
|
|
|
|
if (string.IsNullOrEmpty(url))
|
|
|
|
|
|
url = baseUrl + socketPaths[type];
|
2026-07-17 17:50:31 +08:00
|
|
|
|
socketUrls[type] = url;
|
|
|
|
|
|
handshakeActions[type] = action;
|
2026-02-03 11:35:19 +08:00
|
|
|
|
var ws = new WebSocket(new Uri(url));
|
2026-07-24 01:37:11 +08:00
|
|
|
|
// Best.WebSockets 1.22.3 bug:服务器返回 3xx 重定向时,InternalRequest_OnBeforeRedirection
|
|
|
|
|
|
// 会再次调用 StartOpen(),但实例已 open 过,抛 "Open already called!"。
|
|
|
|
|
|
// 这里禁用内部 HTTP 请求的重定向跟随来规避。
|
|
|
|
|
|
ws.OnInternalRequestCreated += (socket, req) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
req.RedirectSettings.MaxRedirects = 0;
|
|
|
|
|
|
};
|
2026-02-03 11:35:19 +08:00
|
|
|
|
sockets[type] = ws;
|
|
|
|
|
|
// 事件绑定
|
|
|
|
|
|
ws.OnOpen += (web) =>
|
|
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#if UNITY_EDITOR
|
2026-02-03 11:35:19 +08:00
|
|
|
|
Debug.Log($"[WebSocket] {type} 已连接: {url}");
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
2026-10-05 01:02:55 +08:00
|
|
|
|
SetReconnecting(type, false);
|
2026-07-17 17:50:31 +08:00
|
|
|
|
handshakeCompleted.Remove(type);
|
|
|
|
|
|
// SignalR 握手完成前不能发送 Bind、JoinRoom 等 Hub 调用。
|
|
|
|
|
|
web.Send("{\"protocol\":\"json\",\"version\":1}\u001e");
|
2026-02-03 11:35:19 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
ws.OnClosed += (web, code, reason) =>
|
|
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#if UNITY_EDITOR
|
2026-02-03 11:35:19 +08:00
|
|
|
|
Debug.Log($"[WebSocket] {type} 连接关闭 Code:{code} Reason:{reason}");
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
2026-07-17 17:50:31 +08:00
|
|
|
|
handshakeCompleted.Remove(type);
|
|
|
|
|
|
if (sockets.TryGetValue(type, out var current) && ReferenceEquals(current, web))
|
|
|
|
|
|
sockets.Remove(type);
|
|
|
|
|
|
if (manualClosing.Remove(type))
|
|
|
|
|
|
return;
|
2026-08-01 15:27:12 +08:00
|
|
|
|
// ⭐ 登出中或未登录:不触发自动重连。
|
|
|
|
|
|
// isLoggingOut 可能因 ws.Close 异步导致 OnClosed 触发时已重置,
|
|
|
|
|
|
// 所以同时检查 user==null 做双重保险(登出后 user 必为 null)
|
|
|
|
|
|
if (isLoggingOut || GameManager.Instance.user == null)
|
|
|
|
|
|
return;
|
2026-07-17 17:50:31 +08:00
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
// 自动重连
|
2026-03-18 17:26:14 +08:00
|
|
|
|
switch (type)
|
2026-03-09 09:25:01 +08:00
|
|
|
|
{
|
2026-03-18 17:26:14 +08:00
|
|
|
|
case SocketType.login:
|
2026-07-17 17:50:31 +08:00
|
|
|
|
StartReconnect(type);
|
2026-03-18 17:26:14 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case SocketType.room:
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SocketType.game:
|
|
|
|
|
|
if (isGame)
|
|
|
|
|
|
{
|
|
|
|
|
|
int sitpos = 0;
|
|
|
|
|
|
foreach (var item in GameManager.Instance.gameData.currRoomData.players)
|
|
|
|
|
|
{
|
2026-07-17 17:50:31 +08:00
|
|
|
|
if (item.userId == GameManager.Instance.user.id)
|
2026-03-18 17:26:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
sitpos = item.position; break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-17 17:50:31 +08:00
|
|
|
|
StartReconnect(type, () =>
|
2026-03-18 17:26:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
WebSocketSendList.JoinRoom(GameManager.Instance.gameData.currRoomData.roomId, GameManager.Instance.user.id, sitpos);
|
2026-07-17 17:50:31 +08:00
|
|
|
|
});
|
2026-03-18 17:26:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SocketType.chess:
|
|
|
|
|
|
break;
|
2026-04-15 18:30:37 +08:00
|
|
|
|
case SocketType.customer:
|
|
|
|
|
|
break;
|
2026-03-18 17:26:14 +08:00
|
|
|
|
default:
|
|
|
|
|
|
break;
|
2026-03-09 09:25:01 +08:00
|
|
|
|
}
|
2026-02-03 11:35:19 +08:00
|
|
|
|
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
ws.OnMessage += (web, msg) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
OnMessage(web, msg, type);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 保存
|
|
|
|
|
|
sockets[type] = ws;
|
|
|
|
|
|
|
|
|
|
|
|
// 发起连接
|
|
|
|
|
|
ws.Open();
|
|
|
|
|
|
|
|
|
|
|
|
return ws;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 发送消息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Send(SocketType type, string msg)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sockets.TryGetValue(type, out var ws) && ws.IsOpen)
|
|
|
|
|
|
{
|
|
|
|
|
|
ws.Send(msg + "\u001e");
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
Debug.LogWarning($"[WebSocket] {type}Send :" + msg);
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
#endif
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
|
|
2026-07-17 17:50:31 +08:00
|
|
|
|
Debug.LogWarning($"[WebSocket] {type} 未连接,无法发送消息" + msg);
|
2026-07-20 02:52:51 +08:00
|
|
|
|
UIManager.Instance.ShowBoardError($"[WebSocket] {type} 未连接,无法发送消息");
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
#endif
|
2026-07-22 03:41:06 +08:00
|
|
|
|
//switch (type)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// case SocketType.login:
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case SocketType.room:
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case SocketType.game:
|
2026-07-21 05:13:43 +08:00
|
|
|
|
|
2026-07-22 03:41:06 +08:00
|
|
|
|
// //等待界面添加
|
|
|
|
|
|
// UIManager.Instance.ShowAsync(UIPanel.HomeMainPanel, GameManager.Instance.gameData.currGameType);
|
|
|
|
|
|
// //UIManager.Instance.ShowBoardGongGao("网络异常,请重新进入房间");
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case SocketType.chess:
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case SocketType.customer:
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// default:
|
|
|
|
|
|
// break;
|
|
|
|
|
|
//}
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 关闭某个 socket
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Close(SocketType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sockets.TryGetValue(type, out var ws))
|
|
|
|
|
|
{
|
2026-08-01 15:27:12 +08:00
|
|
|
|
//manualClosing.Add(type);
|
2026-07-17 17:50:31 +08:00
|
|
|
|
handshakeCompleted.Remove(type);
|
2026-03-09 09:25:01 +08:00
|
|
|
|
ws.Close(WebSocketStatusCodes.NormalClosure, "正常关闭"); // 或者 CloseStatusCode.Normal
|
|
|
|
|
|
//ws.Close();
|
2026-02-03 11:35:19 +08:00
|
|
|
|
sockets.Remove(type);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-28 16:48:18 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 关闭所有 socket
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void CloseAll()
|
|
|
|
|
|
{
|
2026-08-01 15:27:12 +08:00
|
|
|
|
// ⭐ 先停止所有重连协程:socket 因 ClosedAbnormally 关闭时已从 sockets 移除,
|
|
|
|
|
|
// CloseAll 遍历 sockets.Keys 会漏掉它,导致重连协程继续运行。这里统一停止。
|
|
|
|
|
|
StopAllReconnects();
|
2026-06-28 16:48:18 +08:00
|
|
|
|
foreach (var type in new List<SocketType>(sockets.Keys))
|
|
|
|
|
|
{
|
|
|
|
|
|
Close(type);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 自动重连
|
|
|
|
|
|
/// </summary>
|
2026-07-17 17:50:31 +08:00
|
|
|
|
private void StartReconnect(SocketType type, Action action = null)
|
|
|
|
|
|
{
|
2026-10-05 01:02:55 +08:00
|
|
|
|
if (!SetReconnecting(type, true))
|
2026-07-17 17:50:31 +08:00
|
|
|
|
return;
|
2026-08-01 15:27:12 +08:00
|
|
|
|
var co = StartCoroutine(Reconnect(type, action));
|
|
|
|
|
|
reconnectCoroutines[type] = co;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 主动停止所有正在等待的重连协程并清空状态。
|
|
|
|
|
|
/// 用于 Logout 时彻底取消所有待重连任务,不依赖 isLoggingOut 时序。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void StopAllReconnects()
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var kv in reconnectCoroutines)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (kv.Value != null)
|
|
|
|
|
|
StopCoroutine(kv.Value);
|
|
|
|
|
|
}
|
|
|
|
|
|
reconnectCoroutines.Clear();
|
2026-10-05 01:02:55 +08:00
|
|
|
|
// 逐个触发"重连取消",界面等待提示能同步收起
|
|
|
|
|
|
foreach (var type in new List<SocketType>(reconnecting))
|
|
|
|
|
|
SetReconnecting(type, false);
|
2026-07-17 17:50:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private System.Collections.IEnumerator Reconnect(SocketType type, Action action = null)
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
2026-05-18 18:39:11 +08:00
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
|
|
2026-02-03 11:35:19 +08:00
|
|
|
|
Debug.Log($"[WebSocket] {type} 3 秒后尝试重连...");
|
2026-05-18 18:39:11 +08:00
|
|
|
|
|
|
|
|
|
|
#endif
|
2026-02-03 11:35:19 +08:00
|
|
|
|
yield return new WaitForSeconds(3f);
|
2026-07-17 17:50:31 +08:00
|
|
|
|
if (manualClosing.Contains(type))
|
|
|
|
|
|
{
|
2026-10-05 01:02:55 +08:00
|
|
|
|
SetReconnecting(type, false);
|
2026-08-01 15:27:12 +08:00
|
|
|
|
reconnectCoroutines.Remove(type);
|
|
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
// ⭐ 登出中或未登录:取消已排队等待的重连
|
|
|
|
|
|
if (isLoggingOut || GameManager.Instance.user == null)
|
|
|
|
|
|
{
|
2026-10-05 01:02:55 +08:00
|
|
|
|
SetReconnecting(type, false);
|
2026-08-01 15:27:12 +08:00
|
|
|
|
reconnectCoroutines.Remove(type);
|
2026-07-17 17:50:31 +08:00
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-01 15:27:12 +08:00
|
|
|
|
reconnectCoroutines.Remove(type);
|
2026-07-17 17:50:31 +08:00
|
|
|
|
socketUrls.TryGetValue(type, out var url);
|
|
|
|
|
|
Connect(type, url, action);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnHandshakeCompleted(SocketType type)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!handshakeCompleted.Add(type))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SocketType.login:
|
2026-08-01 15:27:12 +08:00
|
|
|
|
// ⭐ 防御:登出后 user 可能为 null,避免 NRE
|
|
|
|
|
|
if (GameManager.Instance.user == null || string.IsNullOrEmpty(GameManager.Instance.user.id))
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("[WebSocket] login 握手完成但 user 为 null,跳过 Bind(可能登出中)");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-17 17:50:31 +08:00
|
|
|
|
WebSocketSendList.Bind(GameManager.Instance.user.id);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SocketType.chess:
|
|
|
|
|
|
UIManager.Instance.ShowAsync(UIPanel.MainGamePanelZGXQ);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (handshakeActions.TryGetValue(type, out var action) && action != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
handshakeActions[type] = null;
|
|
|
|
|
|
action.Invoke();
|
|
|
|
|
|
}
|
2026-02-03 11:35:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 关闭所有 socket
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
|
|
{
|
2026-08-01 15:27:12 +08:00
|
|
|
|
StopAllReconnects();
|
2026-07-17 17:50:31 +08:00
|
|
|
|
foreach (var type in new List<SocketType>(sockets.Keys))
|
|
|
|
|
|
manualClosing.Add(type);
|
|
|
|
|
|
foreach (var ws in new List<WebSocket>(sockets.Values))
|
2026-02-03 11:35:19 +08:00
|
|
|
|
{
|
|
|
|
|
|
ws.Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
sockets.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-17 17:50:31 +08:00
|
|
|
|
}
|