Files
Rahoot/packages/socket/src/services/registry.ts

159 lines
4.0 KiB
TypeScript

import Game from "@rahoot/socket/services/game"
import dayjs from "dayjs"
interface EmptyGame {
since: number
game: Game
}
class Registry {
private static instance: Registry | null = null
private games: Game[] = []
private emptyGames: EmptyGame[] = []
private cleanupInterval: ReturnType<typeof setTimeout> | null = null
private readonly EMPTY_GAME_TIMEOUT_MINUTES = 5
private readonly CLEANUP_INTERVAL_MS = 60_000
private constructor() {
this.startCleanupTask()
}
static getInstance(): Registry {
Registry.instance ||= new Registry()
return Registry.instance
}
addGame(game: Game): void {
this.games.push(game)
console.log(`Game ${game.gameId} added. Total games: ${this.games.length}`)
}
getGameById(gameId: string): Game | undefined {
return this.games.find((g) => g.gameId === gameId)
}
getGameByInviteCode(inviteCode: string): Game | undefined {
return this.games.find((g) => g.inviteCode === inviteCode)
}
getPlayerGame(clientId: string): Game | undefined {
return this.games.find((g) =>
g.players.some((p) => p.clientId === clientId)
)
}
getManagerGame(clientId: string): Game | undefined {
return this.games.find((g) => g.manager.clientId === clientId)
}
getGameByManagerSocketId(socketId: string): Game | undefined {
return this.games.find((g) => g.manager.id === socketId)
}
getGameByPlayerSocketId(socketId: string): Game | undefined {
return this.games.find((g) => g.players.some((p) => p.id === socketId))
}
markGameAsEmpty(game: Game): void {
const alreadyEmpty = this.emptyGames.find(
(g) => g.game.gameId === game.gameId
)
if (!alreadyEmpty) {
this.emptyGames.push({
since: dayjs().unix(),
game,
})
console.log(
`Game ${game.gameId} marked as empty. Total empty games: ${this.emptyGames.length}`
)
}
}
reactivateGame(gameId: string): void {
const initialLength = this.emptyGames.length
this.emptyGames = this.emptyGames.filter((g) => g.game.gameId !== gameId)
if (this.emptyGames.length < initialLength) {
console.log(
`Game ${gameId} reactivated. Remaining empty games: ${this.emptyGames.length}`
)
}
}
removeGame(gameId: string): boolean {
const initialLength = this.games.length
this.games = this.games.filter((g) => g.gameId !== gameId)
this.emptyGames = this.emptyGames.filter((g) => g.game.gameId !== gameId)
const removed = this.games.length < initialLength
if (removed) {
console.log(`Game ${gameId} removed. Total games: ${this.games.length}`)
}
return removed
}
getAllGames(): Game[] {
return [...this.games]
}
getGameCount(): number {
return this.games.length
}
getEmptyGameCount(): number {
return this.emptyGames.length
}
private cleanupEmptyGames(): void {
const now = dayjs()
const stillEmpty = this.emptyGames.filter(
(g) =>
now.diff(dayjs.unix(g.since), "minute") <
this.EMPTY_GAME_TIMEOUT_MINUTES
)
if (stillEmpty.length === this.emptyGames.length) {
return
}
const removed = this.emptyGames.filter((g) => !stillEmpty.includes(g))
const removedGameIds = removed.map((r) => r.game.gameId)
this.games = this.games.filter((g) => !removedGameIds.includes(g.gameId))
this.emptyGames = stillEmpty
console.log(
`Removed ${removed.length} empty game(s). Remaining games: ${this.games.length}`
)
}
private startCleanupTask(): void {
this.cleanupInterval = setInterval(() => {
this.cleanupEmptyGames()
}, this.CLEANUP_INTERVAL_MS)
console.log("Game cleanup task started")
}
stopCleanupTask(): void {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval)
this.cleanupInterval = null
console.log("Game cleanup task stopped")
}
}
cleanup(): void {
this.stopCleanupTask()
this.games = []
this.emptyGames = []
console.log("Registry cleaned up")
}
}
export default Registry