using InABox.Core; using InABox.WebSocket.Shared; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using WebSocketSharp; using WebSocketSharp.Server; using static InABox.Server.WebSocket.NotifyState; using InternalServer = WebSocketSharp.Server.WebSocketServer; using Logger = InABox.Core.Logger; namespace InABox.Server.WebSocket { public delegate void PollEvent(Session session); public class NotifyState { public class Session { public string ID { get; set; } public Guid SessionID { get; set; } public Platform Platform { get; set; } public Session(string id, Guid sessionID, Platform platform) { ID = id; SessionID = sessionID; Platform = platform; } } public ConcurrentDictionary SessionMap = new(); public event PollEvent? OnPoll; public void Poll(Session session) { OnPoll?.Invoke(session); } } public class NotifyHandler : WebSocketBehavior { private NotifyState NotifyState; public NotifyHandler(NotifyState state) { NotifyState = state; } protected override void OnOpen() { Logger.Send(LogType.Error, "", $"WebSocket client connected"); } protected override void OnClose(CloseEventArgs e) { Logger.Send(LogType.Error, "", $"WebSocket client disconnected"); var sessionID = NotifyState.SessionMap.Where(x => x.Value.ID == ID).FirstOrDefault().Key; NotifyState.SessionMap.TryRemove(sessionID, out var session); } protected override void OnError(WebSocketSharp.ErrorEventArgs e) { Logger.Send(LogType.Error, "", $"WebSocket Error: {e.Message}"); } private void DoInitial(InitialMessage initial) { var newSession = new Session(ID, initial.SessionID, initial.Platform); NotifyState.SessionMap[initial.SessionID] = newSession; NotifyState.Poll(newSession); } protected override void OnMessage(MessageEventArgs e) { Logger.Send(LogType.Information, "", "Message received"); var message = SocketMessage.ReadMessage(e.RawData); if (message is InitialMessage initial) { DoInitial(initial); } } } public class WebSocketServer { private InternalServer Server; private NotifyState NotifyState = new(); public event PollEvent? Poll; public int Port => Server.Port; public WebSocketServer(int port) { Server = new InternalServer(IPAddress.Any, port); Server.AddWebSocketService("/notify", NewNotify); NotifyState.OnPoll += NotifyState_Poll; } private void NotifyState_Poll(Session session) { Poll?.Invoke(session); } public NotifyHandler NewNotify() { return new NotifyHandler(NotifyState); } public IEnumerable GetSessions(Platform platform) { return NotifyState.SessionMap.Where(x => x.Value.Platform == platform).Select(x => x.Key); } public void Push(Guid sessionID, SocketMessage message) { if(NotifyState.SessionMap.TryGetValue(sessionID, out var session)) { using(var stream = new MemoryStream()) { message.Write(stream); Server.WebSocketServices["/notify"].Sessions.SendToAsync(stream, (int)stream.Length, session.ID, (succ) => { }); } } } private void PushMessage(SocketMessage message) { Server.WebSocketServices["/notify"].Sessions.Broadcast(message.WriteToBytes()); } private void PushMessage(SocketMessage message, string session) { Server.WebSocketServices["/notify"].Sessions.SendTo(message.WriteToBytes(), session); } public void Push(Type TNotification, object notification) { PushMessage(NotifyMessage.Notify(TNotification, notification)); } public void Push(TNotification notification) { PushMessage(NotifyMessage.Notify(notification)); } public void Push(Guid sessionID, Type TNotification, object notification) { if(NotifyState.SessionMap.TryGetValue(sessionID, out var session)) { PushMessage(NotifyMessage.Notify(TNotification, notification), session.ID); } } public void Push(Guid sessionID, TNotification notification) { if(NotifyState.SessionMap.TryGetValue(sessionID, out var session)) { PushMessage(NotifyMessage.Notify(notification), session.ID); } } public void Start() { Server.Start(); } public void Stop() { Server.Stop(); } } }