From a4e22a4c522f5effe610486e14205d22002a0b46 Mon Sep 17 00:00:00 2001 From: Michael Chen Date: Fri, 4 Nov 2022 00:13:00 +0100 Subject: [PATCH] Added game state, allow paddle movement --- PongGame/Hubs/GameState.cs | 2 +- PongGame/Hubs/IPongClient.cs | 2 +- PongGame/Hubs/PongGameState.cs | 35 +++++++++++++++++++++++++ PongGame/Hubs/PongHub.cs | 15 +++++++++++ PongGame/Hubs/PongPaddleDirection.cs | 7 +++++ PongGame/Hubs/PongRoom.cs | 38 ++++++++++++++++++++-------- PongGame/wwwroot/js/pong.js | 10 ++++++++ 7 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 PongGame/Hubs/PongGameState.cs create mode 100644 PongGame/Hubs/PongPaddleDirection.cs diff --git a/PongGame/Hubs/GameState.cs b/PongGame/Hubs/GameState.cs index d878289..786a769 100644 --- a/PongGame/Hubs/GameState.cs +++ b/PongGame/Hubs/GameState.cs @@ -1,6 +1,6 @@ namespace PongGame; -public enum GameState { +public enum GameStatus { WaitingForPlayers, InProgress, Finished, diff --git a/PongGame/Hubs/IPongClient.cs b/PongGame/Hubs/IPongClient.cs index 4a7b51b..77117d5 100644 --- a/PongGame/Hubs/IPongClient.cs +++ b/PongGame/Hubs/IPongClient.cs @@ -1,6 +1,6 @@ namespace PongGame.Hubs; public interface IPongClient { - Task GameStateChanged(GameState state); + Task GameStateChanged(GameStatus state); Task UsernameChanged(string value); } \ No newline at end of file diff --git a/PongGame/Hubs/PongGameState.cs b/PongGame/Hubs/PongGameState.cs new file mode 100644 index 0000000..2981cb9 --- /dev/null +++ b/PongGame/Hubs/PongGameState.cs @@ -0,0 +1,35 @@ +namespace PongGame.Hubs; + +/// +/// Pong game state saving the positions of the paddles and the ball. +/// The Pong board has aspect ratio 1:2 with height 500 and width 1000. +/// +public struct PongGameState { + private const double HEIGHT = 500.0d; + private const double WIDTH = 2 * HEIGHT; + private const double PADDLE_LENGTH = HEIGHT / 10; + private const double BALL_SPEED = 8; + public static readonly PongGameState Initial = new() { + BallPosition = PongBallPosition.Initial, + P1Height = HEIGHT / 2, + P2Height = HEIGHT / 2, + P1Direction = PongPaddleDirection.Stop, + P2Direction = PongPaddleDirection.Stop, + Status = GameStatus.WaitingForPlayers + }; + public double P1Height { get; set; } + public double P2Height { get; set; } + public PongBallPosition BallPosition { get; set; } + public GameStatus Status { get; set; } + public PongPaddleDirection P1Direction { get; set; } + public PongPaddleDirection P2Direction { get; set; } + + public struct PongBallPosition { + public static readonly PongBallPosition Initial = new() { + X = WIDTH / 2, + Y = HEIGHT / 2 + }; + public double X { get; set; } + public double Y { get; set; } + } +} \ No newline at end of file diff --git a/PongGame/Hubs/PongHub.cs b/PongGame/Hubs/PongHub.cs index b5c98d2..f9fc32d 100644 --- a/PongGame/Hubs/PongHub.cs +++ b/PongGame/Hubs/PongHub.cs @@ -30,6 +30,12 @@ public class PongHub : Hub { throw new HubException($"User is already connected to room [{currentRoom}]"); } + private PongRoom AssertInRoom() { + if (Player.ConnectedRoom is not PongRoom currentRoom) + throw new HubException($"User is not in any room!"); + return currentRoom; + } + public Task CreateRoom() { AssertNotInRoom(); var room = Lobby.CreateRoom(Player); @@ -42,6 +48,15 @@ public class PongHub : Hub { return Task.FromResult(room.ID); } + public Task MovePaddle(int dir) { + var room = AssertInRoom(); + var direction = (PongPaddleDirection)dir; + if (!Enum.IsDefined(direction)) + throw new HubException($"Invalid direction: {dir}!"); + room.MovePaddle(Player, direction); + return Task.CompletedTask; + } + public Task LeaveRoom() { Lobby.LeaveRoom(Player); return Task.CompletedTask; diff --git a/PongGame/Hubs/PongPaddleDirection.cs b/PongGame/Hubs/PongPaddleDirection.cs new file mode 100644 index 0000000..fd42c1c --- /dev/null +++ b/PongGame/Hubs/PongPaddleDirection.cs @@ -0,0 +1,7 @@ +namespace PongGame; + +public enum PongPaddleDirection { + Up = -1, + Stop, + Down, +} \ No newline at end of file diff --git a/PongGame/Hubs/PongRoom.cs b/PongGame/Hubs/PongRoom.cs index 25b8de4..26ee9c8 100644 --- a/PongGame/Hubs/PongRoom.cs +++ b/PongGame/Hubs/PongRoom.cs @@ -5,6 +5,9 @@ namespace PongGame.Hubs; public class PongRoom { public string ID { get; } private readonly ILogger Logger; + private const string JOIN_LOG_TEMPLATE = "[{ID}] {Player} joined pong room as player {Number}!"; + private const string LEAVE_LOG_TEMPLATE = "[{ID}] Player {Number} {Player} left pong room!"; + private const string DIRECTION_LOG_TEMPLATE = "[{ID}] Player {Number} {player} moves paddle in direction: {direction}!"; public PongRoom(string id, ILogger logger) { ID = id; @@ -13,16 +16,16 @@ public class PongRoom { public PongPlayer? Player1 { get; private set; } public PongPlayer? Player2 { get; private set; } - public GameState State { get; private set; } = GameState.WaitingForPlayers; + private PongGameState State = PongGameState.Initial; public void Join(PongPlayer player) { // TODO: synchronize this if (Player1 is null) { Player1 = player; - Logger.LogInformation($"[{ID}] {player} joined pong room as player 1!"); + Logger.LogInformation(JOIN_LOG_TEMPLATE, ID, player, 1); } else if (Player2 is null) { Player2 = player; - Logger.LogInformation($"[{ID}] {player} joined pong room as player 2!"); + Logger.LogInformation(JOIN_LOG_TEMPLATE, ID, player, 1); } else throw new HubException($"Lobby [{ID}] is already full!"); _ = Task.Run(PlayersChanged); @@ -32,10 +35,10 @@ public class PongRoom { public void Leave(PongPlayer player) { if (Player1 == player) { Player1 = null; - Logger.LogInformation($"[{ID}] Player 1 {player} left pong room!"); + Logger.LogInformation(LEAVE_LOG_TEMPLATE, ID, 1, player); } else if (Player2 == player) { Player2 = null; - Logger.LogInformation($"[{ID}] Player 2 {player} left pong room!"); + Logger.LogInformation(LEAVE_LOG_TEMPLATE, ID, 2, player); } player.ConnectedRoom = null; _ = Task.Run(PlayersChanged); @@ -52,14 +55,27 @@ public class PongRoom { } private void ResumeGame(PongPlayer player1, PongPlayer player2) { - Logger.LogInformation($"[{ID}] Pong game started: {player1} vs. {player2}"); - State = GameState.InProgress; - player1.Client.GameStateChanged(State); - player2.Client.GameStateChanged(State); + Logger.LogInformation("[{ID}] Pong game started: {player1} vs. {player2}", ID, player1, player2); + State.Status = GameStatus.InProgress; + player1.Client.GameStateChanged(State.Status); + player2.Client.GameStateChanged(State.Status); } - private void PauseGame() => State = GameState.WaitingForPlayers; - private void CloseRoom() => State = GameState.Finished; + private void PauseGame() => State.Status = GameStatus.WaitingForPlayers; + private void CloseRoom() => State.Status = GameStatus.Finished; public override string ToString() => $"[{ID}]"; + + public void MovePaddle(PongPlayer player, PongPaddleDirection direction) { + if (Player1 == player) { + State.P1Direction = direction; + Logger.LogInformation(DIRECTION_LOG_TEMPLATE, ID, 1, player, direction); + return; + } else if (Player2 == player) { + State.P2Direction = direction; + Logger.LogInformation(DIRECTION_LOG_TEMPLATE, ID, 2, player, direction); + return; + } + throw new InvalidOperationException("Player is not in this room, but moved! Assumably players room wasn't deleted."); + } } \ No newline at end of file diff --git a/PongGame/wwwroot/js/pong.js b/PongGame/wwwroot/js/pong.js index 465062e..966ba8b 100644 --- a/PongGame/wwwroot/js/pong.js +++ b/PongGame/wwwroot/js/pong.js @@ -79,6 +79,16 @@ leavelobby.addEventListener("click", function (event) { event.preventDefault(); }); +function movePaddle(direction) { + connection.invoke("MovePaddle", direction).catch(function (err) { + return console.error(err.toString()); + }); +} + +function moveUp() { return movePaddle(-1); } +function stopPaddle() { return movePaddle(0); } +function moveDown() { return movePaddle(1); } + connection.start().then(function () { console.info(`Connected!`); connectionStatus.textContent = "Connected!";