diff --git a/src/__tests__/common/service/redis/mocks/RedisServiceInMemory.ts b/src/__tests__/common/service/redis/mocks/RedisServiceInMemory.ts index 7bbf58b78..b7c0f4574 100644 --- a/src/__tests__/common/service/redis/mocks/RedisServiceInMemory.ts +++ b/src/__tests__/common/service/redis/mocks/RedisServiceInMemory.ts @@ -7,11 +7,17 @@ import IRedisService from '../../../../../common/service/redis/IRedisService'; */ @Injectable() export class RedisServiceInMemory implements IRedisService, OnModuleDestroy { + private static instance: RedisServiceInMemory; private readonly store = new Map< string, { value: string; expiresAt?: number } >(); + constructor() { + if (RedisServiceInMemory.instance) return RedisServiceInMemory.instance; + RedisServiceInMemory.instance = this; + } + private isDestroyed = false; async set(key: string, value: string, ttlS?: number): Promise { diff --git a/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts b/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts index 6b832f347..30e22ad62 100644 --- a/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts +++ b/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts @@ -6,6 +6,9 @@ import { CacheKeys } from '../../../common/service/redis/cacheKeys.enum'; import { OnlinePlayerStatus } from '../../../onlinePlayers/enum/OnlinePlayerStatus'; import OnlinePlayersCommonModule from '../modules/onlinePlayersCommon.module'; import { RedisService } from '../../../common/service/redis/redis.service'; +import OnlinePlayersBuilderFactory from '../data/onlinePlayersBuilderFactory'; +import { OnlinePlayerBuilder } from '../data/onlinePlayers/OnlinePlayerBuilder'; +import { BattleWaitStatus } from '../../../onlinePlayers/payload/additionalTypes/BattleWaitStatus'; describe('OnlinePlayersService.addPlayerOnline() test suite', () => { let service: OnlinePlayersService; @@ -18,6 +21,12 @@ describe('OnlinePlayersService.addPlayerOnline() test suite', () => { .setName('player1') .build(); + const addPlayerBuilder = + OnlinePlayersBuilderFactory.getBuilder('AddOnlinePlayer'); + const onlinePlayerBuilder = OnlinePlayersBuilderFactory.getBuilder( + 'OnlinePlayer', + ) as OnlinePlayerBuilder; + const playerModel = PlayerModule.getPlayerModel(); beforeEach(async () => { @@ -33,18 +42,48 @@ describe('OnlinePlayersService.addPlayerOnline() test suite', () => { }); it('Should be able to add one player to cache', async () => { + const playerToAdd = addPlayerBuilder + .setPlayerId(player1._id) + .setStatus(OnlinePlayerStatus.BATTLE) + .build(); + const expectedKey = `${CacheKeys.ONLINE_PLAYERS}:${player1._id}`; - const expectedPayload = JSON.stringify({ - _id: player1._id, - name: player1.name, - status: OnlinePlayerStatus.BATTLE, - }); + const expectedPayload = JSON.stringify( + onlinePlayerBuilder + .setId(playerToAdd.player_id) + .setName(player1.name) + .setStatus(playerToAdd.status) + .build(), + ); + + const redisSet = jest.spyOn(redisService, 'set'); + + await service.addPlayerOnline(playerToAdd); + + expect(redisSet).toHaveBeenCalledTimes(1); + expect(redisSet).toHaveBeenCalledWith(expectedKey, expectedPayload, 90); + }); + + it(`Should set queue number if player has status ${OnlinePlayerStatus.BATTLE_WAIT}`, async () => { + const playerToAdd = addPlayerBuilder + .setPlayerId(player1._id) + .setStatus(OnlinePlayerStatus.BATTLE_WAIT) + .build(); + const expectedKey = `${CacheKeys.ONLINE_PLAYERS}:${player1._id}`; + const expectedPayload = JSON.stringify( + onlinePlayerBuilder + .setId(playerToAdd.player_id) + .setName(player1.name) + .setStatus(playerToAdd.status) + .setAdditional({ queueNumber: 0 }) + .build(), + ); const redisSet = jest.spyOn(redisService, 'set'); await service.addPlayerOnline({ player_id: player1._id, - status: OnlinePlayerStatus.BATTLE, + status: OnlinePlayerStatus.BATTLE_WAIT, }); expect(redisSet).toHaveBeenCalledTimes(1); diff --git a/src/__tests__/onlinePlayers/OnlinePlayersService/getOnlinePlayerById.test.ts b/src/__tests__/onlinePlayers/OnlinePlayersService/getOnlinePlayerById.test.ts new file mode 100644 index 000000000..21c8e6876 --- /dev/null +++ b/src/__tests__/onlinePlayers/OnlinePlayersService/getOnlinePlayerById.test.ts @@ -0,0 +1,59 @@ +import { OnlinePlayersService } from '../../../onlinePlayers/onlinePlayers.service'; +import { RedisService } from '../../../common/service/redis/redis.service'; +import PlayerBuilderFactory from '../../player/data/playerBuilderFactory'; +import PlayerModule from '../../player/modules/player.module'; +import OnlinePlayersModule from '../modules/onlinePlayers.module'; +import OnlinePlayersCommonModule from '../modules/onlinePlayersCommon.module'; +import OnlinePlayer from '../../../onlinePlayers/payload/OnlinePlayer'; +import { OnlinePlayerStatus } from '../../../onlinePlayers/enum/OnlinePlayerStatus'; + +describe('OnlinePlayersService.getOnlinePlayerById() test suite', () => { + let service: OnlinePlayersService; + + let redisService: RedisService; + + const playerBuilder = PlayerBuilderFactory.getBuilder('Player'); + const player1 = playerBuilder + .setUniqueIdentifier('player1') + .setName('player1') + .build(); + + const playerModel = PlayerModule.getPlayerModel(); + + beforeEach(async () => { + jest.clearAllMocks(); + service = await OnlinePlayersModule.getOnlinePlayersService(); + + const player1Resp = await playerModel.create(player1); + player1._id = player1Resp._id.toString(); + + redisService = (await OnlinePlayersCommonModule.getModule()).get( + RedisService, + ); + }); + + it('Should return player if it exists', async () => { + const existingPlayer: OnlinePlayer = { + _id: player1._id, + name: player1.name, + status: OnlinePlayerStatus.UI, + }; + jest + .spyOn(redisService, 'get') + .mockResolvedValue(JSON.stringify(existingPlayer)); + + const [player, errors] = await service.getOnlinePlayerById(player1._id); + + expect(errors).toBeNull(); + expect(player).toEqual(existingPlayer); + }); + + it('Should return ServiceError NOT_FOUND if player does not exists', async () => { + jest.spyOn(redisService, 'get').mockResolvedValue(null); + + const [player, errors] = await service.getOnlinePlayerById(player1._id); + + expect(player).toBeNull(); + expect(errors).toContainSE_NOT_FOUND(); + }); +}); diff --git a/src/__tests__/onlinePlayers/battleQueue/BattleQueueService/getPlayerQueueNumber.test.ts b/src/__tests__/onlinePlayers/battleQueue/BattleQueueService/getPlayerQueueNumber.test.ts new file mode 100644 index 000000000..d823e09b6 --- /dev/null +++ b/src/__tests__/onlinePlayers/battleQueue/BattleQueueService/getPlayerQueueNumber.test.ts @@ -0,0 +1,99 @@ +import BattleQueueModule from '../../modules/battleQueue.module'; +import PlayerModule from '../../../player/modules/player.module'; +import PlayerBuilderFactory from '../../../player/data/playerBuilderFactory'; +import { BattleQueueService } from '../../../../onlinePlayers/battleQueue/battleQueue.service'; +import { OnlinePlayerStatus } from '../../../../onlinePlayers/enum/OnlinePlayerStatus'; +import OnlinePlayersBuilderFactory from '../../data/onlinePlayersBuilderFactory'; +import { OnlinePlayerBuilder } from '../../data/onlinePlayers/OnlinePlayerBuilder'; +import { BattleWaitStatus } from '../../../../onlinePlayers/payload/additionalTypes/BattleWaitStatus'; + +describe('BattleQueueService.getPlayerQueueNumber() test suite', () => { + let service: BattleQueueService; + + const playerBuilder = PlayerBuilderFactory.getBuilder('Player'); + const player1 = playerBuilder + .setName('player1') + .setUniqueIdentifier('player1') + .build(); + const player2 = playerBuilder + .setName('player2') + .setUniqueIdentifier('player2') + .build(); + const player3 = playerBuilder + .setName('player3') + .setUniqueIdentifier('player3') + .build(); + const playerModel = PlayerModule.getPlayerModel(); + + const onlinePlayerBuilder = OnlinePlayersBuilderFactory.getBuilder( + 'OnlinePlayer', + ) as OnlinePlayerBuilder; + + beforeEach(async () => { + service = await BattleQueueModule.getBattleQueueService(); + + const createdPlayer1 = await playerModel.create(player1); + player1._id = createdPlayer1._id.toString(); + const createdPlayer2 = await playerModel.create(player2); + player2._id = createdPlayer2._id.toString(); + const createdPlayer3 = await playerModel.create(player3); + player3._id = createdPlayer3._id.toString(); + }); + + it('Should return increased by 1 order number for each player', async () => { + const onlinePlayer1 = onlinePlayerBuilder + .setId(player1._id) + .setName(player1.name) + .build(); + const onlinePlayer2 = onlinePlayerBuilder + .setId(player2._id) + .setName(player2.name) + .build(); + const onlinePlayer3 = onlinePlayerBuilder + .setId(player3._id) + .setName(player3.name) + .build(); + + const [number1, errors1] = + await service.getPlayerQueueNumber(onlinePlayer1); + const [number2, errors2] = + await service.getPlayerQueueNumber(onlinePlayer2); + const [number3, errors3] = + await service.getPlayerQueueNumber(onlinePlayer3); + + expect(errors1).toBeNull(); + expect(number1).toBe(0); + + expect(errors2).toBeNull(); + expect(number2).toBe(1); + + expect(errors3).toBeNull(); + expect(number3).toBe(2); + }); + + it('Should return the same number the player has if the player is already in queue', async () => { + const queueNumber = 0; + const onlinePlayer1 = onlinePlayerBuilder + .setId(player1._id) + .setName(player1.name) + .setStatus(OnlinePlayerStatus.BATTLE_WAIT) + .setAdditional({ queueNumber }) + .build(); + + const [number1, errors1] = + await service.getPlayerQueueNumber(onlinePlayer1); + const [number2, errors2] = + await service.getPlayerQueueNumber(onlinePlayer1); + const [number3, errors3] = + await service.getPlayerQueueNumber(onlinePlayer1); + + expect(errors1).toBeNull(); + expect(number1).toBe(queueNumber); + + expect(errors2).toBeNull(); + expect(number2).toBe(queueNumber); + + expect(errors3).toBeNull(); + expect(number3).toBe(queueNumber); + }); +}); diff --git a/src/__tests__/onlinePlayers/data/onlinePlayers/AddOnlinePlayerBuilder.ts b/src/__tests__/onlinePlayers/data/onlinePlayers/AddOnlinePlayerBuilder.ts new file mode 100644 index 000000000..d131f86fd --- /dev/null +++ b/src/__tests__/onlinePlayers/data/onlinePlayers/AddOnlinePlayerBuilder.ts @@ -0,0 +1,23 @@ +import AddOnlinePlayer from '../../../../onlinePlayers/payload/AddOnlinePlayer'; +import { OnlinePlayerStatus } from '../../../../onlinePlayers/enum/OnlinePlayerStatus'; + +export class AddOnlinePlayerBuilder { + private readonly base: Partial = { + player_id: undefined, + status: OnlinePlayerStatus.UI, + }; + + build(): AddOnlinePlayer { + return { ...this.base } as AddOnlinePlayer; + } + + setPlayerId(player_id: string): this { + this.base.player_id = player_id; + return this; + } + + setStatus(status: OnlinePlayerStatus): this { + this.base.status = status; + return this; + } +} diff --git a/src/__tests__/onlinePlayers/data/onlinePlayers/OnlinePlayerBuilder.ts b/src/__tests__/onlinePlayers/data/onlinePlayers/OnlinePlayerBuilder.ts new file mode 100644 index 000000000..470049f84 --- /dev/null +++ b/src/__tests__/onlinePlayers/data/onlinePlayers/OnlinePlayerBuilder.ts @@ -0,0 +1,35 @@ +import OnlinePlayer from '../../../../onlinePlayers/payload/OnlinePlayer'; +import { OnlinePlayerStatus } from '../../../../onlinePlayers/enum/OnlinePlayerStatus'; + +export class OnlinePlayerBuilder { + private readonly base: Partial> = { + _id: undefined, + name: 'player1', + status: OnlinePlayerStatus.UI, + additional: undefined, + }; + + build(): OnlinePlayer { + return { ...this.base } as OnlinePlayer; + } + + setId(id: string): this { + this.base._id = id; + return this; + } + + setName(name: string): this { + this.base.name = name; + return this; + } + + setStatus(status: OnlinePlayerStatus): this { + this.base.status = status; + return this; + } + + setAdditional(additional: Additional): this { + this.base.additional = additional; + return this; + } +} diff --git a/src/__tests__/onlinePlayers/data/onlinePlayersBuilderFactory.ts b/src/__tests__/onlinePlayers/data/onlinePlayersBuilderFactory.ts new file mode 100644 index 000000000..efd2d36ac --- /dev/null +++ b/src/__tests__/onlinePlayers/data/onlinePlayersBuilderFactory.ts @@ -0,0 +1,22 @@ +import { OnlinePlayerBuilder } from './onlinePlayers/OnlinePlayerBuilder'; +import { AddOnlinePlayerBuilder } from './onlinePlayers/AddOnlinePlayerBuilder'; + +type BuilderName = 'OnlinePlayer' | 'AddOnlinePlayer'; + +type BuilderMap = { + OnlinePlayer: OnlinePlayerBuilder; + AddOnlinePlayer: AddOnlinePlayerBuilder; +}; + +export default class OnlinePlayersBuilderFactory { + static getBuilder(builderName: T): BuilderMap[T] { + switch (builderName) { + case 'OnlinePlayer': + return new OnlinePlayerBuilder() as BuilderMap[T]; + case 'AddOnlinePlayer': + return new AddOnlinePlayerBuilder() as BuilderMap[T]; + default: + throw new Error(`Unknown builder name: ${builderName}`); + } + } +} diff --git a/src/__tests__/onlinePlayers/modules/battleQueue.module.ts b/src/__tests__/onlinePlayers/modules/battleQueue.module.ts new file mode 100644 index 000000000..1ba52f73d --- /dev/null +++ b/src/__tests__/onlinePlayers/modules/battleQueue.module.ts @@ -0,0 +1,11 @@ +import { BattleQueueService } from '../../../onlinePlayers/battleQueue/battleQueue.service'; +import OnlinePlayersCommonModule from './onlinePlayersCommon.module'; + +export default class BattleQueueModule { + private constructor() {} + + static async getBattleQueueService() { + const module = await OnlinePlayersCommonModule.getModule(); + return module.resolve(BattleQueueService); + } +} diff --git a/src/__tests__/onlinePlayers/modules/onlinePlayersCommon.module.ts b/src/__tests__/onlinePlayers/modules/onlinePlayersCommon.module.ts index 8eb40e1e2..d13cd3645 100644 --- a/src/__tests__/onlinePlayers/modules/onlinePlayersCommon.module.ts +++ b/src/__tests__/onlinePlayers/modules/onlinePlayersCommon.module.ts @@ -9,6 +9,7 @@ import { PlayerSchema } from '../../../player/schemas/player.schema'; import { RedisModule } from '../../../common/service/redis/redis.module'; import { RedisServiceInMemory } from '../../common/service/redis/mocks/RedisServiceInMemory'; import { RedisService } from '../../../common/service/redis/redis.service'; +import { BattleQueueService } from '../../../onlinePlayers/battleQueue/battleQueue.service'; export default class OnlinePlayersCommonModule { private static module: TestingModule; @@ -25,7 +26,7 @@ export default class OnlinePlayersCommonModule { RequestHelperModule, RedisModule, ], - providers: [OnlinePlayersService], + providers: [OnlinePlayersService, BattleQueueService], }) .overrideProvider(RedisService) .useClass(RedisServiceInMemory) diff --git a/src/leaderboard/leaderboard.controller.ts b/src/leaderboard/leaderboard.controller.ts index 5ea9831a0..efeb9293d 100644 --- a/src/leaderboard/leaderboard.controller.ts +++ b/src/leaderboard/leaderboard.controller.ts @@ -13,6 +13,7 @@ import { PlayerService } from '../player/player.service'; import { LeaderboardPlayerDto } from './dto/leaderboardPlayer.dto'; import ApiResponseDescription from '../common/swagger/response/ApiResponseDescription'; import ClanPositionDto from './dto/clanPosition.dto'; +import SwaggerTags from '../common/swagger/tags/SwaggerTags.decorator'; @Controller('leaderboard') export class LeaderboardController { @@ -21,12 +22,13 @@ export class LeaderboardController { private readonly playerService: PlayerService, ) {} + @SwaggerTags('Release on 01.06.2025', 'Leaderboard') /** * Get top players * * @remarks Leaderboard of players. Top Players are defined by the amount of points that he/she has. * - * Notice that the leaderboards data is updated once every 12 hours. + * Notice that the leaderboards data is updated once every 3h hours. */ @ApiResponseDescription({ success: { @@ -45,12 +47,13 @@ export class LeaderboardController { return this.leaderBoardService.getPlayerLeaderboard(query); } + @SwaggerTags('Release on 01.06.2025', 'Leaderboard') /** * Get top clans * * @remarks Leaderboard of clans. Top Clans are defined by the amount of points that each Clan has. * - * Notice that the leaderboards data is updated once every 12 hours. + * Notice that the leaderboards data is updated once every 3h hours. */ @ApiResponseDescription({ success: { diff --git a/src/onlinePlayers/battleQueue/battleQueue.controller.ts b/src/onlinePlayers/battleQueue/battleQueue.controller.ts new file mode 100644 index 000000000..a7ba2f819 --- /dev/null +++ b/src/onlinePlayers/battleQueue/battleQueue.controller.ts @@ -0,0 +1,32 @@ +import { Controller, Get } from '@nestjs/common'; +import { BattleQueueService } from './battleQueue.service'; +import ApiResponseDescription from 'src/common/swagger/response/ApiResponseDescription'; +import OnlinePlayerDto from '../dto/onlinePlayer.dto'; +import { UniformResponse } from '../../common/decorator/response/UniformResponse'; +import SwaggerTags from '../../common/swagger/tags/SwaggerTags.decorator'; + +@Controller('/online-players/battleQueue') +export class BattleQueueController { + constructor(private readonly service: BattleQueueService) {} + + @SwaggerTags('Release on 01.06.2025', 'OnlinePlayers') + /** + * Get battle queue + * + * @remarks Returns a list of online players waiting to join the battle in a queue order, + * where the first player is the next to play + */ + @ApiResponseDescription({ + success: { + dto: OnlinePlayerDto, + returnsArray: true, + hasPagination: false, + }, + errors: [401, 404], + }) + @Get() + @UniformResponse(null, OnlinePlayerDto) + async getBattleQueue() { + return []; + } +} diff --git a/src/onlinePlayers/battleQueue/battleQueue.service.ts b/src/onlinePlayers/battleQueue/battleQueue.service.ts new file mode 100644 index 000000000..16d719aca --- /dev/null +++ b/src/onlinePlayers/battleQueue/battleQueue.service.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@nestjs/common'; +import { IServiceReturn } from '../../common/service/basicService/IService'; +import OnlinePlayer from '../payload/OnlinePlayer'; +import { BattleWaitStatus } from '../payload/additionalTypes/BattleWaitStatus'; + +@Injectable() +export class BattleQueueService { + public constructor() {} + + /** + * Max number the queue number can become + * @private + */ + private readonly queueNumberMax = 9999; + + /** + * Auto-incremented positive integer number, which is used to determine queue position of a player + * @private + */ + private nextQueueNumber = 0; + + /** + * Gets a queue number for a player. + * + * Each time a player gets a new queue number, the queue number is increased by one until it becomes its max value. + * Then the number will be set to its max value, it will be reset and will be equal to zero again. + * Notice that if the player already in the queue, the number will not be updated + * + * @param player player for which the number is requested + */ + async getPlayerQueueNumber( + player: OnlinePlayer | null, + ): Promise> { + if (player && player.additional?.queueNumber != null) { + const existingQueueNumber = (player as OnlinePlayer) + .additional.queueNumber; + return [existingQueueNumber, null]; + } + + const playerQueueNumber = this.nextQueueNumber; + this.nextQueueNumber++; + if (this.nextQueueNumber > this.queueNumberMax) this.nextQueueNumber = 0; + + return [playerQueueNumber, null]; + } +} diff --git a/src/onlinePlayers/dto/onlinePlayer.dto.ts b/src/onlinePlayers/dto/onlinePlayer.dto.ts index 983bab67e..f56313651 100644 --- a/src/onlinePlayers/dto/onlinePlayer.dto.ts +++ b/src/onlinePlayers/dto/onlinePlayer.dto.ts @@ -1,7 +1,7 @@ import { Expose } from 'class-transformer'; import { OnlinePlayerStatus } from '../enum/OnlinePlayerStatus'; -export default class OnlinePlayerDto { +export default class OnlinePlayerDto { /** * _id of the player * @@ -25,4 +25,12 @@ export default class OnlinePlayerDto { */ @Expose() status: OnlinePlayerStatus; + + /** + * Any additional information online player has + * + * @example { queueNumber: 239 } + */ + @Expose() + additional?: Additional; } diff --git a/src/onlinePlayers/onlinePlayers.controller.ts b/src/onlinePlayers/onlinePlayers.controller.ts index 9d46c0316..2d72697f1 100644 --- a/src/onlinePlayers/onlinePlayers.controller.ts +++ b/src/onlinePlayers/onlinePlayers.controller.ts @@ -7,11 +7,13 @@ import ApiResponseDescription from '../common/swagger/response/ApiResponseDescri import OnlinePlayerDto from './dto/onlinePlayer.dto'; import InformPlayerIsOnlineDto from './dto/InformPlayerIsOnline.dto'; import OnlinePlayerSearchQueryDto from './dto/OnlinePlayerSearchQuery.dto'; +import SwaggerTags from '../common/swagger/tags/SwaggerTags.decorator'; @Controller('online-players') export class OnlinePlayersController { constructor(private readonly onlinePlayersService: OnlinePlayersService) {} + @SwaggerTags('Release on 01.06.2025', 'OnlinePlayers') /** * Inform the API if player is still online * @@ -34,6 +36,7 @@ export class OnlinePlayersController { }); } + @SwaggerTags('Release on 01.06.2025', 'OnlinePlayers') /** * Inform the API if player is still online * diff --git a/src/onlinePlayers/onlinePlayers.module.ts b/src/onlinePlayers/onlinePlayers.module.ts index 0342ca223..10781868c 100644 --- a/src/onlinePlayers/onlinePlayers.module.ts +++ b/src/onlinePlayers/onlinePlayers.module.ts @@ -3,10 +3,13 @@ import { OnlinePlayersService } from './onlinePlayers.service'; import { OnlinePlayersController } from './onlinePlayers.controller'; import { PlayerModule } from '../player/player.module'; import { RedisModule } from '../common/service/redis/redis.module'; +import { BattleQueueService } from './battleQueue/battleQueue.service'; +import { BattleQueueController } from './battleQueue/battleQueue.controller'; @Module({ imports: [PlayerModule, RedisModule], - providers: [OnlinePlayersService], - controllers: [OnlinePlayersController], + providers: [OnlinePlayersService, BattleQueueService], + controllers: [OnlinePlayersController, BattleQueueController], + exports: [OnlinePlayersService], }) export class OnlinePlayersModule {} diff --git a/src/onlinePlayers/onlinePlayers.service.ts b/src/onlinePlayers/onlinePlayers.service.ts index bd3063b0f..33aed4441 100644 --- a/src/onlinePlayers/onlinePlayers.service.ts +++ b/src/onlinePlayers/onlinePlayers.service.ts @@ -6,6 +6,10 @@ import { RedisService } from '../common/service/redis/redis.service'; import { OnlinePlayerStatus } from './enum/OnlinePlayerStatus'; import AddOnlinePlayer from './payload/AddOnlinePlayer'; import OnlinePlayer from './payload/OnlinePlayer'; +import ServiceError from '../common/service/basicService/ServiceError'; +import { SEReason } from '../common/service/basicService/SEReason'; +import { BattleWaitStatus } from './payload/additionalTypes/BattleWaitStatus'; +import { BattleQueueService } from './battleQueue/battleQueue.service'; @Injectable() export class OnlinePlayersService { @@ -19,6 +23,7 @@ export class OnlinePlayersService { constructor( private readonly redisService: RedisService, private readonly playerService: PlayerService, + private readonly battleQueueService: BattleQueueService, ) {} /** @@ -41,6 +46,13 @@ export class OnlinePlayersService { status: status ?? OnlinePlayerStatus.UI, }; + if (status === OnlinePlayerStatus.BATTLE_WAIT) { + const [onlinePlayer] = await this.getOnlinePlayerById(player_id); + const [queueNumber] = + await this.battleQueueService.getPlayerQueueNumber(onlinePlayer); + (payload as OnlinePlayer).additional = { queueNumber }; + } + await this.redisService.set( `${this.ONLINE_PLAYERS_KEY}:${player_id}`, JSON.stringify(payload), @@ -77,4 +89,32 @@ export class OnlinePlayersService { return onlinePlayers; } + + /** + * Gets online player by its _id. + * + * @param player_id player _id to be found + * + * @returns found online player or ServiceError NOT_FOUND if player is not found + */ + async getOnlinePlayerById( + player_id: string, + ): Promise> { + const player = await this.redisService.get( + `${this.ONLINE_PLAYERS_KEY}:${player_id}`, + ); + if (!player) + return [ + null, + [ + new ServiceError({ + reason: SEReason.NOT_FOUND, + field: 'player_id', + value: player_id, + message: 'Player with this _id is not found in online players', + }), + ], + ]; + return [JSON.parse(player), null]; + } } diff --git a/src/onlinePlayers/payload/OnlinePlayer.ts b/src/onlinePlayers/payload/OnlinePlayer.ts index 9ec0029e7..5591a7816 100644 --- a/src/onlinePlayers/payload/OnlinePlayer.ts +++ b/src/onlinePlayers/payload/OnlinePlayer.ts @@ -1,6 +1,6 @@ import { OnlinePlayerStatus } from '../enum/OnlinePlayerStatus'; -export default class OnlinePlayer { +export default class OnlinePlayer { /** * Player _id */ @@ -15,4 +15,9 @@ export default class OnlinePlayer { * Player status */ status: OnlinePlayerStatus; + + /** + * Any additional information online player has + */ + additional?: Additional; } diff --git a/src/onlinePlayers/payload/additionalTypes/BattleWaitStatus.ts b/src/onlinePlayers/payload/additionalTypes/BattleWaitStatus.ts new file mode 100644 index 000000000..4decfcefb --- /dev/null +++ b/src/onlinePlayers/payload/additionalTypes/BattleWaitStatus.ts @@ -0,0 +1,9 @@ +/** + * Additional information for a player with status BattleWait + */ +export type BattleWaitStatus = { + /** + * Player number in the queue + */ + queueNumber: number; +};