233 lines
8.6 KiB
C#
233 lines
8.6 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using UnityEngine;
|
|
using System.Net;
|
|
using asap.core;
|
|
using Microsoft.AspNetCore.Http.Connections;
|
|
using Microsoft.AspNetCore.SignalR.Client;
|
|
using Newtonsoft.Json;
|
|
using UniRx;
|
|
using GameCore;
|
|
using game;
|
|
|
|
public interface IRTService
|
|
{
|
|
public void WaitForAPISvrLogin();
|
|
public Task Connect();
|
|
public Task Disconnect();
|
|
public bool IsConnected { get; }
|
|
public IDisposable OnEvent<T>(Action<T> handler);
|
|
public Task<string> Publish(string methodName, string arg1);
|
|
public Task<string> Publish(string methodName, string arg1, string arg2);
|
|
//public void Reset();
|
|
public void Release();
|
|
public Task ReleaseAsync();
|
|
}
|
|
|
|
public class SignalRService : IRTService
|
|
{
|
|
private HubConnection _hubConnection;
|
|
private readonly ICustomServerMgr _serverMgr;
|
|
private readonly IEventAggregator _eventAgg = new EventAggregator();
|
|
private readonly string _url;
|
|
private readonly Dictionary<string, Action<string>> handlers = new Dictionary<string, Action<string>>();
|
|
private readonly System.Threading.SemaphoreSlim lifecycle = new System.Threading.SemaphoreSlim(1, 1);
|
|
private IDisposable loginSubscription;
|
|
private bool released;
|
|
private int connectionVersion;
|
|
private System.Threading.CancellationTokenSource connecting;
|
|
|
|
public SignalRService(ICustomServerMgr serverMgr, IConfig config)
|
|
{
|
|
_serverMgr = serverMgr;
|
|
_url = new Uri(serverMgr.ApiBaseUri, "gamehub").AbsoluteUri;
|
|
_serverMgr.SessionChanged += OnSessionChanged;
|
|
}
|
|
|
|
public void WaitForAPISvrLogin()
|
|
{
|
|
if (released || loginSubscription != null) return;
|
|
loginSubscription = GContext.OnEvent<EvtAPISvrLoginSuccess>().Subscribe(OnApiSvrLoginSuccess);
|
|
}
|
|
|
|
private async void OnApiSvrLoginSuccess(EvtAPISvrLoginSuccess evt)
|
|
{
|
|
try { await Connect(); }
|
|
catch (Exception ex) { Debug.LogException(ex); }
|
|
}
|
|
|
|
private async void OnSessionChanged()
|
|
{
|
|
connectionVersion++;
|
|
connecting?.Cancel();
|
|
try { await Disconnect(); }
|
|
catch (Exception ex) { Debug.LogException(ex); }
|
|
}
|
|
|
|
private async Task CloseConnection()
|
|
{
|
|
var previous = _hubConnection;
|
|
_hubConnection = null;
|
|
if (previous == null) return;
|
|
try { await previous.StopAsync(); }
|
|
finally { await previous.DisposeAsync(); }
|
|
}
|
|
|
|
public async void Release()
|
|
{
|
|
try { await ReleaseAsync(); }
|
|
catch (Exception ex) { Debug.LogException(ex); }
|
|
}
|
|
|
|
public async Task ReleaseAsync()
|
|
{
|
|
released = true;
|
|
connectionVersion++;
|
|
connecting?.Cancel();
|
|
loginSubscription?.Dispose();
|
|
loginSubscription = null;
|
|
_serverMgr.SessionChanged -= OnSessionChanged;
|
|
await lifecycle.WaitAsync();
|
|
try { await CloseConnection(); handlers.Clear(); }
|
|
finally { lifecycle.Release(); }
|
|
}
|
|
|
|
private bool IsCurrent(HubConnection hub, int version, int session) =>
|
|
!released && version == connectionVersion && session == _serverMgr.SessionVersion && ReferenceEquals(hub, _hubConnection);
|
|
|
|
public bool IsConnected => !released && _hubConnection != null && _hubConnection.State == HubConnectionState.Connected;
|
|
|
|
public async Task Connect()
|
|
{
|
|
var session = _serverMgr.SessionVersion;
|
|
var version = connectionVersion;
|
|
var cookies = _serverMgr.Cookie;
|
|
await lifecycle.WaitAsync();
|
|
try
|
|
{
|
|
if (released || version != connectionVersion || session != _serverMgr.SessionVersion) return;
|
|
if (_hubConnection != null && _hubConnection.State != HubConnectionState.Disconnected) return;
|
|
await CloseConnection();
|
|
if (released || version != connectionVersion || session != _serverMgr.SessionVersion) return;
|
|
var hub = new HubConnectionBuilder()
|
|
.WithUrl(_url, option =>
|
|
{
|
|
option.Cookies = cookies;
|
|
option.SkipNegotiation = true;
|
|
option.Transports = HttpTransportType.WebSockets;
|
|
})
|
|
.WithAutomaticReconnect()
|
|
.Build();
|
|
_hubConnection = hub;
|
|
hub.On<string, string, string>("ForceQuit", (title, info, button) =>
|
|
{
|
|
if (IsCurrent(hub, version, session)) ForceQuit(title, info, button, version, session);
|
|
});
|
|
foreach (var handler in handlers)
|
|
Bind(hub, version, session, handler.Key, handler.Value);
|
|
connecting?.Dispose();
|
|
connecting = new System.Threading.CancellationTokenSource();
|
|
try { await hub.StartAsync(connecting.Token); }
|
|
catch { await CloseConnection(); throw; }
|
|
if (!IsCurrent(hub, version, session)) { await CloseConnection(); return; }
|
|
Debug.Log("[RTService] Connected to SignalR server True");
|
|
}
|
|
finally { lifecycle.Release(); }
|
|
}
|
|
|
|
private void Bind(HubConnection hub, int version, int session, string name, Action<string> handler)
|
|
{
|
|
hub.On<string>(name, data =>
|
|
{
|
|
if (IsCurrent(hub, version, session)) handler(data);
|
|
});
|
|
}
|
|
|
|
public async Task Disconnect()
|
|
{
|
|
await lifecycle.WaitAsync();
|
|
try { await CloseConnection(); }
|
|
finally { lifecycle.Release(); }
|
|
}
|
|
|
|
public IDisposable OnEvent<T>(Action<T> handler)
|
|
{
|
|
if (released) throw new ObjectDisposedException(nameof(SignalRService));
|
|
var name = typeof(T).Name;
|
|
if (!handlers.ContainsKey(name))
|
|
{
|
|
Action<string> dispatch = data => _eventAgg.Publish(JsonConvert.DeserializeObject<T>(data));
|
|
handlers.Add(name, dispatch);
|
|
if (_hubConnection != null)
|
|
Bind(_hubConnection, connectionVersion, _serverMgr.SessionVersion, name, dispatch);
|
|
}
|
|
return _eventAgg.GetEvent<T>().Subscribe(handler);
|
|
}
|
|
|
|
public async Task<string> Publish(string methodName, string arg1)
|
|
{
|
|
var hub = _hubConnection;
|
|
var version = connectionVersion;
|
|
var session = _serverMgr.SessionVersion;
|
|
if (hub == null || !IsCurrent(hub, version, session)) return null;
|
|
try
|
|
{
|
|
var result = await hub.InvokeAsync<string>(methodName, arg1);
|
|
return IsCurrent(hub, version, session) ? result : null;
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task<string> Publish(string methodName, string arg1, string arg2)
|
|
{
|
|
var hub = _hubConnection;
|
|
var version = connectionVersion;
|
|
var session = _serverMgr.SessionVersion;
|
|
if (hub == null || !IsCurrent(hub, version, session)) return null;
|
|
try
|
|
{
|
|
var result = await hub.InvokeAsync<string>(methodName, arg1, arg2);
|
|
return IsCurrent(hub, version, session) ? result : null;
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private async void ForceQuit(string titleKey, string infoKey, string btnKey, int version, int session)
|
|
{
|
|
await Awaiters.NextFrame;
|
|
if (released || version != connectionVersion || session != _serverMgr.SessionVersion) return;
|
|
GameObject go = await UIManager.Instance.ShowUI(UITypes.NoticeConfirmPopupPanel);
|
|
if (released || version != connectionVersion || session != _serverMgr.SessionVersion) return;
|
|
var noticeConfirmPopupPanel = go.GetComponent<NoticeConfirmPopupPanel>();
|
|
string title = LocalizationMgr.GetText(titleKey ?? "UI_NoticePopupPanel_3");
|
|
string info = LocalizationMgr.GetText(infoKey ?? "UI_NoticePopupPanel_4");
|
|
string label = LocalizationMgr.GetText(btnKey ?? "UI_COMMON_confirm");
|
|
noticeConfirmPopupPanel.Init(1, title, info, onClickLeft: () => { if (!released && version == connectionVersion && session == _serverMgr.SessionVersion) Restart(); }, onClose: () => { if (!released && version == connectionVersion && session == _serverMgr.SessionVersion) Restart(); }, btn_left_text: label);
|
|
}
|
|
|
|
private async void Restart()
|
|
{
|
|
QuitStage.Logout = true;
|
|
await LoadingScreen.Show();
|
|
await GContext.container.Resolve<IStageService>().SwitchStageAsync("QuitStage");
|
|
}
|
|
|
|
public async void Test()
|
|
{
|
|
Debug.Log("[RTService] Test invoking");
|
|
var returnValue = await _hubConnection.InvokeAsync<string>("Test","arg1", "arg2");
|
|
Debug.Log($"[RTService] Test invoked Value {returnValue}");
|
|
}
|
|
|
|
}
|