import { Body, Controller, HttpException, HttpStatus, Param, Post, Session, Sse, UseGuards, } from '@nestjs/common'; import { LobbyService } from '../services/games.lobby.service'; import { AuthGuard } from '../guards/auth.guard'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { StartingDeckDto } from '../dto/update-game.dto'; import { Game } from '../schemas/game.schema'; import mongoose, { Document } from 'mongoose'; import { GamePipe, GameStartedPipe } from '../pipe/game.pipe'; import { Observable, fromEvent, map } from 'rxjs'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { GameService } from '../services/games.service'; import { getCurrentTeam, getPlayer, isInTeam, WithTransaction } from '../utils'; import { User } from 'src/users/schemas/user.entity'; import { ReadTeamDto } from '../dto/read-games.dto'; @ApiTags('game') @Controller('games/:id') export class GamesController { constructor( private readonly lobbyService: LobbyService, private readonly gameService: GameService, private eventEmitter: EventEmitter2, ) {} @Post('changeStartingDeck') @ApiOperation({ summary: 'Pick a different starting deck' }) @UseGuards(AuthGuard) async changeStartingDeck( @Param('id') id: string, @Param('id', GamePipe) game: Document & Game, @Body() startingDeckDto: StartingDeckDto, @Session() session: Record, ) { if (game.started) { throw new HttpException( 'You cannot do that after the game has started', HttpStatus.BAD_REQUEST, ); } const player = getPlayer(session, game); await this.lobbyService.changeStartingDeck(startingDeckDto, player, game); } @Post('endTurn') @UseGuards(AuthGuard) async endTurn( @Param('id') id: string, @Param('id', GameStartedPipe) game: Document & Game, @Session() session: Record, ) { const currentTeam = getCurrentTeam(game); const currentPlayer = getPlayer(session, game); if (!isInTeam(currentTeam, currentPlayer)) { throw new HttpException( 'It is not your turn to play', HttpStatus.FORBIDDEN, ); } if (currentPlayer.discardMarkers || currentPlayer.fuckUMarkers) { throw new HttpException( 'You must consume all your discard markers before ending your turn', HttpStatus.FORBIDDEN, ); } await this.gameService.endTurn(game); } @Post(':id/leave') @ApiOperation({ summary: "Leave a game that hasn't already started" }) @UseGuards(AuthGuard) async leave( @Param('id') id: string, @Session() session: Record, @Param('id', GamePipe) game: Document & Game, ) { if (game.started) { throw new HttpException('Game already started', HttpStatus.BAD_REQUEST); } return await this.gameService.leaveGame(game, session.user); } @Post(':id/kick/:playerId') @ApiOperation({ summary: 'Kick a player' }) @UseGuards(AuthGuard) async kick( @Param('id') id: string, @Session() session: Record, @Param('id', GamePipe) game: Document & Game, @Param('playerId') playerId: string, ) { if (game.started) { throw new HttpException('Game already started', HttpStatus.BAD_REQUEST); } if (session.user.userId != game.owner.userId) { throw new HttpException( 'You do do not own this game', HttpStatus.FORBIDDEN, ); } return await this.gameService.kickPlayer(game, session.user, playerId); } @Sse('/subscribe') subscribe( @Param('id') id: string, // eslint-disable-next-line @typescript-eslint/no-unused-vars @Param('id', GamePipe) _game: Document & Game, ): Observable<{ data: string }> { return fromEvent(this.eventEmitter, 'sse.game.' + id).pipe( map((payload: any) => { return { data: JSON.stringify(payload), }; }), ); } }