Add MainHUD gameplay UI, net handshake updates, and server maintenance mode.
Introduce UiManager/MainHUD prefab flow, strengthen GameNet/WsClient, and add AIServer maintenance endpoint support. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,7 @@ using UnityEngine;
|
||||
namespace HotUpdate.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 登录 → WS 连接编排。
|
||||
/// 登录 → WS 连接编排(含指数退避重连与维护公告)。
|
||||
/// </summary>
|
||||
public sealed class GameNet : MonoBehaviour
|
||||
{
|
||||
@@ -21,9 +21,14 @@ namespace HotUpdate.Net
|
||||
public event Action OnWsReady;
|
||||
public event Action<string> OnStatus;
|
||||
public event Action<string> OnWsMessage;
|
||||
public event Action<string> OnWsDisconnected;
|
||||
public event Action<int> OnReconnecting;
|
||||
public event Action<string> OnMaintenance;
|
||||
|
||||
BootContext _boot;
|
||||
bool _started;
|
||||
bool _maintenance;
|
||||
bool _everConnected;
|
||||
|
||||
public void Bind(BootContext boot)
|
||||
{
|
||||
@@ -35,7 +40,6 @@ namespace HotUpdate.Net
|
||||
|
||||
if (!string.IsNullOrEmpty(_boot.WsUrlTemplate))
|
||||
{
|
||||
// 旧模板只有 pid 时补上 token
|
||||
var tpl = _boot.WsUrlTemplate;
|
||||
if (!tpl.Contains("{token}"))
|
||||
{
|
||||
@@ -54,11 +58,37 @@ namespace HotUpdate.Net
|
||||
StartCoroutine(RunGuestFlow());
|
||||
}
|
||||
|
||||
/// <summary>UI「立即重连」:重置退避并马上再连。</summary>
|
||||
public void RequestReconnect()
|
||||
{
|
||||
if (_maintenance || Session == null || !Session.IsValid) return;
|
||||
SetStatus("手动重连…");
|
||||
if (Socket != null)
|
||||
{
|
||||
Socket.AutoReconnect = true;
|
||||
Socket.ForceReconnect();
|
||||
}
|
||||
else
|
||||
StartCoroutine(ConnectWs());
|
||||
}
|
||||
|
||||
public void NotifyMaintenance(string message)
|
||||
{
|
||||
if (_maintenance) return;
|
||||
_maintenance = true;
|
||||
if (Socket != null)
|
||||
{
|
||||
Socket.AutoReconnect = false;
|
||||
Socket.Close();
|
||||
}
|
||||
SetStatus("maintenance");
|
||||
OnMaintenance?.Invoke(message ?? "服务器维护中");
|
||||
}
|
||||
|
||||
IEnumerator RunGuestFlow()
|
||||
{
|
||||
SetStatus("auth…");
|
||||
var deviceId = DeviceId.GetOrCreate();
|
||||
// 服务器要求 username/password;编辑器自动账号
|
||||
var username = $"guest_{deviceId}";
|
||||
var password = $"pw_{deviceId}";
|
||||
|
||||
@@ -89,6 +119,7 @@ namespace HotUpdate.Net
|
||||
|
||||
IEnumerator ConnectWs()
|
||||
{
|
||||
if (_maintenance) yield break;
|
||||
if (Session == null || !Session.IsValid)
|
||||
{
|
||||
SetStatus("no session");
|
||||
@@ -99,14 +130,13 @@ namespace HotUpdate.Net
|
||||
Socket?.Dispose();
|
||||
Socket = new WsClient();
|
||||
Socket.AutoReconnect = true;
|
||||
Socket.OnConnected += () =>
|
||||
Socket.OnConnected += HandleConnected;
|
||||
Socket.OnDisconnected += HandleDisconnected;
|
||||
Socket.OnReconnecting += attempt =>
|
||||
{
|
||||
SetStatus("ws connected");
|
||||
OnWsReady?.Invoke();
|
||||
// 应用层握手(服务端当前可无响应,保留扩展)
|
||||
Socket.SendText($"{{\"op\":\"hello\",\"proto\":1,\"app\":\"{_boot?.AppVersion}\",\"res\":\"{_boot?.ResVersion}\",\"pid\":{Session.Pid}}}");
|
||||
SetStatus($"ws reconnecting #{attempt}");
|
||||
OnReconnecting?.Invoke(attempt);
|
||||
};
|
||||
Socket.OnDisconnected += reason => SetStatus($"ws down: {reason}");
|
||||
Socket.OnMessage += msg =>
|
||||
{
|
||||
Debug.Log($"[GameNet] << {msg}");
|
||||
@@ -116,7 +146,6 @@ namespace HotUpdate.Net
|
||||
var url = Session.BuildWsUrl(WsUrlTemplate);
|
||||
Socket.Connect(url);
|
||||
|
||||
// 等待最多 15s
|
||||
float t = 0f;
|
||||
while (t < 15f && Socket.State != WsState.Connected && Socket.State != WsState.Disconnected)
|
||||
{
|
||||
@@ -132,6 +161,28 @@ namespace HotUpdate.Net
|
||||
SetStatus($"ws state={Socket.State}");
|
||||
}
|
||||
|
||||
void HandleConnected()
|
||||
{
|
||||
if (_maintenance) return;
|
||||
SetStatus(_everConnected ? "ws reconnected" : "ws connected");
|
||||
_everConnected = true;
|
||||
SendHello();
|
||||
OnWsReady?.Invoke();
|
||||
}
|
||||
|
||||
void HandleDisconnected(string reason)
|
||||
{
|
||||
SetStatus($"ws down: {reason}");
|
||||
OnWsDisconnected?.Invoke(reason ?? "closed");
|
||||
}
|
||||
|
||||
void SendHello()
|
||||
{
|
||||
if (Socket == null || Session == null) return;
|
||||
Socket.SendText(
|
||||
$"{{\"op\":\"hello\",\"proto\":1,\"app\":\"{_boot?.AppVersion}\",\"res\":\"{_boot?.ResVersion}\",\"pid\":{Session.Pid}}}");
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Socket?.Tick();
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace HotUpdate.Net
|
||||
public event Action OnConnected;
|
||||
public event Action<string> OnDisconnected;
|
||||
public event Action<string> OnMessage;
|
||||
public event Action<int> OnReconnecting;
|
||||
|
||||
public bool AutoReconnect { get; set; } = true;
|
||||
public float KeepAliveSeconds { get; set; } = 20f;
|
||||
@@ -50,6 +51,20 @@ namespace HotUpdate.Net
|
||||
_ = ConnectInternalAsync();
|
||||
}
|
||||
|
||||
/// <summary>立刻断开并重新连接(重置退避计数)。</summary>
|
||||
public void ForceReconnect()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_url)) return;
|
||||
_manualClose = false;
|
||||
AutoReconnect = true;
|
||||
_reconnectAttempt = 0;
|
||||
SafeCancel();
|
||||
try { _ws?.Dispose(); } catch { /* ignore */ }
|
||||
_ws = null;
|
||||
State = WsState.Disconnected;
|
||||
_ = ConnectInternalAsync();
|
||||
}
|
||||
|
||||
public void SendText(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return;
|
||||
@@ -119,6 +134,7 @@ namespace HotUpdate.Net
|
||||
_reconnectAttempt++;
|
||||
int delayMs = Math.Min(30000, 1000 * (1 << Math.Min(5, _reconnectAttempt)));
|
||||
Debug.Log($"[WsClient] reconnect in {delayMs}ms attempt={_reconnectAttempt}");
|
||||
OnReconnecting?.Invoke(_reconnectAttempt);
|
||||
try
|
||||
{
|
||||
await Task.Delay(delayMs, CancellationToken.None);
|
||||
|
||||
Reference in New Issue
Block a user