Skip to content

Commit

Permalink
fix(games): better player substitute events (#1218)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored Sep 13, 2021
1 parent f3118c4 commit 3865b18
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 21 deletions.
12 changes: 12 additions & 0 deletions e2e/player-substitutes-himself.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('Player substitutes himself (e2e)', () => {
let adminToken: string;
let gameId: string;
let playerSocket: Socket;
let substituteRequests: any[];

beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
Expand Down Expand Up @@ -49,6 +50,12 @@ describe('Player substitutes himself (e2e)', () => {
auth: { token: `Bearer ${playerToken}` },
},
);

substituteRequests = [];
playerSocket.on(
'substitute requests update',
(requests) => (substituteRequests = requests),
);
});

beforeAll(async () => {
Expand Down Expand Up @@ -147,6 +154,7 @@ describe('Player substitutes himself (e2e)', () => {
});

it('should substitute a player', async () => {
expect(substituteRequests.length).toEqual(0);
const player = await playersService.findBySteamId(players[1]);

// admin requests substitute
Expand All @@ -164,6 +172,8 @@ describe('Player substitutes himself (e2e)', () => {
expect(slot.status).toEqual('waiting for substitute');
});

expect(substituteRequests.length).toEqual(1);

// player substitutes himself
await new Promise<void>((resolve) => {
playerSocket.emit(
Expand All @@ -182,5 +192,7 @@ describe('Player substitutes himself (e2e)', () => {
const body = response.body;
expect(body.slots.every((s) => s.status === 'active')).toBe(true);
});

expect(substituteRequests.length).toEqual(0);
});
});
17 changes: 17 additions & 0 deletions src/events/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ export class Events {

readonly gameCreated = new Subject<{ game: Game }>();
readonly gameChanges = new Subject<{ game: Game; adminId?: string }>();

readonly substituteRequested = new Subject<{
gameId: string;
playerId: string;
}>();
readonly substituteCanceled = new Subject<{
gameId: string;
playerId: string;
}>();
readonly playerReplaced = new Subject<{
gameId: string;
replaceeId: string;
replacementId: string;
}>();
/**
* @deprecated
*/
readonly substituteRequestsChange = new Subject<void>();

readonly gameServerAdded = new Subject<{
Expand Down
47 changes: 31 additions & 16 deletions src/games/services/player-substitution.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,14 +197,17 @@ describe('PlayerSubstitutionService', () => {
);
});

it('should emit the substituteRequestsChange event', async () => {
let eventEmitted = false;
events.substituteRequestsChange.subscribe(() => {
eventEmitted = true;
it('should emit the substituteRequested event', async () => {
let emittedGameId: string;
let emittedPlayerId: string;
events.substituteRequested.subscribe(({ gameId, playerId }) => {
emittedGameId = gameId;
emittedPlayerId = playerId;
});

await service.substitutePlayer(mockGame.id, player1.id);
expect(eventEmitted).toBe(true);
expect(emittedGameId).toEqual(mockGame.id);
expect(emittedPlayerId).toEqual(player1.id);
});

it('should do nothing if the player is already marked', async () => {
Expand Down Expand Up @@ -294,14 +297,17 @@ describe('PlayerSubstitutionService', () => {
});
});

it('should emit the substituteRequestsChange event', async () => {
let eventEmitted = false;
events.substituteRequestsChange.subscribe(() => {
eventEmitted = true;
it('should emit the substituteCanceled event', async () => {
let emittedGameId: string;
let emittedPlayerId: string;
events.substituteCanceled.subscribe(({ gameId, playerId }) => {
emittedGameId = gameId;
emittedPlayerId = playerId;
});

await service.cancelSubstitutionRequest(mockGame.id, player1.id);
expect(eventEmitted).toBe(true);
expect(emittedGameId).toEqual(mockGame.id);
expect(emittedPlayerId).toEqual(player1.id);
});

it('should get rid of discord announcement', async () => {
Expand Down Expand Up @@ -438,14 +444,23 @@ describe('PlayerSubstitutionService', () => {
});
});

it('should emit the substituteRequestsChange event', async () => {
let eventEmitted = false;
events.substituteRequestsChange.subscribe(() => {
eventEmitted = true;
});
it('should emit the playerReplaced event', async () => {
let emittedGameId: string;
let emittedReplaceeId: string;
let emittedReplacementId: string;

events.playerReplaced.subscribe(
({ gameId, replaceeId, replacementId }) => {
emittedGameId = gameId;
emittedReplaceeId = replaceeId;
emittedReplacementId = replacementId;
},
);

await service.replacePlayer(mockGame.id, player1.id, player3.id);
expect(eventEmitted).toBe(true);
expect(emittedGameId).toEqual(mockGame.id);
expect(emittedReplaceeId).toEqual(player1.id);
expect(emittedReplacementId).toEqual(player3.id);
});

it('should reject if the replacement player does not exist', async () => {
Expand Down
29 changes: 24 additions & 5 deletions src/games/services/player-substitution.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Inject,
forwardRef,
Optional,
OnModuleInit,
} from '@nestjs/common';
import { GamesService } from './games.service';
import { PlayersService } from '@/players/services/players.service';
Expand All @@ -23,12 +24,13 @@ import { plainToClass } from 'class-transformer';
import { PlayerNotInThisGameError } from '../errors/player-not-in-this-game.error';
import { GameInWrongStateError } from '../errors/game-in-wrong-state.error';
import { WrongGameSlotStatusError } from '../errors/wrong-game-slot-status.error';
import { merge } from 'rxjs';

/**
* A service that handles player substitution logic.
*/
@Injectable()
export class PlayerSubstitutionService {
export class PlayerSubstitutionService implements OnModuleInit {
private logger = new Logger(PlayerSubstitutionService.name);
private discordNotifications = new Map<string, Message>(); // playerId <-> message pairs

Expand All @@ -50,6 +52,15 @@ export class PlayerSubstitutionService {
);
}

onModuleInit() {
// all substitute events trigger the substituteRequestsChange event
merge(
this.events.substituteRequested,
this.events.substituteCanceled,
this.events.playerReplaced,
).subscribe(() => this.events.substituteRequestsChange.next());
}

async substitutePlayer(gameId: string, playerId: string) {
let game = await this.gamesService.getById(gameId);
const slot = game.findPlayerSlot(playerId);
Expand Down Expand Up @@ -93,7 +104,7 @@ export class PlayerSubstitutionService {
);

this.events.gameChanges.next({ game });
this.events.substituteRequestsChange.next();
this.events.substituteRequested.next({ gameId, playerId });

const channel = this.discordService?.getPlayersChannel();
if (channel) {
Expand Down Expand Up @@ -171,7 +182,7 @@ export class PlayerSubstitutionService {
);

this.events.gameChanges.next({ game });
this.events.substituteRequestsChange.next();
this.events.substituteCanceled.next({ gameId, playerId });

const message = this.discordNotifications.get(playerId);
if (message) {
Expand Down Expand Up @@ -229,7 +240,11 @@ export class PlayerSubstitutionService {
);

this.events.gameChanges.next({ game });
this.events.substituteRequestsChange.next();
this.events.playerReplaced.next({
gameId,
replaceeId,
replacementId,
});
await this.deleteDiscordAnnouncement(replaceeId);
this.logger.verbose(`player has taken his own slot`);
return game;
Expand Down Expand Up @@ -273,7 +288,11 @@ export class PlayerSubstitutionService {
);

this.events.gameChanges.next({ game });
this.events.substituteRequestsChange.next();
this.events.playerReplaced.next({
gameId,
replaceeId,
replacementId,
});
this.queueService.kick(replacementId);

const replacee = await this.playersService.getById(replaceeId);
Expand Down

0 comments on commit 3865b18

Please sign in to comment.