From ebffbb63e9ad563267513f153f2ea3955baa8601 Mon Sep 17 00:00:00 2001 From: CapoMK25 Date: Mon, 9 Feb 2026 21:29:10 +0200 Subject: [PATCH 1/5] Implemented enforcing the game version --- .../battleQueue/battleQueue.controller.ts | 9 ++++++--- .../battleQueue/battleQueue.service.ts | 15 +++++++++++++++ .../dto/InformPlayerIsOnline.dto.ts | 11 ++++++++++- src/onlinePlayers/onlinePlayers.controller.ts | 1 + src/onlinePlayers/onlinePlayers.service.ts | 16 +++++++++++++--- src/onlinePlayers/payload/AddOnlinePlayer.ts | 15 ++++++++++++++- src/onlinePlayers/payload/OnlinePlayer.ts | 12 ++++++++++++ 7 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/onlinePlayers/battleQueue/battleQueue.controller.ts b/src/onlinePlayers/battleQueue/battleQueue.controller.ts index e876591ae..a53da64c9 100644 --- a/src/onlinePlayers/battleQueue/battleQueue.controller.ts +++ b/src/onlinePlayers/battleQueue/battleQueue.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get } from '@nestjs/common'; +import { Controller, Get, Query } from '@nestjs/common'; import { BattleQueueService } from './battleQueue.service'; import OnlinePlayerDto from '../dto/onlinePlayer.dto'; import { UniformResponse } from '../../common/decorator/response/UniformResponse'; @@ -29,9 +29,12 @@ export class BattleQueueController { }) @Get() @UniformResponse(null, OnlinePlayerDto) - async getBattleQueue() { + async getBattleQueue(@Query('version') clientVersion: string) { const queuePlayers = await this.onlinePlayersService.getOnlinePlayers({ - filter: { status: [OnlinePlayerStatus.BATTLE_WAIT] }, + filter: { + status: [OnlinePlayerStatus.BATTLE_WAIT], + client_version: clientVersion + } }); return this.service.sortPlayersByQueueNumber(queuePlayers); } diff --git a/src/onlinePlayers/battleQueue/battleQueue.service.ts b/src/onlinePlayers/battleQueue/battleQueue.service.ts index 6bccc0c25..7cc201386 100644 --- a/src/onlinePlayers/battleQueue/battleQueue.service.ts +++ b/src/onlinePlayers/battleQueue/battleQueue.service.ts @@ -62,6 +62,21 @@ export class BattleQueueService { if (filterErrors) return [null, filterErrors]; + const firstVersion = validPlayers[0].client_version; + const hasVersionMismatch = validPlayers.some(p => p.client_version !== firstVersion); + + if (hasVersionMismatch) { + return [ + null, + [ + new ServiceError({ + reason: SEReason.VALIDATION, + message: 'Version mismatch: Players in queue have different client versions', + }), + ], + ]; + } + const queueNumbers = validPlayers.map((p) => p.additional.queueNumber); const queueMax = this.queueNumberMax + 1; diff --git a/src/onlinePlayers/dto/InformPlayerIsOnline.dto.ts b/src/onlinePlayers/dto/InformPlayerIsOnline.dto.ts index 16ee38f8b..b5f9af4e0 100644 --- a/src/onlinePlayers/dto/InformPlayerIsOnline.dto.ts +++ b/src/onlinePlayers/dto/InformPlayerIsOnline.dto.ts @@ -1,5 +1,5 @@ import { OnlinePlayerStatus } from '../enum/OnlinePlayerStatus'; -import { IsEnum, IsOptional } from 'class-validator'; +import { IsEnum, IsOptional, IsString, IsNotEmpty } from 'class-validator'; export default class InformPlayerIsOnlineDto { /** @@ -11,4 +11,13 @@ export default class InformPlayerIsOnlineDto { @IsOptional() @IsEnum(OnlinePlayerStatus) status?: OnlinePlayerStatus; + + /** + * The version of the game client. + * Required to ensure version-compatible matchmaking. + * @example "0.6.2" + */ + @IsString() + @IsNotEmpty() + client_version: string; } diff --git a/src/onlinePlayers/onlinePlayers.controller.ts b/src/onlinePlayers/onlinePlayers.controller.ts index 0fb8d3e71..ec4e0090c 100644 --- a/src/onlinePlayers/onlinePlayers.controller.ts +++ b/src/onlinePlayers/onlinePlayers.controller.ts @@ -31,6 +31,7 @@ export class OnlinePlayersController { return this.onlinePlayersService.addPlayerOnline({ player_id: user.player_id, status: body.status, + client_version: body.client_version, }); } diff --git a/src/onlinePlayers/onlinePlayers.service.ts b/src/onlinePlayers/onlinePlayers.service.ts index 3ff36ead6..80ebf8aac 100644 --- a/src/onlinePlayers/onlinePlayers.service.ts +++ b/src/onlinePlayers/onlinePlayers.service.ts @@ -35,7 +35,7 @@ export class OnlinePlayersService { async addPlayerOnline( playerInfo: AddOnlinePlayer, ): Promise> { - const { player_id, status } = playerInfo; + const { player_id, status, client_version } = playerInfo; const [player, errors] = await this.playerService.getPlayerById(player_id); if (errors) return [null, errors]; @@ -44,6 +44,7 @@ export class OnlinePlayersService { _id: player_id, name: player.name, status: status ?? OnlinePlayerStatus.UI, + client_version: client_version, }; if (status === OnlinePlayerStatus.BATTLE_WAIT) { @@ -69,7 +70,10 @@ export class OnlinePlayersService { * @returns Array of OnlinePlayers or empty array if nothing found */ async getOnlinePlayers(options?: { - filter?: { status?: OnlinePlayerStatus[] }; + filter?: { + status?: OnlinePlayerStatus[]; + client_version?: string; + }; }): Promise { const players = await this.redisService.getValuesByKeyPattern( `${this.ONLINE_PLAYERS_KEY}:*`, @@ -82,10 +86,16 @@ export class OnlinePlayersService { ) as OnlinePlayer[]; //TODO: Remove it after there are no versions anymore that uses old implementation of saving online players - const filteredPlayers = onlinePlayers.filter( + let filteredPlayers = onlinePlayers.filter( (player) => typeof player !== 'string' && typeof player !== 'number', ); + if (options?.filter?.client_version) { + filteredPlayers = filteredPlayers.filter( + (p) => p.client_version === options.filter.client_version, + ); + } + if (options?.filter?.status) { return filteredPlayers.filter((p) => options.filter.status.includes(p.status), diff --git a/src/onlinePlayers/payload/AddOnlinePlayer.ts b/src/onlinePlayers/payload/AddOnlinePlayer.ts index e8853d887..c05f6eac0 100644 --- a/src/onlinePlayers/payload/AddOnlinePlayer.ts +++ b/src/onlinePlayers/payload/AddOnlinePlayer.ts @@ -1,15 +1,28 @@ +import { IsEnum, IsOptional, IsString } from 'class-validator'; import { OnlinePlayerStatus } from '../enum/OnlinePlayerStatus'; export default class AddOnlinePlayer { /** * Player _id to be added */ + @IsString() player_id: string; /** - * Player status to set + * Player status to setAddOnlinePlayer * * @default "UI" */ + @IsEnum(OnlinePlayerStatus) + @IsOptional() status?: OnlinePlayerStatus; + + /** + * The version of the game client. + * Used to isolate matchmaking pools and prevent desyncs between incompatible builds. + * @example "1.0.4-beta" + */ + @IsString() + client_version: string; + } diff --git a/src/onlinePlayers/payload/OnlinePlayer.ts b/src/onlinePlayers/payload/OnlinePlayer.ts index 5591a7816..527dac118 100644 --- a/src/onlinePlayers/payload/OnlinePlayer.ts +++ b/src/onlinePlayers/payload/OnlinePlayer.ts @@ -1,14 +1,17 @@ import { OnlinePlayerStatus } from '../enum/OnlinePlayerStatus'; +import { Expose } from 'class-transformer'; export default class OnlinePlayer { /** * Player _id */ + @Expose() _id: string; /** * Player's name */ + @Expose() name: string; /** @@ -16,8 +19,17 @@ export default class OnlinePlayer { */ status: OnlinePlayerStatus; + /** + * The version of the game client. + * Used to isolate matchmaking pools and prevent desyncs between incompatible builds. + * @example "1.0.4-beta" + */ + @Expose() + client_version: string; + /** * Any additional information online player has */ + @Expose() additional?: Additional; } From 4f427a86457b8f21216a8129c44240322d137f30 Mon Sep 17 00:00:00 2001 From: CapoMK25 Date: Mon, 9 Feb 2026 21:31:14 +0200 Subject: [PATCH 2/5] Changed the test functionality for the game version check --- .../handleNewClanMessage.test.ts | 2 +- .../addPlayerOnline.test.ts | 25 ++++++++++++------- .../getOnlinePlayerById.test.ts | 1 + 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/__tests__/chat/clanChatService/handleNewClanMessage.test.ts b/src/__tests__/chat/clanChatService/handleNewClanMessage.test.ts index d2b3b7fe7..5d4a9e706 100644 --- a/src/__tests__/chat/clanChatService/handleNewClanMessage.test.ts +++ b/src/__tests__/chat/clanChatService/handleNewClanMessage.test.ts @@ -66,4 +66,4 @@ describe('ClanChatService.handleNewClanMessage() test suite', () => { undefined, ); }); -}); \ No newline at end of file +}); diff --git a/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts b/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts index b932d9e8f..fbaa1c46d 100644 --- a/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts +++ b/src/__tests__/onlinePlayers/OnlinePlayersService/addPlayerOnline.test.ts @@ -70,22 +70,29 @@ describe('OnlinePlayersService.addPlayerOnline() test suite', () => { .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 builtPlayer = onlinePlayerBuilder + .setId(playerToAdd.player_id) + .setName(player1.name) + .setStatus(playerToAdd.status) + .setAdditional({ queueNumber: 0 }) + .build(); + + (builtPlayer as any).client_version = '0.0.0'; + + const expectedPayload = JSON.stringify(builtPlayer); const redisSet = jest.spyOn(redisService, 'set'); await service.addPlayerOnline({ player_id: player1._id, status: OnlinePlayerStatus.BATTLE_WAIT, + client_version: '0.0.0', }); - expect(redisSet).toHaveBeenCalledWith(expectedKey, expectedPayload, 90); + const actualPayloadSent = redisSet.mock.calls[2][1]; + + expect(JSON.parse(actualPayloadSent)).toEqual(JSON.parse(expectedPayload)); + + expect(redisSet).toHaveBeenCalledWith(expectedKey, expect.any(String), 90); }); }); diff --git a/src/__tests__/onlinePlayers/OnlinePlayersService/getOnlinePlayerById.test.ts b/src/__tests__/onlinePlayers/OnlinePlayersService/getOnlinePlayerById.test.ts index 21c8e6876..4c597e001 100644 --- a/src/__tests__/onlinePlayers/OnlinePlayersService/getOnlinePlayerById.test.ts +++ b/src/__tests__/onlinePlayers/OnlinePlayersService/getOnlinePlayerById.test.ts @@ -37,6 +37,7 @@ describe('OnlinePlayersService.getOnlinePlayerById() test suite', () => { _id: player1._id, name: player1.name, status: OnlinePlayerStatus.UI, + client_version: '0.0.0', }; jest .spyOn(redisService, 'get') From 7f5e8ea8b8785d90cce8e147c808107365cca240 Mon Sep 17 00:00:00 2001 From: CapoMK25 Date: Mon, 9 Feb 2026 21:41:12 +0200 Subject: [PATCH 3/5] chore: trigger ci From b426151063fcd3c3a1b71465a213598062b93244 Mon Sep 17 00:00:00 2001 From: CapoMK25 Date: Tue, 10 Feb 2026 10:25:46 +0200 Subject: [PATCH 4/5] linter changes --- src/chat/chat.gateway.ts | 29 +++++----- src/chat/service/baseChat.service.ts | 4 +- src/chat/service/globalChat.service.ts | 2 - src/clanShop/clanShop.service.ts | 10 ++-- src/clanShop/clanShopVoting.processor.ts | 1 - src/dailyTasks/dailyTasks.service.ts | 54 ++++++++++--------- .../uiDailyTasks/uiDailyTasks.service.ts | 25 ++++----- src/gameEventsHandler/clanEventHandler.ts | 5 +- .../battleQueue/battleQueue.controller.ts | 6 +-- .../battleQueue/battleQueue.service.ts | 7 ++- src/onlinePlayers/onlinePlayers.service.ts | 6 +-- src/onlinePlayers/payload/AddOnlinePlayer.ts | 1 - .../playerRewarder/playerRewarder.service.ts | 5 +- 13 files changed, 76 insertions(+), 79 deletions(-) diff --git a/src/chat/chat.gateway.ts b/src/chat/chat.gateway.ts index 3cb118934..9e0eafd6d 100644 --- a/src/chat/chat.gateway.ts +++ b/src/chat/chat.gateway.ts @@ -85,7 +85,6 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { @MessageBody() message: WsMessageBodyDto, @ConnectedSocket() client: WebSocketUser, ): Promise> { - const [session, initErrors] = await initializeSession(this.connection); if (!session) return [null, initErrors]; @@ -111,15 +110,13 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { @MessageBody() reaction: AddReactionDto, @ConnectedSocket() client: WebSocketUser, ): Promise> { - const [session, initErrors] = await initializeSession(this.connection); if (!session) return [null, initErrors]; - const [updatedMessage, error] = await this.clanChatService.handleNewClanReaction( - client, - reaction, - { session }, - ); + const [updatedMessage, error] = + await this.clanChatService.handleNewClanReaction(client, reaction, { + session, + }); if (error) return cancelTransaction(session, error); @@ -135,11 +132,10 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { const [session, initErrors] = await initializeSession(this.connection); if (!session) return [null, initErrors]; - const [newMessage, error] = await this.globalChatService.handleNewGlobalMessage( - message, - client, - { session }, - ); + const [newMessage, error] = + await this.globalChatService.handleNewGlobalMessage(message, client, { + session, + }); if (error) return cancelTransaction(session, error); @@ -160,11 +156,10 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { const [session, initErrors] = await initializeSession(this.connection); if (!session) return [null, initErrors]; - const [updatedMessage, error] = await this.globalChatService.handleNewGlobalReaction( - client, - reaction, - { session }, - ); + const [updatedMessage, error] = + await this.globalChatService.handleNewGlobalReaction(client, reaction, { + session, + }); if (error) return cancelTransaction(session, error); diff --git a/src/chat/service/baseChat.service.ts b/src/chat/service/baseChat.service.ts index d526e6641..82b8d74f4 100644 --- a/src/chat/service/baseChat.service.ts +++ b/src/chat/service/baseChat.service.ts @@ -37,7 +37,6 @@ export abstract class BaseChatService { recipients: Set, options?: TIServiceCreateOneOptions, ): Promise> { - const errors = await validate(chatMessage); if (errors.length > 0) { @@ -107,7 +106,7 @@ export abstract class BaseChatService { /** * Handles reaction to a chat message. - * + * * Adds the reaction to the message in DB * and broadcasts the updated message. * @param client - The WebSocket user who sent the message. @@ -122,7 +121,6 @@ export abstract class BaseChatService { recipients: Set, options?: TIServiceUpdateByIdOptions, ): Promise> { - const [updatedMessage, error] = await this.chatService.addReaction( reaction.message_id, client.user.name, diff --git a/src/chat/service/globalChat.service.ts b/src/chat/service/globalChat.service.ts index af9070a60..d23f3fe32 100644 --- a/src/chat/service/globalChat.service.ts +++ b/src/chat/service/globalChat.service.ts @@ -53,7 +53,6 @@ export class GlobalChatService extends BaseChatService { client: WebSocketUser, options?: TIServiceCreateOneOptions, ): Promise> { - const chatMessage: CreateChatMessageDto = { type: ChatType.GLOBAL, sender_id: client.user.playerId, @@ -84,7 +83,6 @@ export class GlobalChatService extends BaseChatService { reaction: AddReactionDto, options?: TIServiceUpdateByIdOptions, ): Promise> { - return this.handleNewReaction( client, reaction, diff --git a/src/clanShop/clanShop.service.ts b/src/clanShop/clanShop.service.ts index 1def1e4aa..08210a26b 100644 --- a/src/clanShop/clanShop.service.ts +++ b/src/clanShop/clanShop.service.ts @@ -28,7 +28,6 @@ import { IServiceReturn } from '../common/service/basicService/IService'; @Injectable() export class ClanShopService { - constructor( private readonly clanService: ClanService, private readonly votingService: VotingService, @@ -43,7 +42,7 @@ export class ClanShopService { * This method performs several operations including validating the clan's funds, * reserving the required amount, initiating a voting process, and scheduling a voting check job. * All operations are executed within a transaction to ensure consistency. - * + * * @param playerId - The unique identifier of the player attempting to buy the item. * @param clanId - The unique identifier of the clan associated with the purchase. * @param item - The item being purchased, including its properties such as price. @@ -135,11 +134,12 @@ export class ClanShopService { * 4. Commits the transaction and ends the session. * * If any error occurs during the process, the transaction is canceled, and the session is ended. - * + * * @returns A promise that resolves to a boolean indicating the success of the operation or an error if any step fails. */ - async checkVotingOnExpire(data: VotingQueueParams): Promise> { - + async checkVotingOnExpire( + data: VotingQueueParams, + ): Promise> { const { voting, price, clanId, stockId } = data; const [session, sessionError] = await initializeSession(this.connection); if (sessionError) return [null, sessionError]; diff --git a/src/clanShop/clanShopVoting.processor.ts b/src/clanShop/clanShopVoting.processor.ts index d3c06a142..ef9a88370 100644 --- a/src/clanShop/clanShopVoting.processor.ts +++ b/src/clanShop/clanShopVoting.processor.ts @@ -6,7 +6,6 @@ import { VotingQueueName } from '../voting/enum/VotingQueue.enum'; @Processor(VotingQueueName.CLAN_SHOP) export class ClanShopVotingProcessor extends WorkerHost { - constructor(private readonly clanShopService: ClanShopService) { super(); } diff --git a/src/dailyTasks/dailyTasks.service.ts b/src/dailyTasks/dailyTasks.service.ts index 1b92a83ba..2bb91f1f6 100644 --- a/src/dailyTasks/dailyTasks.service.ts +++ b/src/dailyTasks/dailyTasks.service.ts @@ -22,7 +22,7 @@ import { ClanRewarder } from '../rewarder/clanRewarder/clanRewarder.service'; import { cancelTransaction, endTransaction, - initializeSession + initializeSession, } from '../common/function/Transactions'; @Injectable() @@ -84,16 +84,13 @@ export class DailyTasksService { * @param clanId - The ID of the clan to which the task belongs. * @returns DailyTask with the given player _id on succeed or an array of ServiceErrors if any occurred. */ - async reserveTask( - playerId: string, - taskId: string, - clanId: string, - ) { + async reserveTask(playerId: string, taskId: string, clanId: string) { const [task, error] = await this.basicService.readOne({ filter: { _id: taskId, clan_id: clanId }, }); if (error) throw error; - if (task.player_id && task.player_id !== playerId) return [null, taskReservedError]; + if (task.player_id && task.player_id !== playerId) + return [null, taskReservedError]; const [session, initErrors] = await initializeSession(this.connection); if (initErrors) return [null, initErrors]; @@ -128,10 +125,7 @@ export class DailyTasksService { * @param session - Optional MongoDB client session for transaction support. * @returns A promise that resolves with the result of the update operation. */ - async unreserveTask( - playerId: string, - session?: ClientSession, - ) { + async unreserveTask(playerId: string, session?: ClientSession) { return this.basicService.updateOne( { $unset: { player_id: '', startedAt: '' } }, { filter: { player_id: playerId }, session }, @@ -198,7 +192,12 @@ export class DailyTasksService { task.amountLeft--; if (task.amountLeft <= 0) { - await this.deleteTask(task._id.toString(), task.clan_id, playerId, session); + await this.deleteTask( + task._id.toString(), + task.clan_id, + playerId, + session, + ); this.notifier.taskCompleted(playerId, task); } else { const [_, updateError] = await this.basicService.updateOne(task, { @@ -282,7 +281,6 @@ export class DailyTasksService { serverTaskName: ServerTaskName; needsClanReward?: boolean; }): Promise> { - const [session, initErrors] = await initializeSession(this.connection); if (!session) return [null, initErrors]; @@ -295,22 +293,26 @@ export class DailyTasksService { if (updateErrors) return cancelTransaction(session, updateErrors); if (task.amountLeft <= 0) { - const [_, playerRewardErrors] = await this.playerRewarder.rewardForPlayerTask( - payload.playerId, - task.points, - session, - ); - - if (playerRewardErrors) return cancelTransaction(session, playerRewardErrors); - - if (payload.needsClanReward ?? false) { - const [_, clanRewardErrors] = await this.clanRewarder.rewardClanForPlayerTask( - task.clan_id, + const [_, playerRewardErrors] = + await this.playerRewarder.rewardForPlayerTask( + payload.playerId, task.points, - task.coins, session, ); - if (clanRewardErrors) return cancelTransaction(session, clanRewardErrors); + + if (playerRewardErrors) + return cancelTransaction(session, playerRewardErrors); + + if (payload.needsClanReward ?? false) { + const [_, clanRewardErrors] = + await this.clanRewarder.rewardClanForPlayerTask( + task.clan_id, + task.points, + task.coins, + session, + ); + if (clanRewardErrors) + return cancelTransaction(session, clanRewardErrors); } } return endTransaction(session, task); diff --git a/src/dailyTasks/uiDailyTasks/uiDailyTasks.service.ts b/src/dailyTasks/uiDailyTasks/uiDailyTasks.service.ts index 13b316b4e..6b7a3fd60 100644 --- a/src/dailyTasks/uiDailyTasks/uiDailyTasks.service.ts +++ b/src/dailyTasks/uiDailyTasks/uiDailyTasks.service.ts @@ -4,15 +4,19 @@ import { DailyTask } from '../dailyTasks.schema'; import { Connection, Model } from 'mongoose'; import BasicService from '../../common/service/basicService/BasicService'; import { UIDailyTaskData, uiDailyTasks } from './uiDailyTasks'; -import { IServiceReturn, TIServiceDeleteByIdOptions, TIServiceUpdateByIdOptions } from '../../common/service/basicService/IService'; +import { + IServiceReturn, + TIServiceDeleteByIdOptions, + TIServiceUpdateByIdOptions, +} from '../../common/service/basicService/IService'; import ServiceError from '../../common/service/basicService/ServiceError'; import { SEReason } from '../../common/service/basicService/SEReason'; import { DailyTaskDto } from '../dto/dailyTask.dto'; import { UITaskName } from '../enum/uiTaskName.enum'; -import { +import { cancelTransaction, - endTransaction, - initializeSession + endTransaction, + initializeSession, } from '../../common/function/Transactions'; @Injectable() @@ -85,10 +89,9 @@ export default class UIDailyTasksService { if (!session) return [null, initErrors]; if (isTaskCompleted) { - const [_isSuccess, errors] = await this.handleTaskCompletion( - task, - { session }, - ); + const [_isSuccess, errors] = await this.handleTaskCompletion(task, { + session, + }); if (errors) return cancelTransaction(session, errors); const [_, endErrors] = await endTransaction(session); @@ -100,7 +103,7 @@ export default class UIDailyTasksService { const [_isSuccess, updateErrors] = await this.handleTaskAmountUpdate( task, amount, - { session } + { session }, ); if (updateErrors) return cancelTransaction(session, updateErrors); @@ -161,7 +164,6 @@ export default class UIDailyTasksService { decreaseAmount: number, options?: TIServiceUpdateByIdOptions, ): Promise> { - const updatedAmount = task.amountLeft - decreaseAmount; const [_, updateErrors] = await this.basicService.updateOneById( @@ -186,9 +188,8 @@ export default class UIDailyTasksService { */ private async handleTaskCompletion( task: DailyTask, - options?: TIServiceDeleteByIdOptions + options?: TIServiceDeleteByIdOptions, ): Promise> { - const [, deletionErrors] = await this.basicService.deleteOneById( task._id.toString(), options, diff --git a/src/gameEventsHandler/clanEventHandler.ts b/src/gameEventsHandler/clanEventHandler.ts index 0e4be8a5c..c1bfdaed8 100644 --- a/src/gameEventsHandler/clanEventHandler.ts +++ b/src/gameEventsHandler/clanEventHandler.ts @@ -21,9 +21,10 @@ export class ClanEventHandler { async handlePlayerTask(player_id: string): Promise> { try { - const [taskUpdate, errors] = await this.tasksService.updateTask(player_id); + const [taskUpdate, errors] = + await this.tasksService.updateTask(player_id); if (errors) return [null, errors]; - + return this.handleClanAndPlayerReward(player_id, taskUpdate); } catch ( // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/src/onlinePlayers/battleQueue/battleQueue.controller.ts b/src/onlinePlayers/battleQueue/battleQueue.controller.ts index a53da64c9..37de80c17 100644 --- a/src/onlinePlayers/battleQueue/battleQueue.controller.ts +++ b/src/onlinePlayers/battleQueue/battleQueue.controller.ts @@ -31,10 +31,10 @@ export class BattleQueueController { @UniformResponse(null, OnlinePlayerDto) async getBattleQueue(@Query('version') clientVersion: string) { const queuePlayers = await this.onlinePlayersService.getOnlinePlayers({ - filter: { + filter: { status: [OnlinePlayerStatus.BATTLE_WAIT], - client_version: clientVersion - } + client_version: clientVersion, + }, }); return this.service.sortPlayersByQueueNumber(queuePlayers); } diff --git a/src/onlinePlayers/battleQueue/battleQueue.service.ts b/src/onlinePlayers/battleQueue/battleQueue.service.ts index 7cc201386..47a046689 100644 --- a/src/onlinePlayers/battleQueue/battleQueue.service.ts +++ b/src/onlinePlayers/battleQueue/battleQueue.service.ts @@ -63,7 +63,9 @@ export class BattleQueueService { if (filterErrors) return [null, filterErrors]; const firstVersion = validPlayers[0].client_version; - const hasVersionMismatch = validPlayers.some(p => p.client_version !== firstVersion); + const hasVersionMismatch = validPlayers.some( + (p) => p.client_version !== firstVersion, + ); if (hasVersionMismatch) { return [ @@ -71,7 +73,8 @@ export class BattleQueueService { [ new ServiceError({ reason: SEReason.VALIDATION, - message: 'Version mismatch: Players in queue have different client versions', + message: + 'Version mismatch: Players in queue have different client versions', }), ], ]; diff --git a/src/onlinePlayers/onlinePlayers.service.ts b/src/onlinePlayers/onlinePlayers.service.ts index 80ebf8aac..ba8669049 100644 --- a/src/onlinePlayers/onlinePlayers.service.ts +++ b/src/onlinePlayers/onlinePlayers.service.ts @@ -70,9 +70,9 @@ export class OnlinePlayersService { * @returns Array of OnlinePlayers or empty array if nothing found */ async getOnlinePlayers(options?: { - filter?: { - status?: OnlinePlayerStatus[]; - client_version?: string; + filter?: { + status?: OnlinePlayerStatus[]; + client_version?: string; }; }): Promise { const players = await this.redisService.getValuesByKeyPattern( diff --git a/src/onlinePlayers/payload/AddOnlinePlayer.ts b/src/onlinePlayers/payload/AddOnlinePlayer.ts index c05f6eac0..4df708fb9 100644 --- a/src/onlinePlayers/payload/AddOnlinePlayer.ts +++ b/src/onlinePlayers/payload/AddOnlinePlayer.ts @@ -24,5 +24,4 @@ export default class AddOnlinePlayer { */ @IsString() client_version: string; - } diff --git a/src/rewarder/playerRewarder/playerRewarder.service.ts b/src/rewarder/playerRewarder/playerRewarder.service.ts index 4030f9e1a..1bd33e969 100644 --- a/src/rewarder/playerRewarder/playerRewarder.service.ts +++ b/src/rewarder/playerRewarder/playerRewarder.service.ts @@ -91,9 +91,10 @@ export class PlayerRewarder { session?: ClientSession, ): Promise> { const [_, errors] = await this.playerService.updateOneById( - player_id, + player_id, { $inc: { points } }, - { session }); + { session }, + ); if (errors) return [null, errors]; From fe09cea5d868c65d6f72c438c3d6da3354ba332c Mon Sep 17 00:00:00 2001 From: CapoMK25 Date: Sun, 8 Mar 2026 20:04:43 +0200 Subject: [PATCH 5/5] SEReason addition --- src/common/service/basicService/SEReason.ts | 5 +++++ src/onlinePlayers/battleQueue/battleQueue.service.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/common/service/basicService/SEReason.ts b/src/common/service/basicService/SEReason.ts index 6b9f3c335..11c45c05e 100644 --- a/src/common/service/basicService/SEReason.ts +++ b/src/common/service/basicService/SEReason.ts @@ -68,6 +68,11 @@ export enum SEReason { */ VALIDATION = 'VALIDATION', + /** + * Provided game versions are incompatible with each other + */ + VERSION_MISMATCH = 'VERSION_MISMATCH', + /** * The error is unexpected */ diff --git a/src/onlinePlayers/battleQueue/battleQueue.service.ts b/src/onlinePlayers/battleQueue/battleQueue.service.ts index 47a046689..d1fe70b12 100644 --- a/src/onlinePlayers/battleQueue/battleQueue.service.ts +++ b/src/onlinePlayers/battleQueue/battleQueue.service.ts @@ -72,7 +72,7 @@ export class BattleQueueService { null, [ new ServiceError({ - reason: SEReason.VALIDATION, + reason: SEReason.VERSION_MISMATCH, message: 'Version mismatch: Players in queue have different client versions', }),