From 554b8cff2a1658ca8c69a33fa5d24c033e9c267d Mon Sep 17 00:00:00 2001 From: Martin Berg Alstad <600878@stud.hvl.no> Date: Thu, 20 Jul 2023 23:54:20 +0200 Subject: [PATCH] Moved event to Game, added leave game button --- .../ClientApp/src/AppRoutes.tsx | 3 +- .../src/components/gameComponent.tsx | 18 +++++-- .../ClientApp/src/utils/actions.ts | 9 ++-- .../Controllers/GameController.cs | 26 +++------- pac-man-board-game/GameStuff/Character.cs | 2 +- pac-man-board-game/Services/ActionService.cs | 52 ++++++++++++++----- pac-man-board-game/Services/GameService.cs | 7 --- 7 files changed, 67 insertions(+), 50 deletions(-) diff --git a/pac-man-board-game/ClientApp/src/AppRoutes.tsx b/pac-man-board-game/ClientApp/src/AppRoutes.tsx index ba2695d..22c388c 100644 --- a/pac-man-board-game/ClientApp/src/AppRoutes.tsx +++ b/pac-man-board-game/ClientApp/src/AppRoutes.tsx @@ -1,6 +1,5 @@ import React from "react"; import {Counter} from "./pages/counter"; -import Home from "./pages/home"; import Game from "./pages/game"; import LobbyPage from "./pages/lobby"; import Login from "./pages/login"; @@ -8,7 +7,7 @@ import Login from "./pages/login"; const AppRoutes = [ { index: true, - element: + element: }, { path: "/counter", diff --git a/pac-man-board-game/ClientApp/src/components/gameComponent.tsx b/pac-man-board-game/ClientApp/src/components/gameComponent.tsx index 1b52fe4..ff37c8a 100644 --- a/pac-man-board-game/ClientApp/src/components/gameComponent.tsx +++ b/pac-man-board-game/ClientApp/src/components/gameComponent.tsx @@ -9,19 +9,21 @@ import PlayerStats from "../components/playerStats"; import {useAtom, useAtomValue, useSetAtom} from "jotai"; import {diceAtom, ghostsAtom, playersAtom, rollDiceButtonAtom, selectedDiceAtom} from "../utils/state"; import GameButton from "./gameButton"; +import {Button} from "./button"; +import {useNavigate} from "react-router-dom"; const wsService = new WebSocketService(import.meta.env.VITE_API_WS); -// TODO bug, when taking player on last dice, the currentPlayer changes and the wrong character get to steal -// TODO bug, first player can sometimes roll dice twice (maybe only on firefox) +// TODO bug, when taking player on last dice, the currentPlayer changes and the wrong character gets to steal +// TODO bug, first player can sometimes roll dice twice +// TODO bug, when refreshing page, the player is still in disconnected state // TODO bug, when refreshing page, the characters are reset for all players // TODO bug, when refreshing page, some data is missing until other clients make a move +// TODO bug, teleportation doesn't work // TODO store map in backend and save it in state on each client // TODO add debug menu on dev, for testing and cheating -// TODO join/new game lobby // TODO sign up player page -// TODO sign in page // TODO show box with collected pellets // TODO layout @@ -33,6 +35,8 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map const setActiveRollDiceButton = useSetAtom(rollDiceButtonAtom); const ghosts = useAtomValue(ghostsAtom); + const navigate = useNavigate(); + function rollDice(): void { if (!player.isTurn()) return; @@ -79,6 +83,11 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map wsService.send({action: GameAction.nextPlayer}); } + function leaveGame(): void { + wsService.send({action: GameAction.disconnect}); + navigate("/lobby"); + } + useEffect(() => { wsService.onReceive = doAction; wsService.open(); @@ -90,6 +99,7 @@ export const GameComponent: FC<{ player: Player, map: GameMap }> = ({player, map return ( <> +
{players?.map(p => )}
diff --git a/pac-man-board-game/ClientApp/src/utils/actions.ts b/pac-man-board-game/ClientApp/src/utils/actions.ts index 7dac3f5..cbb869c 100644 --- a/pac-man-board-game/ClientApp/src/utils/actions.ts +++ b/pac-man-board-game/ClientApp/src/utils/actions.ts @@ -50,6 +50,9 @@ export const doAction: MessageEventFunction = (event): void => { // TODO case GameAction.nextPlayer: nextPlayer(message.data); break; + case GameAction.disconnect: + updatePlayers(message.data); + break; } }; @@ -61,14 +64,12 @@ type MoveCharacterData = { dice: number[], players: PlayerProps[], ghosts: Chara function moveCharacter(data?: MoveCharacterData): void { store.set(diceAtom, data?.dice); - updatePlayers(data); + updatePlayers(data?.players); updateGhosts(data); removeEatenPellets(data); } -function updatePlayers(data?: MoveCharacterData): void { - const updatedPlayers = data?.players; - +function updatePlayers(updatedPlayers?: PlayerProps[]): void { if (updatedPlayers) { const newList: Player[] = updatedPlayers.map(p => new Player(p)); store.set(playersAtom, newList); diff --git a/pac-man-board-game/Controllers/GameController.cs b/pac-man-board-game/Controllers/GameController.cs index b496466..199192a 100644 --- a/pac-man-board-game/Controllers/GameController.cs +++ b/pac-man-board-game/Controllers/GameController.cs @@ -66,6 +66,11 @@ public class GameController : GenericController } } + protected override Task Echo() + { + _actionService.WebSocket = WebSocket ?? throw new NullReferenceException("WebSocket is null"); + return base.Echo(); + } protected override ArraySegment Run(WebSocketReceiveResult result, byte[] data) { @@ -78,24 +83,7 @@ public class GameController : GenericController return action.ToArraySegment(); } - protected override void Send(ArraySegment segment) => _gameService.SendToAll(segment); + protected override void Send(ArraySegment segment) => _actionService.SendToAll(segment); - protected override Task Echo() - { - _gameService.Connections += WsServiceOnFire; // TODO move to ActionService - // _actionService.Game.Connections += WsServiceOnFire; - return base.Echo(); - } - - protected override void Disconnect() - { - _gameService.Connections -= WsServiceOnFire; - _actionService.Disconnect(); - } - - private async Task WsServiceOnFire(ArraySegment segment) - { - if (WebSocket == null) return; - await GameService.Send(WebSocket, segment); - } + protected override void Disconnect() => _actionService.Disconnect(); } diff --git a/pac-man-board-game/GameStuff/Character.cs b/pac-man-board-game/GameStuff/Character.cs index fc451c7..66211e2 100644 --- a/pac-man-board-game/GameStuff/Character.cs +++ b/pac-man-board-game/GameStuff/Character.cs @@ -31,7 +31,7 @@ public class Character : IEquatable return obj.GetType() == GetType() && Equals((Character)obj); } - public override int GetHashCode() => HashCode.Combine(Colour, Position, IsEatable, SpawnPosition, (int)Type); + public override int GetHashCode() => HashCode.Combine(Colour, Position, IsEatable, SpawnPosition, (int?)Type); } public enum CharacterType diff --git a/pac-man-board-game/Services/ActionService.cs b/pac-man-board-game/Services/ActionService.cs index a4b2237..54923d3 100644 --- a/pac-man-board-game/Services/ActionService.cs +++ b/pac-man-board-game/Services/ActionService.cs @@ -1,3 +1,4 @@ +using System.Net.WebSockets; using System.Text.Json; using System.Text.Json.Serialization; using pacMan.GameStuff; @@ -9,12 +10,15 @@ public interface IActionService { Player Player { set; } Game Game { set; } + WebSocket? WebSocket { set; } void DoAction(ActionMessage message); List RollDice(); List SetPlayerInfo(JsonElement? jsonElement); - object? HandleMoveCharacter(JsonElement? jsonElement); // TODO test + object? HandleMoveCharacter(JsonElement? jsonElement); object Ready(); string FindNextPlayer(); + List LeaveGame(); + void SendToAll(ArraySegment segment); void Disconnect(); } @@ -29,6 +33,8 @@ public class ActionService : IActionService _gameService = gameService; } + public WebSocket? WebSocket { private get; set; } + public Game? Game { get; set; } public Player? Player { get; set; } @@ -42,6 +48,7 @@ public class ActionService : IActionService GameAction.PlayerInfo => SetPlayerInfo(message.Data), GameAction.Ready => Ready(), GameAction.NextPlayer => FindNextPlayer(), + GameAction.Disconnect => LeaveGame(), _ => message.Data }; } @@ -55,19 +62,28 @@ public class ActionService : IActionService return rolls; } + public object? HandleMoveCharacter(JsonElement? jsonElement) + { + if (Game != null && jsonElement.HasValue) + Game.Ghosts = jsonElement.Value.GetProperty("ghosts").Deserialize>() ?? + throw new JsonException("Ghosts is null"); + + return jsonElement; + } + public List SetPlayerInfo(JsonElement? jsonElement) // TODO split up into two actions { var data = jsonElement?.Deserialize() ?? throw new NullReferenceException("Data is null"); Player = data.Player; - Game? group; + Game? game; Player? player; - if ((group = _gameService.FindGameByUsername(Player.Username)) != null && - (player = group.Players.Find(p => p.Username == Player.Username))?.State == State.Disconnected) + if ((game = _gameService.FindGameByUsername(Player.Username)) != null && + (player = game.Players.Find(p => p.Username == Player.Username))?.State == State.Disconnected) { - player.State = group.IsGameStarted ? State.InGame : State.WaitingForPlayers; + player.State = game.IsGameStarted ? State.InGame : State.WaitingForPlayers; // TODO doesn't work anymore Player = player; - Game = group; + Game = game; // TODO send missing data: Dices, CurrentPlayer, Ghosts } else @@ -75,6 +91,8 @@ public class ActionService : IActionService Game = _gameService.AddPlayer(Player, data.Spawns); } + Game.Connections += SendSegment; + return Game.Players; } @@ -100,18 +118,26 @@ public class ActionService : IActionService public string FindNextPlayer() => Game?.NextPlayer().Username ?? "Error: No group found"; - public void Disconnect() + public List LeaveGame() { - if (Player != null) Player.State = State.Disconnected; + if (Game == null || Player == null) throw new NullReferenceException("Game or Player is null"); + Game.RemovePlayer(Player.Username); + return Game.Players; } - public object? HandleMoveCharacter(JsonElement? jsonElement) + public void Disconnect() { - if (Game != null && jsonElement.HasValue) - Game.Ghosts = jsonElement.Value.GetProperty("ghosts").Deserialize>() ?? - throw new JsonException("Ghosts is null"); + if (Player == null) return; + Player.State = State.Disconnected; + if (Game != null) Game.Connections -= SendSegment; + } - return jsonElement; + public void SendToAll(ArraySegment segment) => Game?.SendToAll(segment); + + private async Task SendSegment(ArraySegment segment) + { + if (WebSocket != null) await _gameService.Send(WebSocket, segment); + else await Task.FromCanceled(new CancellationToken(true)); } } diff --git a/pac-man-board-game/Services/GameService.cs b/pac-man-board-game/Services/GameService.cs index c8296be..39b3e47 100644 --- a/pac-man-board-game/Services/GameService.cs +++ b/pac-man-board-game/Services/GameService.cs @@ -19,13 +19,6 @@ public class GameService : WebSocketService /// public SynchronizedCollection Games { get; } = new(); - public event Func, Task>? Connections; // TODO remove and use Game - - public void SendToAll(ArraySegment segment) - { - Connections?.Invoke(segment); - } - public Game AddPlayer(Player player, Queue spawns) { var index = 0;