Versus
This commit is contained in:
parent
456cdcb4aa
commit
ac9116e138
14 changed files with 249 additions and 36 deletions
|
|
@ -2,6 +2,8 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { GameController } from './game.controller';
|
import { GameController } from './game.controller';
|
||||||
import {GameService} from "./game.service";
|
import {GameService} from "./game.service";
|
||||||
import {GameServiceMock} from "../mocks/game-service.mock";
|
import {GameServiceMock} from "../mocks/game-service.mock";
|
||||||
|
import {VersusService} from "./versus/versus.service";
|
||||||
|
import {VersusServiceMock} from "../mocks/versus-service.mock";
|
||||||
|
|
||||||
describe('GameController', () => {
|
describe('GameController', () => {
|
||||||
let controller: GameController;
|
let controller: GameController;
|
||||||
|
|
@ -11,6 +13,7 @@ describe('GameController', () => {
|
||||||
controllers: [GameController],
|
controllers: [GameController],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: GameService, useValue: GameServiceMock },
|
{ provide: GameService, useValue: GameServiceMock },
|
||||||
|
{ provide: VersusService, useValue: VersusServiceMock },
|
||||||
]
|
]
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import { Controller, Get, Logger, Param, Post } from '@nestjs/common';
|
import { Controller, Get, Logger, Param, Post } from '@nestjs/common';
|
||||||
import { GameService } from './game.service';
|
import { GameService } from './game.service';
|
||||||
|
import {VersusService} from "./versus/versus.service";
|
||||||
|
|
||||||
@Controller('game')
|
@Controller('game')
|
||||||
export class GameController {
|
export class GameController {
|
||||||
private readonly logger = new Logger(GameController.name);
|
private readonly logger = new Logger(GameController.name);
|
||||||
constructor(private gameService: GameService) {
|
constructor(private gameService: GameService, private versusService: VersusService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id/complete')
|
@Post(':id/complete')
|
||||||
|
|
@ -32,14 +33,16 @@ export class GameController {
|
||||||
return this.gameService.playExtraCards();
|
return this.gameService.playExtraCards();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('simulate-versus')
|
|
||||||
async SimulateVersus() {
|
|
||||||
this.logger.verbose('[SimulateVersus] enter');
|
|
||||||
return this.gameService.simulateVersus();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get('state-details')
|
@Get('state-details')
|
||||||
async getStateDetails() {
|
async getStateDetails() {
|
||||||
return this.gameService.getStateDetails();
|
return this.gameService.getStateDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('clear-queue')
|
||||||
|
async clearQueue() {
|
||||||
|
this.logger.warn(`[clearQueue] enter`);
|
||||||
|
await this.gameService.clearGameQueue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@ import {ConfigService} from "@nestjs/config";
|
||||||
import {SelectTargetPlayerHandler} from "./comand-handlers/select-target-player.handler";
|
import {SelectTargetPlayerHandler} from "./comand-handlers/select-target-player.handler";
|
||||||
import {SendBetweenRoundsActionsHandler} from "../guests/command/send-between-rounds-actions.command";
|
import {SendBetweenRoundsActionsHandler} from "../guests/command/send-between-rounds-actions.command";
|
||||||
import {GuestsModule} from "../guests/guests.module";
|
import {GuestsModule} from "../guests/guests.module";
|
||||||
|
import { VersusService } from './versus/versus.service';
|
||||||
|
import { VersusController } from './versus/versus.controller';
|
||||||
|
import {Versus, VersusSchema} from "../schemas/versus.schema";
|
||||||
|
|
||||||
|
|
||||||
const eventHandlers = [
|
const eventHandlers = [
|
||||||
|
|
@ -42,11 +45,12 @@ const commandHandlers = [
|
||||||
CqrsModule,
|
CqrsModule,
|
||||||
MongooseModule.forFeature([
|
MongooseModule.forFeature([
|
||||||
{ name: GameQueue.name, schema: GameQueueSchema },
|
{ name: GameQueue.name, schema: GameQueueSchema },
|
||||||
|
{ name: Versus.name, schema: VersusSchema }
|
||||||
]),
|
]),
|
||||||
forwardRef(() => GuestsModule)
|
forwardRef(() => GuestsModule)
|
||||||
],
|
],
|
||||||
providers: [GameService, ConfigService, ...eventHandlers, ...commandHandlers],
|
providers: [GameService, ConfigService, ...eventHandlers, ...commandHandlers, VersusService],
|
||||||
exports: [GameService],
|
exports: [GameService],
|
||||||
controllers: [GameController],
|
controllers: [GameController, VersusController],
|
||||||
})
|
})
|
||||||
export class GameModule {}
|
export class GameModule {}
|
||||||
|
|
|
||||||
|
|
@ -116,34 +116,12 @@ export class GameService implements OnApplicationBootstrap{
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async simulateVersus() {
|
|
||||||
const guests = (await this.guestService.findAll()).slice(0,2).map((guest) => {
|
|
||||||
return {
|
|
||||||
id: guest.telegramId,
|
|
||||||
name: guest.name,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(guests.length < 2) {
|
|
||||||
throw new Error("Can't simulate, in db less than 2 players")
|
|
||||||
}
|
|
||||||
await this.beginVersus(guests[0].id, guests[1].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async beginVersus(player1: number, player2: number) {
|
|
||||||
await this.sharedService.setConfig('current_action', JSON.stringify({
|
|
||||||
action:'versus',
|
|
||||||
data: {
|
|
||||||
player1: player1,
|
|
||||||
player2: player2,
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
this.sharedService.sendSocketNotificationToAllClients(
|
|
||||||
SocketEvents.BEGIN_VERSUS,
|
|
||||||
{ player1, player2 }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async getStateDetails() {
|
async getStateDetails() {
|
||||||
return await this.sharedService.getConfig('current_action') || null;
|
return await this.sharedService.getConfig('current_action') || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearGameQueue() {
|
||||||
|
await this.gameQueueModel.deleteMany({}).exec();
|
||||||
|
return { result: true };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
src/game/versus/versus.controller.spec.ts
Normal file
23
src/game/versus/versus.controller.spec.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { VersusController } from './versus.controller';
|
||||||
|
import {VersusService} from "./versus.service";
|
||||||
|
import {VersusServiceMock} from "../../mocks/versus-service.mock";
|
||||||
|
|
||||||
|
describe('VersusController', () => {
|
||||||
|
let controller: VersusController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [VersusController],
|
||||||
|
providers: [
|
||||||
|
{ provide: VersusService, useValue: VersusServiceMock },
|
||||||
|
]
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<VersusController>(VersusController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
37
src/game/versus/versus.controller.ts
Normal file
37
src/game/versus/versus.controller.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import {Body, Controller, Get, Logger, Post} from '@nestjs/common';
|
||||||
|
import {VersusService} from "./versus.service";
|
||||||
|
import {VersusDto} from "./versus.types";
|
||||||
|
|
||||||
|
@Controller('versus')
|
||||||
|
export class VersusController {
|
||||||
|
private logger = new Logger(VersusController.name);
|
||||||
|
constructor(private versusService: VersusService) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('simulate-versus')
|
||||||
|
async SimulateVersus() {
|
||||||
|
this.logger.verbose('[SimulateVersus] enter');
|
||||||
|
return this.versusService.simulateVersus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('import')
|
||||||
|
async Import(@Body() data: VersusDto[]) {
|
||||||
|
return await this.versusService.importVersus(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
async GetVersusTask() {
|
||||||
|
return await this.versusService.getVersusTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('complete')
|
||||||
|
async Completed(@Body() payload: { winner: number }) {
|
||||||
|
return await this.versusService.complete(payload.winner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('reset-all')
|
||||||
|
async markAllUncompleted() {
|
||||||
|
return await this.versusService.markAllAsUncompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/game/versus/versus.service.spec.ts
Normal file
31
src/game/versus/versus.service.spec.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { VersusService } from './versus.service';
|
||||||
|
import {GuestsService} from "../../guests/guests.service";
|
||||||
|
import {GuestsServiceMock} from "../../mocks/guests-service.mock";
|
||||||
|
import {SharedService} from "../../shared/shared.service";
|
||||||
|
import {getModelToken} from "@nestjs/mongoose";
|
||||||
|
import {Versus} from "../../schemas/versus.schema";
|
||||||
|
import {Model} from "mongoose";
|
||||||
|
import {CommandBus} from "@nestjs/cqrs";
|
||||||
|
|
||||||
|
describe('VersusService', () => {
|
||||||
|
let service: VersusService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
VersusService,
|
||||||
|
{ provide: GuestsService, useValue: GuestsServiceMock },
|
||||||
|
{ provide: SharedService, useValue: SharedService },
|
||||||
|
{ provide: getModelToken(Versus.name), useValue: Model },
|
||||||
|
{ provide: CommandBus, useValue: CommandBus },
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<VersusService>(VersusService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
98
src/game/versus/versus.service.ts
Normal file
98
src/game/versus/versus.service.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {SocketEvents} from "../../shared/events.consts";
|
||||||
|
import {GuestsService} from "../../guests/guests.service";
|
||||||
|
import {SharedService} from "../../shared/shared.service";
|
||||||
|
import {InjectModel} from "@nestjs/mongoose";
|
||||||
|
import {Versus, VersusDocument} from "../../schemas/versus.schema";
|
||||||
|
import {Model} from "mongoose";
|
||||||
|
import {VersusDto} from "./versus.types";
|
||||||
|
import {CommandBus} from "@nestjs/cqrs";
|
||||||
|
import {IncreasePlayerScoreCommand} from "../../guests/command/increase-player-score.command";
|
||||||
|
import {IncreasePlayerWinningRateCommand} from "../commands/increase-player-winning-rate.command";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class VersusService {
|
||||||
|
static configKeyCurrentAction = 'current_action';
|
||||||
|
static configKeyActiveVersus = 'active_versus';
|
||||||
|
constructor(
|
||||||
|
private guestService: GuestsService,
|
||||||
|
private sharedService: SharedService,
|
||||||
|
@InjectModel(Versus.name) private versusModel: Model<VersusDocument>,
|
||||||
|
private cmdBus: CommandBus,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
async simulateVersus() {
|
||||||
|
const guests = (await this.guestService.findAll()).slice(0,2).map((guest) => {
|
||||||
|
return {
|
||||||
|
id: guest.telegramId,
|
||||||
|
name: guest.name,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(guests.length < 2) {
|
||||||
|
throw new Error("Can't simulate, in db less than 2 players")
|
||||||
|
}
|
||||||
|
await this.beginVersus(guests[0].id, guests[1].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async beginVersus(player1: number, player2: number) {
|
||||||
|
const [p1data,p2data] = await Promise.all([this.guestService.findById(player1), this.guestService.findById(player2)]);
|
||||||
|
await this.sharedService.setConfig(VersusService.configKeyCurrentAction, JSON.stringify({
|
||||||
|
action:'versus',
|
||||||
|
data: {
|
||||||
|
player1: player1,
|
||||||
|
player2: player2,
|
||||||
|
player1name: p1data.name,
|
||||||
|
player2name: p2data.name,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.sharedService.sendSocketNotificationToAllClients(
|
||||||
|
SocketEvents.BEGIN_VERSUS,
|
||||||
|
{ player1, player2, player1name: p1data.name, player2name: p2data.name }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async importVersus(data: VersusDto[]) {
|
||||||
|
data.map(async (record) => {
|
||||||
|
const item = new this.versusModel({
|
||||||
|
...record
|
||||||
|
});
|
||||||
|
await item.save();
|
||||||
|
});
|
||||||
|
return { result: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVersusTask() {
|
||||||
|
const rand = await this.versusModel
|
||||||
|
.aggregate([{ $match: { completed: false } }, { $sample: { size: 1 } }])
|
||||||
|
.exec();
|
||||||
|
// @TODO check who win with telegram
|
||||||
|
|
||||||
|
const item = await this.versusModel.findOne({ _id: rand[0]._id }).exec();
|
||||||
|
await this.sharedService.setConfig(VersusService.configKeyActiveVersus, item.id);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
async markAllAsUncompleted() {
|
||||||
|
const versuses = await this.versusModel.find().exec();
|
||||||
|
versuses.map(async (versus) => {
|
||||||
|
versus.completed = false;
|
||||||
|
await versus.save();
|
||||||
|
});
|
||||||
|
return { result: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async complete(winner: number) {
|
||||||
|
const activeVersus = await this.sharedService.getConfig(VersusService.configKeyActiveVersus);
|
||||||
|
const item = await this.versusModel.findOne({ _id: activeVersus.value }).exec();
|
||||||
|
item.completed = true;
|
||||||
|
await item.save();
|
||||||
|
await this.cmdBus.execute(new IncreasePlayerScoreCommand(winner, 2));
|
||||||
|
await this.cmdBus.execute(new IncreasePlayerWinningRateCommand(winner, 30));
|
||||||
|
await this.sharedService.setConfig(VersusService.configKeyCurrentAction, '');
|
||||||
|
this.sharedService.sendSocketNotificationToAllClients(
|
||||||
|
SocketEvents.END_VERSUS,
|
||||||
|
{ winner: winner }
|
||||||
|
)
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/game/versus/versus.types.d.ts
vendored
Normal file
6
src/game/versus/versus.types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface VersusDto {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
completed: boolean;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
Get, Param, Res
|
Get, Param, Post, Res
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { GuestsService } from './guests.service';
|
import { GuestsService } from './guests.service';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
|
|
@ -42,4 +42,9 @@ export class GuestsController {
|
||||||
// this.echoService.enterQuiz(u.chatId);
|
// this.echoService.enterQuiz(u.chatId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@Post('reset-score')
|
||||||
|
async resetAllPlayersScore() {
|
||||||
|
await this.guestService.resetPlayersScore();
|
||||||
|
return { result: true };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
src/mocks/versus-service.mock.ts
Normal file
3
src/mocks/versus-service.mock.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export class VersusServiceMock {
|
||||||
|
|
||||||
|
}
|
||||||
15
src/schemas/versus.schema.ts
Normal file
15
src/schemas/versus.schema.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import {Prop, Schema, SchemaFactory} from "@nestjs/mongoose";
|
||||||
|
import {Document} from "mongoose";
|
||||||
|
|
||||||
|
@Schema()
|
||||||
|
export class Versus {
|
||||||
|
@Prop()
|
||||||
|
text: string;
|
||||||
|
@Prop({ default: false})
|
||||||
|
completed: boolean;
|
||||||
|
@Prop()
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VersusDocument = Versus & Document;
|
||||||
|
export const VersusSchema = SchemaFactory.createForClass(Versus);
|
||||||
|
|
@ -14,4 +14,5 @@ export enum SocketEvents {
|
||||||
NOTIFICATION = 'notification',
|
NOTIFICATION = 'notification',
|
||||||
FEATURE_FLAG_CHANGED = 'feature_flag_changed',
|
FEATURE_FLAG_CHANGED = 'feature_flag_changed',
|
||||||
BEGIN_VERSUS = 'begin_versus',
|
BEGIN_VERSUS = 'begin_versus',
|
||||||
|
END_VERSUS = 'end_versus',
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,12 @@ export class SharedService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies all connected socket about changes
|
||||||
|
* @deprecated Use specific handlers in this class
|
||||||
|
* @param event
|
||||||
|
* @param payload
|
||||||
|
*/
|
||||||
sendSocketNotificationToAllClients(event: string, payload?: any) {
|
sendSocketNotificationToAllClients(event: string, payload?: any) {
|
||||||
this.logger.verbose(`Sending notification to client: ${event}, ${JSON.stringify(payload)}`);
|
this.logger.verbose(`Sending notification to client: ${event}, ${JSON.stringify(payload)}`);
|
||||||
this.socketGateway.notifyAllClients(event, payload);
|
this.socketGateway.notifyAllClients(event, payload);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue