Partial rewrite, missing:
- Correctly parsing incoming messages - Sending the gamestate after relevant actions
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
using System.Text;
|
using System.Text.Json;
|
||||||
using System.Text.Json;
|
using MauMau_Server.Room;
|
||||||
using MauMau_Server.Websockets;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace MauMau_Server.Controllers;
|
namespace MauMau_Server.Controllers;
|
||||||
|
|||||||
25
Mau/Card.cs
25
Mau/Card.cs
@@ -25,6 +25,31 @@ public class Card
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CardExtensions
|
||||||
|
{
|
||||||
|
public static bool IsSameCard(this Card card1, Card card2)
|
||||||
|
{
|
||||||
|
return card1.IsSameCardType(card2) && card1.IsSameCardValue(card2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSameCardType(this Card card1, Card card2)
|
||||||
|
{
|
||||||
|
return card1.CardType == card2.CardType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSameCardValue(this Card card1, Card card2)
|
||||||
|
{
|
||||||
|
return card1.CardValue == card2.CardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanBePlayedOn(this Card playedCard, Card currentCard)
|
||||||
|
{
|
||||||
|
return playedCard.IsSameCardType(currentCard)
|
||||||
|
|| playedCard.IsSameCardValue(currentCard)
|
||||||
|
|| playedCard.CardType == CardType.JOKER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum CardType
|
public enum CardType
|
||||||
{
|
{
|
||||||
SPADES,
|
SPADES,
|
||||||
|
|||||||
91
Mau/Deck.cs
91
Mau/Deck.cs
@@ -2,47 +2,86 @@
|
|||||||
|
|
||||||
public class Deck
|
public class Deck
|
||||||
{
|
{
|
||||||
public List<Card> UnusedDeck = new();
|
private List<Card> _unusedDeck = new();
|
||||||
public List<Card> UsedDeck = new();
|
private List<Card> _usedDeck = new();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Creates a new deck instance with a new shuffled set of cards
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
public Deck()
|
public Deck()
|
||||||
{
|
{
|
||||||
CreateSet();
|
CreateSet();
|
||||||
ShuffleDeck();
|
ShuffleDeck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Adds the given card to the used cards deck.
|
||||||
|
* </summary>
|
||||||
|
* <param name="card">The card to add to the used cards deck.</param>
|
||||||
|
*/
|
||||||
|
public void AddCardToUsedDeck(Card card)
|
||||||
|
{
|
||||||
|
_usedDeck.Add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Adds the given list of cards to the used cards deck.
|
||||||
|
* </summary>
|
||||||
|
* <param name="cards">The list of cards to add to the used cards deck.</param>
|
||||||
|
*/
|
||||||
|
public void AddCardsToUsedDeck(IEnumerable<Card> cards)
|
||||||
|
{
|
||||||
|
_usedDeck.AddRange(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Creates a new deck of cards and adds them to the unused deck.
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
private void CreateSet()
|
private void CreateSet()
|
||||||
{
|
{
|
||||||
foreach (CardType cardType in Enum.GetValues(typeof(CardType)))
|
foreach (CardType cardType in Enum.GetValues(typeof(CardType)))
|
||||||
{
|
{
|
||||||
if (cardType == CardType.JOKER)
|
if (cardType == CardType.JOKER)
|
||||||
{
|
{
|
||||||
UnusedDeck.Add(new Card(cardType, CardValue.RED));
|
_unusedDeck.Add(new Card(cardType, CardValue.RED));
|
||||||
UnusedDeck.Add(new Card(cardType, CardValue.BLACK));
|
_unusedDeck.Add(new Card(cardType, CardValue.BLACK));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue)))
|
foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue)))
|
||||||
{
|
{
|
||||||
if (cardValue is CardValue.RED or CardValue.BLACK) continue;
|
if (cardValue is CardValue.RED or CardValue.BLACK) continue;
|
||||||
UnusedDeck.Add(new Card(cardType, cardValue));
|
_unusedDeck.Add(new Card(cardType, cardValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Card> GetUnusedDeck()
|
/**
|
||||||
{
|
* <summary>
|
||||||
return UnusedDeck;
|
* Draws a card from the deck.
|
||||||
}
|
* If the deck is empty, the deck is reshuffled with <see cref="ReshuffleDeck"/>.
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
public Card DrawCard()
|
public Card DrawCard()
|
||||||
{
|
{
|
||||||
if (UnusedDeck.Count == 0) ReshuffleDeck();
|
if (_unusedDeck.Count == 0) ReshuffleDeck();
|
||||||
var card = UnusedDeck[0];
|
var card = _unusedDeck[0];
|
||||||
UnusedDeck.RemoveAt(0);
|
_unusedDeck.RemoveAt(0);
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Card> DrawCards(int amount)
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Take a given amount of cards from the deck. This method calls <see cref="DrawCard"/> for each card.
|
||||||
|
* </summary>
|
||||||
|
* <param name="amount">The amount of cards to draw from the deck.</param>
|
||||||
|
*/
|
||||||
|
public IEnumerable<Card> DrawCards(int amount)
|
||||||
{
|
{
|
||||||
var cards = new List<Card>();
|
var cards = new List<Card>();
|
||||||
for (var i = 0; i < amount; i++)
|
for (var i = 0; i < amount; i++)
|
||||||
@@ -52,17 +91,18 @@ public class Deck
|
|||||||
return cards;
|
return cards;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddCardToUsedDeck(Card card)
|
/**
|
||||||
{
|
* <summary>
|
||||||
UsedDeck.Add(card);
|
* Moves all the used cards back to the unused deck and shuffles it.
|
||||||
}
|
* If there are no cards to reshuffle, a new set of cards is created and shuffled.
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
private void ReshuffleDeck()
|
private void ReshuffleDeck()
|
||||||
{
|
{
|
||||||
UnusedDeck.AddRange(UsedDeck);
|
_unusedDeck.AddRange(_usedDeck);
|
||||||
UsedDeck.Clear();
|
_usedDeck.Clear();
|
||||||
|
|
||||||
if (UnusedDeck.Count == 0)
|
if (_unusedDeck.Count == 0)
|
||||||
{
|
{
|
||||||
CreateSet();
|
CreateSet();
|
||||||
}
|
}
|
||||||
@@ -70,8 +110,13 @@ public class Deck
|
|||||||
ShuffleDeck();
|
ShuffleDeck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Shuffles all the cards in the deck using the Fisher-Yates algorithm.
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
private void ShuffleDeck()
|
private void ShuffleDeck()
|
||||||
{
|
{
|
||||||
UnusedDeck = UnusedDeck.OrderBy(x => Guid.NewGuid()).ToList();
|
_unusedDeck = _unusedDeck.OrderBy(x => Guid.NewGuid()).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
332
Mau/Game.cs
332
Mau/Game.cs
@@ -1,92 +1,207 @@
|
|||||||
using System.Text.Json;
|
using MauMau_Server.Mau.GameMessages;
|
||||||
|
using MauMau_Server.Mau.Managers;
|
||||||
using MauMau_Server.Websockets;
|
using MauMau_Server.Websockets;
|
||||||
|
using MauMau_Server.Room;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace MauMau_Server.Mau;
|
namespace MauMau_Server.Mau;
|
||||||
|
|
||||||
public class Game
|
public class Game : RoomType
|
||||||
{
|
{
|
||||||
public readonly Deck Deck = new();
|
private readonly Deck _deck = new();
|
||||||
public Card CurrentCard;
|
public Card CurrentCard;
|
||||||
public List<Player> Players = new();
|
private readonly TurnManager _turnManager = new();
|
||||||
public Player CurrentPlayer;
|
|
||||||
public int TurnDirection = 1;
|
|
||||||
private readonly Room _room;
|
|
||||||
|
|
||||||
public Game(Room room)
|
public Game(Room.Room room, IEnumerable<ConnectionInstance> connections) : base(room)
|
||||||
{
|
{
|
||||||
_room = room;
|
CurrentCard = _deck.DrawCard();
|
||||||
CurrentCard = Deck.DrawCard();
|
_deck.AddCardToUsedDeck(CurrentCard);
|
||||||
Deck.AddCardToUsedDeck(CurrentCard);
|
|
||||||
|
List<Player> players = new();
|
||||||
|
foreach (var player in connections.Select(connection => new Player(connection)))
|
||||||
|
{
|
||||||
|
var initialHand = _deck.DrawCards(8);
|
||||||
|
player.GiveCards(initialHand);
|
||||||
|
players.Add(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddPlayerToGame(ConnectionInstance connection)
|
_turnManager.Initialize(players);
|
||||||
{
|
|
||||||
var player = new Player(connection)
|
|
||||||
{
|
|
||||||
Hand = Deck.DrawCards(8)
|
|
||||||
};
|
|
||||||
Players.Add(player);
|
|
||||||
if (Players.Count > 1) return;
|
|
||||||
CurrentPlayer = player;
|
|
||||||
CurrentPlayer.State = PlayerState.TURN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemovePlayer(string playerId)
|
/**
|
||||||
|
* <inheritdoc cref="RoomType.OnMessage"/>
|
||||||
|
*/
|
||||||
|
public override void OnMessage(ConnectionInstance sender, string message)
|
||||||
{
|
{
|
||||||
var player = GetPlayer(playerId);
|
// Get the player that sent the message
|
||||||
Players.Remove(player);
|
var player = _turnManager.Players.FirstOrDefault(x => x.IsMe(sender.Id));
|
||||||
}
|
|
||||||
|
|
||||||
public void HandleAction(string playerId, ActionDTO action)
|
// If the player is not the player that is currently playing, ignore the message
|
||||||
{
|
if (_turnManager.CurrentPlayer != player) return;
|
||||||
var player = GetPlayer(playerId);
|
|
||||||
if (CurrentPlayer != player) return;
|
// Deserialize the message
|
||||||
switch (action.Action)
|
var gameMessage = JsonConvert.DeserializeObject<GameMessage>(message);
|
||||||
{
|
|
||||||
case "PLAYCARD":
|
// Based on the message intent, handle the message
|
||||||
{
|
switch (gameMessage.Intent)
|
||||||
if (player.State != PlayerState.TURN)
|
|
||||||
{
|
{
|
||||||
|
case GameIntent.CHOOSE:
|
||||||
|
Choose(player, gameMessage.Data);
|
||||||
break;
|
break;
|
||||||
}
|
case GameIntent.DRAW:
|
||||||
var card = JsonSerializer.Deserialize<CardDTO>(action.Data).ToCard();
|
default:
|
||||||
PlayCard(player, card);
|
Draw(player, gameMessage.Data);
|
||||||
break;
|
break;
|
||||||
}
|
case GameIntent.PLAY:
|
||||||
case "CHOOSE":
|
Play(player, gameMessage.Data);
|
||||||
var choice = action.Data;
|
|
||||||
if (!Enum.TryParse(choice, out CardType cardType))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CurrentPlayer.State = PlayerState.WAIT;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
DrawCard(player);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PlayCard(Player player, Card card)
|
/**
|
||||||
|
* <inheritdoc cref="RoomType.OnConnect"/>
|
||||||
|
*/
|
||||||
|
public override void OnConnect(ConnectionInstance connection)
|
||||||
{
|
{
|
||||||
var hand = player.Hand;
|
var player = new Player(connection);
|
||||||
if (!IsCardInHand(hand, card) || !IsCardPlayable(CurrentCard, card)) return;
|
var initialHand = _deck.DrawCards(8);
|
||||||
Deck.AddCardToUsedDeck(card);
|
player.GiveCards(initialHand);
|
||||||
hand.Remove(GetSameCardFromHand(hand, card));
|
_turnManager.Players.Add(player);
|
||||||
CurrentCard = card;
|
}
|
||||||
if (hand.Count == 0)
|
|
||||||
|
/**
|
||||||
|
* <inheritdoc cref="RoomType.OnDisconnect"/>
|
||||||
|
*/
|
||||||
|
public override void OnDisconnect(ConnectionInstance connection)
|
||||||
|
{
|
||||||
|
var player = _turnManager.Players.FirstOrDefault(x => x.IsMe(connection.Id));
|
||||||
|
if (player is null) return;
|
||||||
|
var playerHand = player.Hand;
|
||||||
|
_deck.AddCardsToUsedDeck(playerHand);
|
||||||
|
if (player == _turnManager.CurrentPlayer)
|
||||||
|
{
|
||||||
|
_turnManager.ChangeTurn();
|
||||||
|
}
|
||||||
|
|
||||||
|
_turnManager.Players.Remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* The player had to choose a new card type
|
||||||
|
* </summary>
|
||||||
|
* <param name="player">The player that chose a new card type</param>
|
||||||
|
* <param name="data">A string that represents a CardType</param>
|
||||||
|
*/
|
||||||
|
private void Choose(Player player, string data)
|
||||||
|
{
|
||||||
|
// Convert the data to a CardType, if it fails, ignore the message
|
||||||
|
if (!Enum.TryParse(data, out CardType cardType))
|
||||||
{
|
{
|
||||||
_room.EndGame(player);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
HandleNextPlayer(card);
|
|
||||||
|
// Does the current card require the next player to draw cards?
|
||||||
|
var isCardGivingCard = CurrentCard.CardType == CardType.JOKER || CurrentCard.CardValue == CardValue.TWO;
|
||||||
|
|
||||||
|
// Can the player that received a card giving card counter the card?
|
||||||
|
var skippedPlayerCanPlay = player.CanPlayCard(CurrentCard);
|
||||||
|
|
||||||
|
// If the card is a card giving card and the player cannot counter it, skip the next player
|
||||||
|
var shouldSkipPlayer = isCardGivingCard && !skippedPlayerCanPlay;
|
||||||
|
var nextPlayer = shouldSkipPlayer
|
||||||
|
? _turnManager.GetNextPlayer(2)
|
||||||
|
: _turnManager.GetNextPlayer();
|
||||||
|
|
||||||
|
// TODO: Not make it a jack, as it would not work when a Joker is played
|
||||||
|
// Set the new current card
|
||||||
|
CurrentCard = new Card(cardType, CardValue.JACK);
|
||||||
|
|
||||||
|
// Change the turns
|
||||||
|
_turnManager.ChangeTurn(nextPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* The player either:
|
||||||
|
* <list type="bullet">
|
||||||
|
* <item>Could not play a card and had to draw a card</item>
|
||||||
|
* <item>Chose to draw a card as a strategic move</item>
|
||||||
|
* </list>
|
||||||
|
* </summary>
|
||||||
|
* <param name="player">The player that drew a card</param>
|
||||||
|
* <param name="data">A string that can be serialized to a drawcard instance</param>
|
||||||
|
*/
|
||||||
|
private void Draw(Player player, string data)
|
||||||
|
{
|
||||||
|
// TODO: data will contain the amount of cards to draw in the future, for now, just draw 1 card
|
||||||
|
|
||||||
|
// Draw a card from the deck
|
||||||
|
var drawnCard = _deck.DrawCard();
|
||||||
|
|
||||||
|
// Give the card to the player
|
||||||
|
player.GiveCard(drawnCard);
|
||||||
|
|
||||||
|
// If the player can play the card, do not change the player
|
||||||
|
if (drawnCard.CanBePlayedOn(CurrentCard)) return;
|
||||||
|
|
||||||
|
// Change the turn to the next player
|
||||||
|
_turnManager.ChangeTurn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* The player plays a card, this will only be possible if:
|
||||||
|
* <list type="bullet">
|
||||||
|
* <item>The player has the correct state</item>
|
||||||
|
* <item>The player has the card in their hand</item>
|
||||||
|
* <item>The card is playable on the current card</item>
|
||||||
|
* </list>
|
||||||
|
* </summary>
|
||||||
|
* <param name="player">The player that played</param>
|
||||||
|
* <param name="data">A string that can be serialized to a playcard instance</param>
|
||||||
|
*/
|
||||||
|
private void Play(Player player, string data)
|
||||||
|
{
|
||||||
|
// Check if the player has the correct state to play a card
|
||||||
|
if (player.State != PlayerState.TURN)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the data to a Card instance
|
||||||
|
var cardData = JsonConvert.DeserializeObject<PlayCard>(data).ToCard();
|
||||||
|
|
||||||
|
// Check if the player indeed has the card they claim to have
|
||||||
|
var playerCard = player.TakeCardFromHand(cardData);
|
||||||
|
if (playerCard is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the played card is compatible with the current card
|
||||||
|
// If not, ignore the play
|
||||||
|
if (!playerCard.CanBePlayedOn(CurrentCard))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the card from the player's hand
|
||||||
|
player.Hand.Remove(playerCard);
|
||||||
|
|
||||||
|
// Add the card to the used deck
|
||||||
|
_deck.AddCardToUsedDeck(playerCard);
|
||||||
|
|
||||||
|
// Set the new current card
|
||||||
|
CurrentCard = playerCard;
|
||||||
|
|
||||||
|
if (player.Hand.Count == 0)
|
||||||
|
{
|
||||||
|
EndGame(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleNextPlayer(playerCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleNextPlayer(Card card)
|
private void HandleNextPlayer(Card card)
|
||||||
@@ -96,41 +211,31 @@ public class Game
|
|||||||
case CardValue.RED:
|
case CardValue.RED:
|
||||||
case CardValue.BLACK:
|
case CardValue.BLACK:
|
||||||
{
|
{
|
||||||
var nextPlayer = GetNextPlayer();
|
_turnManager.GetNextPlayer().GiveCards(_deck.DrawCards(5));
|
||||||
var cardsToDraw = Deck.DrawCards(5);
|
_turnManager.CurrentPlayer.State = PlayerState.CHOOSE;
|
||||||
foreach (var drawnCard in cardsToDraw)
|
|
||||||
{
|
|
||||||
nextPlayer.Hand.Add(drawnCard);
|
|
||||||
}
|
|
||||||
CurrentPlayer.State = PlayerState.CHOOSE;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CardValue.TWO:
|
case CardValue.TWO:
|
||||||
{
|
{
|
||||||
var nextPlayer = GetNextPlayer();
|
_turnManager.GetNextPlayer().GiveCards(_deck.DrawCards(2));
|
||||||
var cardsToDraw = Deck.DrawCards(2);
|
_turnManager.ChangeTurn(_turnManager.GetNextPlayer(2));
|
||||||
foreach (var drawnCard in cardsToDraw)
|
|
||||||
{
|
|
||||||
nextPlayer.Hand.Add(drawnCard);
|
|
||||||
}
|
|
||||||
HandleNextPlayer(CurrentPlayer, GetNextPlayer(2));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CardValue.SEVEN:
|
case CardValue.SEVEN:
|
||||||
case CardValue.KING:
|
case CardValue.KING:
|
||||||
break;
|
break;
|
||||||
case CardValue.EIGHT:
|
case CardValue.EIGHT:
|
||||||
HandleNextPlayer(CurrentPlayer, GetNextPlayer(2));
|
_turnManager.ChangeTurn(_turnManager.GetNextPlayer(2));
|
||||||
break;
|
break;
|
||||||
case CardValue.ACE:
|
case CardValue.ACE:
|
||||||
if (Players.Count > 2)
|
if (_turnManager.Players.Count > 2)
|
||||||
{
|
{
|
||||||
TurnDirection *= -1;
|
_turnManager.ChangeDirection();
|
||||||
HandleNextPlayer(CurrentPlayer, GetNextPlayer());
|
_turnManager.ChangeTurn();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CardValue.JACK:
|
case CardValue.JACK:
|
||||||
CurrentPlayer.State = PlayerState.CHOOSE;
|
_turnManager.CurrentPlayer.State = PlayerState.CHOOSE;
|
||||||
break;
|
break;
|
||||||
case CardValue.THREE:
|
case CardValue.THREE:
|
||||||
case CardValue.FOUR:
|
case CardValue.FOUR:
|
||||||
@@ -140,69 +245,18 @@ public class Game
|
|||||||
case CardValue.TEN:
|
case CardValue.TEN:
|
||||||
case CardValue.QUEEN:
|
case CardValue.QUEEN:
|
||||||
default:
|
default:
|
||||||
HandleNextPlayer(CurrentPlayer, GetNextPlayer());
|
_turnManager.ChangeTurn();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleNextPlayer(Player current, Player next)
|
private void EndGame(Player winner)
|
||||||
{
|
{
|
||||||
current.State = PlayerState.WAIT;
|
_room._roomType = new Lobby(_room, winner.Connection);
|
||||||
next.State = PlayerState.TURN;
|
|
||||||
CurrentPlayer = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCard(Player player)
|
public Player? GetPlayer(string playerId)
|
||||||
{
|
{
|
||||||
player.Hand.Add(Deck.DrawCard());
|
return _turnManager.Players.FirstOrDefault(p => p.IsMe(playerId));
|
||||||
HandleNextPlayer(player, GetNextPlayer());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Player GetNextPlayer(int numberOfPlayers = 1)
|
|
||||||
{
|
|
||||||
var index = Players.IndexOf(CurrentPlayer);
|
|
||||||
for (var i = 0; i < numberOfPlayers; i++)
|
|
||||||
{
|
|
||||||
index += TurnDirection;
|
|
||||||
if (index >= Players.Count) index = 0;
|
|
||||||
if (index < 0) index = Players.Count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Players[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player GetPlayer(string playerId)
|
|
||||||
{
|
|
||||||
return Players.FirstOrDefault(p => p.IsMe(playerId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Card GetSameCardFromHand(IEnumerable<Card> hand, Card card)
|
|
||||||
{
|
|
||||||
return hand.FirstOrDefault(handCard => IsSameCard(handCard, card));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsCardPlayable(Card currentCard, Card playedCard)
|
|
||||||
{
|
|
||||||
return IsSameCardType(currentCard, playedCard) || IsSameCardValue(currentCard, playedCard) || playedCard.CardType == CardType.JOKER;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsCardInHand(IEnumerable<Card> hand, Card card)
|
|
||||||
{
|
|
||||||
return hand.Any(handCard => IsSameCard(handCard, card));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsSameCard(Card card1, Card card2)
|
|
||||||
{
|
|
||||||
return IsSameCardType(card1, card2) && IsSameCardValue(card1, card2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsSameCardType(Card card1, Card card2)
|
|
||||||
{
|
|
||||||
return card1.CardType == card2.CardType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsSameCardValue(Card card1, Card card2)
|
|
||||||
{
|
|
||||||
return card1.CardValue == card2.CardValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6
Mau/GameMessages/ChooseCard.cs
Normal file
6
Mau/GameMessages/ChooseCard.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace MauMau_Server.Mau.GameMessages;
|
||||||
|
|
||||||
|
public class ChooseCard
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
0
Mau/GameMessages/DrawCard.cs
Normal file
0
Mau/GameMessages/DrawCard.cs
Normal file
14
Mau/GameMessages/GameMessage.cs
Normal file
14
Mau/GameMessages/GameMessage.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace MauMau_Server.Mau.GameMessages;
|
||||||
|
|
||||||
|
public class GameMessage
|
||||||
|
{
|
||||||
|
public GameIntent Intent { get; set; }
|
||||||
|
public string Data { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum GameIntent
|
||||||
|
{
|
||||||
|
CHOOSE,
|
||||||
|
DRAW,
|
||||||
|
PLAY,
|
||||||
|
}
|
||||||
30
Mau/GameMessages/PlayCard.cs
Normal file
30
Mau/GameMessages/PlayCard.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
namespace MauMau_Server.Mau.GameMessages;
|
||||||
|
|
||||||
|
public class PlayCard
|
||||||
|
{
|
||||||
|
public string CardType { get; set; }
|
||||||
|
public string CardValue { get; set; }
|
||||||
|
|
||||||
|
public PlayCard(Card card)
|
||||||
|
{
|
||||||
|
CardType = card.CardType.ToString();
|
||||||
|
CardValue = card.CardValue.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayCard(string cardType, string cardValue)
|
||||||
|
{
|
||||||
|
CardType = cardType;
|
||||||
|
CardValue = cardValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayCard()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Card ToCard()
|
||||||
|
{
|
||||||
|
return new Card((CardType)Enum.Parse(typeof(CardType), CardType),
|
||||||
|
(CardValue)Enum.Parse(typeof(CardValue), CardValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ public class PlayerDTO
|
|||||||
public PlayerDTO(Player player)
|
public PlayerDTO(Player player)
|
||||||
{
|
{
|
||||||
Name = player.Connection.Name;
|
Name = player.Connection.Name;
|
||||||
Id = player.Connection.ConnectionId;
|
Id = player.Connection.Id;
|
||||||
CardsLeft = player.Hand.Count;
|
CardsLeft = player.Hand.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
80
Mau/Managers/TurnManager.cs
Normal file
80
Mau/Managers/TurnManager.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
namespace MauMau_Server.Mau.Managers;
|
||||||
|
|
||||||
|
public class TurnManager
|
||||||
|
{
|
||||||
|
private const int CLOCKWISE = 1;
|
||||||
|
private const int COUNTER_CLOCKWISE = -1;
|
||||||
|
|
||||||
|
private int _currentDirection { get; set; } = CLOCKWISE;
|
||||||
|
public List<Player> Players;
|
||||||
|
public Player CurrentPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Initialize the turn manager with a list of players.
|
||||||
|
* </summary>
|
||||||
|
* <param name="players">The list of players to initialize the turn manager with.</param>
|
||||||
|
*/
|
||||||
|
public void Initialize(IEnumerable<Player> players)
|
||||||
|
{
|
||||||
|
Players = players.OrderBy(x => Guid.NewGuid()).ToList();
|
||||||
|
ShufflePlayers();
|
||||||
|
CurrentPlayer = Players.First();
|
||||||
|
CurrentPlayer.State = PlayerState.TURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Change the direction of the turn.
|
||||||
|
* If the direction is clockwise, it will change to counter-clockwise and vice versa.
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
|
public void ChangeDirection()
|
||||||
|
{
|
||||||
|
_currentDirection *= COUNTER_CLOCKWISE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Change the turn to another player.
|
||||||
|
* If no player is given, the next player in the current direction is chosen.
|
||||||
|
* </summary>
|
||||||
|
* <param name="nextPlayer">The player to change the turn to. Defaults to the next player in the current direction.</param>
|
||||||
|
*/
|
||||||
|
public void ChangeTurn(Player? nextPlayer = null)
|
||||||
|
{
|
||||||
|
var player = nextPlayer ?? GetNextPlayer();
|
||||||
|
CurrentPlayer.State = PlayerState.WAIT;
|
||||||
|
player.State = PlayerState.TURN;
|
||||||
|
CurrentPlayer = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Get a player that is a given amount of players away from the current player in the current direction.
|
||||||
|
* </summary>
|
||||||
|
* <param name="numberOfPlayers">The amount of players to skip. Defaults to 1</param>
|
||||||
|
*/
|
||||||
|
public Player GetNextPlayer(int numberOfPlayers = 1)
|
||||||
|
{
|
||||||
|
var playerIndex = Players.IndexOf(CurrentPlayer);
|
||||||
|
for (var i = 0; i < numberOfPlayers; i++)
|
||||||
|
{
|
||||||
|
playerIndex += _currentDirection;
|
||||||
|
if (playerIndex >= Players.Count) playerIndex = 0;
|
||||||
|
if (playerIndex < 0) playerIndex = Players.Count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Players[playerIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Shuffle the list of players for a play random order.
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
|
private void ShufflePlayers()
|
||||||
|
{
|
||||||
|
Players = Players.OrderBy(x => Guid.NewGuid()).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,5 +13,25 @@ public class Player
|
|||||||
Connection = connection;
|
Connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsMe(string playerId) => Connection.ConnectionId == playerId;
|
public void GiveCard(Card card)
|
||||||
|
{
|
||||||
|
Hand.Add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GiveCards(IEnumerable<Card> cards)
|
||||||
|
{
|
||||||
|
Hand.AddRange(cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Card? TakeCardFromHand(Card card)
|
||||||
|
{
|
||||||
|
return Hand.FirstOrDefault(handCard => handCard.IsSameCard(card));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMe(string playerId) => Connection.Id == playerId;
|
||||||
|
|
||||||
|
public bool CanPlayCard(Card currentCard)
|
||||||
|
{
|
||||||
|
return Hand.Any(card => card.CanBePlayedOn(currentCard));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Hangfire.MemoryStorage;
|
using Hangfire.MemoryStorage;
|
||||||
|
using MauMau_Server.Room;
|
||||||
using MauMau_Server.Websockets;
|
using MauMau_Server.Websockets;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using System.Net.WebSockets;
|
using System.Text.Json;
|
||||||
using System.Text.Json;
|
|
||||||
using MauMau_Server.Websockets;
|
using MauMau_Server.Websockets;
|
||||||
|
|
||||||
namespace MauMau_Server.Mau;
|
namespace MauMau_Server.Room.Chat;
|
||||||
|
|
||||||
public class Chat
|
public class Chat
|
||||||
{
|
{
|
||||||
|
|||||||
39
Room/Lobby.cs
Normal file
39
Room/Lobby.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using MauMau_Server.Mau;
|
||||||
|
using MauMau_Server.Websockets;
|
||||||
|
|
||||||
|
namespace MauMau_Server.Room;
|
||||||
|
|
||||||
|
public class Lobby : RoomType
|
||||||
|
{
|
||||||
|
private readonly Room _room;
|
||||||
|
|
||||||
|
public Lobby(Room room)
|
||||||
|
{
|
||||||
|
_room = room;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lobby(Room room, ConnectionInstance connection)
|
||||||
|
{
|
||||||
|
_room = room;
|
||||||
|
Console.WriteLine(connection.Name + " won the game!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnMessage(ConnectionInstance sender, string message)
|
||||||
|
{
|
||||||
|
// TODO: Add a way to change game settings
|
||||||
|
if (sender == _room._host)
|
||||||
|
{
|
||||||
|
_room._roomType = new Game(_room, _room.Connections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnConnect(ConnectionInstance connection)
|
||||||
|
{
|
||||||
|
var roomMessage = new RoomMessage("LOBBY", MessageType.INFO, _room.Connections, connection.Name + " joined!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDisconnect(ConnectionInstance connection)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace MauMau_Server.Mau;
|
namespace MauMau_Server.Room;
|
||||||
|
|
||||||
public class MessageDTO
|
public class MessageDTO
|
||||||
{
|
{
|
||||||
|
|||||||
144
Room/Room.cs
144
Room/Room.cs
@@ -1,75 +1,55 @@
|
|||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using MauMau_Server.Mau;
|
using MauMau_Server.Websockets;
|
||||||
|
|
||||||
namespace MauMau_Server.Websockets;
|
namespace MauMau_Server.Room;
|
||||||
|
|
||||||
public class Room
|
public class Room
|
||||||
{
|
{
|
||||||
private readonly IRoomManager _roomManager;
|
private readonly IRoomManager _roomManager;
|
||||||
private readonly string _roomId;
|
private readonly string _roomId;
|
||||||
private readonly List<ConnectionInstance> _connections = new();
|
public readonly List<ConnectionInstance> Connections = new();
|
||||||
private ConnectionInstance? _host;
|
public ConnectionInstance? _host;
|
||||||
private readonly Chat _chat;
|
private readonly Chat.Chat _chat;
|
||||||
private Game? _game;
|
public RoomType _roomType;
|
||||||
private RoomState _state = RoomState.LOBBY;
|
|
||||||
|
|
||||||
public Room(IRoomManager roomManager, string roomId)
|
public Room(IRoomManager roomManager, string roomId)
|
||||||
{
|
{
|
||||||
_roomManager = roomManager;
|
_roomManager = roomManager;
|
||||||
_chat = new Chat(this);
|
_chat = new Chat.Chat(this);
|
||||||
|
_roomType = new Lobby(this);
|
||||||
_roomId = roomId;
|
_roomId = roomId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InstantiateConnection(WebSocket socket, string name)
|
public async Task InstantiateConnection(WebSocket socket, string name)
|
||||||
{
|
{
|
||||||
var connection = AddConnection(socket, name);
|
var connection = AddConnection(socket, name);
|
||||||
_game?.AddPlayerToGame(connection);
|
_roomType.OnConnect(connection);
|
||||||
await HandleConnection(connection);
|
await HandleConnection(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleConnection(ConnectionInstance connection)
|
private async Task HandleConnection(ConnectionInstance connection)
|
||||||
{
|
{
|
||||||
BroadcastState();
|
|
||||||
var webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket);
|
var webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket);
|
||||||
while (!webSocketResponse.Result!.CloseStatus.HasValue)
|
while (!webSocketResponse.Result!.CloseStatus.HasValue)
|
||||||
{
|
{
|
||||||
var message = JsonSerializer.Deserialize<MessageDTO>(webSocketResponse.SlicedBuffer);
|
var message = JsonSerializer.Deserialize<MessageDTO>(webSocketResponse.SlicedBuffer);
|
||||||
switch (message.Type)
|
if (message.Type == "CHAT")
|
||||||
{
|
|
||||||
case "GAME":
|
|
||||||
{
|
|
||||||
if (_state != RoomState.GAME) break;
|
|
||||||
var gameInput = JsonSerializer.Deserialize<ActionDTO>(message.Payload);
|
|
||||||
_game.HandleAction(connection.ConnectionId, gameInput);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "CHAT":
|
|
||||||
{
|
{
|
||||||
var cleanedMessage = StripHTML(message.Payload);
|
var cleanedMessage = StripHTML(message.Payload);
|
||||||
if (string.IsNullOrWhiteSpace(cleanedMessage))
|
if (string.IsNullOrWhiteSpace(cleanedMessage)) cleanedMessage = "Mau!";
|
||||||
{
|
|
||||||
cleanedMessage = "Mau!";
|
|
||||||
};
|
|
||||||
_chat.SendChatMessage(connection, cleanedMessage);
|
_chat.SendChatMessage(connection, cleanedMessage);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case "LOBBY":
|
else
|
||||||
{
|
{
|
||||||
if (connection.ConnectionId == _host?.ConnectionId)
|
_roomType.OnMessage(connection, message.Payload);
|
||||||
{
|
|
||||||
ChangeLobbyState(RoomState.GAME);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BroadcastState();
|
|
||||||
webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket);
|
webSocketResponse = await WebsocketManager.ReceiveAsync(connection.Socket);
|
||||||
}
|
}
|
||||||
WebsocketManager.CloseAsync(connection.Socket, webSocketResponse.Result);
|
WebsocketManager.CloseAsync(connection.Socket, webSocketResponse.Result);
|
||||||
HandleDisconnect(connection.ConnectionId);
|
HandleDisconnect(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConnectionInstance AddConnection(WebSocket socket, string name)
|
private ConnectionInstance AddConnection(WebSocket socket, string name)
|
||||||
@@ -77,108 +57,44 @@ public class Room
|
|||||||
var connectionId = Guid.NewGuid().ToString();
|
var connectionId = Guid.NewGuid().ToString();
|
||||||
var connection = new ConnectionInstance(name, connectionId, socket);
|
var connection = new ConnectionInstance(name, connectionId, socket);
|
||||||
if (IsEmpty()) _host = connection;
|
if (IsEmpty()) _host = connection;
|
||||||
_connections.Add(connection);
|
Connections.Add(connection);
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveConnection(string socketId)
|
private void HandleDisconnect(ConnectionInstance connection)
|
||||||
{
|
{
|
||||||
_connections.RemoveAll(connection => connection.ConnectionId == socketId);
|
Connections.Remove(connection);
|
||||||
}
|
_roomType.OnDisconnect(connection);
|
||||||
|
|
||||||
private void BroadcastGameState()
|
|
||||||
{
|
|
||||||
if (_game == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 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);
|
|
||||||
if (IsEmpty())
|
if (IsEmpty())
|
||||||
{
|
{
|
||||||
_roomManager.RemoveRoom(_roomId);
|
_roomManager.RemoveRoom(_roomId);
|
||||||
}
|
}
|
||||||
else if (socketId == _host.ConnectionId)
|
else if (connection == _host)
|
||||||
{
|
{
|
||||||
_host = _connections.First();
|
_host = Connections.First();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BroadCast(RoomMessage message)
|
||||||
|
{
|
||||||
|
foreach (var connection in Connections)
|
||||||
|
{
|
||||||
|
connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<WebSocket> GetWebsockets()
|
public List<WebSocket> GetWebsockets()
|
||||||
{
|
{
|
||||||
return _connections.Select(connection => connection.Socket).ToList();
|
return Connections.Select(connection => connection.Socket).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsEmpty()
|
public bool IsEmpty()
|
||||||
{
|
{
|
||||||
return _connections.Count == 0;
|
return Connections.Count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string StripHTML(string input)
|
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
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace MauMau_Server.Websockets;
|
namespace MauMau_Server.Room;
|
||||||
|
|
||||||
public class RoomManager : IRoomManager
|
public class RoomManager : IRoomManager
|
||||||
{
|
{
|
||||||
|
|||||||
39
Room/RoomMessage.cs
Normal file
39
Room/RoomMessage.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using MauMau_Server.Websockets;
|
||||||
|
|
||||||
|
namespace MauMau_Server.Room;
|
||||||
|
|
||||||
|
public class RoomMessage
|
||||||
|
{
|
||||||
|
public string CurrentRoomType;
|
||||||
|
public string? MessageType;
|
||||||
|
public List<string> JoinedPlayers;
|
||||||
|
public string? Message;
|
||||||
|
|
||||||
|
public RoomMessage(string currentRoomType, IEnumerable<ConnectionInstance> joinedPlayers, string? message)
|
||||||
|
{
|
||||||
|
CurrentRoomType = currentRoomType;
|
||||||
|
JoinedPlayers = GetNamesFromConnections(joinedPlayers);
|
||||||
|
Message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoomMessage(string currentRoomType, MessageType messageType, IEnumerable<ConnectionInstance> joinedPlayers, string? message)
|
||||||
|
{
|
||||||
|
CurrentRoomType = currentRoomType;
|
||||||
|
MessageType = messageType.ToString();
|
||||||
|
JoinedPlayers = GetNamesFromConnections(joinedPlayers);
|
||||||
|
Message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> GetNamesFromConnections(IEnumerable<ConnectionInstance> connections)
|
||||||
|
{
|
||||||
|
return connections.Select(player => player.Name).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MessageType
|
||||||
|
{
|
||||||
|
INFO,
|
||||||
|
SUCCES,
|
||||||
|
WARNING,
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
38
Room/RoomType.cs
Normal file
38
Room/RoomType.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using MauMau_Server.Websockets;
|
||||||
|
|
||||||
|
namespace MauMau_Server.Room;
|
||||||
|
|
||||||
|
public abstract class RoomType
|
||||||
|
{
|
||||||
|
protected readonly Room _room;
|
||||||
|
|
||||||
|
protected RoomType(Room room)
|
||||||
|
{
|
||||||
|
_room = room;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* This method is called when a message is received from a ConnectionInstance
|
||||||
|
* </summary>
|
||||||
|
* <param name="sender">The ConnectionInstance that sent the message</param>
|
||||||
|
* <param name="message">The message received</param>
|
||||||
|
*/
|
||||||
|
public abstract void OnMessage(ConnectionInstance sender, string message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* This method is called when a new ConnectionInstance is added to the Room.
|
||||||
|
* </summary>
|
||||||
|
* <param name="connection">The ConnectionInstance that was added to the Room</param>
|
||||||
|
*/
|
||||||
|
public abstract void OnConnect(ConnectionInstance connection);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* This method is called when a ConnectionInstance is either removed from the Room or the ConnectionInstance's WebSocket is closed.
|
||||||
|
* </summary>
|
||||||
|
* <param name="connection">The ConnectionInstance that was removed from the Room</param>
|
||||||
|
*/
|
||||||
|
public abstract void OnDisconnect(ConnectionInstance connection);
|
||||||
|
}
|
||||||
@@ -1,17 +1,30 @@
|
|||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace MauMau_Server.Websockets;
|
namespace MauMau_Server.Websockets;
|
||||||
|
|
||||||
public class ConnectionInstance
|
public class ConnectionInstance
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string ConnectionId { get; set; }
|
public string Id { get; set; }
|
||||||
public WebSocket Socket { get; set; }
|
public WebSocket Socket { get; set; }
|
||||||
|
|
||||||
public ConnectionInstance(string name, string connectionId, WebSocket socket)
|
public ConnectionInstance(string name, string id, WebSocket socket)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
ConnectionId = connectionId;
|
Id = id;
|
||||||
Socket = socket;
|
Socket = socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <summary>
|
||||||
|
* Sends a message to the client. This method is asynchronous and formats the message to be ready to be sent.
|
||||||
|
* </summary>
|
||||||
|
*/
|
||||||
|
public void SendMessageAsync(string message)
|
||||||
|
{
|
||||||
|
var bytes = Encoding.Default.GetBytes(message);
|
||||||
|
var arraySegment = new ArraySegment<byte>(bytes);
|
||||||
|
Socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user