Files
4Gewinnt/API/Services/GameManager/GameManager.cs
T
Jonas bec5df7e88 Add turn tracking and enforce turn order
Introduce CurrentTurn to Game (default 1) and expose it via GameInformationDto. Update GameManager to include CurrentTurn in the GameInformation payload, validate that a player can only Drop when it is their turn, and toggle CurrentTurn between 1 and 2 after a successful move. Also use a safe fallback (0) when game is null.
2026-03-12 23:20:05 +01:00

141 lines
4.0 KiB
C#

using API.Controllers;
using API.Models.DataClasses;
using API.Models.Game;
using API.Repository.GameRepo;
using Microsoft.AspNetCore.SignalR;
namespace API.Services.GameManager;
public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSocket> hubContext) : IGameManager
{
public (string, int) CreateGame(Coordinates gFs, Player player)
{
var game = gameRepository.Create(gFs);
game.AddPlayer(player);
return (game.Id, game.GameCode);
}
public async Task<string?> JoinGame(Player player, int gameCode)
{
var game = gameRepository.GetOne(new SixDigitInt(gameCode));
var success = game != null && game.State == GameState.Lobby && game.AddPlayer(player);
if (!success) return null;
if (game.Players.Count == 2)
{
game.StartGame();
await hubContext.Clients.Group(game.Id).SendAsync("GameStarted", game.Players);
}
return game.Id;
}
public async Task RequestGameInformation(string gameId, string playerConnectionId)
{
var game = gameRepository.GetOne(gameId);
if (game == null)
return;
var gameInfoDto = new GameInformationDto
{
Players = game?.Players,
CurrentField = game?.Field.CurrentField,
State = game?.State,
CurrentTurn = game?.CurrentTurn ?? 0
};
await hubContext.Clients.Client(playerConnectionId).SendAsync("GameInformation", gameInfoDto);
}
public async Task Drop(string gameCode, int column, string playerConnectionId)
{
var game = gameRepository.GetOne(gameCode);
var player = game?.GetPlayerByConnectionId(playerConnectionId);
if (player == null)
return;
if(game.CurrentTurn != player.PlayerTag)
return;
var result = game.Field.Drop(column, player.PlayerTag);
if (result != DropResult.Placed)
{
await hubContext.Clients.Client(playerConnectionId).SendAsync("Error", "Ungültiger Zug.");
return;
}
await hubContext.Clients.Group(game.Id).SendAsync("FieldUpdated", game.Field.CurrentField);
var winResult = game.Field.CheckForWin();
if (winResult != 0)
{
var winPlayer = game.GetPlayerByTag(winResult);
game.EndGame();
await hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
{
Method = "Win",
Player = winPlayer
});
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(5));
}
if (game.Field.IsFull())
{
game.EndGame();
await hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
{
Method = "Draw"
});
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(5));
}
game.CurrentTurn = game.CurrentTurn == 1 ? 2 : 1;
}
public Task DisconnectedPlayer(string playerConnectionId)
{
var game = gameRepository.GetOneByConnectionId(playerConnectionId);
if (game == null)
return Task.CompletedTask;
var player = game.GetPlayerByConnectionId(playerConnectionId);
if (player == null)
return Task.CompletedTask;
game.RemovePlayer(playerConnectionId);
game.EndGame();
hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
{
Method = "PlayerDisconnected",
Player = player
});
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void ScheduleGameDeletion(string gameId, TimeSpan delay)
{
_ = Task.Run(async () =>
{
await Task.Delay(delay);
var g = gameRepository.GetOne(gameId);
if (g != null && g.State == GameState.Ended)
{
gameRepository.Destroy(gameId);
await hubContext.Clients.Group(gameId).SendAsync("GameDestroyed");
}
});
}
}