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;