API updates

* test passing: kicks clients that do not log in
 * index exports `Client` and `Server`
 * `createServer`: `options.keepAliveInterval` -> `options.kickTimeout`
 * `Client` event `end` includes `reason` argument
   - `LoginTimeout`
   - `KeepAliveTimeout`
   - `ServerShutdown`
 * `createClient`: Set `options.keepAlive` to `false` to disable sending
   keep alive packets
 * `Client`: `options.isServer` defaults to `false`.
This commit is contained in:
Andrew Kelley 2013-01-04 20:55:53 -05:00
parent c9ed8a2792
commit 1661ef3b4b
4 changed files with 48 additions and 15 deletions

View file

@ -11,6 +11,8 @@ var EventEmitter = require('events').EventEmitter
exports.createClient = createClient; exports.createClient = createClient;
exports.createServer = createServer; exports.createServer = createServer;
exports.Client = Client;
exports.Server = Server;
function createServer(options) { function createServer(options) {
var port = options.port != null ? var port = options.port != null ?
@ -20,7 +22,7 @@ function createServer(options) {
25565 ; 25565 ;
var host = options.host || '0.0.0.0'; var host = options.host || '0.0.0.0';
var timeout = options.timeout || 10 * 1000; var timeout = options.timeout || 10 * 1000;
var keepAliveInterval = options.keepAliveInterval || 4 * 1000; var kickTimeout = options.kickTimeout || 4 * 1000;
var motd = options.motd || "A Minecraft server"; var motd = options.motd || "A Minecraft server";
var onlineMode = options['online-mode'] == null ? true : options['online-mode']; var onlineMode = options['online-mode'] == null ? true : options['online-mode'];
assert.ok(! onlineMode, "online mode for servers is not yet supported"); assert.ok(! onlineMode, "online mode for servers is not yet supported");
@ -36,7 +38,12 @@ function createServer(options) {
var loggedIn = false; var loggedIn = false;
var lastKeepAlive = null; var lastKeepAlive = null;
var keepAliveTimer = setInterval(keepAliveLoop, keepAliveInterval); var keepAliveTimer = null;
var loginKickTimer = setTimeout(kickForNotLoggingIn, kickTimeout);
function kickForNotLoggingIn() {
client.end('LoginTimeout');
}
function keepAliveLoop() { function keepAliveLoop() {
if (keepAlive) { if (keepAlive) {
@ -44,7 +51,7 @@ function createServer(options) {
if (lastKeepAlive) { if (lastKeepAlive) {
var elapsed = new Date() - lastKeepAlive; var elapsed = new Date() - lastKeepAlive;
if (elapsed > timeout) { if (elapsed > timeout) {
client.end(); client.end('KeepAliveTimeout');
return; return;
} }
} }
@ -56,6 +63,7 @@ function createServer(options) {
function onEnd() { function onEnd() {
clearInterval(keepAliveTimer); clearInterval(keepAliveTimer);
clearTimeout(loginKickTimer);
} }
function onKeepAlive(packet) { function onKeepAlive(packet) {
@ -85,6 +93,11 @@ function createServer(options) {
loggedIn = true; loggedIn = true;
keepAlive = true; keepAlive = true;
client.username = packet.username; client.username = packet.username;
clearTimeout(loginKickTimer);
loginKickTimer = null;
keepAliveTimer = setInterval(keepAliveLoop, kickTimeout);
server.emit('login', client); server.emit('login', client);
} }
}); });
@ -99,6 +112,7 @@ function createClient(options) {
var host = options.host || 'localhost'; var host = options.host || 'localhost';
assert.ok(options.username, "username is required"); assert.ok(options.username, "username is required");
var haveCredentials = options.email && options.password; var haveCredentials = options.email && options.password;
var keepAlive = !!options.keepAlive;
var client = new Client({ var client = new Client({
isServer: false isServer: false
@ -112,10 +126,7 @@ function createClient(options) {
serverPort: port, serverPort: port,
}); });
}); });
client.on('packet', function(packet) { if (keepAlive) client.on(0x00, onKeepAlive);
console.log(packet.id, packet);
});
client.on(0x00, onKeepAlive);
client.once(0xFC, onEncryptionKeyResponse); client.once(0xFC, onEncryptionKeyResponse);
client.once(0xFD, onEncryptionKeyRequest); client.once(0xFD, onEncryptionKeyRequest);
client.connect(port, host); client.connect(port, host);

View file

@ -10,7 +10,9 @@ module.exports = Client;
function Client(options) { function Client(options) {
EventEmitter.call(this); EventEmitter.call(this);
this.isServer = options.isServer; options = options || {};
this.isServer = !!options.isServer;
this.socket = null; this.socket = null;
this.encryptionEnabled = false; this.encryptionEnabled = false;
this.cipher = null; this.cipher = null;
@ -40,7 +42,7 @@ Client.prototype.setSocket = function(socket) {
}); });
self.socket.on('close', function() { self.socket.on('close', function() {
self.emit('end'); self.emit('end', self._endReason);
}); });
}; };
@ -51,7 +53,8 @@ Client.prototype.connect = function(port, host) {
})); }));
}; };
Client.prototype.end = function() { Client.prototype.end = function(reason) {
this._endReason = reason;
this.socket.end(); this.socket.end();
}; };

View file

@ -32,13 +32,13 @@ Server.prototype.listen = function(port, host) {
client.on('error', function(err) { client.on('error', function(err) {
self.emit('error', err); self.emit('error', err);
}); });
client.setSocket(socket);
self.emit('connection', client);
client.on('end', function() { client.on('end', function() {
delete self.clients[client.id]; delete self.clients[client.id];
this.playerCount -= 1; this.playerCount -= 1;
}); });
this.playerCount += 1; this.playerCount += 1;
client.setSocket(socket);
self.emit('connection', client);
}); });
self.socketServer.on('error', function(err) { self.socketServer.on('error', function(err) {
self.emit('error', err); self.emit('error', err);
@ -56,7 +56,7 @@ Server.prototype.close = function() {
var client; var client;
for(var clientId in this.clients) { for(var clientId in this.clients) {
client = this.clients[clientId]; client = this.clients[clientId];
client.end(); client.end('ServerShutdown');
} }
this.socketServer.close(); this.socketServer.close();
}; };

View file

@ -214,7 +214,7 @@ describe("client", function() {
}); });
}); });
describe("server", function() { describe("server", function() {
it("starts listening", function(done) { it("starts listening and shuts down cleanly", function(done) {
var server = mc.createServer({ 'online-mode': false }); var server = mc.createServer({ 'online-mode': false });
var listening = false; var listening = false;
server.on('listening', function() { server.on('listening', function() {
@ -226,6 +226,25 @@ describe("server", function() {
done(); done();
}); });
}); });
it("kicks clients that do not emit keep alive"); it("kicks clients that do not log in", function(done) {
var server = mc.createServer({
'online-mode': false,
kickTimeout: 500,
});
server.on('connection', function(client) {
client.on('end', function(reason) {
assert.strictEqual(reason, "LoginTimeout");
});
});
server.on('listening', function() {
var client = new mc.Client();
client.on('end', function() {
done();
});
client.connect(25565, 'localhost');
});
});
it("responds to ping requests"); it("responds to ping requests");
it("clients can log in and chat");
it("gives correct reason for kicking clients when shutting down");
}); });