mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-06-02 22:08:36 -05:00
229 lines
6.2 KiB
JavaScript
229 lines
6.2 KiB
JavaScript
/**
|
|
* Login server abstraction layer
|
|
* Pokemon Showdown - http://pokemonshowdown.com/
|
|
*
|
|
* This file handles communicating with the login server.
|
|
*
|
|
* @license MIT license
|
|
*/
|
|
|
|
const LOGIN_SERVER_TIMEOUT = 15000;
|
|
const LOGIN_SERVER_BATCH_TIME = 1000;
|
|
|
|
module.exports = (function() {
|
|
var http = require("http");
|
|
var url = require('url');
|
|
|
|
function LoginServer(uri) {
|
|
console.log('Creating LoginServer object for ' + uri + '...');
|
|
this.uri = uri;
|
|
this.requestQueue = [];
|
|
LoginServer.loginServers[this.uri] = this;
|
|
}
|
|
|
|
// "static" mapping of URIs to LoginServer objects
|
|
LoginServer.loginServers = {};
|
|
|
|
// "static" flag
|
|
LoginServer.disabled = false;
|
|
|
|
LoginServer.prototype.requestTimer = null;
|
|
LoginServer.prototype.requestTimeoutTimer = null;
|
|
LoginServer.prototype.requestLog = '';
|
|
LoginServer.prototype.lastRequest = 0;
|
|
LoginServer.prototype.openRequests = 0;
|
|
|
|
var getLoginServer = function(action) {
|
|
var uri;
|
|
if (Config.loginservers) {
|
|
uri = Config.loginservers[action] || Config.loginservers[null];
|
|
} else {
|
|
uri = Config.loginserver;
|
|
}
|
|
if (!uri) {
|
|
console.log('ERROR: No login server specified for action: ' + action);
|
|
return;
|
|
}
|
|
return LoginServer.loginServers[uri] || new LoginServer(uri);
|
|
};
|
|
LoginServer.instantRequest = function(action, data, callback) {
|
|
return getLoginServer(action).instantRequest(action, data, callback);
|
|
};
|
|
LoginServer.request = function(action, data, callback) {
|
|
return getLoginServer(action).request(action, data, callback);
|
|
};
|
|
|
|
var parseJSON = function(json) {
|
|
if (json[0] === ']') json = json.substr(1);
|
|
return JSON.parse(json);
|
|
};
|
|
|
|
LoginServer.prototype.instantRequest = function(action, data, callback) {
|
|
if (typeof data === 'function') {
|
|
callback = data;
|
|
data = null;
|
|
}
|
|
if (this.openRequests > 5) {
|
|
callback(null, null, 'overflow');
|
|
return;
|
|
}
|
|
this.openRequests++;
|
|
var dataString = '';
|
|
if (data) {
|
|
for (var i in data) {
|
|
dataString += '&' + i + '=' + encodeURIComponent('' + data[i]);
|
|
}
|
|
}
|
|
var req = http.get(url.parse(this.uri + 'action.php?act=' + action + '&serverid=' + Config.serverid + '&servertoken=' + Config.servertoken + '&nocache=' + new Date().getTime() + dataString), function(res) {
|
|
var buffer = '';
|
|
res.setEncoding('utf8');
|
|
|
|
res.on('data', function(chunk) {
|
|
buffer += chunk;
|
|
});
|
|
|
|
res.on('end', function() {
|
|
var data = null;
|
|
try {
|
|
data = parseJSON(buffer);
|
|
} catch (e) {}
|
|
callback(data, res.statusCode);
|
|
this.openRequests--;
|
|
});
|
|
});
|
|
|
|
req.on('error', function(error) {
|
|
callback(null, null, error);
|
|
this.openRequests--;
|
|
});
|
|
|
|
req.end();
|
|
};
|
|
LoginServer.prototype.request = function(action, data, callback) {
|
|
if (typeof data === 'function') {
|
|
callback = data;
|
|
data = null;
|
|
}
|
|
if (LoginServer.disabled) {
|
|
callback(null, null, 'disabled');
|
|
return;
|
|
}
|
|
if (!data) data = {};
|
|
data.act = action;
|
|
data.callback = callback;
|
|
this.requestQueue.push(data);
|
|
this.requestTimerPoke();
|
|
};
|
|
LoginServer.prototype.requestTimerPoke = function() {
|
|
// "poke" the request timer, i.e. make sure it knows it should make
|
|
// a request soon
|
|
|
|
// if we already have it going or the request queue is empty no need to do anything
|
|
if (this.openRequests || this.requestTimer || !this.requestQueue.length) return;
|
|
|
|
this.requestTimer = setTimeout(this.makeRequests.bind(this), LOGIN_SERVER_BATCH_TIME);
|
|
};
|
|
LoginServer.prototype.makeRequests = function() {
|
|
this.requestTimer = null;
|
|
var self = this;
|
|
var requests = this.requestQueue;
|
|
this.requestQueue = [];
|
|
|
|
if (!requests.length) return;
|
|
|
|
var requestCallbacks = [];
|
|
for (var i = 0, len = requests.length; i < len; i++) {
|
|
var request = requests[i];
|
|
requestCallbacks[i] = request.callback;
|
|
delete request.callback;
|
|
}
|
|
|
|
this.requestStart(requests.length);
|
|
var postData = 'serverid=' + Config.serverid + '&servertoken=' + Config.servertoken + '&nocache=' + new Date().getTime() + '&json=' + encodeURIComponent(JSON.stringify(requests)) + '\n';
|
|
var requestOptions = url.parse(this.uri + 'action.php');
|
|
requestOptions.method = 'post';
|
|
requestOptions.headers = {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'Content-Length': postData.length
|
|
};
|
|
|
|
var req = null;
|
|
var reqError = function(error) {
|
|
if (self.requestTimeoutTimer) {
|
|
clearTimeout(self.requestTimeoutTimer);
|
|
self.requestTimeoutTimer = null;
|
|
}
|
|
req.abort();
|
|
for (var i = 0, len = requestCallbacks.length; i < len; i++) {
|
|
requestCallbacks[i](null, null, error);
|
|
}
|
|
self.requestEnd();
|
|
};
|
|
|
|
self.requestTimeoutTimer = setTimeout(function() {
|
|
reqError('timeout');
|
|
}, LOGIN_SERVER_TIMEOUT);
|
|
|
|
req = http.request(requestOptions, function(res) {
|
|
if (self.requestTimeoutTimer) {
|
|
clearTimeout(self.requestTimeoutTimer);
|
|
self.requestTimeoutTimer = null;
|
|
}
|
|
var buffer = '';
|
|
res.setEncoding('utf8');
|
|
|
|
res.on('data', function(chunk) {
|
|
buffer += chunk;
|
|
});
|
|
|
|
var endReq = function() {
|
|
if (self.requestTimeoutTimer) {
|
|
clearTimeout(self.requestTimeoutTimer);
|
|
self.requestTimeoutTimer = null;
|
|
}
|
|
//console.log('RESPONSE: ' + buffer);
|
|
var data = null;
|
|
try {
|
|
data = parseJSON(buffer);
|
|
} catch (e) {}
|
|
for (var i = 0, len = requestCallbacks.length; i < len; i++) {
|
|
if (data) {
|
|
requestCallbacks[i](data[i], res.statusCode);
|
|
} else {
|
|
requestCallbacks[i](null, res.statusCode, 'corruption');
|
|
}
|
|
}
|
|
self.requestEnd();
|
|
}.once();
|
|
res.on('end', endReq);
|
|
res.on('close', endReq);
|
|
|
|
self.requestTimeoutTimer = setTimeout(function(){
|
|
if (res.connection) res.connection.destroy();
|
|
endReq();
|
|
}, LOGIN_SERVER_TIMEOUT);
|
|
});
|
|
|
|
req.on('error', reqError);
|
|
|
|
req.write(postData);
|
|
req.end();
|
|
};
|
|
LoginServer.prototype.requestStart = function(size) {
|
|
this.lastRequest = Date.now();
|
|
this.requestLog += ' | ' + size + ' requests: ';
|
|
this.openRequests++;
|
|
};
|
|
LoginServer.prototype.requestEnd = function() {
|
|
this.openRequests = 0;
|
|
this.requestLog += '' + (Date.now() - this.lastRequest).duration();
|
|
this.requestLog = this.requestLog.substr(-1000);
|
|
this.requestTimerPoke();
|
|
};
|
|
LoginServer.prototype.getLog = function() {
|
|
return this.requestLog + (this.lastRequest ? ' (' + (Date.now() - this.lastRequest).duration() + ' since last request)' : '');
|
|
};
|
|
|
|
return LoginServer;
|
|
})();
|