Ship the WeChat hot-update catalogs and the camp, TideWhale T2, UI atlas, and animation-lab work.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
#if !UNITY_WEBGL || UNITY_EDITOR
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using AOT.Boot;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HotUpdate.Net
|
||||
@@ -17,18 +20,23 @@ namespace HotUpdate.Net
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于 ClientWebSocket 的传输(Editor / Standalone)。
|
||||
/// WebGL/微信需另接平台桥,本类在 WebGL 下不可用。
|
||||
/// Editor/Standalone 使用 ClientWebSocket;WebGL 使用微信 SocketTask/浏览器桥。
|
||||
/// </summary>
|
||||
public sealed class WsClient : IDisposable
|
||||
{
|
||||
readonly ConcurrentQueue<string> _inbox = new ConcurrentQueue<string>();
|
||||
readonly ConcurrentQueue<string> _outbox = new ConcurrentQueue<string>();
|
||||
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
int _browserSocket;
|
||||
float _connectDeadline;
|
||||
float _reconnectAt = -1;
|
||||
#else
|
||||
ClientWebSocket _ws;
|
||||
CancellationTokenSource _cts;
|
||||
Task _recvTask;
|
||||
Task _sendTask;
|
||||
#endif
|
||||
string _url;
|
||||
int _reconnectAttempt;
|
||||
bool _manualClose;
|
||||
@@ -48,7 +56,11 @@ namespace HotUpdate.Net
|
||||
_url = url;
|
||||
_manualClose = false;
|
||||
_reconnectAttempt = 0;
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
StartBrowserConnection();
|
||||
#else
|
||||
_ = ConnectInternalAsync();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>立刻断开并重新连接(重置退避计数)。</summary>
|
||||
@@ -58,11 +70,15 @@ namespace HotUpdate.Net
|
||||
_manualClose = false;
|
||||
AutoReconnect = true;
|
||||
_reconnectAttempt = 0;
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
StartBrowserConnection();
|
||||
#else
|
||||
SafeCancel();
|
||||
try { _ws?.Dispose(); } catch { /* ignore */ }
|
||||
_ws = null;
|
||||
State = WsState.Disconnected;
|
||||
_ = ConnectInternalAsync();
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SendText(string text)
|
||||
@@ -75,26 +91,91 @@ namespace HotUpdate.Net
|
||||
{
|
||||
_manualClose = true;
|
||||
AutoReconnect = false;
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
ReleaseBrowserSocket();
|
||||
_reconnectAt = -1;
|
||||
#else
|
||||
SafeCancel();
|
||||
try { _ws?.Dispose(); } catch { /* ignore */ }
|
||||
_ws = null;
|
||||
#endif
|
||||
State = WsState.Disconnected;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
TickBrowser();
|
||||
#endif
|
||||
while (_inbox.TryDequeue(out var msg))
|
||||
OnMessage?.Invoke(msg);
|
||||
}
|
||||
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
void ReleaseBrowserSocket()
|
||||
{
|
||||
if (_browserSocket != 0) WebSocketBridge.AIGame_WsDispose(_browserSocket);
|
||||
_browserSocket = 0;
|
||||
}
|
||||
|
||||
void StartBrowserConnection()
|
||||
{
|
||||
ReleaseBrowserSocket();
|
||||
_reconnectAt = -1;
|
||||
LastError = null;
|
||||
State = _reconnectAttempt > 0 ? WsState.Reconnecting : WsState.Connecting;
|
||||
_connectDeadline = Time.realtimeSinceStartup + 15f;
|
||||
_browserSocket = WebSocketBridge.AIGame_WsCreate(_url);
|
||||
if (_browserSocket == 0) BrowserDisconnected("WebSocket bridge unavailable");
|
||||
}
|
||||
|
||||
void BrowserDisconnected(string reason)
|
||||
{
|
||||
ReleaseBrowserSocket();
|
||||
LastError = reason;
|
||||
State = WsState.Disconnected;
|
||||
while (_outbox.TryDequeue(out _)) { }
|
||||
_reconnectAt = -1;
|
||||
if (!_manualClose && AutoReconnect)
|
||||
{
|
||||
_reconnectAttempt++;
|
||||
_reconnectAt = Time.realtimeSinceStartup + Math.Min(30, 1 << Math.Min(5, _reconnectAttempt));
|
||||
}
|
||||
OnDisconnected?.Invoke(reason);
|
||||
if (_reconnectAt >= 0) OnReconnecting?.Invoke(_reconnectAttempt);
|
||||
}
|
||||
|
||||
void TickBrowser()
|
||||
{
|
||||
if (_reconnectAt >= 0 && Time.realtimeSinceStartup >= _reconnectAt)
|
||||
{
|
||||
_reconnectAt = -1;
|
||||
if (!_manualClose && AutoReconnect) StartBrowserConnection();
|
||||
}
|
||||
int current = _browserSocket;
|
||||
// Bound work per frame; callbacks may close or replace this connection.
|
||||
for (int i = 0; current != 0 && current == _browserSocket && i < 100; i++)
|
||||
{
|
||||
string item = WebSocketBridge.AIGame_WsPoll(current);
|
||||
if (string.IsNullOrEmpty(item)) break;
|
||||
if (item[0] == 'O')
|
||||
{
|
||||
State = WsState.Connected;
|
||||
_reconnectAttempt = 0;
|
||||
LastError = null;
|
||||
OnConnected?.Invoke();
|
||||
}
|
||||
else if (item[0] == 'M') OnMessage?.Invoke(item.Substring(1));
|
||||
else if (item[0] == 'E' || item[0] == 'C') BrowserDisconnected(item.Substring(1));
|
||||
}
|
||||
if (_browserSocket != 0 && State != WsState.Connected && Time.realtimeSinceStartup >= _connectDeadline)
|
||||
BrowserDisconnected("WebSocket connection timed out");
|
||||
for (int i = 0; _browserSocket != 0 && State == WsState.Connected && i < 100 && _outbox.TryDequeue(out var text); i++)
|
||||
WebSocketBridge.AIGame_WsSend(_browserSocket, text);
|
||||
}
|
||||
#else
|
||||
async Task ConnectInternalAsync()
|
||||
{
|
||||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||||
LastError = "ClientWebSocket not supported on WebGL; need WeChat bridge";
|
||||
State = WsState.Disconnected;
|
||||
OnDisconnected?.Invoke(LastError);
|
||||
return;
|
||||
#else
|
||||
SafeCancel();
|
||||
_cts = new CancellationTokenSource();
|
||||
State = _reconnectAttempt > 0 ? WsState.Reconnecting : WsState.Connecting;
|
||||
@@ -144,7 +225,6 @@ namespace HotUpdate.Net
|
||||
catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
async Task RecvLoopAsync(CancellationToken ct)
|
||||
@@ -194,6 +274,7 @@ namespace HotUpdate.Net
|
||||
try { _cts?.Dispose(); } catch { /* ignore */ }
|
||||
_cts = null;
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Dispose() => Close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user