273 lines
11 KiB
C#
273 lines
11 KiB
C#
using qtc_api.Enums;
|
|
using qtc_api.Services.GameRoomService;
|
|
|
|
namespace qtc_api.Hubs
|
|
{
|
|
[Authorize]
|
|
public class TicTacToeHub : Hub
|
|
{
|
|
private IUserService _userService;
|
|
private ILogger<TicTacToeHub> _logger;
|
|
private GameRoomService _gameRoomService;
|
|
public TicTacToeHub(ILogger<TicTacToeHub> logger, IUserService userService, GameRoomService gameRoomService)
|
|
{
|
|
_userService = userService;
|
|
_gameRoomService = gameRoomService;
|
|
_logger = logger;
|
|
}
|
|
|
|
public override async Task OnDisconnectedAsync(Exception? exception)
|
|
{
|
|
// find any existing room user was in
|
|
var room = _gameRoomService.GameRooms.FirstOrDefault(e => (e.Player1?.Id == Context.UserIdentifier) || (e.Player2?.Id == Context.UserIdentifier));
|
|
if (room != null)
|
|
{
|
|
// just end the game
|
|
room.Status = GameStatus.PlayerDisconnected;
|
|
|
|
string usernameDisconnected = string.Empty;
|
|
|
|
// determine which player to null out
|
|
if (room.Player1?.Id == Context.UserIdentifier) { usernameDisconnected = room.Player1!.Username; room.Player1 = null; }
|
|
else { usernameDisconnected = room.Player2!.Username; room.Player2 = null; }
|
|
|
|
bool isWin = room.Status == GameStatus.P1Win || room.Status == GameStatus.P2Win || room.Status == GameStatus.NoWin;
|
|
if (!isWin)
|
|
await Clients.Group(room.Id).SendAsync("GameEnd", room, usernameDisconnected);
|
|
|
|
// remove the room
|
|
_gameRoomService.RemoveRoom(room);
|
|
}
|
|
}
|
|
|
|
[HubMethodName("FindRoom")]
|
|
public async Task FindRoom()
|
|
{
|
|
// find user from useridentifier
|
|
var user = await _userService.GetUserById(Context.UserIdentifier!);
|
|
|
|
if (user != null && user.Success && user.Data != null)
|
|
{
|
|
// find any room waiting on a player
|
|
var roomWOP = _gameRoomService.GameRooms.FirstOrDefault(e => e.Status == GameStatus.WaitingForPlayer && e.Player2 == null && e.Player1?.Id != Context.UserIdentifier);
|
|
if (roomWOP != null)
|
|
{
|
|
// add player to said group
|
|
await Groups.AddToGroupAsync(Context.ConnectionId, roomWOP.Id);
|
|
|
|
// set player two
|
|
roomWOP.Player2 = user.Data;
|
|
|
|
// this game can now start symbol selection
|
|
roomWOP.Status = GameStatus.SelectingSymbol;
|
|
await Clients.User(roomWOP.Player1!.Id).SendAsync("SelectSymbol", roomWOP);
|
|
await Clients.Group(roomWOP.Id).SendAsync("SelectingSymbol", roomWOP);
|
|
|
|
return;
|
|
}
|
|
|
|
// find any room who's player one matches the user and is stuck in 'WaitingForPlayer' status
|
|
var roomWFPP1 = _gameRoomService.GameRooms.FirstOrDefault(e => e.Player1?.Id == user.Data.Id && e.Status == GameStatus.WaitingForPlayer);
|
|
if (roomWFPP1 != null)
|
|
// just remove it
|
|
_gameRoomService.RemoveRoom(roomWFPP1);
|
|
|
|
// if all if statements above don't match, make a new room
|
|
var gameRoom = new GameRoom { Id = Guid.NewGuid().ToString(), Player1 = user.Data, Status = GameStatus.WaitingForPlayer };
|
|
_gameRoomService.AddRoom(gameRoom);
|
|
|
|
// add user to group
|
|
await Groups.AddToGroupAsync(Context.ConnectionId, gameRoom.Id);
|
|
|
|
// waiting for player
|
|
await Clients.Group(gameRoom.Id).SendAsync("WaitingForPlayer", gameRoom);
|
|
}
|
|
}
|
|
|
|
[HubMethodName("SetSymbol")]
|
|
public async Task SetPlayerSymbol(string roomId, TicTacToeSymbol symbol)
|
|
{
|
|
// find the room
|
|
var room = _gameRoomService.GameRooms.FirstOrDefault(e => e.Id == roomId);
|
|
|
|
if (room != null)
|
|
{
|
|
// set player one symbol
|
|
room.P1Symbol = symbol;
|
|
|
|
// set player two symbol based on what player one picked
|
|
switch (room.P1Symbol)
|
|
{
|
|
case TicTacToeSymbol.X:
|
|
room.P2Symbol = TicTacToeSymbol.O;
|
|
break;
|
|
case TicTacToeSymbol.O:
|
|
room.P2Symbol = TicTacToeSymbol.X;
|
|
break;
|
|
}
|
|
|
|
// the game can now start
|
|
room.Status = GameStatus.Ongoing;
|
|
await Clients.Group(room.Id).SendAsync("GameStart", room);
|
|
|
|
// start player ones turn
|
|
await Clients.User(room.Player1!.Id).SendAsync("StartTurn", room);
|
|
await Clients.User(room.Player2!.Id).SendAsync("EndTurn", room);
|
|
}
|
|
}
|
|
|
|
[HubMethodName("MakeMove")]
|
|
public async Task MakeMove(string roomId, TicTacToeMove move)
|
|
{
|
|
// find the room
|
|
var room = _gameRoomService.GameRooms.FirstOrDefault(e => e.Id == roomId);
|
|
|
|
if (room != null)
|
|
{
|
|
// update board based on move (TOOD - figure out a better way to do this)
|
|
if (room.Player1?.Id == move.User.Id)
|
|
{
|
|
switch (move.Point)
|
|
{
|
|
case 1:
|
|
room.Board.Square1 = room.P1Symbol;
|
|
break;
|
|
case 2:
|
|
room.Board.Square2 = room.P1Symbol;
|
|
break;
|
|
case 3:
|
|
room.Board.Square3 = room.P1Symbol;
|
|
break;
|
|
case 4:
|
|
room.Board.Square4 = room.P1Symbol;
|
|
break;
|
|
case 5:
|
|
room.Board.Square5 = room.P1Symbol;
|
|
break;
|
|
case 6:
|
|
room.Board.Square6 = room.P1Symbol;
|
|
break;
|
|
case 7:
|
|
room.Board.Square7 = room.P1Symbol;
|
|
break;
|
|
case 8:
|
|
room.Board.Square8 = room.P1Symbol;
|
|
break;
|
|
case 9:
|
|
room.Board.Square9 = room.P1Symbol;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (move.Point)
|
|
{
|
|
case 1:
|
|
room.Board.Square1 = room.P2Symbol;
|
|
break;
|
|
case 2:
|
|
room.Board.Square2 = room.P2Symbol;
|
|
break;
|
|
case 3:
|
|
room.Board.Square3 = room.P2Symbol;
|
|
break;
|
|
case 4:
|
|
room.Board.Square4 = room.P2Symbol;
|
|
break;
|
|
case 5:
|
|
room.Board.Square5 = room.P2Symbol;
|
|
break;
|
|
case 6:
|
|
room.Board.Square6 = room.P2Symbol;
|
|
break;
|
|
case 7:
|
|
room.Board.Square7 = room.P2Symbol;
|
|
break;
|
|
case 8:
|
|
room.Board.Square8 = room.P2Symbol;
|
|
break;
|
|
case 9:
|
|
room.Board.Square9 = room.P2Symbol;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// send board update
|
|
await Clients.Group(roomId).SendAsync("UpdateBoard", room, move);
|
|
|
|
// check for draw
|
|
if (room.Board.IsDraw())
|
|
{
|
|
room.Status = GameStatus.NoWin;
|
|
await Clients.Group(room.Id).SendAsync("GameEnd", room, string.Empty);
|
|
return;
|
|
}
|
|
|
|
// check for winner
|
|
var winningSymbol = room.Board.GetWinner();
|
|
if (winningSymbol != TicTacToeSymbol.Blank)
|
|
{
|
|
if (winningSymbol == room.P1Symbol)
|
|
{
|
|
// player one has won the game
|
|
room.Status = GameStatus.P1Win;
|
|
await Clients.Group(room.Id).SendAsync("GameEnd", room, string.Empty);
|
|
}
|
|
else
|
|
{
|
|
// player two has won the game
|
|
room.Status = GameStatus.P2Win;
|
|
await Clients.Group(room.Id).SendAsync("GameEnd", room, string.Empty);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// switch turns
|
|
if (move.User.Id == room.Player1?.Id)
|
|
{
|
|
await Clients.User(room.Player1!.Id).SendAsync("EndTurn", room);
|
|
await Clients.User(room.Player2!.Id).SendAsync("StartTurn", room);
|
|
}
|
|
else
|
|
{
|
|
await Clients.User(room.Player2!.Id).SendAsync("EndTurn", room);
|
|
await Clients.User(room.Player1!.Id).SendAsync("StartTurn", room);
|
|
}
|
|
}
|
|
}
|
|
|
|
[HubMethodName("RestartGame")]
|
|
public async Task RestartGame(string roomId)
|
|
{
|
|
// find the room
|
|
var room = _gameRoomService.GameRooms.FirstOrDefault(e => e.Id == roomId);
|
|
if (room != null)
|
|
{
|
|
// room still exists and the game can be restarted
|
|
room.Status = GameStatus.Ongoing;
|
|
room.Board = new TicTacToeBoard(); // clears the board
|
|
|
|
await Clients.Group(room.Id).SendAsync("RestartGame", room);
|
|
|
|
// have player one start their turn
|
|
await Clients.User(room.Player1!.Id).SendAsync("StartTurn", room);
|
|
await Clients.User(room.Player2!.Id).SendAsync("EndTurn", room);
|
|
}
|
|
else
|
|
{
|
|
// the user trying to restart the game needs to be informed that the other user has left
|
|
await Clients.Client(Context.ConnectionId).SendAsync("CannotRestart");
|
|
}
|
|
}
|
|
|
|
[HubMethodName("SendRoomMessage")]
|
|
public async Task SendRoomMessage(string roomId, string message)
|
|
{
|
|
// find the room
|
|
var room = _gameRoomService.GameRooms.FirstOrDefault(e => e.Id == roomId);
|
|
if (room != null) await Clients.Group(room.Id).SendAsync("ReceiveMessage", message);
|
|
}
|
|
}
|
|
}
|