{ "version": 3, "sources": ["../../../server/room-battle.ts"], "sourcesContent": ["/**\r\n * Room Battle\r\n * Pokemon Showdown - http://pokemonshowdown.com/\r\n *\r\n * This file wraps the simulator in an implementation of the RoomGame\r\n * interface. It also abstracts away the multi-process nature of the\r\n * simulator.\r\n *\r\n * For the actual battle simulation, see sim/\r\n *\r\n * @license MIT\r\n */\r\n\r\nimport {FS, Repl, ProcessManager} from '../lib';\r\nimport {execSync} from \"child_process\";\r\nimport {BattleStream} from \"../sim/battle-stream\";\r\nimport * as RoomGames from \"./room-game\";\r\nimport type {Tournament} from './tournaments/index';\r\nimport {RoomSettings} from './rooms';\r\n\r\ntype ChannelIndex = 0 | 1 | 2 | 3 | 4;\r\nexport type PlayerIndex = 1 | 2 | 3 | 4;\r\nexport type ChallengeType = 'rated' | 'unrated' | 'challenge' | 'tour';\r\n\r\ninterface BattleRequestTracker {\r\n\trqid: number;\r\n\trequest: string;\r\n\t/**\r\n\t * - true = user has decided,\r\n\t * - false = user has yet to decide,\r\n\t * - 'cantUndo' = waiting on other user (U-turn, faint-switch) or uncancellable (trapping ability)\r\n\t */\r\n\tisWait: 'cantUndo' | true | false;\r\n\tchoice: string;\r\n}\r\n\r\n/** 5 seconds */\r\nconst TICK_TIME = 5;\r\nconst SECONDS = 1000;\r\n\r\n// Timer constants: In seconds, should be multiple of TICK_TIME\r\nconst STARTING_TIME = 150;\r\nconst MAX_TURN_TIME = 150;\r\nconst STARTING_TIME_CHALLENGE = 300;\r\nconst STARTING_GRACE_TIME = 60;\r\nconst MAX_TURN_TIME_CHALLENGE = 300;\r\n\r\nconst DISCONNECTION_TIME = 60;\r\nconst DISCONNECTION_BANK_TIME = 300;\r\n\r\n// time after a player disabling the timer before they can re-enable it\r\nconst TIMER_COOLDOWN = 20 * SECONDS;\r\nconst LOCKDOWN_PERIOD = 30 * 60 * 1000; // 30 minutes\r\n\r\nexport class RoomBattlePlayer extends RoomGames.RoomGamePlayer {\r\n\treadonly slot: SideID;\r\n\treadonly channelIndex: ChannelIndex;\r\n\trequest: BattleRequestTracker;\r\n\twantsTie: boolean;\r\n\twantsOpenTeamSheets: boolean | null;\r\n\tactive: boolean;\r\n\teliminated: boolean;\r\n\t/**\r\n\t * Total timer.\r\n\t *\r\n\t * Starts at 210 per player in a ladder battle. Goes down by 5\r\n\t * every tick. Goes up by 10 every turn (with some complications -\r\n\t * see `nextRequest`), capped at starting time. The player loses if\r\n\t * this reaches 0.\r\n\t *\r\n\t * The equivalent of \"Your Time\" in VGC.\r\n\t *\r\n\t */\r\n\tsecondsLeft: number;\r\n\t/**\r\n\t * Turn timer.\r\n\t *\r\n\t * Set equal to the player's overall timer, but capped at 150\r\n\t * seconds in a ladder battle. Goes down by 5 every tick.\r\n\t * Tracked separately from the overall timer, and the player also\r\n\t * loses if this reaches 0.\r\n\t */\r\n\tturnSecondsLeft: number;\r\n\t/**\r\n\t * Disconnect timer.\r\n\t * Starts at 60 seconds. While the player is disconnected, this\r\n\t * will go down by 5 every tick. Tracked separately from the\r\n\t * overall timer, and the player also loses if this reaches 0.\r\n\t *\r\n\t * Mostly exists so impatient players don't have to wait the full\r\n\t * 150 seconds against a disconnected opponent.\r\n \t*/\r\n\tdcSecondsLeft: number;\r\n\t/**\r\n\t * Used to track a user's last known connection status, and display\r\n\t * the proper message when it changes.\r\n\t */\r\n\tconnected: boolean;\r\n\tinvite: ID;\r\n\t/**\r\n\t * Has the simulator received this player's team yet?\r\n\t * Basically always yes except when creating a 4-player battle,\r\n\t * in which case players will need to bring their own team.\r\n\t */\r\n\thasTeam: boolean;\r\n\tconstructor(user: User | string | null, game: RoomBattle, num: PlayerIndex) {\r\n\t\tsuper(user, game, num);\r\n\t\tif (typeof user === 'string') user = null;\r\n\r\n\t\tthis.slot = `p${num}` as SideID;\r\n\t\tthis.channelIndex = (game.gameType === 'multi' && num > 2 ? num - 2 : num) as ChannelIndex;\r\n\r\n\t\tthis.request = {rqid: 0, request: '', isWait: 'cantUndo', choice: ''};\r\n\t\tthis.wantsTie = false;\r\n\t\tthis.wantsOpenTeamSheets = null;\r\n\t\tthis.active = true;\r\n\t\tthis.eliminated = false;\r\n\r\n\t\tthis.secondsLeft = 1;\r\n\t\tthis.turnSecondsLeft = 1;\r\n\t\tthis.dcSecondsLeft = 1;\r\n\r\n\t\tthis.connected = true;\r\n\t\tthis.invite = '';\r\n\t\tthis.hasTeam = false;\r\n\r\n\t\tif (user) {\r\n\t\t\tuser.games.add(this.game.roomid);\r\n\t\t\tuser.updateSearch();\r\n\t\t\tfor (const connection of user.connections) {\r\n\t\t\t\tif (connection.inRooms.has(game.roomid)) {\r\n\t\t\t\t\tSockets.channelMove(connection.worker, this.game.roomid, this.channelIndex, connection.socketid);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tgetUser() {\r\n\t\treturn (this.id && Users.get(this.id)) || null;\r\n\t}\r\n\tunlinkUser() {\r\n\t\tconst user = this.getUser();\r\n\t\tif (user) {\r\n\t\t\tfor (const connection of user.connections) {\r\n\t\t\t\tSockets.channelMove(connection.worker, this.game.roomid, 0, connection.socketid);\r\n\t\t\t}\r\n\t\t\tuser.games.delete(this.game.roomid);\r\n\t\t\tuser.updateSearch();\r\n\t\t}\r\n\t\tthis.id = '';\r\n\t\tthis.connected = false;\r\n\t\tthis.active = false;\r\n\t}\r\n\tupdateChannel(user: User | Connection) {\r\n\t\tif (user instanceof Users.Connection) {\r\n\t\t\t// \"user\" is actually a connection\r\n\t\t\tSockets.channelMove(user.worker, this.game.roomid, this.channelIndex, user.socketid);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tfor (const connection of user.connections) {\r\n\t\t\tSockets.channelMove(connection.worker, this.game.roomid, this.channelIndex, connection.socketid);\r\n\t\t}\r\n\t}\r\n\r\n\ttoString() {\r\n\t\treturn this.id;\r\n\t}\r\n\tsend(data: string) {\r\n\t\tconst user = this.getUser();\r\n\t\tif (user) user.send(data);\r\n\t}\r\n\tsendRoom(data: string) {\r\n\t\tconst user = this.getUser();\r\n\t\tif (user) user.sendTo(this.game.roomid, data);\r\n\t}\r\n}\r\n\r\nexport class RoomBattleTimer {\r\n\treadonly battle: RoomBattle;\r\n\treadonly timerRequesters: Set;\r\n\ttimer: NodeJS.Timer | null;\r\n\tisFirstTurn: boolean;\r\n\t/**\r\n\t * Last tick, as milliseconds since UNIX epoch.\r\n\t * Represents the last time a tick happened.\r\n\t */\r\n\tlastTick: number;\r\n\t/** Debug mode; true to output detailed timer info every tick */\r\n\tdebug: boolean;\r\n\tlastDisabledTime: number;\r\n\tlastDisabledByUser: null | ID;\r\n\tsettings: GameTimerSettings;\r\n\tconstructor(battle: RoomBattle) {\r\n\t\tthis.battle = battle;\r\n\r\n\t\tthis.timer = null;\r\n\t\tthis.timerRequesters = new Set();\r\n\t\tthis.isFirstTurn = true;\r\n\r\n\t\tthis.lastTick = 0;\r\n\r\n\t\tthis.debug = false;\r\n\r\n\t\tthis.lastDisabledTime = 0;\r\n\t\tthis.lastDisabledByUser = null;\r\n\r\n\t\tconst hasLongTurns = Dex.formats.get(battle.format, true).gameType !== 'singles';\r\n\t\tconst isChallenge = (battle.challengeType === 'challenge');\r\n\t\tconst timerEntry = Dex.formats.getRuleTable(Dex.formats.get(battle.format, true)).timer;\r\n\t\tconst timerSettings = timerEntry?.[0];\r\n\r\n\t\t// so that Object.assign doesn't overwrite anything with `undefined`\r\n\t\tfor (const k in timerSettings) {\r\n\t\t\t// @ts-ignore\r\n\t\t\tif (timerSettings[k] === undefined) delete timerSettings[k];\r\n\t\t}\r\n\r\n\t\tthis.settings = {\r\n\t\t\tdcTimer: !isChallenge,\r\n\t\t\tdcTimerBank: isChallenge,\r\n\t\t\tstarting: isChallenge ? STARTING_TIME_CHALLENGE : STARTING_TIME,\r\n\t\t\tgrace: STARTING_GRACE_TIME,\r\n\t\t\taddPerTurn: hasLongTurns ? 25 : 10,\r\n\t\t\tmaxPerTurn: isChallenge ? MAX_TURN_TIME_CHALLENGE : MAX_TURN_TIME,\r\n\t\t\tmaxFirstTurn: isChallenge ? MAX_TURN_TIME_CHALLENGE : MAX_TURN_TIME,\r\n\t\t\ttimeoutAutoChoose: false,\r\n\t\t\taccelerate: !timerSettings && !isChallenge,\r\n\t\t\t...timerSettings,\r\n\t\t};\r\n\t\tif (this.settings.maxPerTurn <= 0) this.settings.maxPerTurn = Infinity;\r\n\r\n\t\tfor (const player of this.battle.players) {\r\n\t\t\tplayer.secondsLeft = this.settings.starting + this.settings.grace;\r\n\t\t\tplayer.turnSecondsLeft = -1;\r\n\t\t\tplayer.dcSecondsLeft = this.settings.dcTimerBank ? DISCONNECTION_BANK_TIME : DISCONNECTION_TIME;\r\n\t\t}\r\n\t}\r\n\tstart(requester?: User) {\r\n\t\tconst userid = requester ? requester.id : 'staff' as ID;\r\n\t\tif (this.timerRequesters.has(userid)) return false;\r\n\t\tif (this.battle.ended) {\r\n\t\t\trequester?.sendTo(this.battle.roomid, `|inactiveoff|The timer can't be enabled after a battle has ended.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (this.timer) {\r\n\t\t\tthis.battle.room.add(`|inactive|${requester ? requester.name : userid} also wants the timer to be on.`).update();\r\n\t\t\tthis.timerRequesters.add(userid);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (requester && this.battle.playerTable[requester.id] && this.lastDisabledByUser === requester.id) {\r\n\t\t\tconst remainingCooldownMs = (this.lastDisabledTime || 0) + TIMER_COOLDOWN - Date.now();\r\n\t\t\tif (remainingCooldownMs > 0) {\r\n\t\t\t\tthis.battle.playerTable[requester.id].sendRoom(\r\n\t\t\t\t\t`|inactiveoff|The timer can't be re-enabled so soon after disabling it (${Math.ceil(remainingCooldownMs / SECONDS)} seconds remaining).`\r\n\t\t\t\t);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t\tthis.timerRequesters.add(userid);\r\n\t\tconst requestedBy = requester ? ` (requested by ${requester.name})` : ``;\r\n\t\tthis.battle.room.add(`|inactive|Battle timer is ON: inactive players will automatically lose when time's up.${requestedBy}`).update();\r\n\r\n\t\tthis.nextRequest();\r\n\t\treturn true;\r\n\t}\r\n\tstop(requester?: User) {\r\n\t\tif (requester) {\r\n\t\t\tif (!this.timerRequesters.has(requester.id)) return false;\r\n\t\t\tthis.timerRequesters.delete(requester.id);\r\n\t\t\tthis.lastDisabledByUser = requester.id;\r\n\t\t\tthis.lastDisabledTime = Date.now();\r\n\t\t} else {\r\n\t\t\tthis.timerRequesters.clear();\r\n\t\t}\r\n\t\tif (this.timerRequesters.size) {\r\n\t\t\tthis.battle.room.add(`|inactive|${requester!.name} no longer wants the timer on, but the timer is staying on because ${[...this.timerRequesters].join(', ')} still does.`).update();\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (this.end()) {\r\n\t\t\tthis.battle.room.add(`|inactiveoff|Battle timer is now OFF.`).update();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\tend() {\r\n\t\tthis.timerRequesters.clear();\r\n\t\tif (!this.timer) return false;\r\n\t\tclearTimeout(this.timer);\r\n\t\tthis.timer = null;\r\n\t\treturn true;\r\n\t}\r\n\tnextRequest() {\r\n\t\tif (this.timer) {\r\n\t\t\tclearTimeout(this.timer);\r\n\t\t\tthis.timer = null;\r\n\t\t}\r\n\t\tif (!this.timerRequesters.size) return;\r\n\t\tconst players = this.battle.players;\r\n\t\tif (players.some(player => player.secondsLeft <= 0)) return;\r\n\r\n\t\t/** false = U-turn or single faint, true = \"new turn\" */\r\n\t\tlet isFull = true;\r\n\t\tlet isEmpty = true;\r\n\t\tfor (const player of players) {\r\n\t\t\tif (player.request.isWait) isFull = false;\r\n\t\t\tif (player.request.isWait !== 'cantUndo') isEmpty = false;\r\n\t\t}\r\n\t\tif (isEmpty) {\r\n\t\t\t// there are no active requests\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst isFirst = this.isFirstTurn;\r\n\t\tthis.isFirstTurn = false;\r\n\r\n\t\tconst maxTurnTime = (isFirst ? this.settings.maxFirstTurn : 0) || this.settings.maxPerTurn;\r\n\r\n\t\tlet addPerTurn = isFirst ? 0 : this.settings.addPerTurn;\r\n\t\tif (this.settings.accelerate && addPerTurn) {\r\n\t\t\t// after turn 100ish: 15s/turn -> 10s/turn\r\n\t\t\tif (this.battle.requestCount > 200 && addPerTurn > TICK_TIME) {\r\n\t\t\t\taddPerTurn -= TICK_TIME;\r\n\t\t\t}\r\n\t\t\t// after turn 200ish: 10s/turn -> 7s/turn\r\n\t\t\tif (this.battle.requestCount > 400 && Math.floor(this.battle.requestCount / 2) % 2) {\r\n\t\t\t\taddPerTurn = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!isFull && addPerTurn > TICK_TIME) {\r\n\t\t\taddPerTurn = TICK_TIME;\r\n\t\t}\r\n\r\n\t\tconst room = this.battle.room;\r\n\t\tfor (const player of players) {\r\n\t\t\tif (!isFirst) {\r\n\t\t\t\tplayer.secondsLeft = Math.min(player.secondsLeft + addPerTurn, this.settings.starting);\r\n\t\t\t}\r\n\t\t\tplayer.turnSecondsLeft = Math.min(player.secondsLeft, maxTurnTime);\r\n\r\n\t\t\tconst secondsLeft = player.turnSecondsLeft;\r\n\t\t\tlet grace = player.secondsLeft - this.settings.starting;\r\n\t\t\tif (grace < 0) grace = 0;\r\n\t\t\tif (player) {\r\n\t\t\t\tplayer.sendRoom(`|inactive|Time left: ${secondsLeft} sec this turn | ${player.secondsLeft - grace} sec total` + (grace ? ` | ${grace} sec grace` : ``));\r\n\t\t\t}\r\n\t\t\tif (secondsLeft <= 30 && secondsLeft < this.settings.starting) {\r\n\t\t\t\troom.add(`|inactive|${player.name} has ${secondsLeft} seconds left this turn.`);\r\n\t\t\t}\r\n\t\t\tif (this.debug) {\r\n\t\t\t\troom.add(`||${player.name} | Time left: ${secondsLeft} sec this turn | ${player.secondsLeft} sec total | +${addPerTurn} seconds`);\r\n\t\t\t}\r\n\t\t}\r\n\t\troom.update();\r\n\t\tthis.lastTick = Date.now();\r\n\t\tthis.timer = setTimeout(() => this.nextTick(), TICK_TIME * SECONDS);\r\n\t}\r\n\tnextTick() {\r\n\t\tif (this.timer) clearTimeout(this.timer);\r\n\t\tif (this.battle.ended) return;\r\n\t\tconst room = this.battle.room;\r\n\t\tfor (const player of this.battle.players) {\r\n\t\t\tif (player.request.isWait) continue;\r\n\t\t\tif (player.connected) {\r\n\t\t\t\tplayer.secondsLeft -= TICK_TIME;\r\n\t\t\t\tplayer.turnSecondsLeft -= TICK_TIME;\r\n\t\t\t} else {\r\n\t\t\t\tplayer.dcSecondsLeft -= TICK_TIME;\r\n\t\t\t\tif (!this.settings.dcTimerBank) {\r\n\t\t\t\t\tplayer.secondsLeft -= TICK_TIME;\r\n\t\t\t\t\tplayer.turnSecondsLeft -= TICK_TIME;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tconst dcSecondsLeft = player.dcSecondsLeft;\r\n\t\t\tif (dcSecondsLeft <= 0) player.turnSecondsLeft = 0;\r\n\t\t\tconst secondsLeft = player.turnSecondsLeft;\r\n\t\t\tif (!secondsLeft) continue;\r\n\r\n\t\t\tif (!player.connected && (dcSecondsLeft <= secondsLeft || this.settings.dcTimerBank)) {\r\n\t\t\t\t// dc timer is shown only if it's lower than turn timer or you're in timer bank mode\r\n\t\t\t\tif (dcSecondsLeft % 30 === 0 || dcSecondsLeft <= 20) {\r\n\t\t\t\t\troom.add(`|inactive|${player.name} has ${dcSecondsLeft} seconds to reconnect!`);\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\t// regular turn timer shown\r\n\t\t\t\tif (secondsLeft % 30 === 0 || secondsLeft <= 20) {\r\n\t\t\t\t\troom.add(`|inactive|${player.name} has ${secondsLeft} seconds left.`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (this.debug) {\r\n\t\t\t\troom.add(`||[${player.name} has ${player.turnSecondsLeft}s this turn / ${player.secondsLeft}s total]`);\r\n\t\t\t}\r\n\t\t}\r\n\t\troom.update();\r\n\t\tif (!this.checkTimeout()) {\r\n\t\t\tthis.timer = setTimeout(() => this.nextTick(), TICK_TIME * 1000);\r\n\t\t}\r\n\t}\r\n\tcheckActivity() {\r\n\t\tif (this.battle.ended) return;\r\n\t\tfor (const player of this.battle.players) {\r\n\t\t\tconst isConnected = !!player?.active;\r\n\r\n\t\t\tif (isConnected === player.connected) continue;\r\n\r\n\t\t\tif (!isConnected) {\r\n\t\t\t\t// player has disconnected\r\n\t\t\t\tplayer.connected = false;\r\n\t\t\t\tif (!this.settings.dcTimerBank) {\r\n\t\t\t\t\t// don't wait longer than 6 ticks (1 minute)\r\n\t\t\t\t\tif (this.settings.dcTimer) {\r\n\t\t\t\t\t\tplayer.dcSecondsLeft = DISCONNECTION_TIME;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t// arbitrary large number\r\n\t\t\t\t\t\tplayer.dcSecondsLeft = DISCONNECTION_TIME * 10;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (this.timerRequesters.size) {\r\n\t\t\t\t\tlet msg = `!`;\r\n\r\n\t\t\t\t\tif (this.settings.dcTimer) {\r\n\t\t\t\t\t\tmsg = ` and has a minute to reconnect!`;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (this.settings.dcTimerBank) {\r\n\t\t\t\t\t\tif (player.dcSecondsLeft > 0) {\r\n\t\t\t\t\t\t\tmsg = ` and has ${player.dcSecondsLeft} seconds to reconnect!`;\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tmsg = ` and has no disconnection time left!`;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthis.battle.room.add(`|inactive|${player.name} disconnected${msg}`).update();\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\t// player has reconnected\r\n\t\t\t\tplayer.connected = true;\r\n\t\t\t\tif (this.timerRequesters.size) {\r\n\t\t\t\t\tlet timeLeft = ``;\r\n\t\t\t\t\tif (!player.request.isWait) {\r\n\t\t\t\t\t\ttimeLeft = ` and has ${player.turnSecondsLeft} seconds left`;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthis.battle.room.add(`|inactive|${player.name} reconnected${timeLeft}.`).update();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tcheckTimeout() {\r\n\t\tconst players = this.battle.players;\r\n\t\tif (players.every(player => player.turnSecondsLeft <= 0)) {\r\n\t\t\tif (!this.settings.timeoutAutoChoose || players.every(player => player.secondsLeft <= 0)) {\r\n\t\t\t\tthis.battle.room.add(`|-message|All players are inactive.`).update();\r\n\t\t\t\tthis.battle.tie();\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\t\tlet didSomething = false;\r\n\t\tfor (const player of players) {\r\n\t\t\tif (player.turnSecondsLeft > 0) continue;\r\n\t\t\tif (this.settings.timeoutAutoChoose && player.secondsLeft > 0 && player.connected) {\r\n\t\t\t\tvoid this.battle.stream.write(`>${player.slot} default`);\r\n\t\t\t\tdidSomething = true;\r\n\t\t\t} else {\r\n\t\t\t\tthis.battle.forfeitPlayer(player, ' lost due to inactivity.');\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn didSomething;\r\n\t}\r\n}\r\n\r\ninterface RoomBattlePlayerOptions {\r\n\tuser: User;\r\n\t/** should be '' for random teams */\r\n\tteam?: string;\r\n\trating?: number;\r\n\tinviteOnly?: boolean;\r\n\thidden?: boolean;\r\n}\r\n\r\nexport interface RoomBattleOptions {\r\n\tformat: string;\r\n\tp1?: RoomBattlePlayerOptions;\r\n\tp2?: RoomBattlePlayerOptions;\r\n\tp3?: RoomBattlePlayerOptions;\r\n\tp4?: RoomBattlePlayerOptions;\r\n\r\n\tdelayedStart?: boolean | 'multi';\r\n\tchallengeType?: ChallengeType;\r\n\tallowRenames?: boolean;\r\n\trated?: number | boolean | null;\r\n\ttour?: Tournament | null;\r\n\tinputLog?: string;\r\n\tratedMessage?: string;\r\n\tseed?: PRNGSeed;\r\n\troomid?: RoomID;\r\n\tplayers?: ID[];\r\n\t/** For battles restored after a restart */\r\n\tdelayedTimer?: boolean;\r\n\trestored?: boolean;\r\n}\r\n\r\nexport class RoomBattle extends RoomGames.RoomGame {\r\n\treadonly gameid: ID;\r\n\treadonly room: GameRoom;\r\n\treadonly title: string;\r\n\treadonly allowRenames: boolean;\r\n\treadonly format: string;\r\n\t/** Will exist even if the game is unrated, in case it's later forced to be rated */\r\n\treadonly ladder: string;\r\n\treadonly gameType: string | undefined;\r\n\treadonly challengeType: ChallengeType;\r\n\t/**\r\n\t * The lower player's rating, for searching purposes.\r\n\t * 0 for unrated battles. 1 for unknown ratings.\r\n\t */\r\n\treadonly rated: number;\r\n\t/**\r\n\t * userid that requested extraction -> playerids that accepted the extraction\r\n\t */\r\n\treadonly allowExtraction: {[k: string]: Set};\r\n\treadonly stream: Streams.ObjectReadWriteStream;\r\n\treadonly timer: RoomBattleTimer;\r\n\tmissingBattleStartMessage: boolean | 'multi';\r\n\tstarted: boolean;\r\n\tended: boolean;\r\n\tactive: boolean;\r\n\tneedsRejoin: Set | null;\r\n\treplaySaved: boolean;\r\n\tforcedSettings: {modchat?: string | null, privacy?: string | null} = {};\r\n\tp1: RoomBattlePlayer;\r\n\tp2: RoomBattlePlayer;\r\n\tp3: RoomBattlePlayer;\r\n\tp4: RoomBattlePlayer;\r\n\tinviteOnlySetter: ID | null;\r\n\tlogData: AnyObject | null;\r\n\tendType: string;\r\n\t/**\r\n\t * If the battle is ended: an array of the number of Pokemon left for each side.\r\n\t */\r\n\tscore: number[] | null;\r\n\tinputLog: string[] | null;\r\n\tturn: number;\r\n\trqid: number;\r\n\trequestCount: number;\r\n\toptions: RoomBattleOptions;\r\n\tfrozen?: boolean;\r\n\tdataResolvers?: [((args: string[]) => void), ((error: Error) => void)][];\r\n\tconstructor(room: GameRoom, options: RoomBattleOptions) {\r\n\t\tsuper(room);\r\n\t\tconst format = Dex.formats.get(options.format, true);\r\n\t\tthis.gameid = 'battle' as ID;\r\n\t\tthis.room = room;\r\n\t\tthis.title = format.name;\r\n\t\tthis.options = options;\r\n\t\tif (!this.title.endsWith(\" Battle\")) this.title += \" Battle\";\r\n\t\tthis.allowRenames = options.allowRenames !== undefined ? !!options.allowRenames : (!options.rated && !options.tour);\r\n\r\n\t\tthis.format = options.format;\r\n\t\tthis.gameType = format.gameType;\r\n\t\tthis.challengeType = options.challengeType || 'challenge';\r\n\t\tthis.rated = options.rated === true ? 1 : options.rated || 0;\r\n\t\tthis.ladder = typeof format.rated === 'string' ? toID(format.rated) : options.format;\r\n\t\t// true when onCreateBattleRoom has been called\r\n\t\tthis.missingBattleStartMessage = !!options.inputLog || options.delayedStart || false;\r\n\t\tthis.started = false;\r\n\t\tthis.ended = false;\r\n\t\tthis.active = false;\r\n\t\tthis.replaySaved = false;\r\n\r\n\t\tthis.playerCap = this.gameType === 'multi' || this.gameType === 'freeforall' ? 4 : 2;\r\n\t\tthis.p1 = null!;\r\n\t\tthis.p2 = null!;\r\n\t\tthis.p3 = null!;\r\n\t\tthis.p4 = null!;\r\n\t\tthis.inviteOnlySetter = null;\r\n\r\n\t\tthis.needsRejoin = options.restored ? new Set(options.players) : null;\r\n\r\n\t\t// data to be logged\r\n\t\tthis.allowExtraction = {};\r\n\r\n\t\tthis.logData = null;\r\n\t\tthis.endType = 'normal';\r\n\t\tthis.score = null;\r\n\t\tthis.inputLog = null;\r\n\t\tthis.turn = 0;\r\n\r\n\t\tthis.rqid = 1;\r\n\t\tthis.requestCount = 0;\r\n\r\n\t\tthis.stream = PM.createStream();\r\n\r\n\t\tlet ratedMessage = '';\r\n\t\tif (options.ratedMessage) {\r\n\t\t\tratedMessage = options.ratedMessage;\r\n\t\t}\r\n\t\tif (this.rated) {\r\n\t\t\tratedMessage = 'Rated battle';\r\n\t\t} else if (this.room.tour) {\r\n\t\t\tratedMessage = 'Tournament battle';\r\n\t\t}\r\n\r\n\t\tthis.room.battle = this;\r\n\r\n\t\tconst battleOptions = {\r\n\t\t\tformatid: this.format,\r\n\t\t\troomid: this.roomid,\r\n\t\t\trated: ratedMessage,\r\n\t\t\tseed: options.seed,\r\n\t\t};\r\n\t\tif (options.inputLog) {\r\n\t\t\tvoid this.stream.write(options.inputLog);\r\n\t\t} else {\r\n\t\t\tvoid this.stream.write(`>start ` + JSON.stringify(battleOptions));\r\n\t\t}\r\n\r\n\t\tvoid this.listen();\r\n\r\n\t\tthis.addPlayer(options.p1?.user || null, options.p1);\r\n\t\tthis.addPlayer(options.p2?.user || null, options.p2);\r\n\t\tif (this.playerCap > 2) {\r\n\t\t\tthis.addPlayer(options.p3?.user || null, options.p3);\r\n\t\t\tthis.addPlayer(options.p4?.user || null, options.p4);\r\n\t\t}\r\n\t\tthis.timer = new RoomBattleTimer(this);\r\n\t\tif (Config.forcetimer || this.format.includes('blitz')) this.timer.start();\r\n\t\tthis.start();\r\n\t}\r\n\r\n\tcheckActive() {\r\n\t\tlet active = true;\r\n\t\tif (this.ended || !this.started) {\r\n\t\t\tactive = false;\r\n\t\t} else if (!this.p1?.active) {\r\n\t\t\tactive = false;\r\n\t\t} else if (!this.p2?.active) {\r\n\t\t\tactive = false;\r\n\t\t} else if (this.playerCap > 2) {\r\n\t\t\tif (!this.p3?.active) {\r\n\t\t\t\tactive = false;\r\n\t\t\t} else if (!this.p4?.active) {\r\n\t\t\t\tactive = false;\r\n\t\t\t}\r\n\t\t}\r\n\t\tRooms.global.battleCount += (active ? 1 : 0) - (this.active ? 1 : 0);\r\n\t\tthis.room.active = active;\r\n\t\tthis.active = active;\r\n\t\tif (Rooms.global.battleCount === 0) Rooms.global.automaticKillRequest();\r\n\t}\r\n\tchoose(user: User, data: string) {\r\n\t\tif (this.frozen) {\r\n\t\t\tuser.popup(`Your battle is currently paused, so you cannot move right now.`);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tconst [choice, rqid] = data.split('|', 2);\r\n\t\tif (!player) return;\r\n\t\tconst request = player.request;\r\n\t\tif (request.isWait !== false && request.isWait !== true) {\r\n\t\t\tplayer.sendRoom(`|error|[Invalid choice] There's nothing to choose`);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst allPlayersWait = this.players.every(p => !!p.request.isWait);\r\n\t\tif (allPlayersWait || // too late\r\n\t\t\t(rqid && rqid !== '' + request.rqid)) { // WAY too late\r\n\t\t\tplayer.sendRoom(`|error|[Invalid choice] Sorry, too late to make a different move; the next turn has already started`);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\trequest.isWait = true;\r\n\t\trequest.choice = choice;\r\n\r\n\t\tvoid this.stream.write(`>${player.slot} ${choice}`);\r\n\t}\r\n\tundo(user: User, data: string) {\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tconst [, rqid] = data.split('|', 2);\r\n\t\tif (!player) return;\r\n\t\tconst request = player.request;\r\n\t\tif (request.isWait !== true) {\r\n\t\t\tplayer.sendRoom(`|error|[Invalid choice] There's nothing to cancel`);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tconst allPlayersWait = this.players.every(p => !!p.request.isWait);\r\n\t\tif (allPlayersWait || // too late\r\n\t\t\t(rqid && rqid !== '' + request.rqid)) { // WAY too late\r\n\t\t\tplayer.sendRoom(`|error|[Invalid choice] Sorry, too late to cancel; the next turn has already started`);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\trequest.isWait = false;\r\n\r\n\t\tvoid this.stream.write(`>${player.slot} undo`);\r\n\t}\r\n\tjoinGame(user: User, slot?: SideID, playerOpts?: {team?: string}) {\r\n\t\tif (this.needsRejoin?.size && !this.needsRejoin.has(user.id)) {\r\n\t\t\tuser.popup(`All the original players in this battle must join first.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (user.id in this.playerTable) {\r\n\t\t\tuser.popup(`You have already joined this battle.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tconst validSlots: SideID[] = [];\r\n\t\tfor (const player of this.players) {\r\n\t\t\tif (!player.id) validSlots.push(player.slot);\r\n\t\t}\r\n\r\n\t\tif (slot && !validSlots.includes(slot)) {\r\n\t\t\tuser.popup(`This battle already has a user in slot ${slot}.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tif (!validSlots.length) {\r\n\t\t\tuser.popup(`This battle already has two players.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tif (!slot && validSlots.length > 1) {\r\n\t\t\tuser.popup(`Which slot would you like to join into? Use something like \\`/joingame ${validSlots[0]}\\``);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tif (!slot) slot = validSlots[0];\r\n\r\n\t\tif (this[slot].invite === user.id) {\r\n\t\t\tthis.room.auth.set(user.id, Users.PLAYER_SYMBOL);\r\n\t\t} else if (!user.can('joinbattle', null, this.room)) {\r\n\t\t\tuser.popup(`You must be set as a player to join a battle you didn't start. Ask a player to use /addplayer on you to join this battle.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tthis.updatePlayer(this[slot], user, playerOpts);\r\n\t\tthis.needsRejoin?.delete(user.id);\r\n\t\tif (validSlots.length - 1 < 1 && this.missingBattleStartMessage) {\r\n\t\t\tconst users = this.players.map(player => {\r\n\t\t\t\tconst u = player.getUser();\r\n\t\t\t\tif (!u) throw new Error(`User ${player.name} not found on ${this.roomid} battle creation`);\r\n\t\t\t\treturn u;\r\n\t\t\t});\r\n\t\t\tRooms.global.onCreateBattleRoom(users, this.room, {rated: this.rated});\r\n\t\t\tthis.missingBattleStartMessage = false;\r\n\t\t\tthis.started = true;\r\n\t\t\tthis.room.add(`|uhtmlchange|invites|`);\r\n\t\t} else if (!this.started && this.invitesFull()) {\r\n\t\t\tthis.sendInviteForm(true);\r\n\t\t}\r\n\t\tif (user.inRooms.has(this.roomid)) this.onConnect(user);\r\n\t\tthis.room.update();\r\n\t\treturn true;\r\n\t}\r\n\tleaveGame(user: User) {\r\n\t\tif (!user) return false; // ...\r\n\t\tif (this.room.rated || this.room.tour) {\r\n\t\t\tuser.popup(`Players can't be swapped out in a ${this.room.tour ? \"tournament\" : \"rated\"} battle.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player) {\r\n\t\t\tuser.popup(`Failed to leave battle - you're not a player.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tChat.runHandlers('onBattleLeave', user, this.room);\r\n\r\n\t\tthis.updatePlayer(player, null);\r\n\t\tthis.room.auth.set(user.id, '+');\r\n\t\tthis.room.update();\r\n\t\treturn true;\r\n\t}\r\n\r\n\tasync listen() {\r\n\t\tlet disconnected = false;\r\n\t\ttry {\r\n\t\t\tfor await (const next of this.stream) {\r\n\t\t\t\tif (!this.room) return; // room deleted in the middle of simulation\r\n\t\t\t\tthis.receive(next.split('\\n'));\r\n\t\t\t}\r\n\t\t} catch (err: any) {\r\n\t\t\t// Disconnected processes are already crashlogged when they happen;\r\n\t\t\t// also logging every battle room would overwhelm the crashlogger\r\n\t\t\tif (err.message.includes('Process disconnected')) {\r\n\t\t\t\tdisconnected = true;\r\n\t\t\t} else {\r\n\t\t\t\tMonitor.crashlog(err, 'A sim stream');\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (!this.ended) {\r\n\t\t\tthis.room.add(`|bigerror|The simulator process crashed. We've been notified and will fix this ASAP.`);\r\n\t\t\tif (!disconnected) Monitor.crashlog(new Error(`Sim stream interrupted`), `A sim stream`);\r\n\t\t\tthis.started = true;\r\n\t\t\tthis.ended = true;\r\n\t\t\tthis.checkActive();\r\n\t\t}\r\n\t}\r\n\treceive(lines: string[]) {\r\n\t\tfor (const player of this.players) player.wantsTie = false;\r\n\r\n\t\tswitch (lines[0]) {\r\n\t\tcase 'requesteddata':\r\n\t\t\tlines = lines.slice(1);\r\n\t\t\tconst [resolver] = this.dataResolvers!.shift()!;\r\n\t\t\tresolver(lines);\r\n\t\t\tbreak;\r\n\r\n\t\tcase 'update':\r\n\t\t\tfor (const line of lines.slice(1)) {\r\n\t\t\t\tif (line.startsWith('|turn|')) {\r\n\t\t\t\t\tthis.turn = parseInt(line.slice(6));\r\n\t\t\t\t}\r\n\t\t\t\tthis.room.add(line);\r\n\t\t\t\tif (line.startsWith(`|bigerror|You will auto-tie if `) && Config.allowrequestingties && !this.room.tour) {\r\n\t\t\t\t\tthis.room.add(`|-hint|If you want to tie earlier, consider using \\`/offertie\\`.`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tthis.room.update();\r\n\t\t\tif (!this.ended) this.timer.nextRequest();\r\n\t\t\tthis.checkActive();\r\n\t\t\tbreak;\r\n\r\n\t\tcase 'sideupdate': {\r\n\t\t\tconst slot = lines[1] as SideID;\r\n\t\t\tconst player = this[slot];\r\n\t\t\tif (lines[2].startsWith(`|error|[Invalid choice] Can't do anything`)) {\r\n\t\t\t\t// ... should not happen\r\n\t\t\t} else if (lines[2].startsWith(`|error|[Invalid choice]`)) {\r\n\t\t\t\tconst undoFailed = lines[2].includes(`Can't undo`);\r\n\t\t\t\tconst request = this[slot].request;\r\n\t\t\t\trequest.isWait = undoFailed ? 'cantUndo' : false;\r\n\t\t\t\trequest.choice = '';\r\n\t\t\t} else if (lines[2].startsWith(`|request|`)) {\r\n\t\t\t\tthis.rqid++;\r\n\t\t\t\tconst request = JSON.parse(lines[2].slice(9));\r\n\t\t\t\trequest.rqid = this.rqid;\r\n\t\t\t\tconst requestJSON = JSON.stringify(request);\r\n\t\t\t\tthis[slot].request = {\r\n\t\t\t\t\trqid: this.rqid,\r\n\t\t\t\t\trequest: requestJSON,\r\n\t\t\t\t\tisWait: request.wait ? 'cantUndo' : false,\r\n\t\t\t\t\tchoice: '',\r\n\t\t\t\t};\r\n\t\t\t\tthis.requestCount++;\r\n\t\t\t\tif (player) player.sendRoom(`|request|${requestJSON}`);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (player) player.sendRoom(lines[2]);\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tcase 'error': {\r\n\t\t\tif (process.uptime() * 1000 < LOCKDOWN_PERIOD) {\r\n\t\t\t\tconst error = new Error();\r\n\t\t\t\terror.stack = lines.slice(1).join('\\n');\r\n\t\t\t\t// lock down the server\r\n\t\t\t\tRooms.global.startLockdown(error);\r\n\t\t\t}\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tcase 'end':\r\n\t\t\tthis.logData = JSON.parse(lines[1]);\r\n\t\t\tthis.score = this.logData!.score;\r\n\t\t\tthis.inputLog = this.logData!.inputLog;\r\n\t\t\tthis.started = true;\r\n\t\t\tif (!this.ended) {\r\n\t\t\t\tthis.ended = true;\r\n\t\t\t\tvoid this.onEnd(this.logData!.winner);\r\n\t\t\t\tthis.clearPlayers();\r\n\t\t\t}\r\n\t\t\tthis.checkActive();\r\n\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\tasync onEnd(winner: any) {\r\n\t\tthis.timer.end();\r\n\t\t// Declare variables here in case we need them for non-rated battles logging.\r\n\t\tlet p1score = 0.5;\r\n\t\tconst winnerid = toID(winner);\r\n\r\n\t\t// Check if the battle was rated to update the ladder, return its response, and log the battle.\r\n\t\tconst p1name = this.p1.name;\r\n\t\tconst p2name = this.p2.name;\r\n\t\tconst p1id = toID(p1name);\r\n\t\tconst p2id = toID(p2name);\r\n\t\tChat.runHandlers('onBattleEnd', this, winnerid, [p1id, p2id, this.p3?.id, this.p4?.id].filter(Boolean));\r\n\t\tif (this.room.rated) {\r\n\t\t\tthis.room.rated = 0;\r\n\r\n\t\t\tif (winnerid === p1id) {\r\n\t\t\t\tp1score = 1;\r\n\t\t\t} else if (winnerid === p2id) {\r\n\t\t\t\tp1score = 0;\r\n\t\t\t}\r\n\r\n\t\t\twinner = Users.get(winnerid);\r\n\t\t\tif (winner && !winner.registered) {\r\n\t\t\t\tthis.room.sendUser(winner, '|askreg|' + winner.id);\r\n\t\t\t}\r\n\t\t\tconst [score, p1rating, p2rating] = await Ladders(this.ladder).updateRating(p1name, p2name, p1score, this.room);\r\n\t\t\tvoid this.logBattle(score, p1rating, p2rating);\r\n\t\t\tChat.runHandlers('onBattleRanked', this, winnerid, [p1rating, p2rating], [p1id, p2id]);\r\n\t\t} else if (Config.logchallenges) {\r\n\t\t\tif (winnerid === p1id) {\r\n\t\t\t\tp1score = 1;\r\n\t\t\t} else if (winnerid === p2id) {\r\n\t\t\t\tp1score = 0;\r\n\t\t\t}\r\n\t\t\tvoid this.logBattle(p1score);\r\n\t\t} else {\r\n\t\t\tthis.logData = null;\r\n\t\t}\r\n\t\t// If a replay was saved at any point or we were configured to autosavereplays,\r\n\t\t// reupload when the battle is over to overwrite the partial data (and potentially\r\n\t\t// reflect any changes that may have been made to the replay's hidden status).\r\n\t\tif (this.replaySaved || Config.autosavereplays) {\r\n\t\t\tconst uploader = Users.get(winnerid || p1id);\r\n\t\t\tif (uploader?.connections[0]) {\r\n\t\t\t\tChat.parse('/savereplay silent', this.room, uploader, uploader.connections[0]);\r\n\t\t\t}\r\n\t\t}\r\n\t\tconst parentGame = this.room.parent && this.room.parent.game;\r\n\t\t// @ts-ignore - Tournaments aren't TS'd yet\r\n\t\tif (parentGame?.onBattleWin) {\r\n\t\t\t// @ts-ignore\r\n\t\t\tparentGame.onBattleWin(this.room, winnerid);\r\n\t\t}\r\n\t\t// If the room's replay was hidden, disable users from joining after the game is over\r\n\t\tif (this.room.hideReplay) {\r\n\t\t\tthis.room.settings.modjoin = '%';\r\n\t\t\tthis.room.setPrivate('hidden');\r\n\t\t}\r\n\t\tthis.room.update();\r\n\t}\r\n\tasync logBattle(\r\n\t\tp1score: number, p1rating: AnyObject | null = null, p2rating: AnyObject | null = null,\r\n\t\tp3rating: AnyObject | null = null, p4rating: AnyObject | null = null\r\n\t) {\r\n\t\tif (Dex.formats.get(this.format, true).noLog) return;\r\n\t\tconst logData = this.logData;\r\n\t\tif (!logData) return;\r\n\t\tthis.logData = null; // deallocate to save space\r\n\t\tlogData.log = this.room.getLog(-1).split('\\n'); // replay log (exact damage)\r\n\r\n\t\t// delete some redundant data\r\n\t\tfor (const rating of [p1rating, p2rating, p3rating, p4rating]) {\r\n\t\t\tif (rating) {\r\n\t\t\t\tdelete rating.formatid;\r\n\t\t\t\tdelete rating.username;\r\n\t\t\t\tdelete rating.rpsigma;\r\n\t\t\t\tdelete rating.sigma;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlogData.p1rating = p1rating;\r\n\t\tlogData.p2rating = p2rating;\r\n\t\tif (this.playerCap > 2) {\r\n\t\t\tlogData.p3rating = p3rating;\r\n\t\t\tlogData.p4rating = p4rating;\r\n\t\t}\r\n\t\tlogData.endType = this.endType;\r\n\t\tif (!p1rating) logData.ladderError = true;\r\n\t\tconst date = new Date();\r\n\t\tlogData.timestamp = '' + date;\r\n\t\tlogData.roomid = this.room.roomid;\r\n\t\tlogData.format = this.room.format;\r\n\r\n\t\tconst logsubfolder = Chat.toTimestamp(date).split(' ')[0];\r\n\t\tconst logfolder = logsubfolder.split('-', 2).join('-');\r\n\t\tconst tier = Dex.formats.get(this.room.format).id;\r\n\t\tconst logpath = `logs/${logfolder}/${tier}/${logsubfolder}/`;\r\n\r\n\t\tawait FS(logpath).mkdirp();\r\n\t\tawait FS(`${logpath}${this.room.getReplayData().id}.log.json`).write(JSON.stringify(logData));\r\n\t\t// console.log(JSON.stringify(logData));\r\n\t}\r\n\tonConnect(user: User, connection: Connection | null = null) {\r\n\t\t// this handles joining a battle in which a user is a participant,\r\n\t\t// where the user has already identified before attempting to join\r\n\t\t// the battle\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player) return;\r\n\t\tplayer.updateChannel(connection || user);\r\n\t\tconst request = player.request;\r\n\t\tif (request) {\r\n\t\t\tlet data = `|request|${request.request}`;\r\n\t\t\tif (request.choice) data += `\\n|sentchoice|${request.choice}`;\r\n\t\t\t(connection || user).sendTo(this.roomid, data);\r\n\t\t}\r\n\t\tif (!this.started) {\r\n\t\t\tthis.sendInviteForm(connection || user);\r\n\t\t}\r\n\t\tif (!player.active) this.onJoin(user);\r\n\t}\r\n\tonUpdateConnection(user: User, connection: Connection | null = null) {\r\n\t\tthis.onConnect(user, connection);\r\n\t}\r\n\tonRename(user: User, oldUserid: ID, isJoining: boolean, isForceRenamed: boolean) {\r\n\t\tif (user.id === oldUserid) return;\r\n\t\tif (!this.playerTable) {\r\n\t\t\t// !! should never happen but somehow still does\r\n\t\t\tuser.games.delete(this.roomid);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (!(oldUserid in this.playerTable)) {\r\n\t\t\tif (user.id in this.playerTable) {\r\n\t\t\t\t// this handles a user renaming themselves into a user in the\r\n\t\t\t\t// battle (e.g. by using /nick)\r\n\t\t\t\tthis.onConnect(user);\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (!this.allowRenames) {\r\n\t\t\tconst player = this.playerTable[oldUserid];\r\n\t\t\tif (player) {\r\n\t\t\t\tconst message = isForceRenamed ? \" lost by having an inappropriate name.\" : \" forfeited by changing their name.\";\r\n\t\t\t\tthis.forfeitPlayer(player, message);\r\n\t\t\t}\r\n\t\t\tif (!(user.id in this.playerTable)) {\r\n\t\t\t\tuser.games.delete(this.roomid);\r\n\t\t\t}\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (!user.named) {\r\n\t\t\tthis.onLeave(user, oldUserid);\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (user.id in this.playerTable) return;\r\n\t\tconst player = this.playerTable[oldUserid];\r\n\t\tif (player) {\r\n\t\t\tthis.updatePlayer(player, user);\r\n\t\t}\r\n\t\tconst options = {\r\n\t\t\tname: user.name,\r\n\t\t\tavatar: user.avatar,\r\n\t\t};\r\n\t\tvoid this.stream.write(`>player ${player.slot} ` + JSON.stringify(options));\r\n\t}\r\n\tonJoin(user: User) {\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (player && !player.active) {\r\n\t\t\tplayer.active = true;\r\n\t\t\tthis.timer.checkActivity();\r\n\t\t\tthis.room.add(`|player|${player.slot}|${user.name}|${user.avatar}`);\r\n\t\t}\r\n\t}\r\n\tonLeave(user: User, oldUserid?: ID) {\r\n\t\tconst player = this.playerTable[oldUserid || user.id];\r\n\t\tif (player?.active) {\r\n\t\t\tplayer.sendRoom(`|request|null`);\r\n\t\t\tplayer.active = false;\r\n\t\t\tthis.timer.checkActivity();\r\n\t\t\tthis.room.add(`|player|${player.slot}|`);\r\n\t\t}\r\n\t}\r\n\r\n\twin(user: User) {\r\n\t\tif (!user) {\r\n\t\t\tthis.tie();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tconst player = this.playerTable[user.id];\r\n\t\tif (!player) return false;\r\n\t\tvoid this.stream.write(`>forcewin ${player.slot}`);\r\n\t}\r\n\ttie() {\r\n\t\tvoid this.stream.write(`>forcetie`);\r\n\t}\r\n\ttiebreak() {\r\n\t\tvoid this.stream.write(`>tiebreak`);\r\n\t}\r\n\tforfeit(user: User | string, message = '') {\r\n\t\tif (typeof user !== 'string') user = user.id;\r\n\t\telse user = toID(user);\r\n\r\n\t\tif (!(user in this.playerTable)) return false;\r\n\t\treturn this.forfeitPlayer(this.playerTable[user], message);\r\n\t}\r\n\r\n\tforfeitPlayer(player: RoomBattlePlayer, message = '') {\r\n\t\tif (this.ended || !this.started) return false;\r\n\r\n\t\tif (!message) message = ' forfeited.';\r\n\t\tthis.room.add(`|-message|${player.name}${message}`);\r\n\t\tthis.endType = 'forfeit';\r\n\t\t// multi battles, they need to be removed, else they can do things like spam forfeit\r\n\t\tif (this.playerCap > 2) {\r\n\t\t\tplayer.sendRoom(`|request|null`);\r\n\t\t\tthis.removePlayer(player);\r\n\t\t}\r\n\t\tvoid this.stream.write(`>forcelose ${player.slot}`);\r\n\t\treturn true;\r\n\t}\r\n\r\n\t/**\r\n\t * playerOpts should be empty only if importing an inputlog\r\n\t * (so the player isn't recreated)\r\n\t */\r\n\taddPlayer(user: User | null, playerOpts?: RoomBattlePlayerOptions) {\r\n\t\tconst player = super.addPlayer(user);\r\n\t\tif (!player) return null;\r\n\t\tconst slot = player.slot;\r\n\t\tthis[slot] = player;\r\n\r\n\t\tif (playerOpts) {\r\n\t\t\tconst options = {\r\n\t\t\t\tname: player.name,\r\n\t\t\t\tavatar: user ? '' + user.avatar : '',\r\n\t\t\t\tteam: playerOpts.team || undefined,\r\n\t\t\t\trating: Math.round(playerOpts.rating || 0),\r\n\t\t\t};\r\n\t\t\tvoid this.stream.write(`>player ${slot} ${JSON.stringify(options)}`);\r\n\t\t\tplayer.hasTeam = true;\r\n\t\t}\r\n\r\n\t\tif (user) {\r\n\t\t\tthis.room.auth.set(player.id, Users.PLAYER_SYMBOL);\r\n\t\t}\r\n\t\tif (user?.inRooms.has(this.roomid)) this.onConnect(user);\r\n\t\treturn player;\r\n\t}\r\n\r\n\tcheckPrivacySettings(options: RoomBattleOptions & Partial) {\r\n\t\tlet inviteOnly = false;\r\n\t\tconst privacySetter = new Set([]);\r\n\t\tfor (const p of ['p1', 'p2', 'p3', 'p4'] as const) {\r\n\t\t\tconst playerOptions = options[p];\r\n\t\t\tif (playerOptions) {\r\n\t\t\t\tif (playerOptions.inviteOnly) {\r\n\t\t\t\t\tinviteOnly = true;\r\n\t\t\t\t\tprivacySetter.add(playerOptions.user.id);\r\n\t\t\t\t} else if (playerOptions.hidden) {\r\n\t\t\t\t\tprivacySetter.add(playerOptions.user.id);\r\n\t\t\t\t}\r\n\t\t\t\tif (playerOptions.user) this.checkForcedUserSettings(playerOptions.user);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (privacySetter.size) {\r\n\t\t\tconst room = this.room;\r\n\t\t\tif (this.forcedSettings.privacy) {\r\n\t\t\t\troom.setPrivate(false);\r\n\t\t\t\troom.settings.modjoin = null;\r\n\t\t\t\troom.add(`|raw|
This battle is required to be public due to a player having a name starting with '${this.forcedSettings.privacy}'.
`);\r\n\t\t\t} else if (!options.tour || (room.tour?.allowModjoin)) {\r\n\t\t\t\troom.setPrivate('hidden');\r\n\t\t\t\tif (inviteOnly) room.settings.modjoin = '%';\r\n\t\t\t\troom.privacySetter = privacySetter;\r\n\t\t\t\tif (inviteOnly) {\r\n\t\t\t\t\troom.settings.modjoin = '%';\r\n\t\t\t\t\troom.add(`|raw|
This battle is invite-only!
Users must be invited with /invite (or be staff) to join
`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tcheckForcedUserSettings(user: User) {\r\n\t\tthis.forcedSettings = {\r\n\t\t\tmodchat: this.forcedSettings.modchat || RoomBattle.battleForcedSetting(user, 'modchat'),\r\n\t\t\tprivacy: this.forcedSettings.privacy || RoomBattle.battleForcedSetting(user, 'privacy'),\r\n\t\t};\r\n\t\tif (\r\n\t\t\tthis.players.some(p => p.getUser()?.battleSettings.special) ||\r\n\t\t\t(this.rated && this.forcedSettings.modchat)\r\n\t\t) {\r\n\t\t\tthis.room.settings.modchat = '\\u2606';\r\n\t\t}\r\n\t}\r\n\r\n\tstatic battleForcedSetting(user: User, key: 'modchat' | 'privacy') {\r\n\t\tif (Config.forcedpublicprefixes) {\r\n\t\t\tfor (const prefix of Config.forcedpublicprefixes) {\r\n\t\t\t\tChat.plugins['username-prefixes']?.prefixManager.addPrefix(prefix, 'privacy');\r\n\t\t\t}\r\n\t\t\tdelete Config.forcedpublicprefixes;\r\n\t\t}\r\n\t\tif (!Config.forcedprefixes) return null;\r\n\t\tfor (const {type, prefix} of Config.forcedprefixes) {\r\n\t\t\tif (user.id.startsWith(toID(prefix)) && type === key) return prefix;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tmakePlayer(user: User) {\r\n\t\tconst num = (this.players.length + 1) as PlayerIndex;\r\n\t\treturn new RoomBattlePlayer(user, this, num);\r\n\t}\r\n\r\n\tupdatePlayer(player: RoomBattlePlayer, user: User | null, playerOpts?: {team?: string}) {\r\n\t\tsuper.updatePlayer(player, user);\r\n\r\n\t\tplayer.invite = '';\r\n\t\tconst slot = player.slot;\r\n\t\tif (user) {\r\n\t\t\tconst options = {\r\n\t\t\t\tname: player.name,\r\n\t\t\t\tavatar: user.avatar,\r\n\t\t\t\tteam: playerOpts?.team,\r\n\t\t\t};\r\n\t\t\tvoid this.stream.write(`>player ${slot} ` + JSON.stringify(options));\r\n\t\t\tif (playerOpts) player.hasTeam = true;\r\n\r\n\t\t\tthis.room.add(`|player|${slot}|${player.name}|${user.avatar}`);\r\n\t\t} else {\r\n\t\t\tconst options = {\r\n\t\t\t\tname: '',\r\n\t\t\t};\r\n\t\t\tvoid this.stream.write(`>player ${slot} ` + JSON.stringify(options));\r\n\r\n\t\t\tthis.room.add(`|player|${slot}|`);\r\n\t\t}\r\n\t}\r\n\r\n\tstart() {\r\n\t\t// on start\r\n\t\tconst users = this.players.map(player => {\r\n\t\t\tconst user = player.getUser();\r\n\t\t\tif (!user && !this.missingBattleStartMessage) {\r\n\t\t\t\tthrow new Error(`User ${player.name} not found on ${this.roomid} battle creation`);\r\n\t\t\t}\r\n\t\t\treturn user;\r\n\t\t});\r\n\t\tif (!this.missingBattleStartMessage) {\r\n\t\t\t// @ts-ignore The above error should throw if null is found, or this should be skipped\r\n\t\t\tRooms.global.onCreateBattleRoom(users, this.room, {rated: this.rated});\r\n\t\t\tthis.started = true;\r\n\t\t}\r\n\r\n\t\tif (this.gameType === 'multi') {\r\n\t\t\tthis.room.title = `Team ${this.p1.name} vs. Team ${this.p2.name}`;\r\n\t\t} else if (this.gameType === 'freeforall') {\r\n\t\t\t// p1 vs. p2 vs. p3 vs. p4 is too long of a title\r\n\t\t\tthis.room.title = `${this.p1.name} and friends`;\r\n\t\t} else {\r\n\t\t\tthis.room.title = `${this.p1.name} vs. ${this.p2.name}`;\r\n\t\t}\r\n\t\tthis.room.send(`|title|${this.room.title}`);\r\n\t\tconst suspectTest = Chat.plugins['suspect-tests']?.suspectTests[this.format];\r\n\t\tif (suspectTest) {\r\n\t\t\tconst format = Dex.formats.get(this.format);\r\n\t\t\tthis.room.add(\r\n\t\t\t\t`|html|
${format.name} is currently suspecting ${suspectTest.suspect}! ` +\r\n\t\t\t\t`For information on how to participate check out the suspect thread.
`\r\n\t\t\t).update();\r\n\t\t}\r\n\t\tif (this.missingBattleStartMessage === 'multi') {\r\n\t\t\tthis.room.add(`|uhtml|invites|
This is a 4-player challenge battle
The players will need to add more players before the battle can start.
`);\r\n\t\t}\r\n\t}\r\n\r\n\tinvitesFull() {\r\n\t\treturn this.players.every(player => player.id || player.invite);\r\n\t}\r\n\t/** true = send to every player; falsy = send to no one */\r\n\tsendInviteForm(connection: Connection | User | null | boolean) {\r\n\t\tif (connection === true) {\r\n\t\t\tfor (const player of this.players) this.sendInviteForm(player.getUser());\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (!connection) return;\r\n\t\tconst playerForms = this.players.map(player => (\r\n\t\t\tplayer.id ? (\r\n\t\t\t\t`
`\r\n\t\t\t) : player.invite ? (\r\n\t\t\t\t`
`\r\n\t\t\t) : (\r\n\t\t\t\t`
`\r\n\t\t\t)\r\n\t\t));\r\n\t\tif (this.gameType === 'multi') {\r\n\t\t\t[playerForms[1], playerForms[2]] = [playerForms[2], playerForms[1]];\r\n\t\t\tplayerForms.splice(2, 0, '— vs —');\r\n\t\t}\r\n\t\tconnection.sendTo(\r\n\t\t\tthis.room,\r\n\t\t\t`|uhtmlchange|invites|
This battle needs more players to start

${playerForms.join(``)}
`\r\n\t\t);\r\n\t}\r\n\r\n\tclearPlayers() {\r\n\t\tfor (const player of this.players) {\r\n\t\t\tplayer.unlinkUser();\r\n\t\t}\r\n\t}\r\n\r\n\tdestroy() {\r\n\t\tfor (const player of this.players) {\r\n\t\t\tplayer.destroy();\r\n\t\t}\r\n\t\tthis.playerTable = {};\r\n\t\tthis.players = [];\r\n\t\tthis.p1 = null!;\r\n\t\tthis.p2 = null!;\r\n\t\tthis.p3 = null!;\r\n\t\tthis.p4 = null!;\r\n\r\n\t\tthis.ended = true;\r\n\t\tvoid this.stream.destroy();\r\n\t\tif (this.active) {\r\n\t\t\tRooms.global.battleCount += -1;\r\n\t\t\tthis.active = false;\r\n\t\t}\r\n\r\n\t\t(this as any).room = null;\r\n\t\tif (this.dataResolvers) {\r\n\t\t\tfor (const [, reject] of this.dataResolvers) {\r\n\t\t\t\t// reject the promise, make whatever function called it return undefined\r\n\t\t\t\treject(new Error('Battle was destroyed.'));\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\tasync getTeam(user: User | string) {\r\n\t\t// toID extracts user.id\r\n\t\tconst id = toID(user);\r\n\t\tconst player = this.playerTable[id];\r\n\t\tif (!player) return;\r\n\t\tvoid this.stream.write(`>requestteam ${player.slot}`);\r\n\t\tconst teamDataPromise = new Promise((resolve, reject) => {\r\n\t\t\tif (!this.dataResolvers) this.dataResolvers = [];\r\n\t\t\tthis.dataResolvers.push([resolve, reject]);\r\n\t\t});\r\n\t\tconst resultStrings = await teamDataPromise;\r\n\t\tif (!resultStrings) return;\r\n\t\tconst result = Teams.unpack(resultStrings[0]);\r\n\t\treturn result;\r\n\t}\r\n\tonChatMessage(message: string, user: User) {\r\n\t\tconst parts = message.split('\\n');\r\n\t\tfor (const line of parts) {\r\n\t\t\tvoid this.stream.write(`>chat-inputlogonly ${user.getIdentity(this.room)}|${line}`);\r\n\t\t}\r\n\t}\r\n\tasync getLog(): Promise {\r\n\t\tif (!this.logData) this.logData = {};\r\n\t\tvoid this.stream.write('>requestlog');\r\n\t\tconst logPromise = new Promise((resolve, reject) => {\r\n\t\t\tif (!this.dataResolvers) this.dataResolvers = [];\r\n\t\t\tthis.dataResolvers.push([resolve, reject]);\r\n\t\t});\r\n\t\tconst result = await logPromise;\r\n\t\treturn result;\r\n\t}\r\n}\r\n\r\nexport class RoomBattleStream extends BattleStream {\r\n\treadonly battle: Battle;\r\n\tconstructor() {\r\n\t\tsuper({keepAlive: true});\r\n\t\tthis.battle = null!;\r\n\t}\r\n\r\n\t_write(chunk: string) {\r\n\t\tconst startTime = Date.now();\r\n\t\tif (this.battle && Config.debugsimprocesses && process.send) {\r\n\t\t\tprocess.send('DEBUG\\n' + this.battle.inputLog.join('\\n') + '\\n' + chunk);\r\n\t\t}\r\n\t\ttry {\r\n\t\t\tthis._writeLines(chunk);\r\n\t\t} catch (err: any) {\r\n\t\t\tconst battle = this.battle;\r\n\t\t\tMonitor.crashlog(err, 'A battle', {\r\n\t\t\t\tchunk,\r\n\t\t\t\tinputLog: battle ? '\\n' + battle.inputLog.join('\\n') : '',\r\n\t\t\t\tlog: battle ? '\\n' + battle.getDebugLog() : '',\r\n\t\t\t});\r\n\r\n\t\t\tthis.push(`update\\n|html|
The battle crashed
Don't worry, we're working on fixing it.
`);\r\n\t\t\tif (battle) {\r\n\t\t\t\tfor (const side of battle.sides) {\r\n\t\t\t\t\tif (side?.requestState) {\r\n\t\t\t\t\t\tthis.push(`sideupdate\\n${side.id}\\n|error|[Invalid choice] The battle crashed`);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t// public crashlogs only have the stack anyways\r\n\t\t\tthis.push(`error\\n${err.stack}`);\r\n\t\t}\r\n\t\tif (this.battle) this.battle.sendUpdates();\r\n\t\tconst deltaTime = Date.now() - startTime;\r\n\t\tif (deltaTime > 1000) {\r\n\t\t\tMonitor.slow(`[slow battle] ${deltaTime}ms - ${chunk.replace(/\\n/ig, ' | ')}`);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n/*********************************************************\r\n * Process manager\r\n *********************************************************/\r\n\r\nexport const PM = new ProcessManager.StreamProcessManager(module, () => new RoomBattleStream(), message => {\r\n\tif (message.startsWith(`SLOW\\n`)) {\r\n\t\tMonitor.slow(message.slice(5));\r\n\t}\r\n});\r\n\r\nif (!PM.isParentProcess) {\r\n\t// This is a child process!\r\n\tglobal.Config = require('./config-loader').Config;\r\n\tglobal.Dex = require('../sim/dex').Dex;\r\n\tglobal.Monitor = {\r\n\t\tcrashlog(error: Error, source = 'A simulator process', details: AnyObject | null = null) {\r\n\t\t\tconst repr = JSON.stringify([error.name, error.message, source, details]);\r\n\t\t\tprocess.send!(`THROW\\n@!!@${repr}\\n${error.stack}`);\r\n\t\t},\r\n\t\tslow(text: string) {\r\n\t\t\tprocess.send!(`CALLBACK\\nSLOW\\n${text}`);\r\n\t\t},\r\n\t};\r\n\tglobal.__version = {head: ''};\r\n\ttry {\r\n\t\tconst head = execSync('git rev-parse HEAD', {\r\n\t\t\tstdio: ['ignore', 'pipe', 'ignore'],\r\n\t\t});\r\n\t\tconst merge = execSync('git merge-base origin/master HEAD', {\r\n\t\t\tstdio: ['ignore', 'pipe', 'ignore'],\r\n\t\t});\r\n\t\tglobal.__version.head = ('' + head).trim();\r\n\t\tconst origin = ('' + merge).trim();\r\n\t\tif (origin !== global.__version.head) global.__version.origin = origin;\r\n\t} catch {}\r\n\r\n\tif (Config.crashguard) {\r\n\t\t// graceful crash - allow current battles to finish before restarting\r\n\t\tprocess.on('uncaughtException', err => {\r\n\t\t\tMonitor.crashlog(err, 'A simulator process');\r\n\t\t});\r\n\t\tprocess.on('unhandledRejection', err => {\r\n\t\t\tMonitor.crashlog(err as any || {}, 'A simulator process Promise');\r\n\t\t});\r\n\t}\r\n\r\n\t// eslint-disable-next-line no-eval\r\n\tRepl.start(`sim-${process.pid}`, cmd => eval(cmd));\r\n} else {\r\n\tPM.spawn(global.Config ? Config.simulatorprocesses : 1);\r\n}\r\n"], "mappings": "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", "names": ["player"] }