using Gameboard.ShogiUI.Sockets.Extensions; using Gameboard.ShogiUI.Sockets.Models; using Gameboard.ShogiUI.Sockets.ServiceModels.Socket.Interfaces; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net.WebSockets; using System.Threading.Tasks; namespace Gameboard.ShogiUI.Sockets.Managers { public interface ISocketConnectionManager { Task BroadcastToAll(IResponse response); //Task BroadcastToGame(string gameName, IResponse response); //Task BroadcastToGame(string gameName, IResponse forPlayer1, IResponse forPlayer2); void SubscribeToGame(Session session, string playerName); void SubscribeToBroadcast(WebSocket socket, string playerName); void UnsubscribeFromBroadcastAndGames(string playerName); void UnsubscribeFromGame(string gameName, string playerName); Task BroadcastToPlayers(IResponse response, params string[] playerNames); } /// /// Retains all active socket connections and provides convenient methods for sending messages to clients. /// public class SocketConnectionManager : ISocketConnectionManager { /// Dictionary key is player name. private readonly ConcurrentDictionary connections; /// Dictionary key is game name. private readonly ConcurrentDictionary sessions; private readonly ILogger logger; public SocketConnectionManager(ILogger logger) { this.logger = logger; connections = new ConcurrentDictionary(); sessions = new ConcurrentDictionary(); } public void SubscribeToBroadcast(WebSocket socket, string playerName) { connections.TryAdd(playerName, socket); } public void UnsubscribeFromBroadcastAndGames(string playerName) { connections.TryRemove(playerName, out _); foreach (var kvp in sessions) { var sessionName = kvp.Key; UnsubscribeFromGame(sessionName, playerName); } } /// /// Unsubscribes the player from their current game, then subscribes to the new game. /// public void SubscribeToGame(Session session, string playerName) { // Unsubscribe from any other games foreach (var kvp in sessions) { var gameNameKey = kvp.Key; UnsubscribeFromGame(gameNameKey, playerName); } // Subscribe if (connections.TryGetValue(playerName, out var socket)) { var s = sessions.GetOrAdd(session.Name, session); s.Subscriptions.TryAdd(playerName, socket); } } public void UnsubscribeFromGame(string gameName, string playerName) { if (sessions.TryGetValue(gameName, out var s)) { s.Subscriptions.TryRemove(playerName, out _); if (s.Subscriptions.IsEmpty) sessions.TryRemove(gameName, out _); } } public async Task BroadcastToPlayers(IResponse response, params string[] playerNames) { var tasks = new List(playerNames.Length); foreach (var name in playerNames) { if (connections.TryGetValue(name, out var socket)) { var serialized = JsonConvert.SerializeObject(response); logger.LogInformation("Response to {0} \n{1}\n", name, serialized); tasks.Add(socket.SendTextAsync(serialized)); } } await Task.WhenAll(tasks); } public Task BroadcastToAll(IResponse response) { var message = JsonConvert.SerializeObject(response); logger.LogInformation($"Broadcasting\n{0}", message); var tasks = new List(connections.Count); foreach (var kvp in connections) { var socket = kvp.Value; tasks.Add(socket.SendTextAsync(message)); } return Task.WhenAll(tasks); } //public Task BroadcastToGame(string gameName, IResponse forPlayer1, IResponse forPlayer2) //{ // if (sessions.TryGetValue(gameName, out var session)) // { // var serialized1 = JsonConvert.SerializeObject(forPlayer1); // var serialized2 = JsonConvert.SerializeObject(forPlayer2); // return Task.WhenAll( // session.SendToPlayer1(serialized1), // session.SendToPlayer2(serialized2)); // } // return Task.CompletedTask; //} //public Task BroadcastToGame(string gameName, IResponse messageForAllPlayers) //{ // if (sessions.TryGetValue(gameName, out var session)) // { // var serialized = JsonConvert.SerializeObject(messageForAllPlayers); // return session.Broadcast(serialized); // } // return Task.CompletedTask; //} } }