using System; using System.Collections.Generic; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; using BubbleSocketCore.Simulation; using BubbleSocketCore.Websocket; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace BubbleSocketCore.Handlers { class PlayerConnectionHandler : IHandler { private readonly IConfiguration Configuration; private readonly ILoggerFactory LoggerFactory; private readonly ILogger Logger; public SimulationActionFactory simulationActionFactory { get; private set; } public ConnectionManager connectionManager { get; private set; } public Simulation.Simulation simulation { get; private set; } public PlayerConnectionHandler(IConfiguration configuration, ILoggerFactory loggerFactory) { Configuration = configuration; LoggerFactory = loggerFactory; Logger = LoggerFactory.CreateLogger("PlayerConnectionHandler"); simulationActionFactory = new SimulationActionFactory(Configuration, LoggerFactory); connectionManager = ConnectionManager.GetInstance(); simulation = Simulation.Simulation.GetInstance(); } public void AddSocket(WebSocket webSocket) { connectionManager.Add(webSocket); } public bool IsListening(HttpRequest request) { return request.Path == "/game"; } public async Task Listen(HttpContext context, WebSocket selfSocket, CancellationToken cancellationToken) { try { var buffer = WebSocket.CreateClientBuffer(1024 * 4, 1024 * 4); WebSocketReceiveResult result = await selfSocket.ReceiveAsync(buffer, cancellationToken); while (!result.CloseStatus.HasValue) { var allBytes = new List(); for (int i = 0; i < result.Count; i++) { allBytes.Add(buffer.Array[i]); } while (!result.EndOfMessage) { result = await selfSocket.ReceiveAsync(buffer, cancellationToken); for (int i = 0; i < result.Count; i++) { allBytes.Add(buffer.Array[i]); } } var rawString = Encoding.UTF8.GetString(allBytes.ToArray(), 0, allBytes.Count); var rawMessageType = rawString.Substring(0, 2); var messageData = rawString.Substring(2, rawString.Length - 2); var messageType = SimulationActionType.UNKNOWN; ISimulationAction simulationAction = new NoOpSimulationActionType(); try { messageType = SimulationActionFactory.ParseActionType(rawMessageType); simulationAction = simulationActionFactory.Create(messageType); } catch (Exception e) { Logger.LogWarning($"Invalid Message Type: {messageType}: {e.Message}"); } try { simulationAction.Run(selfSocket, simulationAction.ParseMessageData(messageData)); } catch (System.Text.Json.JsonException e) { Logger.LogWarning($"Data received was not json: {e.Message}"); } //wait for next input message //this is what's failing on server shutdown; the cancellation token isn't right result = await selfSocket.ReceiveAsync(buffer, cancellationToken); } await selfSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, cancellationToken); simulationActionFactory.Create(SimulationActionType.Disconnect).Run(selfSocket, "{}"); } catch (WebSocketException e) { Logger.LogWarning($"Websocket exception: {e.Message}"); simulationActionFactory.Create(SimulationActionType.Disconnect).Run(selfSocket, "{}"); } catch (OperationCanceledException) { Logger.LogInformation("Server shutdown request"); simulationActionFactory.Create(SimulationActionType.Disconnect).Run(selfSocket, "{}"); } catch (Exception e) { Logger.LogWarning($"Unexpected Exception: {e.ToString()}{e.Message}"); } } private async Task Send(WebSocket socket, ArraySegment data, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken) { await socket.SendAsync(data, messageType, endOfMessage, cancellationToken); } } }