using System.Net.WebSockets; using System.Text; using System.Text.Json; using System.Collections.Concurrent; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); var games = new ConcurrentDictionary(); app.UseWebSockets(); app.Map("/", async context => { if (!context.WebSockets.IsWebSocketRequest) { context.Response.StatusCode = 400; return; } var ws = await context.WebSockets.AcceptWebSocketAsync(); var remoteAddress = context.Connection.RemoteIpAddress?.ToString(); var player = new Player(ws, remoteAddress); try { var buffer = new byte[4096]; var messageBuffer = new ArraySegment(buffer); while (ws.State == WebSocketState.Open) { var message = new MemoryStream(); WebSocketReceiveResult result; do { result = await ws.ReceiveAsync(messageBuffer, CancellationToken.None); message.Write(buffer, 0, result.Count); } while (!result.EndOfMessage); if (result.MessageType == WebSocketMessageType.Close) break; var msgText = Encoding.UTF8.GetString(message.ToArray()); if (msgText == "ping") { await player.SendTextAsync("pong"); continue; } var json = JsonDocument.Parse(msgText).RootElement; var action = json.GetProperty("action").GetString(); var gameId = json.GetProperty("gameId").GetString(); var clientId = json.GetProperty("clientId").GetString(); switch (action) { case "enter_game": await GameHandlers.EnterGame(gameId, clientId, player, games); break; case "send_piece": await GameHandlers.SendToOpponent(gameId, player, "receive_piece", json.GetProperty("piece"), games); break; case "send_stack": await GameHandlers.SendToOpponent(gameId, player, "receive_stack", json.GetProperty("stack"), games); break; case "send_stats": await GameHandlers.SendToOpponent(gameId, player, "receive_stats", json.GetProperty("stats"), games); break; default: Console.WriteLine("Unsupported action."); break; } } } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } finally { Console.WriteLine("Disconnecting player..."); await GameHandlers.ExitGame(player, games); Console.WriteLine("Connection closed."); } }); app.Run(); // ------------------------- // Models // ------------------------- record Player(WebSocket Socket, string? RemoteAddress) { public async Task SendTextAsync(string message) { var bytes = Encoding.UTF8.GetBytes(message); await Socket.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, CancellationToken.None); } public async Task SendJsonAsync(object obj) { var json = JsonSerializer.Serialize(obj); await SendTextAsync(json); } } class Game { public string GameId { get; } public List Players { get; } = new(); public List Clients { get; } = new(); public Game(string gameId) { GameId = gameId; } public Player? GetOpponent(Player player) => Players.FirstOrDefault(p => p != player); } // ------------------------- // Handlers // ------------------------- static class GameHandlers { public static async Task EnterGame(string gameId, string clientId, Player player, ConcurrentDictionary games) { var game = games.GetOrAdd(gameId, _ => new Game(gameId)); if (!game.Clients.Contains(clientId)) { game.Clients.Add(clientId); game.Players.Add(player); if (game.Players.Count == 1) { await player.SendJsonAsync(new { type = "wait_for_opponent" }); } else if (game.Players.Count == 2) { foreach (var p in game.Players) await p.SendJsonAsync(new { type = "start_game" }); } } else { Console.WriteLine("Already in game..."); } } public static async Task SendToOpponent(string gameId, Player sender, string type, JsonElement content, ConcurrentDictionary games) { if (!games.TryGetValue(gameId, out var game)) return; var opponent = game.GetOpponent(sender); if (opponent != null) { switch (type) { case "receive_piece": await opponent.SendJsonAsync(new { type, piece = content }); break; case "receive_stack": await opponent.SendJsonAsync(new { type, stack = content }); break; case "receive_stats": await opponent.SendJsonAsync(new { type, stats = content }); break; } } } public static async Task ExitGame(Player disconnectingPlayer, ConcurrentDictionary games) { var game = games.Values.FirstOrDefault(g => g.Players.Contains(disconnectingPlayer)); if (game != null) { games.TryRemove(game.GameId, out _); var opponent = game.GetOpponent(disconnectingPlayer); if (opponent != null) { await opponent.SendJsonAsync(new { type = "exit_game" }); } } } }