Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove 'replaced' game slot status #120

Merged
merged 1 commit into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
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
Loading