import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, Not } from 'typeorm'; import { GameEntity, Game_PersonEntity, ObjectivePointEntity, ObjectivePoint_HistoryEntity, } from './game.entity'; import { GameDTO, FlagboxEventDTO, GameStateDTO, newGameDTO } from './game.dto'; import { PersonEntity } from '../user/user.entity'; import { FactionEntity } from '../faction/faction.entity'; import { NotificationGateway } from '../notifications/notifications.gateway'; @Injectable() export class GameService { constructor( @InjectRepository(GameEntity) private gameRepository: Repository<GameEntity>, @InjectRepository(FactionEntity) private factionRepository: Repository<FactionEntity>, @InjectRepository(Game_PersonEntity) private game_PersonRepository: Repository<Game_PersonEntity>, @InjectRepository(ObjectivePointEntity) private objectivePointRepository: Repository<ObjectivePointEntity>, @InjectRepository(ObjectivePoint_HistoryEntity) private objectivePoint_HistoryRepository: Repository< ObjectivePoint_HistoryEntity >, private notificationGateway: NotificationGateway, ) {} // create a new game async createNewGame(personId: PersonEntity, gameData: newGameDTO) { // checks if a game with the same name exists already if (await this.gameRepository.findOne({ name: gameData.name })) { throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST); } // else add the game to the database const game = await this.gameRepository.create(gameData); game.state = 'CREATED'; await this.gameRepository.insert(game); // add gamePerson with role admin to the game const gamePerson = await this.game_PersonRepository.create({ game: game, person: personId, role: 'admin', }); await this.game_PersonRepository.insert(gamePerson); return { message: 'New game added', }; } // edit already created game async editGame(id, gameData: GameDTO) { // checks if a game with the same name exists already if ( await this.gameRepository.findOne({ name: gameData.name, id: Not(id) }) ) { throw new HttpException( 'Game with the same name already exists', HttpStatus.BAD_REQUEST, ); } // check for duplicate names in gameData const factionNames = gameData.factions.map( ({ factionName }) => factionName, ); const flagboxNodeIds = gameData.objective_points.map( ({ objectivePointDescription }) => objectivePointDescription, ); if ( new Set(factionNames).size !== factionNames.length || new Set(flagboxNodeIds).size !== flagboxNodeIds.length ) { throw new HttpException( 'No duplicate names allowed!', HttpStatus.BAD_REQUEST, ); } // check that there's location data for each added objective point gameData.objective_points.forEach(obj => { if (!obj['data']) throw new HttpException( 'Objective Point error. Add location for each Objective Point.', HttpStatus.BAD_REQUEST, ); }); // get factions that have been added previously let factions = await this.factionRepository.find({ game: id }); // get flagboxes that have been added previously let flagboxes = await this.objectivePointRepository.find({ game: id, }); // update game entry in db const updatedGame = await this.gameRepository.create(gameData); const gameId = await this.gameRepository.save(updatedGame); // iterate factions if any were added if (gameData.factions) { const factionIds = gameData.factions.map(({ factionId }) => factionId); // delete all existing factions that are not in submitted data factions.map(async faction => { if (!factionIds.includes(faction.factionId)) { await this.factionRepository.delete(faction); } }); // create / update factions present in the submitted data gameData.factions.map(async faction => { let name = await this.factionRepository.create({ ...faction, game: gameId, }); await this.factionRepository.save(name); }); } else { // if no factions are present in data, delete all factions associated with the game await this.factionRepository.delete({ game: id }); } // insert the flagboxes to db if (gameData.objective_points) { const flagboxIds = gameData.objective_points.map( ({ objectivePointId }) => objectivePointId, ); flagboxes.map(async flagbox => { if (!flagboxIds.includes(flagbox.objectivePointId)) { await this.objectivePointRepository.delete({ objectivePointId: flagbox.objectivePointId, }); } }); gameData.objective_points.map(async flagbox => { let newFlagbox = await this.objectivePointRepository.create({ ...flagbox, game: gameId, }); await this.objectivePointRepository.save(newFlagbox); // create base status for flagbox this.flagboxEvent(gameId, { node_id: flagbox.objectivePointDescription, owner: 0, action: 0, capture: 0, }); }); } else { await this.objectivePointRepository.delete({ game: id }); } return { message: 'Game updated', }; } async updateGameStatus(game: GameStateDTO) { const updatedGame = await this.gameRepository.findOne({ id: game.id }); if (updatedGame) { updatedGame.state = game.state; await this.gameRepository.save(updatedGame); // notify players about game state change this.notificationGateway.server.emit(game.id, { type: 'gamestate-update', }); return { message: 'State was updated', }; } throw new HttpException("Game doesn't exist", HttpStatus.BAD_REQUEST); } async listFactions(game: GameEntity) { return this.factionRepository.find({ game }); } async deleteGame(id) { // Delete factions from Faction table associated with the deleted game await this.gameRepository.delete({ id }); return { message: 'Game deleted', }; } // returns name and id of each game async listGames(state) { if (state == null) { const games = await this.gameRepository.find(); return games.map(game => { return game.gameObject(); }); } else if (state == 'ONGOING') { const games = await this.gameRepository.find({ where: [ { state: 'CREATED' }, { state: 'STARTED' }, { state: 'PAUSED' }, ], }); return games.map(game => { return game.gameObject(); }); } else { const games = await this.gameRepository.find({ where: { state: state }, }); return games.map(game => { return game.gameObject(); }); } } // returns information about a game identified by id async returnGameInfo(id) { const game = await this.gameRepository.findOne({ where: { id: id }, relations: ['factions', 'objective_points'], }); // sort factions by their name game.factions.sort(function(a, b) { return a['factionName'].localeCompare(b['factionName']); }); return game; } // returns information about game's flagboxes and their most recent event async returnObjectivePointInfo(gameId) { const info = await this.objectivePointRepository.find({ where: { game: gameId }, relations: ['history', 'history.owner', 'history.capture'], }); let response = await Promise.all( info.map(async obj => { let history = obj.history.pop(); return await { objectivePointId: obj.objectivePointId, objectivePointDescription: obj.objectivePointDescription, objectivePointMultiplier: obj.objectivePointMultiplier, action: { status: history.action, message: { 0: 'No capture ongoing', 1: `Captured by ${ history.owner ? history.owner.factionName : 'neutral' }`, 2: `Being captured by ${ history.capture ? history.capture.factionName : 'neutral' }`, }[history.action], }, owner: await this.infoHelper(history.owner), capture: await this.infoHelper(history.capture), data: obj.data, }; }), ); return response; } //returns flagbox colour and faction private async infoHelper(obj) { return (await obj) ? { factionName: obj.factionName, colour: obj.colour, } : { factionName: 'neutral', colour: '#000000', }; } // returns flagbox settings async flagboxQuery(gameId) { const game = await this.gameRepository.findOne({ id: gameId }); return game.nodesettings; } // add events to history and send updates with socket async flagboxEvent(gameId, data: FlagboxEventDTO) { // get all the factions associated with the game const factionRef = await this.factionRepository.find({ game: gameId }); // get reference to the objective const objectiveRef = await this.objectivePointRepository.findOne({ where: { objectivePointDescription: data.node_id, game: gameId }, }); data.oP_HistoryTimestamp = Date.now(); const eventUpdate = await this.objectivePoint_HistoryRepository.create({ oP_HistoryTimestamp: data.oP_HistoryTimestamp, action: data.action, // -1 as 0 means null capture: data.capture !== 0 ? factionRef[data.capture - 1] : null, owner: data.owner !== 0 ? factionRef[data.owner - 1] : null, objective_point: objectiveRef.objectivePointId, }); await this.objectivePoint_HistoryRepository.insert(eventUpdate); // send flagbox event to flagbox subscribers this.notificationGateway.server.emit(gameId, { type: 'flagbox-event' }); return { message: 'OK', }; } }