diff --git a/Controllers/AuthController.cs b/Controllers/AuthController.cs deleted file mode 100644 index 4333a0a..0000000 --- a/Controllers/AuthController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Text.Json; -using Microsoft.AspNetCore.Mvc; - -namespace MauMau_Server.Controllers; - -[ApiController] -[Route("[controller]")] -public class AuthController : ControllerBase -{ - [HttpGet] - public async Task Get() - { - return Ok(JsonSerializer.Serialize(Guid.NewGuid())); - } -} \ No newline at end of file diff --git a/Mau/Game.cs b/Mau/Game.cs index e9c8084..4477b5f 100644 --- a/Mau/Game.cs +++ b/Mau/Game.cs @@ -1,5 +1,6 @@ using System.Net.WebSockets; using System.Text.Json; +using MauMau_Server.Websockets; namespace MauMau_Server.Mau; @@ -17,9 +18,9 @@ public class Game Deck.AddCardToUsedDeck(CurrentCard); } - public void AddPlayerToGame(string playerId, WebSocket socket) + public void AddPlayerToGame(ConnectionInstance connection) { - var player = new Player("Koetje " + playerId.Split('-')[0], playerId, socket) + var player = new Player(connection) { Hand = Deck.DrawCards(8) }; diff --git a/Mau/GameState.cs b/Mau/GameState.cs index 2218875..5f510c1 100644 --- a/Mau/GameState.cs +++ b/Mau/GameState.cs @@ -11,7 +11,7 @@ public class GameState public GameState(Game game, string playerId) { var p = game.GetPlayer(playerId); - PlayerName = p.Name; + PlayerName = p.Connection.ConnectionId; foreach (var card in p.Hand) { Hand.Add(card.ToString()); @@ -19,10 +19,10 @@ public class GameState foreach (var player in game.Players) { - Players.Add(player.Name); + Players.Add(player.Connection.ConnectionId); } CurrentCard = game.CurrentCard.ToString(); - CurrentPlayer = game.CurrentPlayer.Name; + CurrentPlayer = game.CurrentPlayer.Connection.ConnectionId;; } } \ No newline at end of file diff --git a/Mau/Player.cs b/Mau/Player.cs index 0a91b20..0fbec58 100644 --- a/Mau/Player.cs +++ b/Mau/Player.cs @@ -1,20 +1,17 @@ using System.Net.WebSockets; +using MauMau_Server.Websockets; namespace MauMau_Server.Mau; public class Player { - public string Name { get; set; } - public string PlayerId { get; set; } - public WebSocket Socket { get; set; } + public ConnectionInstance Connection { get; set; } public List Hand { get; set; } = new(); - public Player(string name, string playerId, WebSocket socket) + public Player(ConnectionInstance connection) { - Name = name; - PlayerId = playerId; - Socket = socket; + Connection = connection; } - public bool IsMe(string playerId) => PlayerId == playerId; + public bool IsMe(string playerId) => Connection.ConnectionId == playerId; } \ No newline at end of file diff --git a/Room/Chat/Chat.cs b/Room/Chat/Chat.cs new file mode 100644 index 0000000..0e22051 --- /dev/null +++ b/Room/Chat/Chat.cs @@ -0,0 +1,22 @@ +using System.Net.WebSockets; +using System.Text.Json; +using MauMau_Server.Websockets; + +namespace MauMau_Server.Mau; + +public class Chat +{ + private readonly Room _room; + + public Chat(Room room) + { + _room = room; + } + + public void SendChatMessage(string connectionId, string message) + { + var chatMessage = new ChatOutput(connectionId, message); + var formattedMessage = new MessageDTO("CHAT", JsonSerializer.Serialize(chatMessage)); + WebsocketManager.BroadcastAsync(_room.GetWebsockets(), JsonSerializer.Serialize(formattedMessage)); + } +} \ No newline at end of file diff --git a/Room/Chat/ChatOutput.cs b/Room/Chat/ChatOutput.cs new file mode 100644 index 0000000..b7cd9c9 --- /dev/null +++ b/Room/Chat/ChatOutput.cs @@ -0,0 +1,18 @@ +namespace MauMau_Server.Mau; + +public class ChatOutput +{ + public string PlayerName { get; set; } + public string Message { get; set; } + + public ChatOutput(string playerName, string message) + { + PlayerName = playerName; + Message = message; + } + + public ChatOutput() + { + + } +} \ No newline at end of file diff --git a/Room/MessageDTO.cs b/Room/MessageDTO.cs new file mode 100644 index 0000000..6d0fa3f --- /dev/null +++ b/Room/MessageDTO.cs @@ -0,0 +1,18 @@ +namespace MauMau_Server.Mau; + +public class MessageDTO +{ + public string Type { get; set; } + public string Payload { get; set; } + + public MessageDTO(string type, string payload) + { + Type = type; + Payload = payload; + } + + public MessageDTO() + { + + } +} \ No newline at end of file diff --git a/Room/Room.cs b/Room/Room.cs new file mode 100644 index 0000000..b89e655 --- /dev/null +++ b/Room/Room.cs @@ -0,0 +1,106 @@ +using System.Net.WebSockets; +using System.Text.Json; +using MauMau_Server.Mau; + +namespace MauMau_Server.Websockets; + +public class Room +{ + private readonly IRoomManager _roomManager; + private readonly string _roomId; + private readonly List _connections = new(); + private ConnectionInstance _host; + private readonly Chat _chat; + private readonly Game _game = new(); + + public Room(IRoomManager roomManager, string roomId) + { + _roomManager = roomManager; + _chat = new Chat(this); + _roomId = roomId; + } + + public async Task InstantiateConnection(WebSocket socket) + { + var connection = AddConnection(socket); + if (IsEmpty()) _host = connection; + _game.AddPlayerToGame(connection); + await HandleConnection(connection); + } + + private async Task HandleConnection(ConnectionInstance connection) + { + BroadcastGameState(); + var webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket); + while (!webSocketResponse.Result!.CloseStatus.HasValue) + { + var message = JsonSerializer.Deserialize(webSocketResponse.SlicedBuffer); + switch (message.Type) + { + case "GAME": + { + var gameInput = JsonSerializer.Deserialize(message.Payload); + _game.HandleAction(connection.ConnectionId, gameInput); + break; + } + case "CHAT": + { + _chat.SendChatMessage(connection.ConnectionId, message.Payload); + break; + } + } + + BroadcastGameState(); + webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket); + } + WebsocketManager.CloseAsync(connection.Socket, webSocketResponse.Result); + HandleDisconnect(connection.ConnectionId); + } + + private ConnectionInstance AddConnection(WebSocket socket) + { + var connectionId = Guid.NewGuid().ToString(); + var connection = new ConnectionInstance(connectionId, socket); + _connections.Add(connection); + return connection; + } + + private void RemoveConnection(string socketId) + { + _connections.RemoveAll(connection => connection.ConnectionId == socketId); + } + + private void BroadcastGameState() + { + foreach (var connection in _connections) + { + var gameState = new GameState(_game, connection.ConnectionId); + var message = new MessageDTO("GAME", JsonSerializer.Serialize(gameState)); + WebsocketManager.SendAsync(connection.Socket, JsonSerializer.Serialize(message)); + } + } + + private void HandleDisconnect(string socketId) + { + RemoveConnection(socketId); + _game.RemovePlayer(socketId); + if (IsEmpty()) + { + _roomManager.RemoveRoom(_roomId); + } + else if (socketId == _host.ConnectionId) + { + _host = _connections.First(); + } + } + + public List GetWebsockets() + { + return _connections.Select(connection => connection.Socket).ToList(); + } + + private bool IsEmpty() + { + return _connections.Count == 0; + } +} \ No newline at end of file diff --git a/Websockets/RoomManager.cs b/Room/RoomManager.cs similarity index 100% rename from Websockets/RoomManager.cs rename to Room/RoomManager.cs diff --git a/Websockets/ConnectionInstance.cs b/Websockets/ConnectionInstance.cs new file mode 100644 index 0000000..64357d9 --- /dev/null +++ b/Websockets/ConnectionInstance.cs @@ -0,0 +1,15 @@ +using System.Net.WebSockets; + +namespace MauMau_Server.Websockets; + +public class ConnectionInstance +{ + public string ConnectionId { get; set; } + public WebSocket Socket { get; set; } + + public ConnectionInstance(string connectionId, WebSocket socket) + { + ConnectionId = connectionId; + Socket = socket; + } +} \ No newline at end of file diff --git a/Websockets/Room.cs b/Websockets/Room.cs deleted file mode 100644 index 0af94b4..0000000 --- a/Websockets/Room.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.Collections; -using System.Net.WebSockets; -using System.Text; -using System.Text.Json; -using MauMau_Server.Mau; - -namespace MauMau_Server.Websockets; - -public class Room -{ - private readonly IRoomManager _roomManager; - private readonly string _roomId; - private readonly Dictionary _connections = new(); - private string _host; - private readonly Game _game = new(); - - public Room(IRoomManager roomManager, string roomId) - { - _roomManager = roomManager; - _roomId = roomId; - } - - public async Task InstantiateConnection(WebSocket socket) - { - var socketId = AddConnection(socket); - await HandleConnection(socket, socketId); - } - - private async Task HandleConnection(WebSocket socket, string socketId) - { - BroadcastGameState(); - var buffer = EmptyBuffer(); - var result = await ReceiveAsync(socket, buffer); - while (!result.CloseStatus.HasValue) - { - var slicedBuffer = buffer[0..result.Count]; - var action = JsonSerializer.Deserialize(slicedBuffer); - _game.HandleAction(socketId, action); - BroadcastGameState(); - buffer = EmptyBuffer(); - result = await ReceiveAsync(socket, buffer); - } - - await socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - HandleDisconnect(socketId); - } - - private string AddConnection(WebSocket socket) - { - var socketId = Guid.NewGuid().ToString(); - if (_connections.Count == 0) - { - _host = socketId; - } - - _connections.Add(socketId, socket); - _game.AddPlayerToGame(socketId, socket); - return socketId; - } - - public WebSocket GetConnection(string socketId) - { - return _connections[socketId]; - } - - public Dictionary GetAllConnections() - { - return _connections; - } - - public void RemoveConnection(string socketId) - { - _connections.Remove(socketId); - } - - private static async Task ReceiveAsync(WebSocket webSocket, byte[] buffer) - { - var arraySegment = new ArraySegment(buffer); - var result = await webSocket.ReceiveAsync(arraySegment, CancellationToken.None); - return result; - } - - private void BroadcastGameState() - { - foreach (var (id, socket) in GetAllConnections()) - { - var gameState = new GameState(_game, id); - var message = JsonSerializer.Serialize(gameState); - SendAsync(socket, message); - } - } - - private void BroadcastAsync(string message) - { - foreach (var (id, socket) in GetAllConnections()) - { - SendAsync(socket, message); - } - } - - private static void SendAsync(WebSocket socket, string message) - { - var bytes = Encoding.Default.GetBytes(message); - var arraySegment = new ArraySegment(bytes); - socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); - } - - private void HandleDisconnect(string socketId) - { - RemoveConnection(socketId); - _game.RemovePlayer(socketId); - if (IsEmpty()) - { - _roomManager.RemoveRoom(_roomId); - } - else if (socketId == _host) - { - _host = _connections.First().Key; - } - } - - private static byte[] EmptyBuffer() - { - return new byte[4096]; - } - - public bool IsEmpty() - { - return _connections.Count == 0; - } -} \ No newline at end of file diff --git a/Websockets/WebSocketResponse.cs b/Websockets/WebSocketResponse.cs new file mode 100644 index 0000000..a52c74a --- /dev/null +++ b/Websockets/WebSocketResponse.cs @@ -0,0 +1,15 @@ +using System.Net.WebSockets; + +namespace MauMau_Server.Websockets; + +public class WebSocketResponse +{ + public readonly WebSocketReceiveResult? Result; + public readonly byte[] SlicedBuffer; + + public WebSocketResponse(WebSocketReceiveResult? result, byte[] slicedBuffer) + { + Result = result; + SlicedBuffer = slicedBuffer; + } +} \ No newline at end of file diff --git a/Websockets/WebsocketManager.cs b/Websockets/WebsocketManager.cs new file mode 100644 index 0000000..8941c17 --- /dev/null +++ b/Websockets/WebsocketManager.cs @@ -0,0 +1,36 @@ +using System.Net.WebSockets; +using System.Text; + +namespace MauMau_Server.Websockets; + +public static class WebsocketManager +{ + public static void SendAsync(WebSocket socket, string message) + { + var bytes = Encoding.Default.GetBytes(message); + var arraySegment = new ArraySegment(bytes); + socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); + } + + public static void BroadcastAsync(List sockets, string message) + { + foreach (var socket in sockets) + { + SendAsync(socket, message); + } + } + + public static async Task ReceiveAsync(WebSocket webSocket) + { + var buffer = new byte[4096]; + var arraySegment = new ArraySegment(buffer); + var result = await webSocket.ReceiveAsync(arraySegment, CancellationToken.None); + var slicedBuffer = buffer[0..result.Count]; + return new WebSocketResponse(result, slicedBuffer); + } + + public static async void CloseAsync(WebSocket webSocket, WebSocketReceiveResult result) + { + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } +} \ No newline at end of file