import { Body, Controller, Get, HttpException, HttpStatus, Param, Post, Session, Sse, UseGuards, } from '@nestjs/common'; import { LobbyService } from '../services/lobby.service'; import { AuthGuard } from '../../users/guards/auth.guard'; import { ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { Game } from '../entities/game.entity'; 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 { ConfigService } from '@nestjs/config'; import { SessionData } from 'express-session'; import { WordResponseDTO } from 'events/dto/wordresponse.dto'; import { WordRecieveDTO } from 'src/word/dto/wordrecieve.dto'; @ApiTags('game') @Controller('games/:id') export class GamesController { constructor( private readonly lobbyService: LobbyService, private readonly gameService: GameService, private eventEmitter: EventEmitter2, private configService: ConfigService, ) {} @Post('/start') @ApiOperation({ summary: 'Start game' }) @ApiResponse({ status: 201, description: 'Game successfully started.', }) @ApiResponse({ status: 400, description: 'Bad Request', }) @ApiResponse({ status: 403, description: 'You can only start games you own', }) @UseGuards(AuthGuard) async start( @Param('id') id: string, @Session() session: SessionData, @Param('id', GamePipe) game: Game, ) { if (game.started) { throw new HttpException('Game already started', HttpStatus.BAD_REQUEST); } if (game.players.length < 2 && !this.configService.get('DEV_MODE')) { throw new HttpException( 'Not enough teams to start the game', HttpStatus.BAD_REQUEST, ); } if (game.ownerId != session.user?.id) { throw new HttpException( 'You can only start games you own', HttpStatus.FORBIDDEN, ); } await this.gameService.start(game); return; } @Post('surrender') @ApiOperation({ summary: 'Leave a game that has already started' }) @UseGuards(AuthGuard) surrender( @Param('id') id: string, @Session() session: SessionData, @Param('id', GamePipe) game: Game, ) { if (!game.started) { throw new HttpException('Game has not started', HttpStatus.BAD_REQUEST); } return this.lobbyService.leaveGame(game, session.user); } @Post('kick/:playerId') @ApiOperation({ summary: 'Kick a player' }) @UseGuards(AuthGuard) kick( @Session() session: SessionData, @Param('id') id: string, @Param('id', GamePipe) game: Game, @Param('playerId') playerId: string, ) { if (session.user.id != game.ownerId) { throw new HttpException( 'You do do not own this game', HttpStatus.FORBIDDEN, ); } return this.lobbyService.kickPlayer(game, session.user, playerId); } @Get('currentWord') @ApiOperation({ summary: 'Read Current Word' }) @UseGuards(AuthGuard) getCurrentWord( @Session() session: SessionData, @Param('id') id: string, @Param('id', GamePipe) game: Game, ) { if (session.user.id != game.currentPlayer.userId) { throw new HttpException( 'It is not your turn to play', HttpStatus.FORBIDDEN, ); } return new WordResponseDTO(game.currentWord); } @Post('/message') @ApiOperation({ summary: 'try to guess a word' }) @ApiBody({ type: WordRecieveDTO }) @UseGuards(AuthGuard) async postMessage( @Session() session: SessionData, @Param('id') id: string, @Param('id', GamePipe) game: Game, @Body('value') value: string, ) { const player = game.players.find((p) => p.userId == session.user.id); if (!game.started) { throw new HttpException('Game not started', HttpStatus.BAD_REQUEST); } if (!player) { throw new HttpException('You are not in this game', HttpStatus.FORBIDDEN); } await this.gameService.postMessage(game, player, value); } @Sse('/subscribe') subscribe(@Param('id') id: string): Observable<{ data: string }> { return fromEvent(this.eventEmitter, 'sse.game.' + id).pipe( map((payload: any) => { return { data: JSON.stringify(payload), }; }), ); } }