{ "version": 3, "sources": ["../../server/loginserver.ts"], "sourcesContent": ["/**\n * Login server abstraction layer\n * Pokemon Showdown - http://pokemonshowdown.com/\n *\n * This file handles communicating with the login server.\n *\n * @license MIT\n */\n\nconst LOGIN_SERVER_TIMEOUT = 30000;\nconst LOGIN_SERVER_BATCH_TIME = 1000;\n\nimport { Net, FS } from '../lib';\n\n/**\n * A custom error type used when requests to the login server take too long.\n */\nclass TimeoutError extends Error {}\nTimeoutError.prototype.name = TimeoutError.name;\n\nfunction parseJSON(json: string) {\n\tif (json.startsWith(']')) json = json.substr(1);\n\tconst data: { error: string | null, json: any[] | null } = { error: null, json: null };\n\ttry {\n\t\tdata.json = JSON.parse(json);\n\t} catch (err: any) {\n\t\tdata.error = err.message;\n\t}\n\treturn data;\n}\n\ntype LoginServerResponse = [AnyObject, null] | [null, Error];\n\nclass LoginServerInstance {\n\treadonly uri: string;\n\trequestQueue: [AnyObject, (val: LoginServerResponse) => void][];\n\trequestTimer: NodeJS.Timeout | null;\n\trequestLog: string;\n\tlastRequest: number;\n\topenRequests: number;\n\tdisabled: false;\n\t[key: `${string}Server`]: LoginServerInstance | undefined;\n\n\tconstructor() {\n\t\tthis.uri = Config.loginserver;\n\t\tthis.requestQueue = [];\n\t\tthis.requestTimer = null;\n\t\tthis.requestLog = '';\n\t\tthis.lastRequest = 0;\n\t\tthis.openRequests = 0;\n\t\tthis.disabled = false;\n\t}\n\n\tasync instantRequest(action: string, data: AnyObject | null = null): Promise {\n\t\tif (this.openRequests > 5) {\n\t\t\treturn Promise.resolve(\n\t\t\t\t[null, new RangeError(\"Request overflow\")]\n\t\t\t);\n\t\t}\n\t\tthis.openRequests++;\n\n\t\ttry {\n\t\t\tconst request = Net(this.uri);\n\t\t\tconst buffer = await request.get({\n\t\t\t\tquery: {\n\t\t\t\t\t...data,\n\t\t\t\t\tact: action,\n\t\t\t\t\tserverid: Config.serverid,\n\t\t\t\t\tservertoken: Config.servertoken,\n\t\t\t\t\tnocache: new Date().getTime(),\n\t\t\t\t},\n\t\t\t});\n\t\t\tconst json = parseJSON(buffer);\n\t\t\tthis.openRequests--;\n\t\t\tif (json.error) {\n\t\t\t\treturn [null, new Error(json.error)];\n\t\t\t}\n\t\t\tthis.openRequests--;\n\t\t\treturn [json.json!, null];\n\t\t} catch (error: any) {\n\t\t\tthis.openRequests--;\n\t\t\treturn [null, error];\n\t\t}\n\t}\n\n\trequest(action: string, data: AnyObject | null = null): Promise {\n\t\tif (this.disabled) {\n\t\t\treturn Promise.resolve(\n\t\t\t\t[null, new Error(`Login server connection disabled.`)]\n\t\t\t);\n\t\t}\n\n\t\t// ladderupdate and mmr are the most common actions\n\t\t// prepreplay is also common\n\t\tif (this[`${action}Server`]) {\n\t\t\treturn this[`${action}Server`]!.request(action, data);\n\t\t}\n\n\t\tconst actionData = data || {};\n\t\tactionData.act = action;\n\t\treturn new Promise(resolve => {\n\t\t\tthis.requestQueue.push([actionData, resolve]);\n\t\t\tthis.requestTimerPoke();\n\t\t});\n\t}\n\trequestTimerPoke() {\n\t\t// \"poke\" the request timer, i.e. make sure it knows it should make\n\t\t// a request soon\n\n\t\t// if we already have it going or the request queue is empty no need to do anything\n\t\tif (this.openRequests || this.requestTimer || !this.requestQueue.length) return;\n\n\t\tthis.requestTimer = setTimeout(() => void this.makeRequests(), LOGIN_SERVER_BATCH_TIME);\n\t}\n\tasync makeRequests() {\n\t\tthis.requestTimer = null;\n\t\tconst requests = this.requestQueue;\n\t\tthis.requestQueue = [];\n\n\t\tif (!requests.length) return;\n\n\t\tconst resolvers: ((val: LoginServerResponse) => void)[] = [];\n\t\tconst dataList = [];\n\t\tfor (const [data, resolve] of requests) {\n\t\t\tresolvers.push(resolve);\n\t\t\tdataList.push(data);\n\t\t}\n\n\t\tthis.requestStart(requests.length);\n\n\t\ttry {\n\t\t\tconst request = Net(`${this.uri}action.php`);\n\t\t\tlet buffer = await request.post({\n\t\t\t\tbody: {\n\t\t\t\t\tserverid: Config.serverid,\n\t\t\t\t\tservertoken: Config.servertoken,\n\t\t\t\t\tnocache: new Date().getTime(),\n\t\t\t\t\tjson: JSON.stringify(dataList),\n\t\t\t\t},\n\t\t\t\ttimeout: LOGIN_SERVER_TIMEOUT,\n\t\t\t});\n\t\t\t// console.log('RESPONSE: ' + buffer);\n\t\t\tconst data = parseJSON(buffer).json;\n\t\t\tif (buffer.startsWith(`[{\"actionsuccess\":true,`)) {\n\t\t\t\tbuffer = 'stream interrupt';\n\t\t\t}\n\t\t\tif (!data) {\n\t\t\t\tif (buffer.includes('<')) buffer = 'invalid response';\n\t\t\t\tthrow new Error(buffer);\n\t\t\t}\n\t\t\tfor (const [i, resolve] of resolvers.entries()) {\n\t\t\t\tresolve([data[i], null]);\n\t\t\t}\n\n\t\t\tthis.requestEnd();\n\t\t} catch (error: any) {\n\t\t\tfor (const resolve of resolvers) {\n\t\t\t\tresolve([null, error]);\n\t\t\t}\n\t\t\tthis.requestEnd(error);\n\t\t}\n\t}\n\trequestStart(size: number) {\n\t\tthis.lastRequest = Date.now();\n\t\tthis.requestLog += ` | ${size} rqs: `;\n\t\tthis.openRequests++;\n\t}\n\trequestEnd(error?: Error) {\n\t\tthis.openRequests = 0;\n\t\tif (error && error instanceof TimeoutError) {\n\t\t\tthis.requestLog += 'TIMEOUT';\n\t\t} else {\n\t\t\tthis.requestLog += `${(Date.now() - this.lastRequest) / 1000}s`;\n\t\t}\n\t\tthis.requestLog = this.requestLog.substr(-1000);\n\t\tthis.requestTimerPoke();\n\t}\n\tgetLog() {\n\t\tif (!this.lastRequest) return this.requestLog;\n\t\treturn `${this.requestLog} (${Chat.toDurationString(Date.now() - this.lastRequest)} since last request)`;\n\t}\n}\n\nexport const LoginServer = Object.assign(new LoginServerInstance(), {\n\tTimeoutError,\n\n\tladderupdateServer: new LoginServerInstance(),\n\tprepreplayServer: new LoginServerInstance(),\n});\n\nFS('./config/custom.css').onModify(() => {\n\tvoid LoginServer.request('invalidatecss');\n});\nif (!Config.nofswriting) {\n\tvoid LoginServer.request('invalidatecss');\n}\n"], "mappings": "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", "names": [] }