4826760d73
Move SignalR group join into GameManager and add support for player names and improved online join flow. - Moved Groups.AddToGroupAsync call out of GameHubSocket into GameManager (hubContext.Groups.AddToGroupAsync) so group membership is handled when adding players to a game. - GUI: added player name input to GameJoinMenu and updated JoinGameObject to include playerName and use string gameCode. - OnlineMode & LocalMode: constructors updated to instantiate games without passing settings; removed WaitingForOpponent state and cleaned up event bindings. - OnlineGame: rewrote to handle onGameCreated/onGameJoined/onGameStarted/onFieldUpdated/onGameEnded/onError, added joinGame validation (expects 6-digit code), join flow (connect + join), state updates, drop forwarding to player.drop(gameId, index) and place description updates. - LocalGame: constructor signature simplified to no-arg. These changes centralize group management, add player identification, validate join codes, and improve game state/event handling for online play.
188 lines
5.5 KiB
C#
188 lines
5.5 KiB
C#
using API.Controllers;
|
|
using API.Models.DataClasses;
|
|
using API.Models.Game;
|
|
using API.Repository.GameRepo;
|
|
using Microsoft.AspNetCore.SignalR;
|
|
using System.Text.RegularExpressions;
|
|
|
|
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)
|
|
{
|
|
await hubContext.Clients.Client(player.ConnectionId).SendAsync("Error", "Spiel Existiert nicht!");
|
|
return null;
|
|
}
|
|
|
|
await hubContext.Groups.AddToGroupAsync(player.ConnectionId, game.Id);
|
|
|
|
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 || game.State != GameState.Running)
|
|
return;
|
|
|
|
if(game.State != GameState.Running) return;
|
|
|
|
var result = game.Field.Drop(column, player.PlayerTag);
|
|
|
|
if (result != DropResult.Placed)
|
|
{
|
|
await hubContext.Clients.Client(playerConnectionId).SendAsync("Error", "Ungültiger Zug.");
|
|
return;
|
|
}
|
|
|
|
game.CurrentTurn = game.CurrentTurn == 1 ? 2 : 1;
|
|
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("FieldUpdated", gameInfoDto);
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
public Task DisconnectedPlayer(string playerConnectionId)
|
|
{
|
|
var game = gameRepository.GetOneByConnectionId(playerConnectionId);
|
|
if (game == null || game.State == GameState.Ended)
|
|
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");
|
|
}
|
|
});
|
|
}
|
|
}
|