diff --git a/BackendTests/BackendTests.csproj b/BackendTests/BackendTests.csproj
index 3ad8c64..f95e2a7 100644
--- a/BackendTests/BackendTests.csproj
+++ b/BackendTests/BackendTests.csproj
@@ -6,25 +6,27 @@
enable
false
+
+ 12
-
-
-
-
+
+
+
+
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/BackendTests/Controllers/GameControllerTests.cs b/BackendTests/Controllers/GameControllerTests.cs
index 64b75e0..3e236cb 100644
--- a/BackendTests/Controllers/GameControllerTests.cs
+++ b/BackendTests/Controllers/GameControllerTests.cs
@@ -51,4 +51,26 @@ public class GameControllerTests
else
Assert.Fail("Result is not an ArraySegment");
}
+
+ #region DoAction(ActionMessage message)
+
+ [Test]
+ public void DoAction_NegativeAction()
+ {
+ const string data = "Nothing happens";
+ var message = new ActionMessage { Action = (GameAction)(-1), Data = data };
+ _controller.DoAction(message);
+ Assert.That(message.Data, Is.EqualTo(data));
+ }
+
+ [Test]
+ public void DoAction_OutOfBoundsAction()
+ {
+ const string data = "Nothing happens";
+ var message = new ActionMessage { Action = (GameAction)100, Data = data };
+ _controller.DoAction(message);
+ Assert.That(message.Data, Is.EqualTo(data));
+ }
+
+ #endregion
}
diff --git a/BackendTests/Services/ActionServiceTests.cs b/BackendTests/Services/ActionServiceTests.cs
index 503fbd5..1c8a791 100644
--- a/BackendTests/Services/ActionServiceTests.cs
+++ b/BackendTests/Services/ActionServiceTests.cs
@@ -2,6 +2,7 @@ using System.Text.Json;
using BackendTests.TestUtils;
using Microsoft.Extensions.Logging;
using NSubstitute;
+using pacMan.DTOs;
using pacMan.Exceptions;
using pacMan.GameStuff;
using pacMan.GameStuff.Items;
@@ -99,28 +100,6 @@ public class ActionServiceTests
#endregion
- #region DoAction(ActionMessage message)
-
- [Test]
- public void DoAction_NegativeAction()
- {
- const string data = "Nothing happens";
- var message = new ActionMessage { Action = (GameAction)(-1), Data = data };
- _service.DoAction(message);
- Assert.That(message.Data, Is.EqualTo(data));
- }
-
- [Test]
- public void DoAction_OutOfBoundsAction()
- {
- const string data = "Nothing happens";
- var message = new ActionMessage { Action = (GameAction)100, Data = data };
- _service.DoAction(message);
- Assert.That(message.Data, Is.EqualTo(data));
- }
-
- #endregion
-
#region Ready()
[Test]
diff --git a/BackendTests/Services/WebSocketServiceTests.cs b/BackendTests/Services/WebSocketServiceTests.cs
index 2c740f0..5a712f6 100644
--- a/BackendTests/Services/WebSocketServiceTests.cs
+++ b/BackendTests/Services/WebSocketServiceTests.cs
@@ -1,7 +1,6 @@
using System.Net.WebSockets;
using Microsoft.Extensions.Logging;
using NSubstitute;
-using pacMan.Interfaces;
using pacMan.Services;
using pacMan.Utils;
diff --git a/DataAccessLayer/DataAccessLayer.csproj b/DataAccessLayer/DataAccessLayer.csproj
index ae25253..dd09258 100644
--- a/DataAccessLayer/DataAccessLayer.csproj
+++ b/DataAccessLayer/DataAccessLayer.csproj
@@ -5,6 +5,7 @@
enable
enable
DAL
+ 12
diff --git a/pac-man-board-game/ClientApp/package.json b/pac-man-board-game/ClientApp/package.json
index 55e979b..f24f8c8 100644
--- a/pac-man-board-game/ClientApp/package.json
+++ b/pac-man-board-game/ClientApp/package.json
@@ -43,7 +43,7 @@
"serve": "vite preview",
"test": "cross-env CI=true vitest",
"coverage": "vitest run --coverage",
- "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
+ "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\""
},
"browserslist": {
"production": [
diff --git a/pac-man-board-game/Controllers/GameController.cs b/pac-man-board-game/Controllers/GameController.cs
index 4a289db..457700f 100644
--- a/pac-man-board-game/Controllers/GameController.cs
+++ b/pac-man-board-game/Controllers/GameController.cs
@@ -1,5 +1,6 @@
using System.Net.WebSockets;
using Microsoft.AspNetCore.Mvc;
+using pacMan.DTOs;
using pacMan.Exceptions;
using pacMan.GameStuff;
using pacMan.GameStuff.Items;
@@ -10,35 +11,26 @@ namespace pacMan.Controllers;
[ApiController]
[Route("api/[controller]")]
-public class GameController : GenericController
+public class GameController(ILogger logger, IGameService webSocketService, IActionService actionService)
+ : GenericController(logger, webSocketService)
{
- private readonly IActionService _actionService;
- private readonly GameService _gameService;
+ [HttpGet("[action]")]
+ public override async Task Connect() => await base.Connect();
- public GameController(ILogger logger, GameService webSocketService, IActionService actionService) :
- base(logger, webSocketService)
+ [HttpGet("[action]")]
+ public IEnumerable All()
{
- _gameService = webSocketService;
- _actionService = actionService;
+ Logger.LogDebug("Returning all games");
+ return webSocketService.Games;
}
- [HttpGet("connect")]
- public override async Task Accept() => await base.Accept();
-
- [HttpGet("all")]
- public IEnumerable GetAllGames()
+ [HttpPost("[action]/{gameId:guid}")]
+ public IActionResult Join(Guid gameId, [FromBody] Player player) // TODO what if player is in a game already?
{
- Logger.Log(LogLevel.Debug, "Returning all games");
- return _gameService.Games;
- }
-
- [HttpPost("join/{gameId:guid}")]
- public IActionResult JoinGame(Guid gameId, [FromBody] Player player) // TODO what if player is in a game already?
- {
- Logger.Log(LogLevel.Debug, "Joining game {}", gameId);
+ Logger.LogDebug("Joining game {}", gameId);
try
{
- _gameService.JoinById(gameId, player);
+ webSocketService.JoinById(gameId, player);
return Ok("Game joined successfully");
}
catch (GameNotFoundException e)
@@ -51,20 +43,20 @@ public class GameController : GenericController
}
}
- [HttpGet("exists/{gameId:guid}")]
- public IActionResult GameExists(Guid gameId)
+ [HttpGet("[action]/{gameId:guid}")]
+ public IActionResult Exists(Guid gameId)
{
- Logger.Log(LogLevel.Debug, "Checking if game {} exists", gameId);
- return _gameService.Games.Any(game => game.Id == gameId) ? Ok() : NotFound();
+ Logger.LogDebug("Checking if game {} exists", gameId);
+ return webSocketService.Games.Any(game => game.Id == gameId) ? Ok() : NotFound();
}
- [HttpPost("create")]
- public IActionResult CreateGame([FromBody] CreateGameData data)
+ [HttpPost("[action]")]
+ public IActionResult Create([FromBody] CreateGameData data)
{
- Logger.Log(LogLevel.Debug, "Creating game");
+ Logger.LogDebug("Creating game");
try
{
- var game = _gameService.CreateAndJoin(data.Player, data.Spawns);
+ var game = webSocketService.CreateAndJoin(data.Player, data.Spawns);
return Created($"/{game.Id}", game);
}
catch (Exception e)
@@ -75,7 +67,7 @@ public class GameController : GenericController
protected override Task Echo()
{
- _actionService.WebSocket = WebSocket ?? throw new NullReferenceException("WebSocket is null");
+ actionService.WebSocket = WebSocket ?? throw new NullReferenceException("WebSocket is null");
return base.Echo();
}
@@ -83,16 +75,16 @@ public class GameController : GenericController
{
var stringResult = data.GetString(result.Count);
- Logger.Log(LogLevel.Information, "Received: {}", stringResult);
+ Logger.LogInformation("Received: {}", stringResult);
var action = ActionMessage.FromJson(stringResult);
try
{
- _actionService.DoAction(action);
+ DoAction(action);
}
catch (Exception e)
{
- Logger.Log(LogLevel.Error, "{}", e.Message);
+ Logger.LogError("{}", e.Message);
action = new ActionMessage { Action = GameAction.Error, Data = e.Message };
}
@@ -101,15 +93,27 @@ public class GameController : GenericController
protected override async void Send(ArraySegment segment)
{
- if (_actionService.Game is not null)
- _actionService.SendToAll(segment);
+ if (actionService.Game is not null)
+ actionService.SendToAll(segment);
else if (WebSocket is not null)
- await _gameService.Send(WebSocket, segment);
+ await webSocketService.Send(WebSocket, segment);
}
protected override ArraySegment? Disconnect() =>
- new ActionMessage { Action = GameAction.Disconnect, Data = _actionService.Disconnect() }
+ new ActionMessage { Action = GameAction.Disconnect, Data = actionService.Disconnect() }
.ToArraySegment();
- protected override void SendDisconnectMessage(ArraySegment segment) => _actionService.SendToAll(segment);
+ protected override void SendDisconnectMessage(ArraySegment segment) => actionService.SendToAll(segment);
+
+ public void DoAction(ActionMessage message) =>
+ message.Data = message.Action switch
+ {
+ GameAction.RollDice => actionService.RollDice(),
+ GameAction.MoveCharacter => actionService.HandleMoveCharacter(message.Data),
+ GameAction.JoinGame => actionService.FindGame(message.Data),
+ GameAction.Ready => actionService.Ready(),
+ GameAction.NextPlayer => actionService.FindNextPlayer(),
+ GameAction.Disconnect => actionService.LeaveGame(),
+ _ => message.Data
+ };
}
diff --git a/pac-man-board-game/Controllers/GenericController.cs b/pac-man-board-game/Controllers/GenericController.cs
index 8fcee00..1a3f865 100644
--- a/pac-man-board-game/Controllers/GenericController.cs
+++ b/pac-man-board-game/Controllers/GenericController.cs
@@ -1,29 +1,22 @@
using System.Net.WebSockets;
using Microsoft.AspNetCore.Mvc;
-using pacMan.Interfaces;
+using pacMan.Services;
namespace pacMan.Controllers;
-public abstract class GenericController : ControllerBase
+public abstract class GenericController(ILogger logger, IWebSocketService webSocketService)
+ : ControllerBase
{
private const int BufferSize = 1024 * 4;
- private readonly IWebSocketService _webSocketService;
- protected readonly ILogger Logger;
+ protected readonly ILogger Logger = logger;
protected WebSocket? WebSocket;
- protected GenericController(ILogger logger, IWebSocketService webSocketService)
- {
- Logger = logger;
- _webSocketService = webSocketService;
- Logger.Log(LogLevel.Debug, "WebSocket Controller created");
- }
-
- public virtual async Task Accept()
+ public virtual async Task Connect()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
- Logger.Log(LogLevel.Information, "WebSocket connection established to {}", HttpContext.Connection.Id);
+ Logger.LogInformation("WebSocket connection established to {}", HttpContext.Connection.Id);
WebSocket = webSocket;
await Echo();
}
@@ -35,14 +28,14 @@ public abstract class GenericController : ControllerBase
protected virtual async Task Echo()
{
- if (WebSocket == null) return;
+ if (WebSocket is null) return;
try
{
WebSocketReceiveResult? result;
do
{
var buffer = new byte[BufferSize];
- result = await _webSocketService.Receive(WebSocket, buffer);
+ result = await webSocketService.Receive(WebSocket, buffer);
if (result.CloseStatus.HasValue) break;
@@ -52,21 +45,21 @@ public abstract class GenericController : ControllerBase
} while (true);
var disconnectSegment = Disconnect();
- if (disconnectSegment != null)
+ if (disconnectSegment is not null)
SendDisconnectMessage((ArraySegment)disconnectSegment);
- await _webSocketService.Close(WebSocket, result.CloseStatus.Value, result.CloseStatusDescription);
+ await webSocketService.Close(WebSocket, result.CloseStatus.Value, result.CloseStatusDescription);
}
catch (WebSocketException e)
{
- Logger.Log(LogLevel.Error, "{}", e.Message);
+ Logger.LogError("{}", e.Message);
}
}
protected virtual async void Send(ArraySegment segment)
{
- if (WebSocket == null) return;
- await _webSocketService.Send(WebSocket, segment);
+ if (WebSocket is null) return;
+ await webSocketService.Send(WebSocket, segment);
}
protected abstract ArraySegment Run(WebSocketReceiveResult result, byte[] data);
diff --git a/pac-man-board-game/Controllers/PlayerController.cs b/pac-man-board-game/Controllers/PlayerController.cs
index fa82298..dd8558a 100644
--- a/pac-man-board-game/Controllers/PlayerController.cs
+++ b/pac-man-board-game/Controllers/PlayerController.cs
@@ -6,21 +6,17 @@ using pacMan.GameStuff.Items;
namespace pacMan.Controllers;
[ApiController]
-[Route("api/[controller]")]
-public class PlayerController : ControllerBase
+[Route("api/[controller]/[action]")]
+public class PlayerController(UserService userService) : ControllerBase
{
- private readonly UserService _userService;
-
- public PlayerController(UserService userService) => _userService = userService;
-
- [HttpPost("login")]
+ [HttpPost]
public async Task Login([FromBody] User user)
{
- var result = await _userService.Login(user.Username, user.Password);
+ var result = await userService.Login(user.Username, user.Password);
if (result is null) return Unauthorized("Invalid username or password");
return Ok((Player)result);
}
- [HttpPost("register")]
+ [HttpPost]
public async Task Register([FromBody] User user) => throw new NotSupportedException();
}
diff --git a/pac-man-board-game/Controllers/WsController.cs b/pac-man-board-game/Controllers/WsController.cs
index 9767421..b340214 100644
--- a/pac-man-board-game/Controllers/WsController.cs
+++ b/pac-man-board-game/Controllers/WsController.cs
@@ -6,12 +6,11 @@ namespace pacMan.Controllers;
[ApiController]
[Route("api/[controller]")]
-public class WsController : GenericController
+public class WsController(ILogger logger, IWebSocketService gameService) :
+ GenericController(logger, gameService)
{
- public WsController(ILogger logger, GameService gameService) : base(logger, gameService) { }
-
[HttpGet]
- public override async Task Accept() => await base.Accept();
+ public override async Task Connect() => await base.Connect();
protected override ArraySegment Run(WebSocketReceiveResult result, byte[] data)
{
diff --git a/pac-man-board-game/DTOs/DTO.cs b/pac-man-board-game/DTOs/DTO.cs
new file mode 100644
index 0000000..8cb82f8
--- /dev/null
+++ b/pac-man-board-game/DTOs/DTO.cs
@@ -0,0 +1,50 @@
+using System.Text.Json.Serialization;
+using pacMan.GameStuff;
+using pacMan.GameStuff.Items;
+
+namespace pacMan.DTOs;
+
+public readonly record struct JoinGameData(
+ [property: JsonInclude]
+ [property: JsonPropertyName("username")]
+ string Username,
+ [property: JsonInclude]
+ [property: JsonPropertyName("gameId")]
+ Guid GameId
+)
+{
+ public void Deconstruct(out string username, out Guid gameId) => (username, gameId) = (Username, GameId);
+}
+
+public readonly record struct CreateGameData(
+ [property: JsonInclude]
+ [property: JsonPropertyName("player")]
+ Player Player,
+ [property: JsonInclude]
+ [property: JsonPropertyName("spawns")]
+ Queue Spawns
+);
+
+public readonly record struct ReadyData(
+ [property: JsonInclude]
+ [property: JsonPropertyName("allReady")]
+ bool AllReady,
+ [property: JsonInclude]
+ [property: JsonPropertyName("players")]
+ IEnumerable Players
+);
+
+public readonly record struct MovePlayerData(
+ [property: JsonInclude]
+ [property: JsonPropertyName("players")]
+ List Players,
+ [property: JsonInclude]
+ [property: JsonPropertyName("ghosts")]
+ List Ghosts,
+ [property: JsonInclude]
+ [property: JsonPropertyName("dice")]
+ List Dice,
+ [property: JsonInclude]
+ [property: JsonPropertyName("eatenPellets")]
+ List EatenPellets
+);
diff --git a/pac-man-board-game/Exceptions/GameExceptions.cs b/pac-man-board-game/Exceptions/GameExceptions.cs
new file mode 100644
index 0000000..2998ec2
--- /dev/null
+++ b/pac-man-board-game/Exceptions/GameExceptions.cs
@@ -0,0 +1,5 @@
+namespace pacMan.Exceptions;
+
+public class GameNotFoundException(string message = "Game not found") : Exception(message);
+
+public class GameNotPlayableException(string message = "Game is not allowed to be played") : Exception(message);
diff --git a/pac-man-board-game/Exceptions/GameNotFoundException.cs b/pac-man-board-game/Exceptions/GameNotFoundException.cs
deleted file mode 100644
index 62b3cc2..0000000
--- a/pac-man-board-game/Exceptions/GameNotFoundException.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace pacMan.Exceptions;
-
-public class GameNotFoundException : Exception
-{
- public GameNotFoundException(string message = "Game not found") : base(message) { }
-}
diff --git a/pac-man-board-game/Exceptions/GameNotPlayableException.cs b/pac-man-board-game/Exceptions/GameNotPlayableException.cs
deleted file mode 100644
index 6f4a840..0000000
--- a/pac-man-board-game/Exceptions/GameNotPlayableException.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace pacMan.Exceptions;
-
-public class GameNotPlayableException : Exception
-{
- public GameNotPlayableException(string message = "Game is not allowed to be played") : base(message) { }
-}
diff --git a/pac-man-board-game/Exceptions/PlayerExceptions.cs b/pac-man-board-game/Exceptions/PlayerExceptions.cs
new file mode 100644
index 0000000..2a3a52b
--- /dev/null
+++ b/pac-man-board-game/Exceptions/PlayerExceptions.cs
@@ -0,0 +1,3 @@
+namespace pacMan.Exceptions;
+
+public class PlayerNotFoundException(string? message = "Player not found") : Exception(message);
diff --git a/pac-man-board-game/Exceptions/PlayerNotFoundException.cs b/pac-man-board-game/Exceptions/PlayerNotFoundException.cs
deleted file mode 100644
index ababc79..0000000
--- a/pac-man-board-game/Exceptions/PlayerNotFoundException.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace pacMan.Exceptions;
-
-public class PlayerNotFoundException : Exception
-{
- public PlayerNotFoundException(string? message = "Player not found") : base(message) { }
-}
diff --git a/pac-man-board-game/GameStuff/Actions.cs b/pac-man-board-game/GameStuff/Actions.cs
index 2464e25..acfd6a3 100644
--- a/pac-man-board-game/GameStuff/Actions.cs
+++ b/pac-man-board-game/GameStuff/Actions.cs
@@ -23,4 +23,4 @@ public class ActionMessage
public static ActionMessage FromJson(string json) => JsonSerializer.Deserialize(json)!;
}
-public class ActionMessage : ActionMessage { }
+public class ActionMessage : ActionMessage;
diff --git a/pac-man-board-game/GameStuff/Items/Box.cs b/pac-man-board-game/GameStuff/Items/Box.cs
index 4041e71..c0749ff 100644
--- a/pac-man-board-game/GameStuff/Items/Box.cs
+++ b/pac-man-board-game/GameStuff/Items/Box.cs
@@ -4,10 +4,17 @@ namespace pacMan.GameStuff.Items;
public class Box : IEquatable
{
- [JsonPropertyName("pellets")] public int Pellets { get; init; }
- [JsonPropertyName("powerPellets")] public int PowerPellet { get; init; }
+ [JsonInclude]
+ [JsonPropertyName("pellets")]
+ public int Pellets { get; init; }
- [JsonPropertyName("colour")] public required string Colour { get; init; }
+ [JsonInclude]
+ [JsonPropertyName("powerPellets")]
+ public int PowerPellet { get; init; }
+
+ [JsonInclude]
+ [JsonPropertyName("colour")]
+ public required string Colour { get; init; }
public bool Equals(Box? other)
{
diff --git a/pac-man-board-game/GameStuff/Items/DiceCup.cs b/pac-man-board-game/GameStuff/Items/DiceCup.cs
index 1e624a0..40bdf99 100644
--- a/pac-man-board-game/GameStuff/Items/DiceCup.cs
+++ b/pac-man-board-game/GameStuff/Items/DiceCup.cs
@@ -4,14 +4,11 @@ namespace pacMan.GameStuff.Items;
public class DiceCup
{
- private readonly List _dices;
-
- public DiceCup() =>
- _dices = new List
- {
- new(),
- new()
- };
+ private readonly List _dices = new()
+ {
+ new Dice(),
+ new Dice()
+ };
[JsonInclude] public List Values => _dices.Select(dice => dice.Value).ToList();
diff --git a/pac-man-board-game/GameStuff/Rules.cs b/pac-man-board-game/GameStuff/Rules.cs
index 45d8154..5052cc4 100644
--- a/pac-man-board-game/GameStuff/Rules.cs
+++ b/pac-man-board-game/GameStuff/Rules.cs
@@ -1,6 +1,6 @@
namespace pacMan.GameStuff;
-public class Rules
+public static class Rules
{
public const int MinPlayers = 2;
public const int MaxPlayers = 4;
diff --git a/pac-man-board-game/Interfaces/IWebSocketService.cs b/pac-man-board-game/Interfaces/IWebSocketService.cs
deleted file mode 100644
index 52cf1fc..0000000
--- a/pac-man-board-game/Interfaces/IWebSocketService.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Net.WebSockets;
-
-namespace pacMan.Interfaces;
-
-public interface IWebSocketService
-{
- Task Send(WebSocket webSocket, ArraySegment segment);
- Task Receive(WebSocket webSocket, byte[] buffer);
- Task Close(WebSocket webSocket, WebSocketCloseStatus closeStatus, string? closeStatusDescription);
-}
diff --git a/pac-man-board-game/Program.cs b/pac-man-board-game/Program.cs
index 07d04a5..4b4476e 100644
--- a/pac-man-board-game/Program.cs
+++ b/pac-man-board-game/Program.cs
@@ -1,5 +1,4 @@
using DAL.Database.Service;
-using pacMan.Interfaces;
using pacMan.Services;
var builder = WebApplication.CreateBuilder(args);
@@ -8,6 +7,7 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services
+ .AddSingleton()
.AddSingleton()
.AddSingleton()
.AddScoped()
diff --git a/pac-man-board-game/Services/ActionService.cs b/pac-man-board-game/Services/ActionService.cs
index 8f85c17..4ec6b5a 100644
--- a/pac-man-board-game/Services/ActionService.cs
+++ b/pac-man-board-game/Services/ActionService.cs
@@ -1,8 +1,7 @@
using System.Net.WebSockets;
using System.Text.Json;
-using System.Text.Json.Serialization;
+using pacMan.DTOs;
using pacMan.Exceptions;
-using pacMan.GameStuff;
using pacMan.GameStuff.Items;
namespace pacMan.Services;
@@ -11,11 +10,10 @@ public interface IActionService
{
Player Player { set; }
Game? Game { get; set; }
- WebSocket? WebSocket { set; }
- void DoAction(ActionMessage message);
+ WebSocket WebSocket { set; }
List RollDice();
List FindGame(JsonElement? jsonElement);
- object? HandleMoveCharacter(JsonElement? jsonElement);
+ MovePlayerData HandleMoveCharacter(JsonElement? jsonElement);
ReadyData Ready();
string FindNextPlayer();
List LeaveGame();
@@ -23,68 +21,45 @@ public interface IActionService
List? Disconnect();
}
-public class ActionService : IActionService
+public class ActionService(ILogger logger, IGameService gameService) : IActionService
{
- private readonly GameService _gameService;
- private readonly ILogger _logger;
-
- public ActionService(ILogger logger, GameService gameService)
- {
- _logger = logger;
- _gameService = gameService;
- }
-
- public WebSocket? WebSocket { private get; set; }
+ public WebSocket WebSocket { private get; set; } = null!;
public Game? Game { get; set; }
public Player? Player { get; set; }
- public void DoAction(ActionMessage message)
- {
- message.Data = message.Action switch
- {
- GameAction.RollDice => RollDice(),
- GameAction.MoveCharacter => HandleMoveCharacter(message.Data),
- GameAction.JoinGame => FindGame(message.Data),
- GameAction.Ready => Ready(),
- GameAction.NextPlayer => FindNextPlayer(),
- GameAction.Disconnect => LeaveGame(),
- _ => message.Data
- };
- }
-
public List RollDice()
{
Game?.DiceCup.Roll();
var rolls = Game?.DiceCup.Values ?? new List();
- _logger.Log(LogLevel.Information, "Rolled [{}]", string.Join(", ", rolls));
+ logger.LogInformation("Rolled [{}]", string.Join(", ", rolls));
return rolls;
}
- public object? HandleMoveCharacter(JsonElement? jsonElement)
+ public MovePlayerData HandleMoveCharacter(JsonElement? jsonElement)
{
- if (Game != null && jsonElement.HasValue)
+ var data = jsonElement?.Deserialize() ?? throw new NullReferenceException("Data is null");
+ if (Game is not null)
{
- Game.Ghosts = jsonElement.Value.GetProperty("ghosts").Deserialize>() ??
- throw new NullReferenceException("Ghosts is null");
- Game.Players = jsonElement.Value.GetProperty("players").Deserialize>() ??
- throw new NullReferenceException("Players is null");
+ Game.Ghosts = data.Ghosts;
+ Game.Players = data.Players;
}
- return jsonElement;
+ return data;
}
public List FindGame(JsonElement? jsonElement)
{
- var data = jsonElement?.Deserialize() ?? throw new NullReferenceException("Data is null");
+ var (username, gameId) =
+ jsonElement?.Deserialize() ?? throw new NullReferenceException("Data is null");
- var game = _gameService.Games.FirstOrDefault(game => game.Id == data.GameId) ??
- throw new GameNotFoundException($"Game was not found, id \"{data.GameId}\" does not exist");
+ var game = gameService.FindGameById(gameId) ??
+ throw new GameNotFoundException($"Game was not found, id \"{gameId}\" does not exist");
- var player = game.Players.Find(p => p.Username == data.Username)
- ?? throw new PlayerNotFoundException($"Player \"{data.Username}\" was not found in game");
+ var player = game.FindPlayerByUsername(username) ??
+ throw new PlayerNotFoundException($"Player \"{username}\" was not found in game");
player.State = game.IsGameStarted ? State.InGame : State.WaitingForPlayers; // TODO doesn't work anymore
Player = player;
@@ -96,8 +71,10 @@ public class ActionService : IActionService
public ReadyData Ready()
{
- if (Player == null || Game == null)
+ if (Player is null)
throw new PlayerNotFoundException("Player not found, please create a new player");
+ if (Game is null)
+ throw new GameNotFoundException();
var players = Game.SetReady(Player.Username).ToArray();
// TODO roll to start
@@ -111,57 +88,21 @@ public class ActionService : IActionService
public List LeaveGame()
{
- if (Game == null || Player == null) throw new NullReferenceException("Game or Player is null");
+ if (Game is null) throw new NullReferenceException("Game is null");
+ if (Player is null) throw new NullReferenceException("Player is null");
Game.RemovePlayer(Player.Username);
return Game.Players;
}
public List? Disconnect()
{
- if (Player == null) return null;
+ if (Player is null) return null;
Player.State = State.Disconnected;
- if (Game != null) Game.Connections -= SendSegment;
+ if (Game is not null) Game.Connections -= SendSegment;
return Game?.Players;
}
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));
- }
-}
-
-public struct JoinGameData
-{
- [JsonInclude]
- [JsonPropertyName("username")]
- public required string Username { get; init; }
-
- [JsonInclude]
- [JsonPropertyName("gameId")]
- public required Guid GameId { get; init; }
-}
-
-public struct CreateGameData
-{
- [JsonInclude]
- [JsonPropertyName("player")]
- public required Player Player { get; init; }
-
- [JsonInclude]
- [JsonPropertyName("spawns")]
- public required Queue Spawns { get; init; }
-}
-
-public struct ReadyData
-{
- [JsonInclude]
- [JsonPropertyName("allReady")]
- public required bool AllReady { get; init; }
-
- [JsonInclude]
- [JsonPropertyName("players")]
- public required IEnumerable Players { get; set; }
+ private async Task SendSegment(ArraySegment segment) => await gameService.Send(WebSocket, segment);
}
diff --git a/pac-man-board-game/Services/Game.cs b/pac-man-board-game/Services/Game.cs
index 08d88a7..f971448 100644
--- a/pac-man-board-game/Services/Game.cs
+++ b/pac-man-board-game/Services/Game.cs
@@ -5,14 +5,12 @@ using pacMan.GameStuff.Items;
namespace pacMan.Services;
-public class Game
+public class Game(Queue spawns)
{
private readonly Random _random = new();
private int _currentPlayerIndex;
private List _players = new();
- public Game(Queue spawns) => Spawns = spawns;
-
[JsonInclude] public Guid Id { get; } = Guid.NewGuid();
[JsonIgnore]
@@ -36,7 +34,7 @@ public class Game
[JsonIgnore] public List Ghosts { get; set; } = new(); // TODO include
- [JsonIgnore] private Queue Spawns { get; }
+ [JsonIgnore] private Queue Spawns { get; } = spawns;
[JsonIgnore] public DiceCup DiceCup { get; } = new(); // TODO include
@@ -53,7 +51,7 @@ public class Game
}
catch (DivideByZeroException)
{
- throw new InvalidOperationException("There are no players in the game.");
+ throw new PlayerNotFoundException("There are no players in the game.");
}
return Players[_currentPlayerIndex];
@@ -107,9 +105,12 @@ public class Game
public bool SetAllInGame()
{
- if (Players.Any(player => player.State != State.Ready)) return false;
+ if (Players.Any(player => player.State is not State.Ready)) return false;
foreach (var player in Players) player.State = State.InGame;
return true;
}
+
+ public Player? FindPlayerByUsername(string username) =>
+ Players.FirstOrDefault(player => player.Username == username);
}
diff --git a/pac-man-board-game/Services/GameService.cs b/pac-man-board-game/Services/GameService.cs
index 2eb1d8a..c4c6cf5 100644
--- a/pac-man-board-game/Services/GameService.cs
+++ b/pac-man-board-game/Services/GameService.cs
@@ -4,14 +4,21 @@ using pacMan.GameStuff.Items;
namespace pacMan.Services;
+public interface IGameService : IWebSocketService
+{
+ SynchronizedCollection Games { get; }
+ Game JoinById(Guid id, Player player);
+ Game CreateAndJoin(Player player, Queue spawns);
+ Game? FindGameById(Guid id);
+ Game? FindGameByUsername(string username);
+}
+
///
/// The GameService class provides functionality for managing games in a WebSocket environment. It inherits from the
/// WebSocketService class.
///
-public class GameService : WebSocketService
+public class GameService(ILogger logger) : WebSocketService(logger), IGameService
{
- public GameService(ILogger logger) : base(logger) { }
-
///
/// A thread-safe collection (SynchronizedCollection) of "Game" objects. Utilized for managing multiple game instances
/// simultaneously.
@@ -57,6 +64,11 @@ public class GameService : WebSocketService
return game;
}
+ public Game? FindGameById(Guid id)
+ {
+ return Games.FirstOrDefault(game => game.Id == id);
+ }
+
public Game? FindGameByUsername(string username)
{
return Games.FirstOrDefault(game => game.Players.Exists(player => player.Username == username));
diff --git a/pac-man-board-game/Services/WebSocketService.cs b/pac-man-board-game/Services/WebSocketService.cs
index 79fbad2..e4a415c 100644
--- a/pac-man-board-game/Services/WebSocketService.cs
+++ b/pac-man-board-game/Services/WebSocketService.cs
@@ -1,20 +1,17 @@
using System.Net.WebSockets;
-using pacMan.Interfaces;
using pacMan.Utils;
namespace pacMan.Services;
-
-public class WebSocketService : IWebSocketService
+public interface IWebSocketService
{
- protected readonly ILogger Logger;
-
- public WebSocketService(ILogger logger)
- {
- Logger = logger;
- logger.Log(LogLevel.Debug, "WebSocket Service created");
- }
+ Task Send(WebSocket webSocket, ArraySegment segment);
+ Task Receive(WebSocket webSocket, byte[] buffer);
+ Task Close(WebSocket webSocket, WebSocketCloseStatus closeStatus, string? closeStatusDescription);
+}
+public class WebSocketService(ILogger logger) : IWebSocketService
+{
public async Task Send(WebSocket webSocket, ArraySegment segment)
{
await webSocket.SendAsync(
@@ -23,13 +20,13 @@ public class WebSocketService : IWebSocketService
true,
CancellationToken.None);
- Logger.Log(LogLevel.Debug, "Message sent to WebSocket");
+ logger.LogDebug("Message sent through WebSocket");
}
public async Task Receive(WebSocket webSocket, byte[] buffer)
{
var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);
- Logger.Log(LogLevel.Debug,
+ logger.LogDebug(
"Message \"{}\" received from WebSocket",
buffer.GetString(result.Count));
return result;
@@ -42,6 +39,6 @@ public class WebSocketService : IWebSocketService
closeStatusDescription,
CancellationToken.None);
- Logger.Log(LogLevel.Information, "WebSocket connection closed");
+ logger.LogInformation("WebSocket connection closed");
}
}
diff --git a/pac-man-board-game/Utils/Extensions.cs b/pac-man-board-game/Utils/Extensions.cs
index 9cc7a59..fae9f58 100644
--- a/pac-man-board-game/Utils/Extensions.cs
+++ b/pac-man-board-game/Utils/Extensions.cs
@@ -10,7 +10,7 @@ public static partial class Extensions
{
var s = Encoding.UTF8.GetString(bytes, 0, length);
// Removes invalid characters from the string
- return InvalidCharacters().Replace(s, "");
+ return InvalidCharacters().Replace(s, string.Empty);
}
public static ArraySegment ToArraySegment(this object obj)
diff --git a/pac-man-board-game/pac-man-board-game.csproj b/pac-man-board-game/pac-man-board-game.csproj
index 2b6af8c..dd6b581 100644
--- a/pac-man-board-game/pac-man-board-game.csproj
+++ b/pac-man-board-game/pac-man-board-game.csproj
@@ -13,70 +13,71 @@
pacMan
enable
Linux
+ 12
-
+
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
+
- .dockerignore
+ .dockerignore
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
+
+
+
-
-
+
+
-
+
wwwroot\%(RecursiveDir)%(FileName)%(Extension)
PreserveNewest