From 56034b921f16cfdc0d6e592e6ce65ac51a977e99 Mon Sep 17 00:00:00 2001 From: DTieman Date: Fri, 19 Apr 2024 22:10:21 +0200 Subject: [PATCH 1/6] Added the ability to choose your cardtype when playing a jack or the newly added joker --- Mau/Card.cs | 7 +++++-- Mau/Deck.cs | 7 +++++++ Mau/Game.cs | 35 +++++++++++++++++++++++++++++++++-- Mau/GameState.cs | 4 +++- Mau/Player.cs | 4 ++-- Mau/PlayerState.cs | 8 ++++++++ 6 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 Mau/PlayerState.cs diff --git a/Mau/Card.cs b/Mau/Card.cs index 7e5ad7e..b9b48e4 100644 --- a/Mau/Card.cs +++ b/Mau/Card.cs @@ -30,7 +30,8 @@ public enum CardType SPADES, HEARTS, DIAMONDS, - CLUBS + CLUBS, + JOKER } public enum CardValue @@ -47,5 +48,7 @@ public enum CardValue JACK, QUEEN, KING, - ACE + ACE, + RED, + BLACK } \ No newline at end of file diff --git a/Mau/Deck.cs b/Mau/Deck.cs index b2b6ff5..9a74f8d 100644 --- a/Mau/Deck.cs +++ b/Mau/Deck.cs @@ -9,8 +9,15 @@ public class Deck { foreach (CardType cardType in Enum.GetValues(typeof(CardType))) { + if (cardType == CardType.JOKER) + { + UnusedDeck.Add(new Card(cardType, CardValue.RED)); + UnusedDeck.Add(new Card(cardType, CardValue.BLACK)); + continue; + } foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue))) { + if (cardValue is CardValue.RED or CardValue.BLACK) continue; UnusedDeck.Add(new Card(cardType, cardValue)); } } diff --git a/Mau/Game.cs b/Mau/Game.cs index 4477b5f..2e0b95a 100644 --- a/Mau/Game.cs +++ b/Mau/Game.cs @@ -46,6 +46,17 @@ public class Game PlayCard(player, card); break; } + case "CHOOSE": + var choice = action.Data; + if (!Enum.TryParse(choice, out CardType cardType)) + { + break; + } + CurrentCard = new Card(cardType, CardValue.JACK); + CurrentPlayer.State = PlayerState.WAIT; + CurrentPlayer = CurrentCard.CardType == CardType.JOKER ? GetNextPlayer() : GetNextPlayer(2); + CurrentPlayer.State = PlayerState.TURN; + break; case "DRAW": DrawCard(player); break; @@ -66,6 +77,18 @@ public class Game { switch (card.CardValue) { + case CardValue.RED: + case CardValue.BLACK: + { + var nextPlayer = GetNextPlayer(); + var cardsToDraw = Deck.DrawCards(5); + foreach (var drawnCard in cardsToDraw) + { + nextPlayer.Hand.Add(drawnCard); + } + CurrentPlayer.State = PlayerState.CHOOSE; + break; + } case CardValue.TWO: { var nextPlayer = GetNextPlayer(); @@ -74,14 +97,18 @@ public class Game { nextPlayer.Hand.Add(drawnCard); } + CurrentPlayer.State = PlayerState.WAIT; CurrentPlayer = GetNextPlayer(2); + CurrentPlayer.State = PlayerState.TURN; break; } case CardValue.SEVEN: case CardValue.KING: break; case CardValue.EIGHT: + CurrentPlayer.State = PlayerState.WAIT; CurrentPlayer = GetNextPlayer(2); + CurrentPlayer.State = PlayerState.TURN; break; case CardValue.ACE: if (Players.Count > 2) @@ -90,13 +117,15 @@ public class Game CurrentPlayer = GetNextPlayer(); } break; + case CardValue.JACK: + CurrentPlayer.State = PlayerState.CHOOSE; + break; case CardValue.THREE: case CardValue.FOUR: case CardValue.FIVE: case CardValue.SIX: case CardValue.NINE: case CardValue.TEN: - case CardValue.JACK: case CardValue.QUEEN: default: CurrentPlayer = GetNextPlayer(); @@ -108,6 +137,8 @@ public class Game { player.Hand.Add(Deck.DrawCard()); CurrentPlayer = GetNextPlayer(); + player.State = PlayerState.WAIT; + CurrentPlayer.State = PlayerState.TURN; } private Player GetNextPlayer(int numberOfPlayers = 1) @@ -135,7 +166,7 @@ public class Game private static bool IsCardPlayable(Card currentCard, Card playedCard) { - return IsSameCardType(currentCard, playedCard) || IsSameCardValue(currentCard, playedCard); + return IsSameCardType(currentCard, playedCard) || IsSameCardValue(currentCard, playedCard) || playedCard.CardType == CardType.JOKER; } private static bool IsCardInHand(IEnumerable hand, Card card) diff --git a/Mau/GameState.cs b/Mau/GameState.cs index 5f510c1..470fd70 100644 --- a/Mau/GameState.cs +++ b/Mau/GameState.cs @@ -3,6 +3,7 @@ public class GameState { public string PlayerName { get; set; } + public string CurrentState { get; set; } public List Hand { get; set; } = new(); public string CurrentCard { get; set; } public string CurrentPlayer { get; set; } @@ -12,6 +13,7 @@ public class GameState { var p = game.GetPlayer(playerId); PlayerName = p.Connection.ConnectionId; + CurrentState = p.State.ToString(); foreach (var card in p.Hand) { Hand.Add(card.ToString()); @@ -23,6 +25,6 @@ public class GameState } CurrentCard = game.CurrentCard.ToString(); - CurrentPlayer = game.CurrentPlayer.Connection.ConnectionId;; + CurrentPlayer = game.CurrentPlayer.Connection.ConnectionId; } } \ No newline at end of file diff --git a/Mau/Player.cs b/Mau/Player.cs index 0fbec58..0c269c3 100644 --- a/Mau/Player.cs +++ b/Mau/Player.cs @@ -1,11 +1,11 @@ -using System.Net.WebSockets; -using MauMau_Server.Websockets; +using MauMau_Server.Websockets; namespace MauMau_Server.Mau; public class Player { public ConnectionInstance Connection { get; set; } + public PlayerState State { get; set; } = PlayerState.WAIT; public List Hand { get; set; } = new(); public Player(ConnectionInstance connection) diff --git a/Mau/PlayerState.cs b/Mau/PlayerState.cs new file mode 100644 index 0000000..a52669c --- /dev/null +++ b/Mau/PlayerState.cs @@ -0,0 +1,8 @@ +namespace MauMau_Server.Mau; + +public enum PlayerState +{ + TURN, + CHOOSE, + WAIT +} \ No newline at end of file -- 2.49.1 From d51df88fec3d3523ee1e872156ebbc62b94095cf Mon Sep 17 00:00:00 2001 From: DTieman Date: Fri, 19 Apr 2024 23:05:56 +0200 Subject: [PATCH 2/6] Garbage collection setup that might be needed later --- MauMau-Server.csproj | 2 ++ Program.cs | 13 +++++++++++++ Room/Room.cs | 2 +- Room/RoomManager.cs | 11 +++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/MauMau-Server.csproj b/MauMau-Server.csproj index 476e59d..d1cec05 100644 --- a/MauMau-Server.csproj +++ b/MauMau-Server.csproj @@ -7,6 +7,8 @@ + + diff --git a/Program.cs b/Program.cs index fe48a95..057d2f3 100644 --- a/Program.cs +++ b/Program.cs @@ -1,3 +1,5 @@ +using Hangfire; +using Hangfire.MemoryStorage; using MauMau_Server.Websockets; var builder = WebApplication.CreateBuilder(args); @@ -9,6 +11,14 @@ services.AddControllers(); services.AddEndpointsApiExplorer(); services.AddSwaggerGen(); services.AddScoped(); +// var roomManager = services.BuildServiceProvider().GetRequiredService(); +// +// services.AddHangfire((sp, config) => +// { +// config.UseRecommendedSerializerSettings(); +// config.UseMemoryStorage(); +// }); +// services.AddHangfireServer(); var app = builder.Build(); @@ -26,6 +36,9 @@ var webSocketOptions = new WebSocketOptions() app.UseWebSockets(webSocketOptions); +// var recurringJobManager = app.Services.GetRequiredService(); +// recurringJobManager.AddOrUpdate("1", () => roomManager.ClearGhostRooms(), Cron.Hourly); + app.UseCors(policyBuilder => { policyBuilder.AllowAnyOrigin(); diff --git a/Room/Room.cs b/Room/Room.cs index b89e655..9081ff4 100644 --- a/Room/Room.cs +++ b/Room/Room.cs @@ -99,7 +99,7 @@ public class Room return _connections.Select(connection => connection.Socket).ToList(); } - private bool IsEmpty() + public bool IsEmpty() { return _connections.Count == 0; } diff --git a/Room/RoomManager.cs b/Room/RoomManager.cs index 2280be2..4b9dab2 100644 --- a/Room/RoomManager.cs +++ b/Room/RoomManager.cs @@ -31,6 +31,16 @@ public class RoomManager : IRoomManager { return Rooms.ContainsKey(roomId); } + + public void ClearGhostRooms() + { + var ghostRooms = Rooms.Where(room => room.Value.IsEmpty()); + foreach (var room in ghostRooms) + { + GC.Collect(GC.GetGeneration(room.Value)); + RemoveRoom(room.Key); + } + } } public interface IRoomManager @@ -40,4 +50,5 @@ public interface IRoomManager public List GetAllRooms(); public void RemoveRoom(string roomId); public bool RoomExists(string roomId); + public void ClearGhostRooms(); } \ No newline at end of file -- 2.49.1 From b97eb309c0353e872dee1144611b5aa379f3ced2 Mon Sep 17 00:00:00 2001 From: DTieman Date: Sat, 20 Apr 2024 00:07:53 +0200 Subject: [PATCH 3/6] Cleanup --- Controllers/RoomController.cs | 26 ++++++++++++-------------- Mau/Game.cs | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Controllers/RoomController.cs b/Controllers/RoomController.cs index 85fe489..ff786d9 100644 --- a/Controllers/RoomController.cs +++ b/Controllers/RoomController.cs @@ -27,23 +27,21 @@ public class RoomController : ControllerBase [HttpGet("{id}")] public async Task ConnectToRoom(string id) { - if (HttpContext.WebSockets.IsWebSocketRequest) - { - if (_roomManager.RoomExists(id)) - { - using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); - var room = _roomManager.GetRoom(id); - await room.InstantiateConnection(webSocket); - } - else - { - HttpContext.Response.StatusCode = 404; - } - } - else + if (!HttpContext.WebSockets.IsWebSocketRequest) { HttpContext.Response.StatusCode = 400; + return; } + + if (!_roomManager.RoomExists(id)) + { + HttpContext.Response.StatusCode = 404; + return; + } + + using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); + var room = _roomManager.GetRoom(id); + await room.InstantiateConnection(webSocket); } [HttpPost] diff --git a/Mau/Game.cs b/Mau/Game.cs index 2e0b95a..b7516a1 100644 --- a/Mau/Game.cs +++ b/Mau/Game.cs @@ -1,5 +1,4 @@ -using System.Net.WebSockets; -using System.Text.Json; +using System.Text.Json; using MauMau_Server.Websockets; namespace MauMau_Server.Mau; @@ -25,7 +24,9 @@ public class Game Hand = Deck.DrawCards(8) }; Players.Add(player); - if (Players.Count == 1) CurrentPlayer = player; + if (Players.Count > 1) return; + CurrentPlayer = player; + CurrentPlayer.State = PlayerState.TURN; } public void RemovePlayer(string playerId) @@ -42,6 +43,10 @@ public class Game { case "PLAYCARD": { + if (player.State != PlayerState.TURN) + { + break; + } var card = JsonSerializer.Deserialize(action.Data).ToCard(); PlayCard(player, card); break; @@ -58,6 +63,10 @@ public class Game CurrentPlayer.State = PlayerState.TURN; break; case "DRAW": + if (player.State != PlayerState.TURN) + { + break; + } DrawCard(player); break; } -- 2.49.1 From 1e51defad520c3ec381fba857f120cb05683c327 Mon Sep 17 00:00:00 2001 From: DTieman Date: Sat, 20 Apr 2024 23:43:07 +0200 Subject: [PATCH 4/6] Custom names and time in chat --- Controllers/RoomController.cs | 16 ++++++++++------ Mau/GameState.cs | 24 ++++++++++++++++++------ Room/Chat/Chat.cs | 18 ++++++++++++++++-- Room/Room.cs | 10 +++++----- Websockets/ConnectionInstance.cs | 4 +++- 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/Controllers/RoomController.cs b/Controllers/RoomController.cs index ff786d9..7d5af5e 100644 --- a/Controllers/RoomController.cs +++ b/Controllers/RoomController.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.Text; +using System.Text.Json; using MauMau_Server.Websockets; using Microsoft.AspNetCore.Mvc; @@ -24,24 +25,27 @@ public class RoomController : ControllerBase return Ok(rooms); } - [HttpGet("{id}")] - public async Task ConnectToRoom(string id) + [HttpGet("{id}/{name}")] + public async Task ConnectToRoom(string id, string name) { + var response = HttpContext.Response; if (!HttpContext.WebSockets.IsWebSocketRequest) { - HttpContext.Response.StatusCode = 400; + response.StatusCode = 400; + await response.BodyWriter.WriteAsync("Request is not a websocket request"u8.ToArray()); return; } if (!_roomManager.RoomExists(id)) { - HttpContext.Response.StatusCode = 404; + response.StatusCode = 404; + await response.BodyWriter.WriteAsync("Room could not be found"u8.ToArray()); return; } using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); var room = _roomManager.GetRoom(id); - await room.InstantiateConnection(webSocket); + await room.InstantiateConnection(webSocket, name); } [HttpPost] diff --git a/Mau/GameState.cs b/Mau/GameState.cs index 470fd70..8afcf41 100644 --- a/Mau/GameState.cs +++ b/Mau/GameState.cs @@ -2,17 +2,17 @@ public class GameState { - public string PlayerName { get; set; } + public PlayerDTO Me { get; set; } public string CurrentState { get; set; } public List Hand { get; set; } = new(); public string CurrentCard { get; set; } - public string CurrentPlayer { get; set; } - public List Players { get; set; } = new(); + public PlayerDTO CurrentPlayer { get; set; } + public List Players { get; set; } = new(); public GameState(Game game, string playerId) { var p = game.GetPlayer(playerId); - PlayerName = p.Connection.ConnectionId; + Me = new PlayerDTO(game.GetPlayer(playerId)); CurrentState = p.State.ToString(); foreach (var card in p.Hand) { @@ -21,10 +21,22 @@ public class GameState foreach (var player in game.Players) { - Players.Add(player.Connection.ConnectionId); + Players.Add(new PlayerDTO(player)); } CurrentCard = game.CurrentCard.ToString(); - CurrentPlayer = game.CurrentPlayer.Connection.ConnectionId; + CurrentPlayer = new PlayerDTO(game.CurrentPlayer); + } +} + +public class PlayerDTO +{ + public string Name { get; set; } + public string Id { get; set; } + + public PlayerDTO(Player player) + { + Name = player.Connection.Name; + Id = player.Connection.ConnectionId; } } \ No newline at end of file diff --git a/Room/Chat/Chat.cs b/Room/Chat/Chat.cs index 0e22051..e6d7b56 100644 --- a/Room/Chat/Chat.cs +++ b/Room/Chat/Chat.cs @@ -13,10 +13,24 @@ public class Chat _room = room; } - public void SendChatMessage(string connectionId, string message) + public void SendChatMessage(ConnectionInstance connection, string message) { - var chatMessage = new ChatOutput(connectionId, message); + var chatMessage = new ChatMessage(DateTime.UtcNow, connection.Name, message); var formattedMessage = new MessageDTO("CHAT", JsonSerializer.Serialize(chatMessage)); WebsocketManager.BroadcastAsync(_room.GetWebsockets(), JsonSerializer.Serialize(formattedMessage)); } +} + +public class ChatMessage +{ + public DateTime Time { get; set; } + public string Sender { get; set; } + public string Message { get; set; } + + public ChatMessage(DateTime dateTime, string sender, string message) + { + Time = dateTime; + Sender = sender; + Message = message; + } } \ No newline at end of file diff --git a/Room/Room.cs b/Room/Room.cs index 9081ff4..d0c4cc7 100644 --- a/Room/Room.cs +++ b/Room/Room.cs @@ -20,9 +20,9 @@ public class Room _roomId = roomId; } - public async Task InstantiateConnection(WebSocket socket) + public async Task InstantiateConnection(WebSocket socket, string name) { - var connection = AddConnection(socket); + var connection = AddConnection(socket, name); if (IsEmpty()) _host = connection; _game.AddPlayerToGame(connection); await HandleConnection(connection); @@ -45,7 +45,7 @@ public class Room } case "CHAT": { - _chat.SendChatMessage(connection.ConnectionId, message.Payload); + _chat.SendChatMessage(connection, message.Payload); break; } } @@ -57,10 +57,10 @@ public class Room HandleDisconnect(connection.ConnectionId); } - private ConnectionInstance AddConnection(WebSocket socket) + private ConnectionInstance AddConnection(WebSocket socket, string name) { var connectionId = Guid.NewGuid().ToString(); - var connection = new ConnectionInstance(connectionId, socket); + var connection = new ConnectionInstance(name, connectionId, socket); _connections.Add(connection); return connection; } diff --git a/Websockets/ConnectionInstance.cs b/Websockets/ConnectionInstance.cs index 64357d9..8b1884d 100644 --- a/Websockets/ConnectionInstance.cs +++ b/Websockets/ConnectionInstance.cs @@ -4,11 +4,13 @@ namespace MauMau_Server.Websockets; public class ConnectionInstance { + public string Name { get; set; } public string ConnectionId { get; set; } public WebSocket Socket { get; set; } - public ConnectionInstance(string connectionId, WebSocket socket) + public ConnectionInstance(string name, string connectionId, WebSocket socket) { + Name = name; ConnectionId = connectionId; Socket = socket; } -- 2.49.1 From 3c2032a8002b943e73102c06f97e6f659928f075 Mon Sep 17 00:00:00 2001 From: DTieman Date: Sun, 21 Apr 2024 16:27:58 +0200 Subject: [PATCH 5/6] Added: - Chat message cleaning to prevent HTML injection - Player hand size to game state - Deck automatically creates sets when it doesnt have any cards left --- Mau/Deck.cs | 13 ++++++++++++- Mau/GameState.cs | 2 ++ Room/Room.cs | 13 ++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Mau/Deck.cs b/Mau/Deck.cs index 9a74f8d..50ffedc 100644 --- a/Mau/Deck.cs +++ b/Mau/Deck.cs @@ -6,6 +6,12 @@ public class Deck public List UsedDeck = new(); public Deck() + { + CreateSet(); + ShuffleDeck(); + } + + private void CreateSet() { foreach (CardType cardType in Enum.GetValues(typeof(CardType))) { @@ -21,7 +27,6 @@ public class Deck UnusedDeck.Add(new Card(cardType, cardValue)); } } - ShuffleDeck(); } public List GetUnusedDeck() @@ -56,6 +61,12 @@ public class Deck { UnusedDeck.AddRange(UsedDeck); UsedDeck.Clear(); + + if (UnusedDeck.Count == 0) + { + CreateSet(); + } + ShuffleDeck(); } diff --git a/Mau/GameState.cs b/Mau/GameState.cs index 8afcf41..91cac2e 100644 --- a/Mau/GameState.cs +++ b/Mau/GameState.cs @@ -33,10 +33,12 @@ public class PlayerDTO { public string Name { get; set; } public string Id { get; set; } + public int CardsLeft { get; set; } public PlayerDTO(Player player) { Name = player.Connection.Name; Id = player.Connection.ConnectionId; + CardsLeft = player.Hand.Count; } } \ No newline at end of file diff --git a/Room/Room.cs b/Room/Room.cs index d0c4cc7..753e620 100644 --- a/Room/Room.cs +++ b/Room/Room.cs @@ -1,5 +1,6 @@ using System.Net.WebSockets; using System.Text.Json; +using System.Text.RegularExpressions; using MauMau_Server.Mau; namespace MauMau_Server.Websockets; @@ -45,7 +46,12 @@ public class Room } case "CHAT": { - _chat.SendChatMessage(connection, message.Payload); + var cleanedMessage = StripHTML(message.Payload); + if (string.IsNullOrWhiteSpace(cleanedMessage)) + { + cleanedMessage = "Mau!"; + }; + _chat.SendChatMessage(connection, cleanedMessage); break; } } @@ -103,4 +109,9 @@ public class Room { return _connections.Count == 0; } + + private static string StripHTML(string input) + { + return Regex.Replace(input, "<.*?>", String.Empty); + } } \ No newline at end of file -- 2.49.1 From 0c2cb8272d5fb8620a18c6e1e0e04d9b9160eefc Mon Sep 17 00:00:00 2001 From: DTieman Date: Mon, 22 Apr 2024 21:51:35 +0200 Subject: [PATCH 6/6] Better lobby system --- Mau/Game.cs | 36 ++++++++++++-------- Room/Chat/Chat.cs | 6 ++-- Room/Room.cs | 83 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 99 insertions(+), 26 deletions(-) diff --git a/Mau/Game.cs b/Mau/Game.cs index b7516a1..3f15e0f 100644 --- a/Mau/Game.cs +++ b/Mau/Game.cs @@ -10,9 +10,11 @@ public class Game public List Players = new(); public Player CurrentPlayer; public int TurnDirection = 1; + private readonly Room _room; - public Game() + public Game(Room room) { + _room = room; CurrentCard = Deck.DrawCard(); Deck.AddCardToUsedDeck(CurrentCard); } @@ -57,10 +59,10 @@ public class Game { break; } - CurrentCard = new Card(cardType, CardValue.JACK); CurrentPlayer.State = PlayerState.WAIT; - CurrentPlayer = CurrentCard.CardType == CardType.JOKER ? GetNextPlayer() : GetNextPlayer(2); + CurrentPlayer = CurrentCard.CardType == CardType.JOKER ? GetNextPlayer(2) : GetNextPlayer(); CurrentPlayer.State = PlayerState.TURN; + CurrentCard = new Card(cardType, CardValue.JACK); break; case "DRAW": if (player.State != PlayerState.TURN) @@ -79,6 +81,11 @@ public class Game Deck.AddCardToUsedDeck(card); hand.Remove(GetSameCardFromHand(hand, card)); CurrentCard = card; + if (hand.Count == 0) + { + _room.EndGame(player); + return; + } HandleNextPlayer(card); } @@ -106,24 +113,20 @@ public class Game { nextPlayer.Hand.Add(drawnCard); } - CurrentPlayer.State = PlayerState.WAIT; - CurrentPlayer = GetNextPlayer(2); - CurrentPlayer.State = PlayerState.TURN; + HandleNextPlayer(CurrentPlayer, GetNextPlayer(2)); break; } case CardValue.SEVEN: case CardValue.KING: break; case CardValue.EIGHT: - CurrentPlayer.State = PlayerState.WAIT; - CurrentPlayer = GetNextPlayer(2); - CurrentPlayer.State = PlayerState.TURN; + HandleNextPlayer(CurrentPlayer, GetNextPlayer(2)); break; case CardValue.ACE: if (Players.Count > 2) { TurnDirection *= -1; - CurrentPlayer = GetNextPlayer(); + HandleNextPlayer(CurrentPlayer, GetNextPlayer()); } break; case CardValue.JACK: @@ -137,17 +140,22 @@ public class Game case CardValue.TEN: case CardValue.QUEEN: default: - CurrentPlayer = GetNextPlayer(); + HandleNextPlayer(CurrentPlayer, GetNextPlayer()); break; } } + private void HandleNextPlayer(Player current, Player next) + { + current.State = PlayerState.WAIT; + next.State = PlayerState.TURN; + CurrentPlayer = next; + } + private void DrawCard(Player player) { player.Hand.Add(Deck.DrawCard()); - CurrentPlayer = GetNextPlayer(); - player.State = PlayerState.WAIT; - CurrentPlayer.State = PlayerState.TURN; + HandleNextPlayer(player, GetNextPlayer()); } private Player GetNextPlayer(int numberOfPlayers = 1) diff --git a/Room/Chat/Chat.cs b/Room/Chat/Chat.cs index e6d7b56..338321f 100644 --- a/Room/Chat/Chat.cs +++ b/Room/Chat/Chat.cs @@ -15,7 +15,7 @@ public class Chat public void SendChatMessage(ConnectionInstance connection, string message) { - var chatMessage = new ChatMessage(DateTime.UtcNow, connection.Name, message); + var chatMessage = new ChatMessage(connection.Name, message); var formattedMessage = new MessageDTO("CHAT", JsonSerializer.Serialize(chatMessage)); WebsocketManager.BroadcastAsync(_room.GetWebsockets(), JsonSerializer.Serialize(formattedMessage)); } @@ -23,13 +23,11 @@ public class Chat public class ChatMessage { - public DateTime Time { get; set; } public string Sender { get; set; } public string Message { get; set; } - public ChatMessage(DateTime dateTime, string sender, string message) + public ChatMessage(string sender, string message) { - Time = dateTime; Sender = sender; Message = message; } diff --git a/Room/Room.cs b/Room/Room.cs index 753e620..c6bda50 100644 --- a/Room/Room.cs +++ b/Room/Room.cs @@ -10,9 +10,10 @@ public class Room private readonly IRoomManager _roomManager; private readonly string _roomId; private readonly List _connections = new(); - private ConnectionInstance _host; + private ConnectionInstance? _host; private readonly Chat _chat; - private readonly Game _game = new(); + private Game? _game; + private RoomState _state = RoomState.LOBBY; public Room(IRoomManager roomManager, string roomId) { @@ -24,14 +25,13 @@ public class Room public async Task InstantiateConnection(WebSocket socket, string name) { var connection = AddConnection(socket, name); - if (IsEmpty()) _host = connection; - _game.AddPlayerToGame(connection); + _game?.AddPlayerToGame(connection); await HandleConnection(connection); } private async Task HandleConnection(ConnectionInstance connection) { - BroadcastGameState(); + BroadcastState(); var webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket); while (!webSocketResponse.Result!.CloseStatus.HasValue) { @@ -40,6 +40,7 @@ public class Room { case "GAME": { + if (_state != RoomState.GAME) break; var gameInput = JsonSerializer.Deserialize(message.Payload); _game.HandleAction(connection.ConnectionId, gameInput); break; @@ -54,9 +55,17 @@ public class Room _chat.SendChatMessage(connection, cleanedMessage); break; } + case "LOBBY": + { + if (connection.ConnectionId == _host?.ConnectionId) + { + ChangeLobbyState(RoomState.GAME); + } + break; + } } - BroadcastGameState(); + BroadcastState(); webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket); } WebsocketManager.CloseAsync(connection.Socket, webSocketResponse.Result); @@ -67,6 +76,7 @@ public class Room { var connectionId = Guid.NewGuid().ToString(); var connection = new ConnectionInstance(name, connectionId, socket); + if (IsEmpty()) _host = connection; _connections.Add(connection); return connection; } @@ -78,6 +88,10 @@ public class Room private void BroadcastGameState() { + if (_game == null) + { + return; + } foreach (var connection in _connections) { var gameState = new GameState(_game, connection.ConnectionId); @@ -86,10 +100,50 @@ public class Room } } + private void BroadcastState() + { + switch (_state) + { + case RoomState.LOBBY: + var message = new MessageDTO("LOBBY", JsonSerializer.Serialize("a")); + WebsocketManager.BroadcastAsync(GetWebsockets(), JsonSerializer.Serialize(message)); + break; + case RoomState.GAME: + BroadcastGameState(); + break; + default: + // + break; + } + } + + private void ChangeLobbyState(RoomState targetState) + { + switch (targetState) + { + case RoomState.LOBBY: + _state = RoomState.LOBBY; + _game = null; + break; + case RoomState.GAME: + { + _state = RoomState.GAME; + _game = new Game(this); + foreach (var connectionInstance in _connections) + { + _game.AddPlayerToGame(connectionInstance); + } + break; + } + default: + throw new ArgumentOutOfRangeException(nameof(targetState), targetState, null); + } + } + private void HandleDisconnect(string socketId) { RemoveConnection(socketId); - _game.RemovePlayer(socketId); + _game?.RemovePlayer(socketId); if (IsEmpty()) { _roomManager.RemoveRoom(_roomId); @@ -112,6 +166,19 @@ public class Room private static string StripHTML(string input) { - return Regex.Replace(input, "<.*?>", String.Empty); + return Regex.Replace(input, "<.*?>", string.Empty); } + + public void EndGame(Player player) + { + var message = new MessageDTO("END", JsonSerializer.Serialize(new PlayerDTO(player))); + WebsocketManager.BroadcastAsync(GetWebsockets(), JsonSerializer.Serialize(message)); + ChangeLobbyState(RoomState.LOBBY); + } +} + +public enum RoomState +{ + LOBBY, + GAME } \ No newline at end of file -- 2.49.1