diff --git a/package-lock.json b/package-lock.json index dacf241668576875354d1e1b9dd002c78ec835ea..01d2eb7272357854133cd7fe3325d9234a86c4cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -426,11 +426,11 @@ } }, "@nestjs/common": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-6.2.4.tgz", - "integrity": "sha512-YZvJ6/S7yVQZK+9rupCzMCg4tpbc9DyVvLoTx0NBDqExTCUNcNEcCtn0AZrO/hLqbeYODnJwGE2NxkH1R/qw+w==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-6.5.2.tgz", + "integrity": "sha512-vkB6JLPPckjS35usLMV3Q6vljkgzhN3jgQ+U1VY6cKriyjnkIcUKo37tNQSPYkaAGe1pOLK4IkmwMTSyhNieyg==", "requires": { - "axios": "0.18.0", + "axios": "0.19.0", "cli-color": "1.4.0", "uuid": "3.3.2" } @@ -1272,12 +1272,19 @@ "dev": true }, "axios": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", - "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", "requires": { - "follow-redirects": "^1.3.0", - "is-buffer": "^1.1.5" + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + } } }, "babel-jest": { @@ -2172,11 +2179,12 @@ } }, "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "^0.10.50", + "type": "^1.0.1" } }, "dashdash": { @@ -2222,6 +2230,7 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } @@ -2598,13 +2607,13 @@ } }, "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "requires": { "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, @@ -3021,11 +3030,26 @@ } }, "follow-redirects": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", - "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "requires": { - "debug": "^3.2.6" + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "for-in": { @@ -4043,7 +4067,8 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, "is-callable": { "version": "1.1.4", @@ -5090,9 +5115,9 @@ } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", "dev": true }, "lodash.includes": { @@ -5363,9 +5388,9 @@ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -6633,9 +6658,9 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -7622,6 +7647,11 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "dev": true }, + "type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.1.tgz", + "integrity": "sha512-MAM5dBMJCJNKs9E7JXo4CXRAansRfG0nlJxW7Wf6GZzSOvH31zClSaHdIMWLehe/EGMBkqeC55rrkaOr5Oo7Nw==" + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -7819,38 +7849,15 @@ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-string": { diff --git a/package.json b/package.json index 7f9b55f7927a8d9c62a905c3334d0d8025fb688a..3677b2a8db9be496cadc813cab1446501627bbc3 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { - "@nestjs/common": "^6.0.0", + "@nestjs/common": "^6.5.2", "@nestjs/core": "^6.0.0", "@nestjs/jwt": "^6.1.1", "@nestjs/platform-express": "^6.0.0", diff --git a/src/draw/coordinate.entity.ts b/src/draw/coordinate.entity.ts index e9fa35a222c8670171b8b1c4971a70334d34e193..e9a1b6ff35544fa599220367638fd3ada00fa865 100644 --- a/src/draw/coordinate.entity.ts +++ b/src/draw/coordinate.entity.ts @@ -4,17 +4,24 @@ import { PrimaryGeneratedColumn, ManyToOne, CreateDateColumn, + OneToMany, } from 'typeorm'; import { GameEntity } from '../game/game.entity'; import { FactionEntity } from '../faction/faction.entity'; +////////////////////////////////////////////////////////////////////// +/// Entities for different drawings in game and their histories /// +////////////////////////////////////////////////////////////////////// + @Entity('MapDrawing') export class MapDrawingEntity { @PrimaryGeneratedColumn('uuid') mapDrawingId: string; @Column({ type: 'bool', nullable: true }) drawingIsActive: boolean; @Column({ type: 'json', nullable: true }) data: JSON; + // When Faction or game that has the drawing in question is deleted from + // the database, the drawing is also deleted @ManyToOne(type => FactionEntity, faction => faction.mapDrawings, { onDelete: 'CASCADE', }) @@ -38,10 +45,11 @@ export class MapDrawingEntity { @Entity('MapDrawingHistory') export class MapDrawingHistoryEntity { @PrimaryGeneratedColumn('uuid') mapDrawingHistoryId: string; - @CreateDateColumn() timestamp: Date; + @Column('float') timestamp: number; @Column('bool') drawingIsActive: boolean; @Column('json') data: JSON; + // If drawing is deleted, it's histories are deleted also @ManyToOne(() => MapDrawingEntity, mapDrawing => mapDrawing.mapDrawingId, { onDelete: 'CASCADE', }) diff --git a/src/draw/draw.controller.ts b/src/draw/draw.controller.ts index 39db26afdbaab2f8c4c44ca166fc545742daf5f9..08b493940ff0b802a664c4bf5e54d797bf7690f4 100644 --- a/src/draw/draw.controller.ts +++ b/src/draw/draw.controller.ts @@ -1,7 +1,6 @@ import { Controller, Put, - UseGuards, Get, Param, UsePipes, @@ -16,13 +15,16 @@ import { Roles, GameStates } from '../shared/guard.decorator'; import { MapDrawingDTO } from './mapdrawing.dto'; import { GamePerson } from 'src/game/gameperson.decorator'; -/* - DrawController +////////////////////////////////////////////////////////////////////////// +/// DrawController /// +/// /// +/// Functions either insert or return MapDrawing data, /// +/// Insert functions require user to have proper role (either gm /// +/// or commander) in the game to be able store the data: /// +/// MapDrawingDTO data to database. /// +/// Data return functions require atleast spectator role. /// +////////////////////////////////////////////////////////////////////////// - Functions either insert or return MapDrawing data, - Insert functions require user to have proper role (either gm or commander) in the game to be able store the ddata: MapDrawingDTOata to database. - Data return functions require atleast spectator role. - */ @Controller('draw') export class DrawController { constructor(private drawService: DrawService) {} diff --git a/src/draw/draw.module.ts b/src/draw/draw.module.ts index 86bb2e0e885019beedfd801caf6ef50a7fc6023a..bea1b669ffdde53e107179b3379711321a28aceb 100644 --- a/src/draw/draw.module.ts +++ b/src/draw/draw.module.ts @@ -10,10 +10,11 @@ import { import { FactionEntity } from '../faction/faction.entity'; import { Game_PersonEntity } from '../game/game.entity'; import { NotificationModule } from 'src/notifications/notifications.module'; -/* -Draw -- contains everything to do with mapdrawing data. -*/ + +///////////////////////////////////////////////////////////////////// +/// Draw /// +/// - contains everything to do with mapdrawing data. /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [ TypeOrmModule.forFeature([ diff --git a/src/draw/draw.service.ts b/src/draw/draw.service.ts index ef56a85225e4d1ab7999c755224383a3397ff3a6..5f7566cda9d127173cbba11a8f2f92727a38f7dc 100644 --- a/src/draw/draw.service.ts +++ b/src/draw/draw.service.ts @@ -34,6 +34,7 @@ export class DrawService { data: data.data, drawingIsActive: data.drawingIsActive, mapdrawing: mapDrawing.identifiers[0]['mapDrawingId'], + timestamp: Date.now(), }); await this.mapDrawHistoryRepository.insert(history); return mapDrawing.identifiers; @@ -49,6 +50,7 @@ export class DrawService { data: data.data, drawingIsActive: data.drawingIsActive, mapdrawing: data.mapDrawingId, + timestamp: Date.now(), }); await this.mapDrawHistoryRepository.insert(history); return await this.mapDrawingRepository.save(drawing); diff --git a/src/faction/faction.dto.ts b/src/faction/faction.dto.ts index 9064f56f285d3ab2aede368ad91c269ad8c2a334..fbc46d3267dc109010c4450a371ede7300c0d1d0 100644 --- a/src/faction/faction.dto.ts +++ b/src/faction/faction.dto.ts @@ -9,6 +9,7 @@ import { Max, IsOptional, IsHexColor, + IsIn, } from 'class-validator'; import { GameEntity } from '../game/game.entity'; @@ -57,6 +58,8 @@ export class GameGroupDTO { @IsString() @Length(3, 31) name: string; + @IsIn(['infantry', 'recon', 'mechanized']) + class: string; @IsUUID('4') faction: FactionEntity; } diff --git a/src/faction/faction.entity.ts b/src/faction/faction.entity.ts index bc0b9269bdc5618364c536632f226c4974877080..a851e190b349d046aae00c776aef345b7de6f373 100644 --- a/src/faction/faction.entity.ts +++ b/src/faction/faction.entity.ts @@ -5,7 +5,6 @@ import { OneToMany, ManyToOne, OneToOne, - Timestamp, JoinColumn, } from 'typeorm'; @@ -13,8 +12,11 @@ import { GameEntity } from '../game/game.entity'; import { Game_PersonEntity } from '../game/game.entity'; import { MapDrawingEntity } from '../draw/coordinate.entity'; import { Exclude } from 'class-transformer'; +import { ScoreEntity } from 'src/score/score.entity'; -//Faction, PowerUp, Faction_powerUp, FP_History, Score +////////////////////////////////////////////////////////////////////// +/// Entities for Factions and Groups in Factions /// +////////////////////////////////////////////////////////////////////// @Entity('Faction') export class FactionEntity { @@ -23,18 +25,24 @@ export class FactionEntity { @Column({ type: 'float' }) multiplier: number; @Column('text') colour: string; + // Faction's password won't be included when FactionEntity is transformed @Exclude() @Column({ type: 'text' }) factionPassword: string; @OneToMany(type => Game_PersonEntity, game_persons => game_persons.faction) game_persons: Game_PersonEntity[]; + // When Game where a Faction is in is deleted, the Factions in that game are also deleted @ManyToOne(type => GameEntity, game => game.factions, { onDelete: 'CASCADE', }) game: GameEntity; @OneToMany(type => MapDrawingEntity, mapDrawings => mapDrawings.faction) mapDrawings: MapDrawingEntity[]; + @OneToMany(type => ScoreEntity, score => score.scoreId, { + onDelete: 'NO ACTION', + }) + scores: ScoreEntity[]; factionObject() { const { factionId, factionName, game } = this; @@ -46,48 +54,12 @@ export class FactionEntity { } } -/* @Entity('PowerUp') -export class PowerUpEntity { - @PrimaryGeneratedColumn('uuid') powerUpId: string; - @Column({ type: 'text' }) powerUpName: string; - @Column({ type: 'text' }) powerUpDescription: string; - @Column({ type: 'int' }) amount: number; - @Column({ type: 'time' }) cooldown: string; - - @OneToMany(type => FactionEntity, factions => factions.factionId, { - onDelete: 'CASCADE', - }) - factions: Faction_PowerUpEntity[]; -} - -@Entity('Faction_PowerUp') -export class Faction_PowerUpEntity { - @PrimaryGeneratedColumn('uuid') faction_powerUpId: string; - - @ManyToOne(type => FactionEntity, faction => faction.factionId) - faction: FactionEntity; - @ManyToOne(type => PowerUpEntity, powerUp => powerUp.factions) - powerUp: PowerUpEntity; - @OneToMany(type => FP_HistoryEntity, histories => histories.faction_PowerUp) - histories: FP_HistoryEntity[]; -} - -@Entity('FP_History') -export class FP_HistoryEntity { - @PrimaryGeneratedColumn('uuid') historyId: string; - @Column({ type: 'timestamp' }) historyTimeStamp: Timestamp; - - @ManyToOne( - type => Faction_PowerUpEntity, - faction_PowerUp => faction_PowerUp.histories, - ) - faction_PowerUp: Faction_PowerUpEntity; -} */ - @Entity('GameGroup') export class GameGroupEntity { @PrimaryGeneratedColumn('uuid') id: string; @Column('text') name: string; + @Column('text') class: string; + // When Groups leader, players or Faction is deleted, the Group(s) go also @OneToOne(type => Game_PersonEntity, person => person.leaderGroup, { onDelete: 'CASCADE', }) diff --git a/src/faction/faction.module.ts b/src/faction/faction.module.ts index dcb399aa1119851781270477c448ecec7362c804..f53f076b7e88af7536e6307640d7e10414a7c357 100644 --- a/src/faction/faction.module.ts +++ b/src/faction/faction.module.ts @@ -6,6 +6,10 @@ import { FactionService } from './faction.service'; import { GameGroupEntity, FactionEntity } from './faction.entity'; import { Game_PersonEntity } from '../game/game.entity'; +///////////////////////////////////////////////////////////////////// +/// Faction /// +/// - contains everything to do with Faction data. /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [ TypeOrmModule.forFeature([ diff --git a/src/faction/faction.service.ts b/src/faction/faction.service.ts index bcaf4af49583471f614cad9287d4130c5a67aadf..32299861ea7b8a349b89194c7f51e3626b7f2389 100644 --- a/src/faction/faction.service.ts +++ b/src/faction/faction.service.ts @@ -66,6 +66,7 @@ export class FactionService { }; } + // changes factions multiplier to the value given to FactionDTO async changeFactionMultiplier(body: FactionDTO) { const faction = await this.factionRepository.findOne({ where: { factionId: body.factionId }, @@ -102,12 +103,11 @@ export class FactionService { game: gameId, }); // check if the authenticated person already belongs to a group - if ( - await this.game_PersonRepository.findOne({ - group: Not(null), - person: person, - }) - ) { + let check = await this.game_PersonRepository.findOne({ + where: { person: person, game: gameId }, + relations: ['group'], + }); + if (check.group) { throw new HttpException( 'You already belong to a group!', HttpStatus.BAD_REQUEST, @@ -130,6 +130,7 @@ export class FactionService { }; } + // get the groups in the given Faction async showGroups(factionId) { return await this.game_GroupRepository.find({ relations: ['leader', 'players'], @@ -137,6 +138,7 @@ export class FactionService { }); } + // puts a non admin or faction leader player into a specified group async joinGroup(gameperson, data: JoinGameGroupDTO) { gameperson.group = data.groupId; await this.game_PersonRepository.save(gameperson); @@ -145,6 +147,7 @@ export class FactionService { }; } + // lists all members from given faction async listFactionMembers(faction) { const members = await this.game_PersonRepository.find({ where: { faction }, @@ -156,6 +159,7 @@ export class FactionService { return members; } + // checks if player is in a faction and what role the player is in async verifyUser(person, game) { const gameperson = await this.game_PersonRepository.findOne({ where: { person, game }, diff --git a/src/game/game.controller.ts b/src/game/game.controller.ts index a0b85a24e645f99bff5d1b4175bc6e9b24d1353f..27e3248368c7fa54c76db0cc114e66afdc445118 100644 --- a/src/game/game.controller.ts +++ b/src/game/game.controller.ts @@ -20,6 +20,15 @@ import { ValidationPipe } from '../shared/validation.pipe'; import { Roles, GameStates } from '../shared/guard.decorator'; import { GameEntity } from './game.entity'; +///////////////////////////////////////////////////////////////////////// +/// GameController /// +/// /// +/// Functions for creating, editing, deleting and listing games. /// +/// Also there are functions to get objective point info and list /// +/// of Factions in game /// +/// /// +///////////////////////////////////////////////////////////////////////// + @Controller('game') export class GameController { constructor(private gameservice: GameService) {} diff --git a/src/game/game.entity.ts b/src/game/game.entity.ts index 7ae6e29955d02a2c1368b5f720200f009bd92ee1..22e171b67e655917d108a66695dae59f32304e4e 100644 --- a/src/game/game.entity.ts +++ b/src/game/game.entity.ts @@ -56,6 +56,7 @@ export class GameEntity { export class Game_PersonEntity { @PrimaryGeneratedColumn('uuid') gamepersonId: string; @Column({ type: 'text', nullable: true }) role: string; + // If a Faction or Game where the GamePerson was in is deleted, the GamePerson is also deleted @ManyToOne(type => FactionEntity, faction => faction.game_persons, { onDelete: 'CASCADE', }) @@ -68,6 +69,8 @@ export class Game_PersonEntity { person: PersonEntity; @OneToOne(type => GameGroupEntity, group => group.leader) leaderGroup: GameGroupEntity; + + // When a Group where GamePerson is is deleted, nothing happens to the GamePerson @ManyToOne(type => GameGroupEntity, group => group.players, { onDelete: 'NO ACTION', }) @@ -81,6 +84,7 @@ export class ObjectivePointEntity { @Column({ type: 'text' }) objectivePointDescription: string; @Column({ type: 'float' }) objectivePointMultiplier: number; + // If the MapDrawing or Game where the ObjectivePoint was in is deleted, the ObjectivePoint is also deleted @ManyToOne(type => MapDrawingEntity, coordinate => coordinate.data, { onDelete: 'CASCADE', }) @@ -96,6 +100,9 @@ export class ObjectivePoint_HistoryEntity { @PrimaryGeneratedColumn('uuid') oP_HistoryId: string; @Column({ type: 'timestamp' }) oP_HistoryTimestamp: Timestamp; @Column('float') action: number; + + // If the owner Faction, capturer Faction or ObjectivePoint, that has, is trying to have or is the point where + // ObjectivePointHistory points to is deleted, the ObjectivePointHistory is also deleted @ManyToOne(type => FactionEntity, factionEntity => factionEntity.factionId, { onDelete: 'CASCADE', }) diff --git a/src/game/game.module.ts b/src/game/game.module.ts index 1c9e86faf59645bf537e76a8720bb6ab741fe6bd..8fa0638e4a5ef0a0a72d04a416113c15278fc242 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -13,7 +13,14 @@ import { PersonEntity } from '../user/user.entity'; import { GameGroupEntity } from '../faction/faction.entity'; import { FactionEntity } from '../faction/faction.entity'; import { NotificationModule } from '../notifications/notifications.module'; +import { TickService } from './tick.service'; +import { ScoreService } from '../score/score.service'; +import { ScoreEntity } from 'src/score/score.entity'; +///////////////////////////////////////////////////////////////////// +/// Game /// +/// - contains everything to do with Game data /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [ TypeOrmModule.forFeature([ @@ -24,10 +31,11 @@ import { NotificationModule } from '../notifications/notifications.module'; GameGroupEntity, ObjectivePointEntity, ObjectivePoint_HistoryEntity, + ScoreEntity, ]), NotificationModule, ], controllers: [GameController], - providers: [GameService], + providers: [GameService, TickService, ScoreService], }) export class GameModule {} diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 84a8a97a30b53b094912d153ec9736c3fed1e59b..86bc61ef43c3255b83914cc08e2f1dcfae61bf90 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -1,4 +1,4 @@ -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { Injectable, HttpException, HttpStatus, Inject } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository, Not } from 'typeorm'; @@ -12,6 +12,7 @@ import { GameDTO, FlagboxEventDTO, GameStateDTO } from './game.dto'; import { PersonEntity } from '../user/user.entity'; import { FactionEntity } from '../faction/faction.entity'; import { NotificationGateway } from '../notifications/notifications.gateway'; +import { TickService } from './tick.service'; @Injectable() export class GameService { @@ -29,8 +30,9 @@ export class GameService { ObjectivePoint_HistoryEntity >, private notificationGateway: NotificationGateway, - ) {} + private tickService: TickService, + ) {} // create a new game async createNewGame(personId: PersonEntity, gameData: GameDTO) { // checks if a game with the same name exists already @@ -145,7 +147,16 @@ export class GameService { const updatedGame = await this.gameRepository.findOne({ id: game.id }); if (updatedGame) { updatedGame.state = game.state; + console.log(game.state); await this.gameRepository.save(updatedGame); + + //add or remove from scoretick based on gamestate + if (game.state == 'STARTED') { + this.tickService.addGameToTimer(game.id); + } else { + this.tickService.removeGameFromTimer(game.id); + } + // notify players about game state change this.notificationGateway.server.emit(game.id, { type: 'gamestate-update', diff --git a/src/game/tick.service.ts b/src/game/tick.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..8417fe8b0b3b11c464bd4d837743ec3cabdcbc33 --- /dev/null +++ b/src/game/tick.service.ts @@ -0,0 +1,49 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ScoreService } from '../score/score.service'; + +@Injectable() +export class TickService { + constructor(private scoreService: ScoreService) { + // whenever Tickservice is called, it will start ticktimer + /* + WARNING: multiple calls start multiple timers, + if you need to use this service somewhere else remember to call startTimer method from somewhere else + */ + + this.startTimer(); + } + + private logger: Logger = new Logger('TickLogger'); + // tickinterval in milliseconds (10 minutes = 600 000) + private readonly tickInterval: number = 60000; + // dictionary to push gameId linked to start state + private ongoingGames = {}; + + // initializing timer + async startTimer() { + this.logger.log('Started timer'); + setInterval(this.Tick, this.tickInterval); + } + + // add the game to tick queue + async addGameToTimer(gameId: string) { + this.logger.log('Added: ' + gameId); + this.ongoingGames[gameId] = Date.now(); + } + + // remove game if the setting is set to pause + async removeGameFromTimer(gameId: string) { + this.logger.log('Deleted: ' + gameId); + delete this.ongoingGames[gameId]; + } + + // tick score for games with STARTED-status + Tick = () => { + this.logger.log('Ticking'); + if (this.ongoingGames != null) { + for (var game in this.ongoingGames) { + this.scoreService.scoreTick(game); + } + } + }; +} diff --git a/src/notifications/notification.entity.ts b/src/notifications/notification.entity.ts index f9d5dca9290c30da406a63adf56ee06fe716a7b5..5e1f841cf031767574c75d54af65ca209fc7e86b 100644 --- a/src/notifications/notification.entity.ts +++ b/src/notifications/notification.entity.ts @@ -16,6 +16,7 @@ export class NotificationEntity { @Column({ type: 'text' }) message: string; @CreateDateColumn() issued: Date; + // Notifications are deleted if the game is deleted @ManyToOne(type => GameEntity, game => game.id, { onDelete: 'CASCADE', }) diff --git a/src/notifications/notifications.gateway.ts b/src/notifications/notifications.gateway.ts index 5e952d6c78c6f35977f362a7459c0ebf65d1ab5e..cc3ad3a802c877439b877efdc551f5b4f8f43d01 100644 --- a/src/notifications/notifications.gateway.ts +++ b/src/notifications/notifications.gateway.ts @@ -50,12 +50,25 @@ export class NotificationGateway async handleMessage(client: Socket, data: NotificationdDTO) { // check if the game exists and is either started or paused const game = await this.gameRepository.findOne({ id: data.game }); - if (game && ['STARTED', 'PAUSED'].includes(game.state)) { + if (!game) { + // inform user about error + this.server.to(client.id).emit(data.game, { + type: 'error', + message: 'Game was not found', + }); + } + if (['STARTED', 'PAUSED'].includes(game.state)) { // send the message to all clients listening to gameId branch this.server.emit(data.game, data); // create entry for notification in db const message = await this.notificationRepository.create(data); await this.notificationRepository.insert(message); + } else { + // inform user about error + this.server.to(client.id).emit(data.game, { + type: 'error', + message: 'Notifications can be sent only in STARTED and PAUSED state', + }); } } } diff --git a/src/notifications/notifications.module.ts b/src/notifications/notifications.module.ts index 607ea0c2d486bddc8f8919a86915ed6014176628..6133784682e03ee4aba0eacf79fad64298b1a80d 100644 --- a/src/notifications/notifications.module.ts +++ b/src/notifications/notifications.module.ts @@ -7,6 +7,10 @@ import { GameEntity } from '../game/game.entity'; import { NotificationsController } from './notifications.controller'; import { NotificationsService } from './notifications.service'; +///////////////////////////////////////////////////////////////////// +/// Notification /// +/// - contains everything to do with Notification data. /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [TypeOrmModule.forFeature([NotificationEntity, GameEntity])], providers: [NotificationGateway, NotificationsService], diff --git a/src/notifications/notifications.service.ts b/src/notifications/notifications.service.ts index d63f01786f1bbab3b500658ac40c24a977165e73..0bbe016ecba9bde516b99222bbe388b3269bba6c 100644 --- a/src/notifications/notifications.service.ts +++ b/src/notifications/notifications.service.ts @@ -10,6 +10,7 @@ export class NotificationsService { private notificationRepository: Repository<NotificationEntity>, ) {} + // finds all notifications for specified game async getNotifications(game: string) { return this.notificationRepository.find({ game }); } diff --git a/src/replay/replay.controller.ts b/src/replay/replay.controller.ts index acc2ef50dafc1fb1c7f193c8da4834162b6ff4be..1b42439569d54da8d6b2fa8f2986b28669e16a39 100644 --- a/src/replay/replay.controller.ts +++ b/src/replay/replay.controller.ts @@ -1,23 +1,27 @@ -import { Controller, Get, Param, Post } from '@nestjs/common'; +import { + Controller, + Get, + Param, + Post, + UseInterceptors, + ClassSerializerInterceptor, +} from '@nestjs/common'; import { ReplayService } from './replay.service'; -import { Roles } from 'src/shared/guard.decorator'; @Controller('replay') export class ReplayController { constructor(private replayservice: ReplayService) {} + // gets replay data for specified Game @Get(':id') + @UseInterceptors(ClassSerializerInterceptor) async replayInfo(@Param('id') gameId) { return this.replayservice.replayData(gameId); } + // gets mockdata for specified Game @Post('mockdata/:id') async mockData(@Param('id') gameId) { return this.replayservice.mockdata(gameId); } - - @Get('players/:id') - async getPlayers(@Param('id') gameId) { - return this.replayservice.getPlayers(gameId); - } } diff --git a/src/replay/replay.module.ts b/src/replay/replay.module.ts index c7accefa55f867a49c36e2dbc5cc3e75563b211c..d4d5dc095bf95a9fd2316870d4e3d57a17af321f 100644 --- a/src/replay/replay.module.ts +++ b/src/replay/replay.module.ts @@ -3,14 +3,30 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { ReplayController } from './replay.controller'; import { ReplayService } from './replay.service'; -import { GameEntity, Game_PersonEntity } from '../game/game.entity'; +import { + GameEntity, + Game_PersonEntity, + ObjectivePointEntity, + ObjectivePoint_HistoryEntity, +} from '../game/game.entity'; import { FactionEntity, GameGroupEntity } from '../faction/faction.entity'; import { UserService } from '../user/user.service'; import { FactionService } from '../faction/faction.service'; import { TrackingService } from '../tracking/tracking.service'; -import { TrackingEntity } from 'src/tracking/tracking.entity'; -import { PersonEntity } from 'src/user/user.entity'; +import { TrackingEntity } from '../tracking/tracking.entity'; +import { PersonEntity } from '../user/user.entity'; +import { + MapDrawingEntity, + MapDrawingHistoryEntity, +} from '../draw/coordinate.entity'; +import { ScoreService } from '../score/score.service'; +import { ScoreEntity } from '../score/score.entity'; +import { NotificationModule } from 'src/notifications/notifications.module'; +///////////////////////////////////////////////////////////////////// +/// Replay /// +/// - contains everything to do with Replay data. /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [ TypeOrmModule.forFeature([ @@ -20,9 +36,21 @@ import { PersonEntity } from 'src/user/user.entity'; TrackingEntity, GameGroupEntity, Game_PersonEntity, + MapDrawingEntity, + MapDrawingHistoryEntity, + ScoreEntity, + ObjectivePointEntity, + ObjectivePoint_HistoryEntity, ]), + NotificationModule, ], controllers: [ReplayController], - providers: [ReplayService, UserService, FactionService, TrackingService], + providers: [ + ReplayService, + UserService, + FactionService, + TrackingService, + ScoreService, + ], }) export class ReplayModule {} diff --git a/src/replay/replay.service.ts b/src/replay/replay.service.ts index f5430860112e243305a4f0542036b8f9a0bb8a0a..bb06399aa59082ce46cd98b49b0ca21412f912eb 100644 --- a/src/replay/replay.service.ts +++ b/src/replay/replay.service.ts @@ -1,14 +1,20 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { Repository, In } from 'typeorm'; import * as jwt from 'jsonwebtoken'; import { FactionEntity } from '../faction/faction.entity'; -import { GameEntity } from 'src/game/game.entity'; -import { TrackingService } from 'src/tracking/tracking.service'; -import { UserService } from 'src/user/user.service'; -import { FactionService } from 'src/faction/faction.service'; -import { TrackingEntity } from 'src/tracking/tracking.entity'; +import { GameEntity } from '../game/game.entity'; +import { TrackingService } from '../tracking/tracking.service'; +import { UserService } from '../user/user.service'; +import { FactionService } from '../faction/faction.service'; +import { TrackingEntity } from '../tracking/tracking.entity'; +import { + MapDrawingEntity, + MapDrawingHistoryEntity, +} from '../draw/coordinate.entity'; +import { ScoreService } from 'src/score/score.service'; +import { ScoreEntity } from 'src/score/score.entity'; @Injectable() export class ReplayService { @@ -19,89 +25,220 @@ export class ReplayService { private gameRepository: Repository<GameEntity>, @InjectRepository(TrackingEntity) private trackingRepository: Repository<TrackingEntity>, + @InjectRepository(MapDrawingEntity) + private mapdrawingRepository: Repository<MapDrawingEntity>, + @InjectRepository(MapDrawingHistoryEntity) + private mapHistoryRepository: Repository<MapDrawingHistoryEntity>, + @InjectRepository(ScoreEntity) + private scoreRepository: Repository<ScoreEntity>, private trackingService: TrackingService, private userService: UserService, private factionService: FactionService, + private scoreService: ScoreService, ) {} + /* async replayData(gameId) { + let mapDrawingIds = await this.mapdrawingRepository.find({ + where: { gameId: gameId }, + select: ['mapDrawingId'], + // replay data for Factions async replayData(gameId) { const replay = await this.factionRepository.find({ where: { game: gameId }, relations: ['mapDrawings', 'scores', 'trackdata'], }); - return replay; - } + let drawings = []; + await Promise.all( + mapDrawingIds.map(async mapId => { + drawings.push( + await this.mapHistoryRepository.find({ + mapdrawing: mapId.mapDrawingId, + }), + ); + }), + ); + return drawings; + } */ - // get replay data for players - async getPlayers(gameId) { + async replayData(gameId) { + // + // this block returns game's initial location + // + let gameObj = await this.gameRepository.findOne({ + where: { id: gameId }, + select: ['center'], + }); + let gamelocation = [gameObj.center.lat, gameObj.center.lng]; + // + // this block returns all player data from the game + // let playerdata = await this.trackingRepository.find({ where: { game: gameId }, - relations: ['faction', 'gamepersonId'], + relations: ['faction', 'gamepersonId', 'gamepersonId.person'], }); - // parse data const currentdata = await Promise.all( playerdata.map(async player => { + player.data.map(async data => { + data['info'] = [ + { key: 'icon', value: player.icon }, + { key: 'colour', value: player.faction.colour }, + { key: 'name', value: player.gamepersonId.person.name }, + { key: 'role', value: player.gamepersonId.role }, + ]; + }); return player['data']; - /* return { - gamepersonId: player['gamepersonId']['gamepersonId'], - gamepersonRole: player['gamepersonId']['role'], - factionId: player['faction']['factionId'], - coordinates: player['data'], - }; */ }), ); + // + // this block returns all faction data from the game + // + let factions = await this.factionRepository.find({ game: gameId }); + let currentFactions = factions.map(faction => { + return { + name: faction.factionName, + colour: faction.colour, + // all factions will be rendered on the map on default + active: true, + }; + }); + let factionIds = factions.map(faction => faction.factionId); + // + // this block returns all score data from the game + // + let currentScore = []; + await Promise.all( + factions.map(async faction => { + let scores = await this.scoreRepository.find({ + where: { faction: faction }, + relations: ['faction'], + }); + currentScore.push( + scores.map(score => { + return { + score: score.score, + timestamp: score.scoreTimeStamp, + faction: score.faction['factionName'], + }; + }), + ); + }), + ); + // + // this block returns all map drawings from the game + // + let refs = await this.mapdrawingRepository.find({ + where: { gameId: gameId }, + select: ['mapDrawingId'], + }); - return currentdata; + let drawData = await Promise.all( + refs.map(async ref => { + return await this.mapHistoryRepository.find({ + where: { mapdrawing: ref.mapDrawingId }, + }); + }), + ); + + return { + location: gamelocation, + players: currentdata, + factions: currentFactions, + scores: currentScore, + drawings: drawData, + }; } // generate mockdata for a 3 day game + // create x amount of players + // assign them to two separate factions + // assign them to three separate groups + // insert x amount of locations for each players around some lat lng area + // insert x amount of score ticks for score mockdata + // use the game's initial geojson to draw game area + // async mockdata(gameId) { + // initial settings for mockdata + // set the x amount of users to be created + const USER_AMOUNT = 100; + // set the x amount of locations to be created + const USER_LOCATIONS = 10; + // set the LAT and LNG for initial location + const LAT = 62.24147; + const LNG = 25.72088; + // set the score tick amount + // not used at the moment + const SCORE_TICKS = 10; + // setup the arrays for users, gamepersons and groups const users = []; const gamepersons = []; + const groups = []; + // characters for generating random usernames and the length of the username + const chars = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + const N = 15; + // used to equally divide the players to factions + let f = 0; + + // get game info and factions associated with it const game = await this.gameRepository.findOne({ where: { id: gameId }, relations: ['factions'], }); - for (let i = 0; i < 20; i++) { + // get groups for all factions + await game.factions.forEach(async faction => { + groups.push(await this.factionService.showGroups(faction.factionId)); + }); + // create x amount of users for the mock game with random username + for (let i = 0; i < USER_AMOUNT; i++) { let res = await this.userService.register({ - name: 'qqqqq' + i, + name: Array(N) + .join() + .split(',') + .map(function() { + return chars.charAt(Math.floor(Math.random() * chars.length)); + }) + .join(''), password: 'asd', }); + // get user information by verifying the token returned by register let user = await jwt.verify(res.token, process.env.SECRET); + // push the created user to users array users.push(user); + + // reset index if it's out of range + if (f === game.factions.length) f = 0; + // join faction with the created user let gameperson = await this.factionService.joinFaction(user['id'], { - factionId: - i < 10 ? game.factions[0].factionId : game.factions[1].factionId, - factionPassword: - i < 10 - ? game.factions[0].factionPassword - : game.factions[1].factionPassword, + factionId: game.factions[f].factionId, + factionPassword: game.factions[f].factionPassword, game: gameId, }); + // join a group randomly + await this.factionService.joinGroup(gameperson, { + groupId: groups[f][Math.floor(Math.random() * 3)], + }); + // push the gameperson ref to gamepersons array gamepersons.push(gameperson); + f++; } - let date: number = Date.now(); - let y1 = 25.7; - let x1 = 62.3; - let y2 = 25.75; - let x2 = 62.35; - for (let i = 1; i < 16 * 6 * 3; i++) { - let x = 0; - date += 10000; + // push x amount of user locations + for (let i = 1; i < USER_LOCATIONS + 1; i++) { + let date: number = Date.now(); + let x = 1; + // score ticks with players to sync them + await this.scoreService.scoreTick(gameId); + // add location entry for each gameperson await Promise.all( gamepersons.map(async gameperson => { - x++; + // format player locations with x-modifier that same factions are closer to each other + if (x > game.factions.length) x = 1; let dataObject = { - lat: - x < 10 - ? x1 + ((i + Math.random()) * 5) / 2000 - : x2 - ((i + Math.random()) * 5) / 2000, - lng: - x < 10 - ? y1 + ((i + Math.random()) * 5) / 2000 - : y2 - ((i + Math.random()) * 5) / 2000, + lat: LAT + ((i + Math.random()) * 5) / 2000, + lng: LNG + x / 100 + (Math.random() * 5) / 2000, time: date, }; + x++; + // push the tracking data await this.trackingService.trackLocation( gameperson, gameId, @@ -110,6 +247,7 @@ export class ReplayService { }), ); } + return { message: 'all done', }; diff --git a/src/score/score.controller.ts b/src/score/score.controller.ts index dd26dd24daaec51422557339c8d1a5939ca58ffc..a9989a06b77d8ec08b1c84db9dbf24ad87fe9377 100644 --- a/src/score/score.controller.ts +++ b/src/score/score.controller.ts @@ -29,13 +29,6 @@ export class ScoreController { return this.scoreService.addScore(data, gameId); } - // temporary scoreTick path, :id is gameId - @Get('tick-score/:id') - @GameStates('STARTED') - async scoreTick(@Param('id') gameId: GameEntity) { - return this.scoreService.scoreTick(gameId); - } - // shows scores, :id is gameId @Get('get-score/:id') @UseInterceptors(ClassSerializerInterceptor) diff --git a/src/score/score.entity.ts b/src/score/score.entity.ts index 6ca9ab77b968060a6dc88c860425119914cec030..a5321daa56ae78dbe1d54911bf934a37a9b124db 100644 --- a/src/score/score.entity.ts +++ b/src/score/score.entity.ts @@ -12,8 +12,9 @@ import { FactionEntity } from '../faction/faction.entity'; export class ScoreEntity { @PrimaryGeneratedColumn('uuid') scoreId: string; @Column({ type: 'float' }) score: number; - @CreateDateColumn({ type: 'timestamp' }) scoreTimeStamp: Timestamp; + @Column({ type: 'float' }) scoreTimeStamp: number; + // when Faction is deleted, it's Score is also deleted @ManyToOne(type => FactionEntity, factionName => factionName.factionId, { onDelete: 'CASCADE', }) diff --git a/src/score/score.module.ts b/src/score/score.module.ts index c04486fc676fca0d7c6a38c74374e03e23a1008c..fce21d189b233bd6f952ebce79e47869171df60b 100644 --- a/src/score/score.module.ts +++ b/src/score/score.module.ts @@ -11,6 +11,10 @@ import { import { ScoreEntity } from './score.entity'; import { NotificationModule } from '../notifications/notifications.module'; +///////////////////////////////////////////////////////////////////// +/// Score /// +/// - contains everything to do with Score data. /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [ TypeOrmModule.forFeature([ diff --git a/src/score/score.service.ts b/src/score/score.service.ts index 640970c8e5ca99e877982cc2f4174123595792bb..7ef613d0f554bc61ffe9106e64845eaefe43428f 100644 --- a/src/score/score.service.ts +++ b/src/score/score.service.ts @@ -1,4 +1,10 @@ -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; +import { + Injectable, + HttpException, + HttpStatus, + Logger, + OnModuleInit, +} from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; @@ -44,6 +50,7 @@ export class ScoreService { } // add the score for Faction const newScore = await this.scoreRepository.create(scoreData); + newScore.scoreTimeStamp = Date.now(); await this.scoreRepository.insert(newScore); return { message: 'Score updated!', @@ -69,7 +76,8 @@ export class ScoreService { i => i.faction === current.owner.factionId, ); index !== -1 - ? await (scoreData[index]['score'] += box.objectivePointMultiplier * current.owner.multiplier) + ? await (scoreData[index]['score'] += + box.objectivePointMultiplier * current.owner.multiplier) : await scoreData.push({ score: box.objectivePointMultiplier, faction: current.owner.factionId, @@ -109,10 +117,4 @@ export class ScoreService { ); return scores; } -} // - -// Hae kaikki Objective pointit -// aja map funktio pelin objective pointteihin -// jokaisella objective point ID:llä hae historystä -// relaatio, missä uusin timestamp -// katso uusimmista history entrystä omistaja +} diff --git a/src/shared/roles.controller.ts b/src/shared/roles.controller.ts index 0d91e061c7fea732af225819e87f8529ff326eab..0298723936651e1859e76273b14b9086e167f6c9 100644 --- a/src/shared/roles.controller.ts +++ b/src/shared/roles.controller.ts @@ -48,41 +48,4 @@ const grants = { //player & spectator }; -const ac = new AccessControl(grants); - -/*const express = require ('express'); -const router express.Router; - -const ac = new AccessControl(); -ac.grant('faction_leader') // define new or modify existing role. also takes an array. - .createOwn('mapmarker') // equivalent to .createOwn('video', ['*']) - .deleteOwn('mapmarker') - .readOwn('mapmarker') - .grant('admin') // switch to another role without breaking the chain - .extend('user') // inherit role capabilities. also takes an array - .updateAny('mapmarker', ['title']) // explicitly defined attributes - .deleteAny('mapmarker') - .readAny('mapmarker'); - -//const -let permission = ac.can('user').createOwn('mapmarker'); -console.log(permission.granted); // —> true -console.log(permission.attributes); // —> ['*'] (all attributes) - -permission = ac.can('admin').updateAny('mapmarker'); -console.log(permission.granted); // —> true -console.log(permission.attributes); // —> ['title'] - -router.get('/videos/:title', function (req, res, next) { - const permission = ac.can(req.user.role).readAny('video'); - if (permission.granted) { - Video.find(req.params.title, function (err, data) { - if (err || !data) return res.status(404).end(); - // filter data by permission attributes and send. - res.json(permission.filter(data)); - }); - } else { - // resource is forbidden for this user/role - res.status(403).end(); - } -});*/ +const ac = new AccessControl(grants); \ No newline at end of file diff --git a/src/shared/roles.guard.ts b/src/shared/roles.guard.ts index 359a42d8165ee7d579078c300eded626b760df8b..7ae58d2f0c2d9f1258e41ea1a8341e2e664df15d 100644 --- a/src/shared/roles.guard.ts +++ b/src/shared/roles.guard.ts @@ -42,7 +42,7 @@ export class RolesGuard implements CanActivate { request.user = await this.getUserObject(request.headers.authorization); const role = await this.game_PersonRepository.findOne({ where: { person: request.user['id'], game: gameId }, - relations: ['faction'], + relations: ['faction', 'group'], }); // add gameperson role to the request // @GamePerson decorator can access it and pass it to service diff --git a/src/shared/validation.pipe.ts b/src/shared/validation.pipe.ts index cff51f1b068b67886c29dd85587760a1fa016f1e..423f23152e202e3c808d8c1e888f2a5fc00dac30 100644 --- a/src/shared/validation.pipe.ts +++ b/src/shared/validation.pipe.ts @@ -7,7 +7,6 @@ import { } from '@nestjs/common'; import { validate } from 'class-validator'; import { plainToClass } from 'class-transformer'; -import { AdvancedConsoleLogger } from 'typeorm'; @Injectable() export class ValidationPipe implements PipeTransform<any> { diff --git a/src/task/task.entity.ts b/src/task/task.entity.ts index f1c1cf103fa4338a7f10d0305888d13c097d7a09..762dbc038992f394b1bd773b9fd4e2965fe9a145 100644 --- a/src/task/task.entity.ts +++ b/src/task/task.entity.ts @@ -16,6 +16,7 @@ export class TaskEntity { @Column({ type: 'text' }) taskDescription: string; @Column({ type: 'bool' }) taskIsActive: boolean; + // when a Faction or Game is deleted, affiliated Tasks are also deleted @ManyToOne(type => FactionEntity, faction => faction.factionId, { onDelete: 'CASCADE', }) diff --git a/src/task/task.module.ts b/src/task/task.module.ts index 2391f284e62af1eaccb4df9a4cf051735a903775..dc4b8f63d9a1866771f4c68035a09516cfc56bf6 100644 --- a/src/task/task.module.ts +++ b/src/task/task.module.ts @@ -7,6 +7,10 @@ import { TaskEntity } from './task.entity'; import { FactionEntity } from '../faction/faction.entity'; import { NotificationModule } from '../notifications/notifications.module'; +///////////////////////////////////////////////////////////////////// +/// Task /// +/// - contains everything to do with Task data. /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [ TypeOrmModule.forFeature([TaskEntity, FactionEntity]), diff --git a/src/task/task.service.ts b/src/task/task.service.ts index 043f65a130fd37f632b38464112ed353dc4a4233..478a6c18059c5b0388aaad1abe2f3fcc2e05ce5e 100644 --- a/src/task/task.service.ts +++ b/src/task/task.service.ts @@ -5,7 +5,6 @@ import { InjectRepository } from '@nestjs/typeorm'; import { TaskEntity } from './task.entity'; import { CreateTaskDTO, EditTaskDTO, DeleteTaskDTO } from './task.dto'; import { FactionEntity } from '../faction/faction.entity'; -import { Game_PersonEntity } from '../game/game.entity'; import { NotificationGateway } from '../notifications/notifications.gateway'; @Injectable() @@ -76,6 +75,8 @@ export class TaskService { throw new HttpException('Task not found', HttpStatus.BAD_REQUEST); } + // finds all tasks if admin and if non-admin player it finds the playres own + // tasks and general tasks async fetchTasks(gameperson, taskGame) { if (gameperson.role == 'admin') { return await this.taskRepository.find({ diff --git a/src/tracking/tracking.controller.ts b/src/tracking/tracking.controller.ts index 2b409cee30764f1d0d33133828a5fd9f9d47d31f..93d0779a9c1ec36f2446b93167211f9c8eae9dce 100644 --- a/src/tracking/tracking.controller.ts +++ b/src/tracking/tracking.controller.ts @@ -2,7 +2,6 @@ import { Controller, Post, Param, - UseGuards, UsePipes, Body, Get, @@ -15,7 +14,7 @@ import { User } from '../user/user.decorator'; import { Roles, GameStates } from '../shared/guard.decorator'; import { ValidationPipe } from '../shared/validation.pipe'; import { GeoDTO } from './geo.dto'; -import { GamePerson } from 'src/game/gameperson.decorator'; +import { GamePerson } from '../game/gameperson.decorator'; @Controller('tracking') export class TrackingController { @@ -35,6 +34,8 @@ export class TrackingController { return this.trackingservice.trackLocation(gameperson, id, trackdata); } + // finds certain player's location + // :id is the id of the game @Get('players/:id') @Roles('admin', 'factionleader') @GameStates('STARTED', 'PAUSED') @@ -42,6 +43,8 @@ export class TrackingController { return this.trackingservice.getPlayers(gameperson, gameId); } + + // finds certain player @Get('player/:id') @Roles('admin', 'factionleader') @GameStates('STARTED', 'PAUSED') diff --git a/src/tracking/tracking.dto.ts b/src/tracking/tracking.dto.ts index 81cc71597fa93910cb195b1fd4e30f38c62c1f05..3ac93c71d8ccf3035e82fb52a7d21350b6407763 100644 --- a/src/tracking/tracking.dto.ts +++ b/src/tracking/tracking.dto.ts @@ -1,4 +1,3 @@ -import { Game_PersonEntity } from '../game/game.entity'; import { Allow, ValidateNested } from 'class-validator'; import { GeoDTO } from './geo.dto'; import { Type } from 'class-transformer'; diff --git a/src/tracking/tracking.entity.ts b/src/tracking/tracking.entity.ts index 8c5b9ab59c8e4250aa313e6eda540f1847d41f78..4952e18c1f2cbac3b2c31a377909960ed85100c9 100644 --- a/src/tracking/tracking.entity.ts +++ b/src/tracking/tracking.entity.ts @@ -9,6 +9,7 @@ export class TrackingEntity { @Column({ type: 'json', nullable: true }) data: GeoDTO[]; @Column('text') icon: string; + // when the GamePerson is deleted it's tracking data is also deleted @ManyToOne(type => Game_PersonEntity, person => person.gamepersonId, { onDelete: 'CASCADE', }) diff --git a/src/tracking/tracking.module.ts b/src/tracking/tracking.module.ts index fe31de3704dcd39113f8a46f6f718555a0528b2b..b2a731217fadbda0dca4be40af271b711e604482 100644 --- a/src/tracking/tracking.module.ts +++ b/src/tracking/tracking.module.ts @@ -7,6 +7,10 @@ import { TrackingEntity } from './tracking.entity'; import { Game_PersonEntity } from '../game/game.entity'; import { PersonEntity } from '../user/user.entity'; +///////////////////////////////////////////////////////////////////// +/// Tracking /// +/// - contains everything to do with Tracking data. /// +///////////////////////////////////////////////////////////////////// @Module({ imports: [ TypeOrmModule.forFeature([TrackingEntity, Game_PersonEntity, PersonEntity]), diff --git a/src/tracking/tracking.service.ts b/src/tracking/tracking.service.ts index ff97aae3993f62c9dbb342ca9ff5744001d32366..b366784ec03f8c4f67e0ec99bbc563a292733d6f 100644 --- a/src/tracking/tracking.service.ts +++ b/src/tracking/tracking.service.ts @@ -15,6 +15,12 @@ export class TrackingService { private gamepersonrepository: Repository<Game_PersonEntity>, ) {} + private icons = { + infantry: 'infantry.svg', + recon: 'recon.svg', + mechanized: 'mechanized.svg', + }; + async trackLocation( gameperson: Game_PersonEntity, gameId, @@ -37,7 +43,10 @@ export class TrackingService { trackdata['time'] = Date.now(); // initialize data trackedperson = await this.trackingrepository.create(trackedperson); - trackedperson.icon = 'infantry.svg'; + // if group exists, add icon based on that, else add default icon + trackedperson.icon = gameperson.group + ? this.icons[gameperson.group.class] + : 'infantry.svg'; trackedperson.data = [trackdata]; trackedperson.faction = gameperson.faction; trackedperson.game = gameId;