Fixed bugs when disconnecting and reconnecting, disconnect message is sent before closing websocket connection

This commit is contained in:
Martin Berg Alstad 2023-07-21 13:54:44 +02:00
parent 554b8cff2a
commit cb9371b33b
6 changed files with 48 additions and 24 deletions
pac-man-board-game
ClientApp/src/components
Controllers
GameStuff/Items
Services

@ -16,8 +16,6 @@ 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 gets to steal // 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, 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, when refreshing page, some data is missing until other clients make a move
// TODO bug, teleportation doesn't work // TODO bug, teleportation doesn't work

@ -15,10 +15,10 @@ public class GameController : GenericController
private readonly IActionService _actionService; private readonly IActionService _actionService;
private readonly GameService _gameService; private readonly GameService _gameService;
public GameController(ILogger<GameController> logger, GameService gameService, IActionService actionService) : public GameController(ILogger<GameController> logger, GameService webSocketService, IActionService actionService) :
base(logger, gameService) base(logger, webSocketService)
{ {
_gameService = gameService; _gameService = webSocketService;
_actionService = actionService; _actionService = actionService;
} }
@ -85,5 +85,9 @@ public class GameController : GenericController
protected override void Send(ArraySegment<byte> segment) => _actionService.SendToAll(segment); protected override void Send(ArraySegment<byte> segment) => _actionService.SendToAll(segment);
protected override void Disconnect() => _actionService.Disconnect(); protected override ArraySegment<byte>? Disconnect() =>
new ActionMessage { Action = GameAction.Disconnect, Data = _actionService.Disconnect() }
.ToArraySegment();
protected override void SendDisconnectMessage(ArraySegment<byte> segment) => _actionService.SendToAll(segment);
} }

@ -7,14 +7,14 @@ namespace pacMan.Controllers;
public abstract class GenericController : ControllerBase public abstract class GenericController : ControllerBase
{ {
private const int BufferSize = 1024 * 4; private const int BufferSize = 1024 * 4;
protected readonly IWebSocketService GameService; private readonly IWebSocketService _webSocketService;
protected readonly ILogger<GenericController> Logger; protected readonly ILogger<GenericController> Logger;
protected WebSocket? WebSocket; protected WebSocket? WebSocket;
protected GenericController(ILogger<GenericController> logger, IWebSocketService gameService) protected GenericController(ILogger<GenericController> logger, IWebSocketService webSocketService)
{ {
Logger = logger; Logger = logger;
GameService = gameService; _webSocketService = webSocketService;
Logger.Log(LogLevel.Debug, "WebSocket Controller created"); Logger.Log(LogLevel.Debug, "WebSocket Controller created");
} }
@ -42,7 +42,7 @@ public abstract class GenericController : ControllerBase
do do
{ {
var buffer = new byte[BufferSize]; var buffer = new byte[BufferSize];
result = await GameService.Receive(WebSocket, buffer); result = await _webSocketService.Receive(WebSocket, buffer);
if (result.CloseStatus.HasValue) break; if (result.CloseStatus.HasValue) break;
@ -51,23 +51,26 @@ public abstract class GenericController : ControllerBase
Send(segment); Send(segment);
} while (true); } while (true);
await GameService.Close(WebSocket, result.CloseStatus.Value, result.CloseStatusDescription); var disconnectSegment = Disconnect();
if (disconnectSegment != null) SendDisconnectMessage((ArraySegment<byte>)disconnectSegment);
await _webSocketService.Close(WebSocket, result.CloseStatus.Value, result.CloseStatusDescription);
} }
catch (WebSocketException e) catch (WebSocketException e)
{ {
Logger.Log(LogLevel.Error, "{}", e.Message); Logger.Log(LogLevel.Error, "{}", e.Message);
} }
Disconnect();
} }
protected virtual async void Send(ArraySegment<byte> segment) protected virtual async void Send(ArraySegment<byte> segment)
{ {
if (WebSocket == null) return; if (WebSocket == null) return;
await GameService.Send(WebSocket, segment); await _webSocketService.Send(WebSocket, segment);
} }
protected abstract ArraySegment<byte> Run(WebSocketReceiveResult result, byte[] data); protected abstract ArraySegment<byte> Run(WebSocketReceiveResult result, byte[] data);
protected virtual void Disconnect() { } protected virtual ArraySegment<byte>? Disconnect() => null;
protected virtual void SendDisconnectMessage(ArraySegment<byte> segment) { }
} }

@ -15,11 +15,11 @@ public class Player : IEquatable<Player>
{ {
[JsonPropertyName("username")] public required string Username { get; init; } [JsonPropertyName("username")] public required string Username { get; init; }
[JsonPropertyName("pacMan")] public required Character PacMan { get; init; } [JsonPropertyName("pacMan")] public required Character PacMan { get; set; }
[JsonPropertyName("colour")] public required string Colour { get; init; } [JsonPropertyName("colour")] public required string Colour { get; init; }
[JsonPropertyName("box")] public Box? Box { get; init; } [JsonPropertyName("box")] public Box? Box { get; set; }
[JsonPropertyName("state")] public State State { get; set; } = State.WaitingForPlayers; [JsonPropertyName("state")] public State State { get; set; } = State.WaitingForPlayers;

@ -19,7 +19,7 @@ public interface IActionService
string FindNextPlayer(); string FindNextPlayer();
List<Player> LeaveGame(); List<Player> LeaveGame();
void SendToAll(ArraySegment<byte> segment); void SendToAll(ArraySegment<byte> segment);
void Disconnect(); List<Player>? Disconnect();
} }
public class ActionService : IActionService public class ActionService : IActionService
@ -65,8 +65,12 @@ public class ActionService : IActionService
public object? HandleMoveCharacter(JsonElement? jsonElement) public object? HandleMoveCharacter(JsonElement? jsonElement)
{ {
if (Game != null && jsonElement.HasValue) if (Game != null && jsonElement.HasValue)
{
Game.Ghosts = jsonElement.Value.GetProperty("ghosts").Deserialize<List<Character>>() ?? Game.Ghosts = jsonElement.Value.GetProperty("ghosts").Deserialize<List<Character>>() ??
throw new JsonException("Ghosts is null"); throw new JsonException("Ghosts is null");
Game.Players = jsonElement.Value.GetProperty("players").Deserialize<List<Player>>() ??
throw new NullReferenceException("Players is null");
}
return jsonElement; return jsonElement;
} }
@ -77,10 +81,11 @@ public class ActionService : IActionService
Player = data.Player; Player = data.Player;
Game? game; Game? game;
Player? player; if ((game = _gameService.FindGameByUsername(Player.Username)) != null)
if ((game = _gameService.FindGameByUsername(Player.Username)) != null &&
(player = game.Players.Find(p => p.Username == Player.Username))?.State == State.Disconnected)
{ {
var player = game.Players.Find(p => p.Username == Player.Username);
if (player is null) throw new NullReferenceException("Player is null");
player.State = game.IsGameStarted ? State.InGame : State.WaitingForPlayers; // TODO doesn't work anymore player.State = game.IsGameStarted ? State.InGame : State.WaitingForPlayers; // TODO doesn't work anymore
Player = player; Player = player;
Game = game; Game = game;
@ -125,11 +130,12 @@ public class ActionService : IActionService
return Game.Players; return Game.Players;
} }
public void Disconnect() public List<Player>? Disconnect()
{ {
if (Player == null) return; if (Player == null) return null;
Player.State = State.Disconnected; Player.State = State.Disconnected;
if (Game != null) Game.Connections -= SendSegment; if (Game != null) Game.Connections -= SendSegment;
return Game?.Players;
} }
public void SendToAll(ArraySegment<byte> segment) => Game?.SendToAll(segment); public void SendToAll(ArraySegment<byte> segment) => Game?.SendToAll(segment);

@ -9,12 +9,25 @@ public class Game // TODO handle disconnects and reconnects
{ {
private readonly Random _random = new(); private readonly Random _random = new();
private int _currentPlayerIndex; private int _currentPlayerIndex;
private List<Player> _players = new();
public Game(Queue<DirectionalPosition> spawns) => Spawns = spawns; public Game(Queue<DirectionalPosition> spawns) => Spawns = spawns;
[JsonInclude] public Guid Id { get; } = Guid.NewGuid(); [JsonInclude] public Guid Id { get; } = Guid.NewGuid();
[JsonIgnore] public List<Player> Players { get; } = new(); [JsonIgnore]
public List<Player> Players
{
get => _players;
set =>
_players = _players.Select((player, index) =>
{
player.State = value[index].State;
player.PacMan = value[index].PacMan;
player.Box = value[index].Box;
return player;
}).ToList();
}
[JsonIgnore] public List<Character> Ghosts { get; set; } = new(); [JsonIgnore] public List<Character> Ghosts { get; set; } = new();