Skip to content

Commit

Permalink
fix: remove 'replaced' game slot status (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
garrappachc authored Dec 20, 2024
1 parent 32dcfb5 commit 6c77c0e
Show file tree
Hide file tree
Showing 19 changed files with 166 additions and 246 deletions.
6 changes: 0 additions & 6 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,13 @@ export default defineConfig({
},

projects: [
{
name: 'setup database',
testMatch: /database\.setup\.ts/,
},
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
dependencies: ['setup database'],
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
dependencies: ['setup database'],
},

// {
Expand Down
1 change: 0 additions & 1 deletion src/database/models/game-slot.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { SteamId64 } from '../../shared/types/steam-id-64'
export enum SlotStatus {
active = 'active',
waitingForSubstitute = 'waiting for substitute',
replaced = 'replaced',
}

export enum PlayerConnectionStatus {
Expand Down
19 changes: 0 additions & 19 deletions src/games/find-player-slot.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/games/plugins/manage-in-game-players.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export default fp(
events.on(
'game:playerReplaced',
safe(async ({ game, replacee, replacement }) => {
if (replacee === replacement) {
return
}

const re = await collections.players.findOne({ steamId: replacee })
if (!re) {
throw new Error(`player not found: ${replacee}`)
Expand Down
5 changes: 0 additions & 5 deletions src/games/rcon/blacklist-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ import { delGamePlayer } from './commands'
import { withRcon } from './with-rcon'

export async function blacklistPlayer(game: GameModel, steamId: SteamId64) {
const slot = game.slots.find(slot => slot.player === steamId)
if (!slot) {
throw new Error(`player not found in game: ${steamId}`)
}

return await withRcon(game, async ({ rcon }) => {
await rcon.send(delGamePlayer(steamId))
})
Expand Down
17 changes: 7 additions & 10 deletions src/games/rcon/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { deburr } from 'lodash-es'
import { configuration } from '../../configuration'
import { collections } from '../../database/collections'
import { GameEventType } from '../../database/models/game-event.model'
import { SlotStatus } from '../../database/models/game-slot.model'
import { type GameModel, GameState } from '../../database/models/game.model'
import { environment } from '../../environment'
import { logger } from '../../logger'
Expand Down Expand Up @@ -147,15 +146,13 @@ async function compileConfig(game: GameModel, password: string): Promise<string[
.concat(setPassword(password))
.concat(
await Promise.all(
game.slots
.filter(slot => slot.status !== SlotStatus.replaced)
.map(async slot => {
const player = await collections.players.findOne({ steamId: slot.player })
if (player === null) {
throw new Error(`player ${slot.player} not found`)
}
return addGamePlayer(player.steamId, deburr(player.name), slot.team, slot.gameClass)
}),
game.slots.map(async slot => {
const player = await collections.players.findOne({ steamId: slot.player })
if (player === null) {
throw new Error(`player ${slot.player} not found`)
}
return addGamePlayer(player.steamId, deburr(player.name), slot.team, slot.gameClass)
}),
),
)
.concat(enablePlayerWhitelist())
Expand Down
98 changes: 32 additions & 66 deletions src/games/replace-player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,32 +37,7 @@ export async function replacePlayer({
}

let newGame: GameModel

if (replacee === replacement) {
newGame = await update(
{ number },
{
$set: {
'slots.$[slot].status': SlotStatus.active,
},
$push: {
events: {
event: GameEventType.playerReplaced,
at: new Date(),
replacee,
replacement,
},
},
},
{
arrayFilters: [
{
$and: [{ 'slot.player': { $eq: replacee } }],
},
],
},
)
} else {
if (replacee !== replacement) {
const rm = await collections.players.findOne({ steamId: replacement })
if (!rm) {
throw new Error(`replacement player not found: ${replacement}`)
Expand All @@ -71,51 +46,42 @@ export async function replacePlayer({
if (rm.activeGame !== undefined) {
throw new Error(`player denied: player has active game`)
}
}

await update(
{ number },
{
$push: {
slots: {
player: replacement,
team: slot.team,
gameClass: slot.gameClass,
status: SlotStatus.active,
connectionStatus: PlayerConnectionStatus.offline,
},
events: {
event: GameEventType.playerReplaced,
at: new Date(),
replacee,
replacement,
},
},
newGame = await update(
{ number },
{
$set: {
'slots.$[slot].status': SlotStatus.active,
'slots.$[slot].player': replacement,
...(replacee === replacement
? {}
: { 'slots.$[slot].connectionStatus': PlayerConnectionStatus.offline }),
},
)

newGame = await update(
{ number },
{
$set: {
'slots.$[slot].status': SlotStatus.replaced,
$push: {
events: {
event: GameEventType.playerReplaced,
at: new Date(),
replacee,
replacement,
},
},
{
arrayFilters: [
{
$and: [
{ 'slot.player': { $eq: replacee } },
{
'slot.status': {
$eq: SlotStatus.waitingForSubstitute,
},
},
{
arrayFilters: [
{
$and: [
{ 'slot.player': { $eq: replacee } },
{
'slot.status': {
$eq: SlotStatus.waitingForSubstitute,
},
],
},
],
},
)
}
},
],
},
],
},
)

events.emit('game:playerReplaced', { game: newGame, replacee, replacement })
return game
Expand Down
2 changes: 1 addition & 1 deletion src/games/request-substitute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function requestSubstitute({
actor: SteamId64 | Bot
reason?: string
}): Promise<GameModel> {
logger.trace({ number, replacee, actor, reason }, 'substitutePlayer()')
logger.trace({ number, replacee, actor, reason }, 'games.requestSubstitute()')

const game = await collections.games.findOne({ number })
if (game === null) {
Expand Down
1 change: 0 additions & 1 deletion src/games/views/html/game-slot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export async function GameSlot(props: {
{
[SlotStatus.active]: 'active',
[SlotStatus.waitingForSubstitute]: 'waiting-for-substitute',
[SlotStatus.replaced]: 'replaced',
}[props.slot.status],
]}
data-player={player.steamId}
Expand Down
15 changes: 15 additions & 0 deletions src/migrations/005-remove-replaced-game-slots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { collections } from '../database/collections'
import type { SlotStatus } from '../database/models/game-slot.model'

export async function up() {
await collections.games.updateMany(
{},
{
$pull: {
slots: {
status: 'replaced' as SlotStatus,
},
},
},
)
}
70 changes: 70 additions & 0 deletions tests/20-game/08-player-substitutes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { secondsToMilliseconds } from 'date-fns'
import { launchGame as test, expect } from '../fixtures/launch-game'

test.use({ waitForStage: 'started' })
test.describe('substitutes', () => {
test('substitute self', async ({ gameNumber, users, page, gameServer }) => {
const admin = users.getAdmin()
const adminsPage = await admin.gamePage(gameNumber)
const mayflowersPage = await users.byName('Mayflower').gamePage(gameNumber)

await expect(adminsPage.playerLink('Mayflower')).toBeVisible()
await adminsPage.requestSubstitute('Mayflower')
await expect(adminsPage.playerLink('Mayflower')).not.toBeVisible()
await expect(gameServer).toHaveCommand(`say Looking for replacement for Mayflower...`, {
timeout: secondsToMilliseconds(1),
})

await expect(adminsPage.gameEvent(`${admin.playerName} requested substitute`)).toBeVisible()
await expect(adminsPage.playerLink('Mayflower')).not.toBeVisible()
await expect(mayflowersPage.gameEvent(`${admin.playerName} requested substitute`)).toBeVisible()
await expect(mayflowersPage.playerLink('Mayflower')).not.toBeVisible()

await expect(
page.getByText(`Team BLU needs a substitute for scout in game #${mayflowersPage.gameNumber}`),
).toBeVisible()

await mayflowersPage.replacePlayer('Mayflower')

await expect(adminsPage.playerLink('Mayflower')).toBeVisible()
await expect(adminsPage.gameEvent(`Mayflower replaced Mayflower`)).toBeVisible()
await expect(mayflowersPage.playerLink('Mayflower')).toBeVisible()
await expect(mayflowersPage.gameEvent(`Mayflower replaced Mayflower`)).toBeVisible()
})

test('substitute other', async ({ gameNumber, users, page, gameServer }) => {
const admin = users.getAdmin()
const adminsPage = await admin.gamePage(gameNumber)
const tommyGunsPage = await users.byName('TommyGun').gamePage(gameNumber)
await tommyGunsPage.goto()

await expect(adminsPage.playerLink('Mayflower')).toBeVisible()
await adminsPage.requestSubstitute('Mayflower')
await expect(adminsPage.playerLink('Mayflower')).not.toBeVisible()
await expect(gameServer).toHaveCommand(`say Looking for replacement for Mayflower...`, {
timeout: secondsToMilliseconds(1),
})

await expect(adminsPage.gameEvent(`${admin.playerName} requested substitute`)).toBeVisible()
await expect(adminsPage.playerLink('Mayflower')).not.toBeVisible()
await expect(tommyGunsPage.gameEvent(`${admin.playerName} requested substitute`)).toBeVisible()
await expect(tommyGunsPage.playerLink('Mayflower')).not.toBeVisible()

await expect(
page.getByText(`Team BLU needs a substitute for scout in game #${adminsPage.gameNumber}`),
).toBeVisible()

await tommyGunsPage.replacePlayer('Mayflower')

await expect(adminsPage.playerLink('TommyGun')).toBeVisible()
await expect(adminsPage.gameEvent(`TommyGun replaced Mayflower`)).toBeVisible()
await expect(tommyGunsPage.playerLink('TommyGun')).toBeVisible()
await expect(tommyGunsPage.gameEvent(`TommyGun replaced Mayflower`)).toBeVisible()

await expect(gameServer).toHaveCommand(`sm_game_player_add ${users.byName('TommyGun').steamId}`)
await expect(gameServer).toHaveCommand(
`sm_game_player_del ${users.byName('Mayflower').steamId}`,
)
await expect(gameServer).toHaveCommand(`say Mayflower has been replaced by TommyGun`)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { secondsToMilliseconds } from 'date-fns'
import { GamePage } from '../pages/game.page'
import { expect, mergeTests } from '@playwright/test'
import { accessMongoDb } from '../fixtures/access-mongo-db'
import type { UserContext } from '../user-manager'
import { launchGame } from '../fixtures/launch-game'

const test = mergeTests(launchGame, accessMongoDb)
Expand Down
File renamed without changes.
File renamed without changes.
27 changes: 0 additions & 27 deletions tests/30-player-substitutes/01-substitute-self.spec.ts

This file was deleted.

28 changes: 0 additions & 28 deletions tests/30-player-substitutes/02-substitute-other.spec.ts

This file was deleted.

Loading

0 comments on commit 6c77c0e

Please sign in to comment.