diff --git a/app.js b/app.js index d177cdde8d..b20ceefa7b 100644 --- a/app.js +++ b/app.js @@ -311,6 +311,8 @@ global.Tools = require('./tools.js').includeFormats(); global.LoginServer = require('./loginserver.js'); +global.Ladders = require('./ladders-remote.js'); + global.Users = require('./users.js'); global.Rooms = require('./rooms.js'); diff --git a/gulpfile.js b/gulpfile.js index 19469bdf8b..d68a485e8d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,7 +16,8 @@ var fileCache = new CacheSwap({tmpDir: '', cacheDirName: 'gulp-cache'}); var globals = {}; var globalList = [ 'Config', 'ResourceMonitor', 'toId', 'Tools', 'LoginServer', 'Users', 'Rooms', 'Verifier', - 'CommandParser', 'Simulator', 'Tournaments', 'Dnsbl', 'Cidr', 'Sockets', 'TeamValidator' + 'CommandParser', 'Simulator', 'Tournaments', 'Dnsbl', 'Cidr', 'Sockets', 'TeamValidator', + 'Ladders' ]; globalList.forEach(function (identifier) {globals[identifier] = false;}); diff --git a/ladders-remote.js b/ladders-remote.js new file mode 100644 index 0000000000..43f769fec4 --- /dev/null +++ b/ladders-remote.js @@ -0,0 +1,115 @@ +/** + * Ladder library + * Pokemon Showdown - http://pokemonshowdown.com/ + * + * This file handles ladder rating retrieval. + * + * @license MIT license + */ + +/* global Ladders: true */ +var Ladders = module.exports = getLadder; + +function getLadder(formatid) { + return new Ladder(formatid); +} + +function Ladder(formatid) { + this.formatid = toId(formatid); +} + +Ladder.prototype.getRating = function (userid) { + var formatid = this.formatid; + var user = Users.getExact(userid); + if (user && user.mmrCache[formatid]) { + return Promise.resolve(user.mmrCache[formatid]); + } + return new Promise(function (resolve, reject) { + LoginServer.request('mmr', { + format: formatid, + user: userid + }, function (data, statusCode, error) { + if (!data) return resolve(1000); + if (data.errorip) { + return resolve(1000); + } + + var mmr = parseInt(data, 10); + if (isNaN(mmr)) return resolve(1000); + if (user.userid !== userid) return reject("Expired rating"); + + user.mmrCache[formatid] = mmr; + resolve(mmr); + }); + }); +}; + +Ladder.prototype.updateRating = function (p1name, p2name, p1score, room) { + var formatid = this.formatid; + var p1rating, p2rating; + room.update(); + room.send('||Ladder updating...'); + LoginServer.request('ladderupdate', { + p1: p1name, + p2: p2name, + score: p1score, + format: formatid + }, function (data, statusCode, error) { + if (!room.battle) { + console.log('room expired before ladder update was received'); + return; + } + if (!data) { + room.add('||Ladder (probably) updated, but score could not be retrieved (' + error.message + ').'); + // log the battle anyway + if (!Tools.getFormat(room.format).noLog) { + room.logBattle(p1score); + } + return; + } else if (data.errorip) { + room.add("||This server's request IP " + data.errorip + " is not a registered server."); + room.add("||We currently only support ladder ratings on registered servers."); + room.update(); + return; + } else { + try { + p1rating = data.p1rating; + p2rating = data.p2rating; + + var oldacre = Math.round(p1rating.oldacre); + var acre = Math.round(p1rating.acre); + var reasons = '' + (acre - oldacre) + ' for ' + (p1score > 0.99 ? 'winning' : (p1score < 0.01 ? 'losing' : 'tying')); + if (reasons.charAt(0) !== '-') reasons = '+' + reasons; + room.addRaw(Tools.escapeHTML(p1name) + '\'s rating: ' + oldacre + ' → ' + acre + '
(' + reasons + ')'); + + oldacre = Math.round(p2rating.oldacre); + acre = Math.round(p2rating.acre); + reasons = '' + (acre - oldacre) + ' for ' + (p1score > 0.99 ? 'losing' : (p1score < 0.01 ? 'winning' : 'tying')); + if (reasons.charAt(0) !== '-') reasons = '+' + reasons; + room.addRaw(Tools.escapeHTML(p2name) + '\'s rating: ' + oldacre + ' → ' + acre + '
(' + reasons + ')'); + + var p1 = Users.getExact(p1name); + if (p1) { + Users.getExact(p1name).mmrCache[formatid] = +p1rating.acre; + } + var p2 = Users.getExact(p2name); + if (p2) { + Users.getExact(p2name).mmrCache[formatid] = +p2rating.acre; + } + room.update(); + } catch (e) { + room.addRaw('There was an error calculating rating changes.'); + room.update(); + } + + if (!Tools.getFormat(formatid).noLog) { + room.logBattle(p1score, p1rating, p2rating); + } + + if (!Tools.getFormat(formatid).noLog) { + room.logBattle(p1score, p1rating, p2rating); + } + } + }); +}; + diff --git a/rooms.js b/rooms.js index 68af635cff..7eeed7b3ab 100644 --- a/rooms.js +++ b/rooms.js @@ -539,13 +539,11 @@ var GlobalRoom = (function () { time: new Date().getTime() }; var self = this; - user.doWithMMR(formatid, function (mmr, error) { - if (error) { - user.popup("Connection to ladder server failed with error: " + error.message + "; please try again later"); - return; - } - newSearch.rating = mmr; + Ladders(formatid).getRating(user.userid).then(function (rating) { + newSearch.rating = rating; self.addSearch(newSearch, user, formatid); + }, function (error) { + user.popup("Connection to ladder server failed with error: " + error.message + "; please try again later"); }); }; GlobalRoom.prototype.matchmakingOK = function (search1, search2, user1, user2, formatid) { @@ -930,62 +928,8 @@ var BattleRoom = (function () { if (winner && !winner.registered) { this.sendUser(winner, '|askreg|' + winner.userid); } - var p1rating, p2rating; // update rankings - this.push('|raw|Ladder updating...'); - var self = this; - LoginServer.request('ladderupdate', { - p1: p1name, - p2: p2name, - score: p1score, - format: toId(rated.format) - }, function (data, statusCode, error) { - if (!self.battle) { - console.log('room expired before ladder update was received'); - return; - } - if (!data) { - self.addRaw('Ladder (probably) updated, but score could not be retrieved (' + error.message + ').'); - // log the battle anyway - if (!Tools.getFormat(self.format).noLog) { - self.logBattle(p1score); - } - return; - } else if (data.errorip) { - self.addRaw("This server's request IP " + data.errorip + " is not a registered server."); - return; - } else { - try { - p1rating = data.p1rating; - p2rating = data.p2rating; - - //self.add("Ladder updated."); - - var oldacre = Math.round(data.p1rating.oldacre); - var acre = Math.round(data.p1rating.acre); - var reasons = '' + (acre - oldacre) + ' for ' + (p1score > 0.99 ? 'winning' : (p1score < 0.01 ? 'losing' : 'tying')); - if (reasons.charAt(0) !== '-') reasons = '+' + reasons; - self.addRaw(Tools.escapeHTML(p1name) + '\'s rating: ' + oldacre + ' → ' + acre + '
(' + reasons + ')'); - - oldacre = Math.round(data.p2rating.oldacre); - acre = Math.round(data.p2rating.acre); - reasons = '' + (acre - oldacre) + ' for ' + (p1score > 0.99 ? 'losing' : (p1score < 0.01 ? 'winning' : 'tying')); - if (reasons.charAt(0) !== '-') reasons = '+' + reasons; - self.addRaw(Tools.escapeHTML(p2name) + '\'s rating: ' + oldacre + ' → ' + acre + '
(' + reasons + ')'); - - if (p1 && p1.userid === rated.p1) p1.cacheMMR(rated.format, data.p1rating); - if (p2 && p2.userid === rated.p2) p2.cacheMMR(rated.format, data.p2rating); - self.update(); - } catch (e) { - self.addRaw('There was an error calculating rating changes.'); - self.update(); - } - - if (!Tools.getFormat(self.format).noLog) { - self.logBattle(p1score, p1rating, p2rating); - } - } - }); + Ladders(rated.format).updateRating(p1name, p2name, p1score, this); } } else if (Config.logchallenges) { // Log challenges if the challenge logging config is enabled. diff --git a/users.js b/users.js index 413f89cbca..d7cdab6844 100644 --- a/users.js +++ b/users.js @@ -1260,40 +1260,6 @@ User = (function () { } return alts; }; - User.prototype.doWithMMR = function (formatid, callback) { - var self = this; - var userid = this.userid; - formatid = toId(formatid); - - // this should relieve login server strain - // this.mmrCache[formatid] = 1000; - - if (this.mmrCache[formatid]) { - callback(this.mmrCache[formatid]); - return; - } - LoginServer.request('mmr', { - format: formatid, - user: userid - }, function (data, statusCode, error) { - if (!data) return callback(1000, error || new Error("No data received")); - if (data.errorip) return self.popup("This server's request IP " + data.errorip + " is not a registered server."); - - var mmr = parseInt(data, 10); - if (isNaN(mmr)) return callback(1000, error || new Error("Invalid rating")); - if (self.userid !== userid) return callback(1000, new Error("Expired rating")); - - self.mmrCache[formatid] = mmr; - callback(mmr, null); - }); - }; - User.prototype.cacheMMR = function (formatid, mmr) { - if (typeof mmr === 'number') { - this.mmrCache[formatid] = mmr; - } else { - this.mmrCache[formatid] = Number(mmr.acre); - } - }; User.prototype.ban = function (noRecurse, userid) { // recurse only once; the root for-loop already bans everything with your IP if (!userid) userid = this.userid;