{
"version": 3,
"sources": ["../../server/ladders-remote.ts"],
"sourcesContent": ["/**\n * Main server ladder library\n * Pokemon Showdown - http://pokemonshowdown.com/\n *\n * This file handles ladders for the main server on\n * play.pokemonshowdown.com.\n *\n * Ladders for all other servers is handled by ladders.ts.\n *\n * Matchmaking is currently still implemented in rooms.ts.\n *\n * @license MIT\n */\nimport { Utils } from '../lib';\n\nexport class LadderStore {\n\tformatid: string;\n\tstatic readonly formatsListPrefix = '';\n\n\tconstructor(formatid: string) {\n\t\tthis.formatid = formatid;\n\t}\n\n\t/**\n\t * Returns [formatid, html], where html is an the HTML source of a\n\t * ladder toplist, to be displayed directly in the ladder tab of the\n\t * client.\n\t */\n\t// This requires to be `async` because it must conform with the `LadderStore` interface\n\t// eslint-disable-next-line @typescript-eslint/require-await\n\tasync getTop(prefix?: string): Promise<[string, string] | null> {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns a Promise for the Elo rating of a user\n\t */\n\tasync getRating(userid: string) {\n\t\tconst formatid = this.formatid;\n\t\tconst user = Users.getExact(userid);\n\t\tif (user?.mmrCache[formatid]) {\n\t\t\treturn user.mmrCache[formatid];\n\t\t}\n\t\tconst [data] = await LoginServer.request('mmr', {\n\t\t\tformat: formatid,\n\t\t\tuser: userid,\n\t\t});\n\t\tlet mmr = NaN;\n\t\tif (data && !data.errorip) {\n\t\t\tmmr = Number(data);\n\t\t}\n\t\tif (isNaN(mmr)) return 1000;\n\n\t\tif (user && user.id === userid) {\n\t\t\tuser.mmrCache[formatid] = mmr;\n\t\t}\n\t\treturn mmr;\n\t}\n\n\t/**\n\t * Update the Elo rating for two players after a battle, and display\n\t * the results in the passed room.\n\t */\n\tasync updateRating(p1name: string, p2name: string, p1score: number, room: AnyObject): Promise<[\n\t\tnumber, AnyObject | undefined | null, AnyObject | undefined | null,\n\t]> {\n\t\tif (Ladders.disabled) {\n\t\t\troom.addRaw(`Ratings not updated. The ladders are currently disabled.`).update();\n\t\t\treturn [p1score, null, null];\n\t\t}\n\n\t\tconst formatid = this.formatid;\n\t\tconst p1 = Users.getExact(p1name);\n\t\tconst p2 = Users.getExact(p2name);\n\t\tconst p1id = toID(p1name);\n\t\tconst p2id = toID(p2name);\n\n\t\tconst ladderUpdatePromise = LoginServer.request('ladderupdate', {\n\t\t\tp1: p1name,\n\t\t\tp2: p2name,\n\t\t\tscore: p1score,\n\t\t\tformat: formatid,\n\t\t});\n\n\t\t// calculate new Elo scores and display to room while loginserver updates the ladder\n\t\tconst [p1OldElo, p2OldElo] = (await Promise.all([this.getRating(p1id), this.getRating(p2id)])).map(Math.round);\n\t\tconst p1NewElo = Math.round(this.calculateElo(p1OldElo, p1score, p2OldElo));\n\t\tconst p2NewElo = Math.round(this.calculateElo(p2OldElo, 1 - p1score, p1OldElo));\n\n\t\tconst p1Act = (p1score > 0.9 ? `winning` : (p1score < 0.1 ? `losing` : `tying`));\n\t\tlet p1Reasons = `${p1NewElo - p1OldElo} for ${p1Act}`;\n\t\tif (!p1Reasons.startsWith('-')) p1Reasons = '+' + p1Reasons;\n\t\troom.addRaw(Utils.html`${p1name}'s rating: ${p1OldElo} → ${p1NewElo}
(${p1Reasons})`);\n\n\t\tconst p2Act = (p1score > 0.9 || p1score < 0 ? `losing` : (p1score < 0.1 ? `winning` : `tying`));\n\t\tlet p2Reasons = `${p2NewElo - p2OldElo} for ${p2Act}`;\n\t\tif (!p2Reasons.startsWith('-'))\tp2Reasons = '+' + p2Reasons;\n\t\troom.addRaw(Utils.html`${p2name}'s rating: ${p2OldElo} → ${p2NewElo}
(${p2Reasons})`);\n\n\t\troom.rated = Math.min(p1NewElo, p2NewElo);\n\n\t\tif (p1) p1.mmrCache[formatid] = +p1NewElo;\n\t\tif (p2) p2.mmrCache[formatid] = +p2NewElo;\n\n\t\troom.update();\n\n\t\tconst [data, error] = await ladderUpdatePromise;\n\n\t\tlet problem = false;\n\t\tif (error) {\n\t\t\tif (error.message !== 'stream interrupt') {\n\t\t\t\troom.add(`||Ladder isn't responding, score probably updated but might not have (${error.message}).`);\n\t\t\t\tproblem = true;\n\t\t\t}\n\t\t} else if (!room.battle) {\n\t\t\tproblem = true;\n\t\t} else if (!data) {\n\t\t\troom.add(`|error|Unexpected response ${data} from ladder server.`);\n\t\t\troom.update();\n\t\t\tproblem = true;\n\t\t} else if (data.errorip) {\n\t\t\troom.add(`|error|This server's request IP ${data.errorip} is not a registered server.`);\n\t\t\troom.add(`|error|You should be using ladders.js and not ladders-remote.js for ladder tracking.`);\n\t\t\troom.update();\n\t\t\tproblem = true;\n\t\t}\n\n\t\tif (problem) {\n\t\t\t// We used to clear mmrCache for the format to get the users updated rating next search\n\t\t\t// we now no longer do that because that results in the user getting paired with other users as though they have 1000 elo\n\t\t\t// if the next query times out, which happens very frequently. This results in a lot of confusion, so we're just\n\t\t\t// going to not clear this cache. If the user gets the proper rating later - great. If they don't,\n\t\t\t// this will ensure they still get matched up in a much more accurate fashion.\n\t\t\treturn [p1score, null, null];\n\t\t}\n\n\t\treturn [p1score, data?.p1rating, data?.p2rating];\n\t}\n\n\t/**\n\t * Returns a Promise for an array of strings of