Add hot-update boot pipeline, UI bundles/atlas assets, and design docs.
Ignore AIClient/Library and AIClient/HybridCLRData permanently; include HybridCLR/YooAsset project setup and UI tooling. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using AOT.Boot;
|
||||
using UnityEngine;
|
||||
|
||||
namespace HotUpdate.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 登录 → WS 连接编排。
|
||||
/// </summary>
|
||||
public sealed class GameNet : MonoBehaviour
|
||||
{
|
||||
public string HttpBase = AuthApi.DefaultHttpBase;
|
||||
public string WsUrlTemplate = "wss://game.xworld.ren/ws?pid={pid}&token={token}";
|
||||
|
||||
public SessionData Session { get; private set; }
|
||||
public WsClient Socket { get; private set; }
|
||||
public string Status { get; private set; } = "idle";
|
||||
|
||||
public event Action<SessionData> OnAuthed;
|
||||
public event Action OnWsReady;
|
||||
public event Action<string> OnStatus;
|
||||
public event Action<string> OnWsMessage;
|
||||
|
||||
BootContext _boot;
|
||||
bool _started;
|
||||
|
||||
public void Bind(BootContext boot)
|
||||
{
|
||||
_boot = boot;
|
||||
if (_boot != null && !string.IsNullOrEmpty(_boot.WsUrlTemplate))
|
||||
{
|
||||
// 旧模板只有 pid 时补上 token
|
||||
var tpl = _boot.WsUrlTemplate;
|
||||
if (!tpl.Contains("{token}"))
|
||||
{
|
||||
tpl = tpl.Contains("?")
|
||||
? tpl + (tpl.EndsWith("?") || tpl.EndsWith("&") ? "token={token}" : "&token={token}")
|
||||
: tpl + "?pid={pid}&token={token}";
|
||||
}
|
||||
WsUrlTemplate = tpl;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartGuestSession()
|
||||
{
|
||||
if (_started) return;
|
||||
_started = true;
|
||||
StartCoroutine(RunGuestFlow());
|
||||
}
|
||||
|
||||
IEnumerator RunGuestFlow()
|
||||
{
|
||||
SetStatus("auth…");
|
||||
var deviceId = DeviceId.GetOrCreate();
|
||||
// 服务器要求 username/password;编辑器自动账号
|
||||
var username = $"guest_{deviceId}";
|
||||
var password = $"pw_{deviceId}";
|
||||
|
||||
AuthResponse auth = null;
|
||||
string err = null;
|
||||
yield return AuthApi.EnsureAccount(HttpBase, username, password, (r, e) => { auth = r; err = e; });
|
||||
|
||||
if (auth == null || string.IsNullOrEmpty(auth.token))
|
||||
{
|
||||
SetStatus($"auth fail: {err}");
|
||||
Debug.LogError($"[GameNet] auth failed: {err}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
Session = new SessionData
|
||||
{
|
||||
Username = username,
|
||||
Token = auth.token,
|
||||
Pid = auth.pid,
|
||||
DeviceId = deviceId,
|
||||
};
|
||||
Debug.Log($"[GameNet] authed pid={Session.Pid} user={username}");
|
||||
OnAuthed?.Invoke(Session);
|
||||
SetStatus($"authed pid={Session.Pid}");
|
||||
|
||||
yield return ConnectWs();
|
||||
}
|
||||
|
||||
IEnumerator ConnectWs()
|
||||
{
|
||||
if (Session == null || !Session.IsValid)
|
||||
{
|
||||
SetStatus("no session");
|
||||
yield break;
|
||||
}
|
||||
|
||||
SetStatus("ws connecting…");
|
||||
Socket?.Dispose();
|
||||
Socket = new WsClient();
|
||||
Socket.AutoReconnect = true;
|
||||
Socket.OnConnected += () =>
|
||||
{
|
||||
SetStatus("ws connected");
|
||||
OnWsReady?.Invoke();
|
||||
// 应用层握手(服务端当前可无响应,保留扩展)
|
||||
Socket.SendText($"{{\"op\":\"hello\",\"proto\":1,\"app\":\"{_boot?.AppVersion}\",\"res\":\"{_boot?.ResVersion}\",\"pid\":{Session.Pid}}}");
|
||||
};
|
||||
Socket.OnDisconnected += reason => SetStatus($"ws down: {reason}");
|
||||
Socket.OnMessage += msg =>
|
||||
{
|
||||
Debug.Log($"[GameNet] << {msg}");
|
||||
OnWsMessage?.Invoke(msg);
|
||||
};
|
||||
|
||||
var url = Session.BuildWsUrl(WsUrlTemplate);
|
||||
Socket.Connect(url);
|
||||
|
||||
// 等待最多 15s
|
||||
float t = 0f;
|
||||
while (t < 15f && Socket.State != WsState.Connected && Socket.State != WsState.Disconnected)
|
||||
{
|
||||
t += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (Socket.State == WsState.Connected)
|
||||
SetStatus("ws connected");
|
||||
else if (Socket.State == WsState.Disconnected && !string.IsNullOrEmpty(Socket.LastError))
|
||||
SetStatus($"ws fail: {Socket.LastError}");
|
||||
else if (Socket.State != WsState.Connected)
|
||||
SetStatus($"ws state={Socket.State}");
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
Socket?.Tick();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
Socket?.Dispose();
|
||||
Socket = null;
|
||||
}
|
||||
|
||||
void SetStatus(string s)
|
||||
{
|
||||
Status = s;
|
||||
Debug.Log($"[GameNet] {s}");
|
||||
OnStatus?.Invoke(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user