Handle disconnects, add restart flow and UI fixes

Server: Make DisconnectedPlayer async, await hub notifications, add logging, and delay scheduled deletion (2s). Harden ScheduleGameDeletion with try/catch and only destroy/send GameDestroyed when game is not running or has no players.

Client: Add restart-game flow — expose restartGame on OnlineGame, propagate event from GameEndedMenu (adds local restarted state and disables button), and hook restart handling + player disconnect on unmount in OnlineMode. Also conditionally show bot switch in GameCreationMenu and include replayGameCode in GameEnded interface. Remove automatic reconnect on SignalR connection.

Overall: Improves robustness around player disconnects and adds a UI/logic path for restarting games.
This commit is contained in:
Jonas
2026-03-12 23:18:58 +01:00
parent 0e4fb5d828
commit 7a073b6fff
6 changed files with 61 additions and 18 deletions
+25 -13
View File
@@ -131,27 +131,28 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
}
}
public Task DisconnectedPlayer(string playerConnectionId)
public async Task DisconnectedPlayer(string playerConnectionId)
{
var game = gameRepository.GetOneByConnectionId(playerConnectionId);
if (game == null || game.State == GameState.Ended)
return Task.CompletedTask;
return;
var player = game.GetPlayerByConnectionId(playerConnectionId);
if (player == null)
return Task.CompletedTask;
return;
game.RemovePlayer(playerConnectionId);
game.EndGame();
hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
Console.WriteLine("Play has Disconnected");
await hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
{
Method = "PlayerDisconnected",
Player = player
});
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(0));
return Task.CompletedTask;
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(2));
}
private static int[][] ConvertField(int[,]? field)
@@ -180,14 +181,25 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
{
_ = Task.Run(async () =>
{
await Task.Delay(delay);
var g = gameRepository.GetOne(gameId);
if (g != null && g.State != GameState.Running)
try
{
gameRepository.Destroy(gameId);
await Task.Delay(delay);
await hubContext.Clients.Group(gameId).SendAsync("GameDestroyed");
var g = gameRepository.GetOne(gameId);
if (g == null)
return;
if (g.State != GameState.Running || g.Players.Count == 0)
{
gameRepository.Destroy(gameId);
await hubContext.Clients.Group(gameId).SendAsync("GameDestroyed");
}
Console.WriteLine($"Scheduled deletion of game {gameId} executed. {gameRepository.GetAll().Count}");
}
catch (Exception ex)
{
Console.WriteLine($"Error deleting game {gameId}: {ex}");
}
});
}