0eed8020b8
Change game field representation to jagged arrays (int[][]) for JSON/SignalR compatibility and add conversion helpers in GameManager. Make Coordinates JSON-serializable (constructor and JsonPropertyName attributes). Update GameHubSocket: validate field size, fix JoinGame parameter order/Group join, rename Place->Drop and send proper game id. Add client-side local play support: GameConnection SignalR client, LocalGame orchestration for two local players, and UI components (GameCreationMenu, Slider) plus GameSettings interface. Update LocalMode route to use the new creation UI and start local games. These changes enable reliable serialization over SignalR and a local two-player flow with a creation UI.
172 lines
4.9 KiB
C#
172 lines
4.9 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();
|
|
|
|
var gameInfoDto = new GameInformationDto
|
|
{
|
|
Players = game?.Players,
|
|
CurrentField = ConvertField(game?.Field.CurrentField),
|
|
State = game?.State,
|
|
CurrentTurn = game?.CurrentTurn ?? 0
|
|
};
|
|
|
|
await hubContext.Clients.Group(game.Id).SendAsync("GameStarted", gameInfoDto);
|
|
}
|
|
|
|
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 = ConvertField(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", ConvertField(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 static int[][] ConvertField(int[,]? field)
|
|
{
|
|
if (field == null)
|
|
return Array.Empty<int[]>();
|
|
|
|
var rows = field.GetLength(0);
|
|
var cols = field.GetLength(1);
|
|
var convertedField = new int[rows][];
|
|
|
|
for (var y = 0; y < rows; y++)
|
|
{
|
|
convertedField[y] = new int[cols];
|
|
|
|
for (var x = 0; x < cols; x++)
|
|
{
|
|
convertedField[y][x] = field[y, x];
|
|
}
|
|
}
|
|
|
|
return convertedField;
|
|
}
|
|
|
|
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");
|
|
}
|
|
});
|
|
}
|
|
}
|