{ "version": 3, "sources": ["../../../server/ladders.ts"], "sourcesContent": ["/**\r\n * Matchmaker\r\n * Pokemon Showdown - http://pokemonshowdown.com/\r\n *\r\n * This keeps track of challenges to battle made between users, setting up\r\n * matches between users looking for a battle, and starting new battles.\r\n *\r\n * @license MIT\r\n */\r\n\r\nconst LadderStore: typeof import('./ladders-remote').LadderStore = (\r\n\ttypeof Config === 'object' && Config.remoteladder ? require('./ladders-remote') : require('./ladders-local')\r\n).LadderStore;\r\n\r\nconst SECONDS = 1000;\r\nconst PERIODIC_MATCH_INTERVAL = 60 * SECONDS;\r\n\r\nimport type {ChallengeType} from './room-battle';\r\nimport {BattleReady, BattleChallenge, GameChallenge, BattleInvite, challenges} from './ladders-challenges';\r\n\r\n/**\r\n * Keys are formatids\r\n */\r\nconst searches = new Map,\r\n}>();\r\n\r\n/**\r\n * This keeps track of searches for battles, creating a new battle for a newly\r\n * added search if a valid match can be made, otherwise periodically\r\n * attempting to make a match with looser restrictions until one can be made.\r\n */\r\nclass Ladder extends LadderStore {\r\n\tconstructor(formatid: string) {\r\n\t\tsuper(formatid);\r\n\t}\r\n\r\n\tasync prepBattle(connection: Connection, challengeType: ChallengeType, team: string | null = null, isRated = false) {\r\n\t\t// all validation for a battle goes through here\r\n\t\tconst user = connection.user;\r\n\t\tconst userid = user.id;\r\n\t\tif (team === null) team = user.battleSettings.team;\r\n\r\n\t\tif (Rooms.global.lockdown && Rooms.global.lockdown !== 'pre') {\r\n\t\t\tlet message = `The server is restarting. Battles will be available again in a few minutes.`;\r\n\t\t\tif (Rooms.global.lockdown === 'ddos') {\r\n\t\t\t\tmessage = `The server is under attack. Battles cannot be started at this time.`;\r\n\t\t\t}\r\n\t\t\tconnection.popup(message);\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tif (Punishments.isBattleBanned(user)) {\r\n\t\t\tconnection.popup(`You are barred from starting any new games until your battle ban expires.`);\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tconst gameCount = user.games.size;\r\n\t\tif (Monitor.countConcurrentBattle(gameCount, connection)) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tif (Monitor.countPrepBattle(connection.ip, connection)) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tthis.formatid = Dex.formats.validate(this.formatid);\r\n\t\t} catch (e: any) {\r\n\t\t\tconnection.popup(`Your selected format is invalid:\\n\\n- ${e.message}`);\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tlet rating = 0;\r\n\t\tlet valResult;\r\n\t\tlet removeNicknames = !!(user.locked || user.namelocked);\r\n\r\n\r\n\t\tconst regex = /(?:^|])([^|]*)\\|([^|]*)\\|/g;\r\n\t\tlet match = regex.exec(team);\r\n\t\tlet unownWord = '';\r\n\t\twhile (match) {\r\n\t\t\tconst nickname = match[1];\r\n\t\t\tconst speciesid = toID(match[2] || match[1]);\r\n\t\t\tif (speciesid.length <= 6 && speciesid.startsWith('unown')) {\r\n\t\t\t\tunownWord += speciesid.charAt(5) || 'a';\r\n\t\t\t}\r\n\t\t\tif (nickname) {\r\n\t\t\t\tconst filtered = Chat.nicknamefilter(nickname, user);\r\n\t\t\t\tif (typeof filtered === 'string' && (!filtered || filtered !== match[1])) {\r\n\t\t\t\t\tconnection.popup(\r\n\t\t\t\t\t\t`Your team was rejected for the following reason:\\n\\n` +\r\n\t\t\t\t\t\t`- Your Pok\u00E9mon has a banned nickname: ${match[1]}`\r\n\t\t\t\t\t);\r\n\t\t\t\t\treturn null;\r\n\t\t\t\t} else if (filtered === false) {\r\n\t\t\t\t\tremoveNicknames = true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tmatch = regex.exec(team);\r\n\t\t}\r\n\t\tif (unownWord) {\r\n\t\t\tconst filtered = Chat.nicknamefilter(unownWord, user);\r\n\t\t\tif (!filtered || filtered !== unownWord) {\r\n\t\t\t\tconnection.popup(\r\n\t\t\t\t\t`Your team was rejected for the following reason:\\n\\n` +\r\n\t\t\t\t\t`- Your Unowns spell out a banned word: ${unownWord.toUpperCase()}`\r\n\t\t\t\t);\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (isRated && !Ladders.disabled) {\r\n\t\t\tconst uid = user.id;\r\n\t\t\t[valResult, rating] = await Promise.all([\r\n\t\t\t\tTeamValidatorAsync.get(this.formatid).validateTeam(team, {removeNicknames}),\r\n\t\t\t\tthis.getRating(uid),\r\n\t\t\t]);\r\n\t\t\tif (uid !== user.id) {\r\n\t\t\t\t// User feedback for renames handled elsewhere.\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\tif (!rating) rating = 1;\r\n\t\t} else {\r\n\t\t\tif (Ladders.disabled) {\r\n\t\t\t\tconnection.popup(`The ladder is temporarily disabled due to technical difficulties - you will not receive ladder rating for this game.`);\r\n\t\t\t\trating = 1;\r\n\t\t\t}\r\n\t\t\tconst validator = TeamValidatorAsync.get(this.formatid);\r\n\t\t\tvalResult = await validator.validateTeam(team, {removeNicknames});\r\n\t\t}\r\n\r\n\t\tif (!valResult.startsWith('1')) {\r\n\t\t\tconnection.popup(\r\n\t\t\t\t`Your team was rejected for the following reasons:\\n\\n` +\r\n\t\t\t\t`- ` + valResult.slice(1).replace(/\\n/g, `\\n- `)\r\n\t\t\t);\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tconst settings = {...user.battleSettings, team: valResult.slice(1)};\r\n\t\tuser.battleSettings.inviteOnly = false;\r\n\t\tuser.battleSettings.hidden = false;\r\n\t\treturn new BattleReady(userid, this.formatid, settings, rating, challengeType);\r\n\t}\r\n\r\n\tstatic getChallenging(userid: ID) {\r\n\t\tconst userChalls = Ladders.challenges.get(userid);\r\n\t\tif (userChalls) {\r\n\t\t\tfor (const chall of userChalls) {\r\n\t\t\t\tif (chall.from === userid) return chall;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tasync makeChallenge(connection: Connection, targetUser: User) {\r\n\t\tconst user = connection.user;\r\n\t\tif (targetUser === user) {\r\n\t\t\tconnection.popup(`You can't battle yourself. The best you can do is open PS in Private Browsing (or another browser) and log into a different username, and battle that username.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (Ladder.getChallenging(user.id)) {\r\n\t\t\tconnection.popup(`You are already challenging someone. Cancel that challenge before challenging someone else.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tlet blockChallenge: boolean;\r\n\t\tif (typeof targetUser.settings.blockChallenges === 'boolean') {\r\n\t\t\tblockChallenge = targetUser.settings.blockChallenges;\r\n\t\t} else if (targetUser.settings.blockChallenges === 'friends') {\r\n\t\t\tblockChallenge = !targetUser.friends?.has(user.id);\r\n\t\t} else {\r\n\t\t\tblockChallenge = !Users.globalAuth.atLeast(user, targetUser.settings.blockChallenges);\r\n\t\t}\r\n\r\n\t\tif (blockChallenge && !user.can('bypassblocks', targetUser)) {\r\n\t\t\tconnection.popup(`The user '${targetUser.name}' is not accepting challenges right now.`);\r\n\t\t\tChat.maybeNotifyBlocked('challenge', targetUser, user);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tif (Date.now() < user.lastChallenge + 10 * SECONDS && !Config.nothrottle) {\r\n\t\t\t// 10 seconds ago, probable misclick\r\n\t\t\tconnection.popup(`You challenged less than 10 seconds after your last challenge! It's cancelled in case it's a misclick.`);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tconst currentChallenges = Ladders.challenges.get(targetUser.id);\r\n\t\tif (currentChallenges && currentChallenges.length >= 3 && !user.autoconfirmed) {\r\n\t\t\tconnection.popup(\r\n\t\t\t\t`This user already has 3 pending challenges.\\n` +\r\n\t\t\t\t`You must be autoconfirmed to challenge them.`\r\n\t\t\t);\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tconst ready = await this.prepBattle(connection, 'challenge');\r\n\t\tif (!ready) return false;\r\n\t\t// If our target is already challenging us in the same format,\r\n\t\t// simply accept the pending challenge instead of creating a new one.\r\n\t\tconst existingChall = Ladders.challenges.search(user.id, targetUser.id);\r\n\t\tif (existingChall) {\r\n\t\t\tif (\r\n\t\t\t\texistingChall.from === targetUser.id &&\r\n\t\t\t\texistingChall.to === user.id &&\r\n\t\t\t\texistingChall.format === this.formatid &&\r\n\t\t\t\texistingChall.ready\r\n\t\t\t) {\r\n\t\t\t\tif (Ladders.challenges.remove(existingChall)) {\r\n\t\t\t\t\tLadders.match([existingChall.ready, ready]);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tconnection.popup(`There's already a challenge (${existingChall.format}) between you and ${targetUser.name}!`);\r\n\t\t\t\tLadders.challenges.update(user.id, targetUser.id);\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t\tLadders.challenges.add(new BattleChallenge(user.id, targetUser.id, ready));\r\n\t\tLadders.challenges.send(user.id, targetUser.id, `/log ${user.name} wants to battle!`);\r\n\t\tuser.lastChallenge = Date.now();\r\n\t\treturn true;\r\n\t}\r\n\tstatic async acceptChallenge(connection: Connection, chall: BattleChallenge) {\r\n\t\tconst ladder = Ladders(chall.format);\r\n\t\tconst ready = await ladder.prepBattle(connection, 'challenge');\r\n\t\tif (!ready) return;\r\n\t\tif (Ladders.challenges.remove(chall)) {\r\n\t\t\treturn Ladders.match([chall.ready, ready]);\r\n\t\t}\r\n\t\treturn;\r\n\t}\r\n\r\n\tcancelSearch(user: User) {\r\n\t\tconst formatid = toID(this.formatid);\r\n\r\n\t\tconst formatTable = Ladders.searches.get(formatid);\r\n\t\tif (!formatTable) return false;\r\n\t\tif (!formatTable.searches.has(user.id)) return false;\r\n\t\tformatTable.searches.delete(user.id);\r\n\r\n\t\tLadder.updateSearch(user);\r\n\t\treturn true;\r\n\t}\r\n\r\n\tstatic cancelSearches(user: User) {\r\n\t\tlet cancelCount = 0;\r\n\r\n\t\tfor (const formatTable of Ladders.searches.values()) {\r\n\t\t\tconst search = formatTable.searches.get(user.id);\r\n\t\t\tif (!search) continue;\r\n\t\t\tformatTable.searches.delete(user.id);\r\n\t\t\tcancelCount++;\r\n\t\t}\r\n\r\n\t\tLadder.updateSearch(user);\r\n\t\treturn cancelCount;\r\n\t}\r\n\r\n\tgetSearcher(search: BattleReady) {\r\n\t\tconst formatid = toID(this.formatid);\r\n\t\tconst user = Users.get(search.userid);\r\n\t\tif (!user?.connected || user.id !== search.userid) {\r\n\t\t\tconst formatTable = Ladders.searches.get(formatid);\r\n\t\t\tif (formatTable) formatTable.searches.delete(search.userid);\r\n\t\t\tif (user?.connected) {\r\n\t\t\t\tuser.popup(`You changed your name and are no longer looking for a battle in ${formatid}`);\r\n\t\t\t\tLadder.updateSearch(user);\r\n\t\t\t}\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\treturn user;\r\n\t}\r\n\r\n\tstatic getSearches(user: User) {\r\n\t\tconst userSearches = [];\r\n\t\tfor (const [formatid, formatTable] of Ladders.searches) {\r\n\t\t\tif (formatTable.searches.has(user.id)) userSearches.push(formatid);\r\n\t\t}\r\n\t\treturn userSearches;\r\n\t}\r\n\tstatic updateSearch(user: User, connection: Connection | null = null) {\r\n\t\tlet games: {[k: string]: string} | null = {};\r\n\t\tlet atLeastOne = false;\r\n\t\tfor (const roomid of user.games) {\r\n\t\t\tconst room = Rooms.get(roomid);\r\n\t\t\tif (!room) {\r\n\t\t\t\tMonitor.warn(`while searching, room ${roomid} expired for user ${user.id} in rooms ${[...user.inRooms]} and games ${[...user.games]}`);\r\n\t\t\t\tuser.games.delete(roomid);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tconst game = room.game;\r\n\t\t\tif (!game) {\r\n\t\t\t\tMonitor.warn(`while searching, room ${roomid} has no game for user ${user.id} in rooms ${[...user.inRooms]} and games ${[...user.games]}`);\r\n\t\t\t\tuser.games.delete(roomid);\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tgames[roomid] = game.title + (game.allowRenames ? '' : '*');\r\n\t\t\tatLeastOne = true;\r\n\t\t}\r\n\t\tif (!atLeastOne) games = null;\r\n\t\tconst searching = Ladders.getSearches(user);\r\n\t\t(connection || user).send(`|updatesearch|` + JSON.stringify({\r\n\t\t\tsearching,\r\n\t\t\tgames,\r\n\t\t}));\r\n\t}\r\n\thasSearch(user: User) {\r\n\t\tconst formatid = toID(this.formatid);\r\n\t\tconst formatTable = Ladders.searches.get(formatid);\r\n\t\tif (!formatTable) return false;\r\n\t\treturn formatTable.searches.has(user.id);\r\n\t}\r\n\r\n\t/**\r\n\t * Validates a user's team and fetches their rating for a given format\r\n\t * before creating a search for a battle.\r\n\t */\r\n\tasync searchBattle(user: User, connection: Connection) {\r\n\t\tif (!user.connected) return;\r\n\r\n\t\tconst format = Dex.formats.get(this.formatid);\r\n\t\tif (!format.searchShow) {\r\n\t\t\tconnection.popup(`Error: Your format ${format.id} is not ladderable.`);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst oldUserid = user.id;\r\n\t\tconst search = await this.prepBattle(connection, format.rated ? 'rated' : 'unrated', null, format.rated !== false);\r\n\r\n\t\tif (oldUserid !== user.id) return;\r\n\t\tif (!search) return;\r\n\r\n\t\tthis.addSearch(search, user);\r\n\t}\r\n\r\n\t/**\r\n\t * Verifies whether or not a match made between two users is valid. Returns\r\n\t */\r\n\tmatchmakingOK(matches: [BattleReady, User][]) {\r\n\t\tconst formatid = toID(this.formatid);\r\n\t\tconst users = matches.map(([ready, user]) => user);\r\n\t\tconst userids = users.map(user => user.id);\r\n\r\n\t\t// users must be different\r\n\t\tif (new Set(users).size !== users.length) return false;\r\n\r\n\t\tif (Config.noipchecks) {\r\n\t\t\tusers[0].lastMatch = users[1].id;\r\n\t\t\tusers[1].lastMatch = users[0].id;\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t// users must have different IPs\r\n\t\tif (new Set(users.map(user => user.latestIp)).size !== users.length) return false;\r\n\r\n\t\t// users must not have been matched immediately previously\r\n\t\tfor (const user of users) {\r\n\t\t\tif (userids.includes(user.lastMatch)) return false;\r\n\t\t}\r\n\r\n\t\t// search must be within range\r\n\t\tlet searchRange = 100;\r\n\t\tconst times = matches.map(([search]) => search.time);\r\n\t\tconst elapsed = Date.now() - Math.min(...times);\r\n\t\tif (formatid === 'gen8ou' || formatid === 'gen8oucurrent' ||\r\n\t\t\t\tformatid === 'gen8oususpecttest' || formatid === 'gen8randombattle') {\r\n\t\t\tsearchRange = 50;\r\n\t\t}\r\n\r\n\t\tsearchRange += elapsed / 300; // +1 every .3 seconds\r\n\t\tif (searchRange > 300) searchRange = 300 + (searchRange - 300) / 10; // +1 every 3 sec after 300\r\n\t\tif (searchRange > 600) searchRange = 600;\r\n\t\tconst ratings = matches.map(([search]) => search.rating);\r\n\t\tif (Math.max(...ratings) - Math.min(...ratings) > searchRange) return false;\r\n\r\n\t\tmatches[0][1].lastMatch = matches[1][1].id;\r\n\t\tmatches[1][1].lastMatch = matches[0][1].id;\r\n\t\treturn true;\r\n\t}\r\n\r\n\t/**\r\n\t * Starts a search for a battle for a user under the given format.\r\n\t */\r\n\taddSearch(newSearch: BattleReady, user: User) {\r\n\t\tconst formatid = newSearch.formatid;\r\n\t\tlet formatTable = Ladders.searches.get(formatid);\r\n\t\tif (!formatTable) {\r\n\t\t\tformatTable = {\r\n\t\t\t\tnumPlayers: ['multi', 'freeforall'].includes(Dex.formats.get(formatid).gameType) ? 4 : 2,\r\n\t\t\t\tsearches: new Map(),\r\n\t\t\t};\r\n\t\t\tLadders.searches.set(formatid, formatTable);\r\n\t\t}\r\n\t\tif (formatTable.searches.has(user.id)) {\r\n\t\t\tuser.popup(`Couldn't search: You are already searching for a ${formatid} battle.`);\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tconst matches = [newSearch];\r\n\t\t// In order from longest waiting to shortest waiting\r\n\t\tfor (const search of formatTable.searches.values()) {\r\n\t\t\tconst searcher = this.getSearcher(search);\r\n\t\t\tif (!searcher) continue;\r\n\t\t\tconst matched = this.matchmakingOK([[search, searcher], [newSearch, user]]);\r\n\t\t\tif (matched) {\r\n\t\t\t\tmatches.push(search);\r\n\t\t\t}\r\n\t\t\tif (matches.length >= formatTable.numPlayers) {\r\n\t\t\t\tfor (const matchedSearch of matches) formatTable.searches.delete(matchedSearch.userid);\r\n\t\t\t\tLadder.match(matches);\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tformatTable.searches.set(newSearch.userid, newSearch);\r\n\t\tLadder.updateSearch(user);\r\n\t}\r\n\r\n\t/**\r\n\t * Creates a match for a new battle for each format in this.searches if a\r\n\t * valid match can be made. This is run periodically depending on\r\n\t * PERIODIC_MATCH_INTERVAL.\r\n\t */\r\n\tstatic periodicMatch() {\r\n\t\t// In order from longest waiting to shortest waiting\r\n\t\tfor (const [formatid, formatTable] of Ladders.searches) {\r\n\t\t\tif (formatTable.numPlayers > 2) continue; // TODO: implement\r\n\t\t\tconst matchmaker = Ladders(formatid);\r\n\t\t\tlet longest: [BattleReady, User] | null = null;\r\n\t\t\tfor (const search of formatTable.searches.values()) {\r\n\t\t\t\tif (!longest) {\r\n\t\t\t\t\tconst longestSearcher = matchmaker.getSearcher(search);\r\n\t\t\t\t\tif (!longestSearcher) continue;\r\n\t\t\t\t\tlongest = [search, longestSearcher];\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t\tconst searcher = matchmaker.getSearcher(search);\r\n\t\t\t\tif (!searcher) continue;\r\n\r\n\t\t\t\tconst [longestSearch, longestSearcher] = longest;\r\n\t\t\t\tconst matched = matchmaker.matchmakingOK([[search, searcher], [longestSearch, longestSearcher]]);\r\n\t\t\t\tif (matched) {\r\n\t\t\t\t\tformatTable.searches.delete(search.userid);\r\n\t\t\t\t\tformatTable.searches.delete(longestSearch.userid);\r\n\t\t\t\t\tLadder.match([longestSearch, search]);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tstatic match(readies: BattleReady[]) {\r\n\t\tconst formatid = readies[0].formatid;\r\n\t\tif (readies.some(ready => ready.formatid !== formatid)) throw new Error(`Format IDs don't match`);\r\n\t\tconst players = [];\r\n\t\tlet missingUser = null;\r\n\t\tlet minRating = Infinity;\r\n\t\tfor (const ready of readies) {\r\n\t\t\tconst user = Users.get(ready.userid);\r\n\t\t\tif (!user) {\r\n\t\t\t\tmissingUser = ready.userid;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tplayers.push({\r\n\t\t\t\tuser,\r\n\t\t\t\tteam: ready.settings.team,\r\n\t\t\t\trating: ready.rating,\r\n\t\t\t\thidden: ready.settings.hidden,\r\n\t\t\t\tinviteOnly: ready.settings.inviteOnly,\r\n\t\t\t});\r\n\t\t\tif (ready.rating < minRating) minRating = ready.rating;\r\n\t\t}\r\n\t\tif (missingUser) {\r\n\t\t\tfor (const ready of readies) {\r\n\t\t\t\tUsers.get(ready.userid)?.popup(`Sorry, your opponent ${missingUser} went offline before your battle could start.`);\r\n\t\t\t}\r\n\t\t\treturn undefined;\r\n\t\t}\r\n\t\tconst format = Dex.formats.get(formatid);\r\n\t\tconst delayedStart = (['multi', 'freeforall'].includes(format.gameType) && players.length === 2) ?\r\n\t\t\t'multi' : false;\r\n\t\treturn Rooms.createBattle({\r\n\t\t\tformat: formatid,\r\n\t\t\tp1: players[0],\r\n\t\t\tp2: players[1],\r\n\t\t\tp3: players[2],\r\n\t\t\tp4: players[3],\r\n\t\t\trated: minRating,\r\n\t\t\tchallengeType: readies[0].challengeType,\r\n\t\t\tdelayedStart,\r\n\t\t});\r\n\t}\r\n}\r\n\r\nfunction getLadder(formatid: string) {\r\n\treturn new Ladder(formatid);\r\n}\r\n\r\nconst periodicMatchInterval = setInterval(\r\n\t() => Ladder.periodicMatch(),\r\n\tPERIODIC_MATCH_INTERVAL\r\n);\r\n\r\nexport const Ladders = Object.assign(getLadder, {\r\n\tBattleReady,\r\n\tLadderStore,\r\n\tLadder,\r\n\r\n\tBattleChallenge,\r\n\tGameChallenge,\r\n\tBattleInvite,\r\n\r\n\tcancelSearches: Ladder.cancelSearches,\r\n\tupdateSearch: Ladder.updateSearch,\r\n\tacceptChallenge: Ladder.acceptChallenge,\r\n\tvisualizeAll: Ladder.visualizeAll,\r\n\tgetSearches: Ladder.getSearches,\r\n\tmatch: Ladder.match,\r\n\r\n\tsearches,\r\n\tchallenges,\r\n\tperiodicMatchInterval,\r\n\r\n\t// tells the client to ask the server for format information\r\n\tformatsListPrefix: LadderStore.formatsListPrefix,\r\n\tdisabled: false as boolean | 'db',\r\n});\r\n"], "mappings": "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", "names": ["longestSearcher"] }