2026-07-23 13:54:06 -07:00

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);
}
}
}