don't require both email and username. closes

This change breaks backwards compatibility.

`createClient` no longer takes an `email` argument. Instead, the
`username` argument is used to authenticate and determine the
case correct username.

There is a special case if you leave out the `password` argument.
In this case, `username` is used to connect directly to the server,
and you may get kicked if the server is in online mode.
This commit is contained in:
Andrew Kelley 2013-01-30 19:56:43 -05:00
parent 9c12b6d3d3
commit df3f95e1ff
4 changed files with 49 additions and 49 deletions

View file

@ -34,11 +34,10 @@ no more. If you want a higher-level API with which to write bots, see
```js ```js
var mc = require('minecraft-protocol'); var mc = require('minecraft-protocol');
var client = mc.createClient({ var client = mc.createClient({
host: "localhost", // optional host: "localhost", // optional
port: 25565, // optional port: 25565, // optional
username: "player", username: "email@example.com",
email: "email@example.com", // email and password are required only for password: "12345678",
password: "12345678", // online-mode=true servers
}); });
client.on(0x03, function(packet) { client.on(0x03, function(packet) {
// Listen for chat messages and echo them back. // Listen for chat messages and echo them back.
@ -49,6 +48,8 @@ client.on(0x03, function(packet) {
}); });
``` ```
If the server is in offline mode, you may leave out the `password` option.
### Hello World server example ### Hello World server example
```js ```js
@ -128,7 +129,7 @@ correct data type.
* Ensure your system has the `java` executable in `PATH`. * Ensure your system has the `java` executable in `PATH`.
* Download the appropriate version of `minecraft_server.jar`. * Download the appropriate version of `minecraft_server.jar`.
* `MC_SERVER_JAR=path/to/minecraft_server.jar MC_USERNAME=username MC_EMAIL=email@example.com MC_PASSWORD=password npm test` * `MC_SERVER_JAR=path/to/minecraft_server.jar MC_EMAIL=email@example.com MC_PASSWORD=password npm test`
### Test Coverage ### Test Coverage

View file

@ -1,9 +1,7 @@
var mc = require('../'); var mc = require('../');
var client = mc.createClient({ var client = mc.createClient({
username: process.env.MC_USERNAME, username: process.env.MC_USERNAME,
email: process.env.MC_EMAIL,
password: process.env.MC_PASSWORD, password: process.env.MC_PASSWORD,
verbose: true,
}); });
client.on('connect', function() { client.on('connect', function() {
console.info("connected"); console.info("connected");

View file

@ -193,27 +193,43 @@ function createClient(options) {
var port = options.port || 25565; var port = options.port || 25565;
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 = typeof options.password !== "undefined"; var haveCredentials = options.password != null;
var keepAlive = options.keepAlive == null ? true : options.keepAlive; var keepAlive = options.keepAlive == null ? true : options.keepAlive;
var email = options.email || options.username;
var password = options.password;
var client = new Client(false); var client = new Client(false);
client.username = options.username; client.on('connect', onConnect);
client.on('connect', function() { if (keepAlive) client.on(0x00, onKeepAlive);
client.once(0xFC, onEncryptionKeyResponse);
client.once(0xFD, onEncryptionKeyRequest);
if (haveCredentials) {
// make a request to get the case-correct username before connecting.
getLoginSession(options.username, options.password, function(err, session) {
if (err) {
client.emit('error', err);
} else {
client.session = session;
client.username = session.username;
client.emit('session');
client.connect(port, host);
}
});
} else {
// assume the server is in offline mode and just go for it.
client.username = options.username;
client.connect(port, host);
}
return client;
function onConnect() {
client.write(0x02, { client.write(0x02, {
protocolVersion: protocol.version, protocolVersion: protocol.version,
username: client.username, username: client.username,
serverHost: host, serverHost: host,
serverPort: port, serverPort: port,
}); });
}); }
if (keepAlive) client.on(0x00, onKeepAlive);
client.once(0xFC, onEncryptionKeyResponse);
client.once(0xFD, onEncryptionKeyRequest);
client.connect(port, host);
return client;
function onKeepAlive(packet) { function onKeepAlive(packet) {
client.write(0x00, { client.write(0x00, {
@ -222,30 +238,21 @@ function createClient(options) {
} }
function onEncryptionKeyRequest(packet) { function onEncryptionKeyRequest(packet) {
var batch = new Batch(); crypto.randomBytes(16, gotSharedSecret);
var hash;
if (haveCredentials) { function gotSharedSecret(err, sharedSecret) {
hash = crypto.createHash('sha1');
hash.update(packet.serverId);
batch.push(function(cb) { getLoginSession(email, password, cb); });
}
batch.push(function(cb) { crypto.randomBytes(16, cb); });
batch.end(function (err, results) {
if (err) { if (err) {
client.emit('error', err); client.emit('error', err);
client.end(); client.end();
return return
} }
var sharedSecret; var hash = crypto.createHash('sha1');
hash.update(packet.serverId);
if (haveCredentials) { if (haveCredentials) {
client.session = results[0];
client.username = client.session.username;
client.emit('session');
sharedSecret = results[1];
joinServerRequest(onJoinServerResponse); joinServerRequest(onJoinServerResponse);
} else { } else {
sharedSecret = results[0];
sendEncryptionKeyResponse(); sendEncryptionKeyResponse();
} }
@ -298,7 +305,7 @@ function createClient(options) {
verifyToken: encryptedVerifyTokenBuffer, verifyToken: encryptedVerifyTokenBuffer,
}); });
} }
}); }
} }
function onEncryptionKeyResponse(packet) { function onEncryptionKeyResponse(packet) {

View file

@ -264,7 +264,6 @@ describe("client", function() {
startServer({ 'online-mode': 'true' }, function() { startServer({ 'online-mode': 'true' }, function() {
var client = mc.createClient({ var client = mc.createClient({
username: process.env.MC_USERNAME, username: process.env.MC_USERNAME,
email: process.env.MC_EMAIL,
password: process.env.MC_PASSWORD, password: process.env.MC_PASSWORD,
}); });
mcServer.on('line', function(line) { mcServer.on('line', function(line) {
@ -299,12 +298,12 @@ describe("client", function() {
it("connects successfully - offline mode", function(done) { it("connects successfully - offline mode", function(done) {
startServer({ 'online-mode': 'false' }, function() { startServer({ 'online-mode': 'false' }, function() {
var client = mc.createClient({ var client = mc.createClient({
username: process.env.MC_USERNAME, username: 'Player',
}); });
mcServer.on('line', function(line) { mcServer.on('line', function(line) {
var match = line.match(/\[INFO\] <(.+?)> (.+)$/); var match = line.match(/\[INFO\] <(.+?)> (.+)$/);
if (! match) return; if (! match) return;
assert.strictEqual(match[1], process.env.MC_USERNAME); assert.strictEqual(match[1], 'Player');
assert.strictEqual(match[2], "hello everyone; I have logged in."); assert.strictEqual(match[2], "hello everyone; I have logged in.");
mcServer.stdin.write("say hello\n"); mcServer.stdin.write("say hello\n");
}); });
@ -322,7 +321,7 @@ describe("client", function() {
chatCount += 1; chatCount += 1;
assert.ok(chatCount <= 2); assert.ok(chatCount <= 2);
if (chatCount === 1) { if (chatCount === 1) {
assert.strictEqual(packet.message, "<" + process.env.MC_USERNAME + ">" + " hello everyone; I have logged in."); assert.strictEqual(packet.message, "<Player>" + " hello everyone; I have logged in.");
} else if (chatCount === 2) { } else if (chatCount === 2) {
assert.strictEqual(packet.message, "[Server] hello"); assert.strictEqual(packet.message, "[Server] hello");
done(); done();
@ -333,7 +332,7 @@ describe("client", function() {
it("gets kicked when no credentials supplied in online mode", function(done) { it("gets kicked when no credentials supplied in online mode", function(done) {
startServer({ 'online-mode': 'true' }, function() { startServer({ 'online-mode': 'true' }, function() {
var client = mc.createClient({ var client = mc.createClient({
username: process.env.MC_USERNAME, username: 'Player',
}); });
var gotKicked = false; var gotKicked = false;
client.on(0xff, function(packet) { client.on(0xff, function(packet) {
@ -349,7 +348,7 @@ describe("client", function() {
it("does not crash for " + SURVIVE_TIME + "ms", function(done) { it("does not crash for " + SURVIVE_TIME + "ms", function(done) {
startServer({ 'online-mode': 'false' }, function() { startServer({ 'online-mode': 'false' }, function() {
var client = mc.createClient({ var client = mc.createClient({
username: process.env.MC_USERNAME, username: 'Player',
}); });
client.on(0x01, function(packet) { client.on(0x01, function(packet) {
client.write(0x03, { client.write(0x03, {
@ -357,7 +356,7 @@ describe("client", function() {
}); });
}); });
client.on(0x03, function(packet) { client.on(0x03, function(packet) {
assert.strictEqual(packet.message, "<" + process.env.MC_USERNAME + ">" + " hello everyone; I have logged in."); assert.strictEqual(packet.message, "<Player>" + " hello everyone; I have logged in.");
setTimeout(function() { setTimeout(function() {
done(); done();
}, SURVIVE_TIME); }, SURVIVE_TIME);
@ -533,7 +532,7 @@ describe("mc-server", function() {
}); });
it("kicks clients when invalid credentials", function(done) { it("kicks clients when invalid credentials", function(done) {
var server = mc.createServer(); var server = mc.createServer();
var count = 5; var count = 4;
server.on('connection', function(client) { server.on('connection', function(client) {
client.on('end', function(reason) { client.on('end', function(reason) {
resolve(); resolve();
@ -547,11 +546,6 @@ describe("mc-server", function() {
resolve(); resolve();
var client = mc.createClient({ var client = mc.createClient({
username: 'lalalal', username: 'lalalal',
password: "this is wrong",
});
client.on('error', function(err) {
assert.strictEqual(err.code, 'ELOGIN400');
resolve();
}); });
client.on('end', function() { client.on('end', function() {
resolve(); resolve();