51 lines
6.2 KiB
C#
51 lines
6.2 KiB
C#
using System.Net;
|
|
using Microsoft.AspNetCore.SignalR.Client;
|
|
using asap.core;
|
|
var mgr=new Manager();var rt=new SignalRService(mgr,null);int calls=0;
|
|
void Assert(bool ok){if(!ok)throw new Exception("RT assertion failed");}
|
|
using var sub=rt.OnEvent<Message>(x=>calls++);
|
|
rt.WaitForAPISvrLogin();rt.WaitForAPISvrLogin();
|
|
GContext.Publish(new game.EvtAPISvrLoginSuccess());
|
|
var first=HubConnection.All.Single();Assert(first.State==HubConnectionState.Connected && first.Actions.ContainsKey("Message"));
|
|
first.Emit("Message");Assert(calls==1);Assert(HubConnectionBuilder.LastOptions.SkipNegotiation && HubConnectionBuilder.LastOptions.Transports==Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets);
|
|
Console.WriteLine("PASS initial binding, unique login subscription, direct websocket options");
|
|
first.StopGate=new TaskCompletionSource<bool>();mgr.Change();GContext.Publish(new game.EvtAPISvrLoginSuccess());first.Emit("Message");Assert(calls==1 && HubConnection.All.Count==1);
|
|
first.StopGate.SetResult(true);for(int i=0;i<100 && HubConnection.All.Count<2;i++)await Task.Delay(1);
|
|
var second=HubConnection.All.Last();Assert(first.Disposed && second!=first && second.State==HubConnectionState.Connected);second.Emit("Message");Assert(calls==2);
|
|
Console.WriteLine("PASS account change drops old callbacks and waits for old disposal before replacement");
|
|
second.State=HubConnectionState.Reconnecting;second.State=HubConnectionState.Connected;second.Emit("Message");Assert(calls==3);
|
|
Assert(await rt.Publish("Test","arg")=="OK");second.RpcGate=new TaskCompletionSource<bool>();var rpc=rt.Publish("Test","arg");
|
|
second.StopGate=new TaskCompletionSource<bool>();var release=rt.ReleaseAsync();Assert(!release.IsCompleted);second.Emit("Message");Assert(calls==3);second.StopGate.SetResult(true);await release;second.RpcGate.SetResult(true);Assert(await rpc==null);Assert(second.Disposed && !rt.IsConnected);
|
|
GContext.Publish(new game.EvtAPISvrLoginSuccess());Assert(HubConnection.All.Count==2);
|
|
Console.WriteLine("PASS existing bindings survive reconnect state, awaited release, no login subscription after release");
|
|
class Message {}
|
|
public interface ICustomServerMgr { Uri ApiBaseUri{get;} CookieContainer Cookie{get;} int SessionVersion{get;} event Action SessionChanged; }
|
|
class Manager:ICustomServerMgr { public Uri ApiBaseUri=>new("http://example.test/");public CookieContainer Cookie{get;private set;}=new();public int SessionVersion{get;private set;} public event Action SessionChanged;public void Change(){SessionVersion++;Cookie=new();SessionChanged?.Invoke();} }
|
|
namespace asap.core { public interface IConfig {} public interface IEventAggregator {void Publish<T>(T x);Stream<T> GetEvent<T>();} public class EventAggregator:IEventAggregator { readonly Dictionary<Type,object> events=new();public Stream<T> GetEvent<T>(){if(!events.ContainsKey(typeof(T)))events[typeof(T)]=new Stream<T>();return (Stream<T>)events[typeof(T)];} public void Publish<T>(T x)=>GetEvent<T>().Emit(x); } public interface IStageService{Task SwitchStageAsync(string s);} }
|
|
public class Stream<T> {public event Action<T> Event;public IDisposable Subscribe(Action<T> action){Event+=action;return new Subscription(()=>Event-=action);}public void Emit(T x)=>Event?.Invoke(x);}
|
|
public class Subscription:IDisposable {Action end;public Subscription(Action a){end=a;}public void Dispose(){end?.Invoke();end=null;}}
|
|
public static class GContext {static EventAggregator events=new();public static Stream<T> OnEvent<T>()=>events.GetEvent<T>();public static void Publish<T>(T x)=>events.Publish(x);public static Container container=new();}
|
|
public class Container {public T Resolve<T>()=>default;}
|
|
namespace game {public class EvtAPISvrLoginSuccess{} public static class QuitStage{public static bool Logout;}}
|
|
namespace UniRx {} namespace GameCore {}
|
|
namespace UnityEngine {public static class Debug {public static void Log(object x){}public static void LogException(Exception x){if(x is not OperationCanceledException)throw x;}}public class GameObject{public T GetComponent<T>() where T:new()=>new();}}
|
|
public static class Awaiters { public static Task NextFrame=>Task.CompletedTask; }
|
|
public class UIManager {public static UIManager Instance=new();public Task<UnityEngine.GameObject> ShowUI(object x)=>Task.FromResult(new UnityEngine.GameObject());}
|
|
public static class UITypes {public static object NoticeConfirmPopupPanel=new();}
|
|
public class NoticeConfirmPopupPanel {public void Init(int i,string title,string info,Action onClickLeft,Action onClose,string btn_left_text){}}
|
|
public static class LocalizationMgr {public static string GetText(string s)=>s;}
|
|
public static class LoadingScreen {public static Task Show()=>Task.CompletedTask;}
|
|
namespace Microsoft.AspNetCore.Http.Connections { public enum HttpTransportType {WebSockets} }
|
|
namespace Microsoft.AspNetCore.SignalR.Client {
|
|
public enum HubConnectionState {Disconnected,Connected,Reconnecting}
|
|
public class Options {public CookieContainer Cookies;public bool SkipNegotiation;public Microsoft.AspNetCore.Http.Connections.HttpTransportType Transports;}
|
|
public class HubConnectionBuilder { public static Options LastOptions;public HubConnectionBuilder WithUrl(string u,Action<Options> action){LastOptions=new();action(LastOptions);return this;}public HubConnectionBuilder WithAutomaticReconnect()=>this;public HubConnection Build()=>new();}
|
|
public class HubConnection {
|
|
public static List<HubConnection> All=new();public Dictionary<string,Action<string>> Actions=new();public HubConnection(){All.Add(this);}public HubConnectionState State;public bool Disposed;public TaskCompletionSource<bool> StopGate;public TaskCompletionSource<bool> RpcGate;
|
|
public void On<T>(string name,Action<T> action){Actions.Add(name,s=>action((T)(object)s));}public void On<T,U,V>(string name,Action<T,U,V> action){}
|
|
public void Emit(string name)=>Actions[name]("{}");public Task StartAsync(CancellationToken token){token.ThrowIfCancellationRequested();State=HubConnectionState.Connected;return Task.CompletedTask;}
|
|
public async Task StopAsync(){if(StopGate!=null)await StopGate.Task;State=HubConnectionState.Disconnected;}public ValueTask DisposeAsync(){Disposed=true;return default;}
|
|
public async Task<T> InvokeAsync<T>(string method,params object[] args){if(RpcGate!=null)await RpcGate.Task;return (T)(object)"OK";}
|
|
}
|
|
}
|