Convert field to jagged arrays; add local SignalR UI
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.
This commit is contained in:
@@ -11,6 +11,12 @@ public class GameHubSocket(IGameManager gameManager) : Hub
|
||||
|
||||
public async Task CreateGame(string playerName, Coordinates gFs)
|
||||
{
|
||||
if (gFs.X <= 0 || gFs.Y <= 0)
|
||||
{
|
||||
await Clients.Caller.SendAsync("Error", "Ungültige Spielfeldgröße.");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = new Player(playerName, Context.ConnectionId);
|
||||
|
||||
var result = _gameManager.CreateGame(gFs, player);
|
||||
@@ -24,7 +30,7 @@ public class GameHubSocket(IGameManager gameManager) : Hub
|
||||
});
|
||||
}
|
||||
|
||||
public async Task JoinGame(int gameCode, string playerName)
|
||||
public async Task JoinGame(string playerName, int gameCode)
|
||||
{
|
||||
var player = new Player(playerName, Context.ConnectionId);
|
||||
|
||||
@@ -36,7 +42,7 @@ public class GameHubSocket(IGameManager gameManager) : Hub
|
||||
return;
|
||||
}
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, gameCode.ToString());
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, result);
|
||||
|
||||
await Clients.Caller.SendAsync("GameJoined", new
|
||||
{
|
||||
@@ -50,7 +56,7 @@ public class GameHubSocket(IGameManager gameManager) : Hub
|
||||
await _gameManager.RequestGameInformation(gameId, Context.ConnectionId);
|
||||
}
|
||||
|
||||
public async Task Place(string gameId, int column)
|
||||
public async Task Drop(string gameId, int column)
|
||||
{
|
||||
await _gameManager.Drop(gameId, column, Context.ConnectionId);
|
||||
}
|
||||
@@ -61,4 +67,4 @@ public class GameHubSocket(IGameManager gameManager) : Hub
|
||||
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
namespace API.Models.Game;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
public readonly struct Coordinates(int x, int y)
|
||||
namespace API.Models.Game;
|
||||
|
||||
public readonly struct Coordinates
|
||||
{
|
||||
public readonly int X = x;
|
||||
public readonly int Y = y;
|
||||
[JsonConstructor]
|
||||
public Coordinates(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
[JsonPropertyName("x")]
|
||||
public int X { get; init; }
|
||||
|
||||
[JsonPropertyName("y")]
|
||||
public int Y { get; init; }
|
||||
}
|
||||
|
||||
public enum DropResult
|
||||
@@ -146,4 +158,4 @@ public class GameField(Coordinates gFs)
|
||||
{
|
||||
Array.Copy(CurrentField, BackupField, CurrentField.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
public string Id { get; set; }
|
||||
public List<Player> Players { get; set; }
|
||||
public GameState? State { get; set; }
|
||||
public int[,] CurrentField { get; set; }
|
||||
public int[][] CurrentField { get; set; }
|
||||
public int CurrentTurn { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,15 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
|
||||
{
|
||||
game.StartGame();
|
||||
|
||||
await hubContext.Clients.Group(game.Id).SendAsync("GameStarted", game.Players);
|
||||
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;
|
||||
@@ -42,7 +50,7 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
|
||||
var gameInfoDto = new GameInformationDto
|
||||
{
|
||||
Players = game?.Players,
|
||||
CurrentField = game?.Field.CurrentField,
|
||||
CurrentField = ConvertField(game?.Field.CurrentField),
|
||||
State = game?.State,
|
||||
CurrentTurn = game?.CurrentTurn ?? 0
|
||||
};
|
||||
@@ -69,7 +77,7 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
|
||||
return;
|
||||
}
|
||||
|
||||
await hubContext.Clients.Group(game.Id).SendAsync("FieldUpdated", game.Field.CurrentField);
|
||||
await hubContext.Clients.Group(game.Id).SendAsync("FieldUpdated", ConvertField(game.Field.CurrentField));
|
||||
|
||||
var winResult = game.Field.CheckForWin();
|
||||
if (winResult != 0)
|
||||
@@ -123,6 +131,28 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
|
||||
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 () =>
|
||||
@@ -138,4 +168,4 @@ public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSock
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user