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 {GameService} from "./game.service";
|
||||
import {GameServiceMock} from "../mocks/game-service.mock";
|
||||
import {VersusService} from "./versus/versus.service";
|
||||
import {VersusServiceMock} from "../mocks/versus-service.mock";
|
||||
|
||||
describe('GameController', () => {
|
||||
let controller: GameController;
|
||||
|
|
@ -11,6 +13,7 @@ describe('GameController', () => {
|
|||
controllers: [GameController],
|
||||
providers: [
|
||||
{ provide: GameService, useValue: GameServiceMock },
|
||||
{ provide: VersusService, useValue: VersusServiceMock },
|
||||
]
|
||||
}).compile();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { Controller, Get, Logger, Param, Post } from '@nestjs/common';
|
||||
import { GameService } from './game.service';
|
||||
import {VersusService} from "./versus/versus.service";
|
||||
|
||||
@Controller('game')
|
||||
export class GameController {
|
||||
private readonly logger = new Logger(GameController.name);
|
||||
constructor(private gameService: GameService) {
|
||||
constructor(private gameService: GameService, private versusService: VersusService) {
|
||||
}
|
||||
|
||||
@Post(':id/complete')
|
||||
|
|
@ -32,14 +33,16 @@ export class GameController {
|
|||
return this.gameService.playExtraCards();
|
||||
}
|
||||
|
||||
@Post('simulate-versus')
|
||||
async SimulateVersus() {
|
||||
this.logger.verbose('[SimulateVersus] enter');
|
||||
return this.gameService.simulateVersus();
|
||||
}
|
||||
|
||||
|
||||
@Get('state-details')
|
||||
async 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 {SendBetweenRoundsActionsHandler} from "../guests/command/send-between-rounds-actions.command";
|
||||
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 = [
|
||||
|
|
@ -42,11 +45,12 @@ const commandHandlers = [
|
|||
CqrsModule,
|
||||
MongooseModule.forFeature([
|
||||
{ name: GameQueue.name, schema: GameQueueSchema },
|
||||
{ name: Versus.name, schema: VersusSchema }
|
||||
]),
|
||||
forwardRef(() => GuestsModule)
|
||||
],
|
||||
providers: [GameService, ConfigService, ...eventHandlers, ...commandHandlers],
|
||||
providers: [GameService, ConfigService, ...eventHandlers, ...commandHandlers, VersusService],
|
||||
exports: [GameService],
|
||||
controllers: [GameController],
|
||||
controllers: [GameController, VersusController],
|
||||
})
|
||||
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() {
|
||||
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 {
|
||||
Controller,
|
||||
Get, Param, Res
|
||||
Get, Param, Post, Res
|
||||
} from "@nestjs/common";
|
||||
import { GuestsService } from './guests.service';
|
||||
import { Response } from 'express';
|
||||
|
|
@ -42,4 +42,9 @@ export class GuestsController {
|
|||
// 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',
|
||||
FEATURE_FLAG_CHANGED = 'feature_flag_changed',
|
||||
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) {
|
||||
this.logger.verbose(`Sending notification to client: ${event}, ${JSON.stringify(payload)}`);
|
||||
this.socketGateway.notifyAllClients(event, payload);
|
||||
|
|
|
|||
Loading…
Reference in a new issue