test all packets

This commit is contained in:
Andrew Kelley 2013-01-07 23:36:14 -05:00
parent 5df31e4d88
commit 10cb3e0100
6 changed files with 173 additions and 26 deletions

View file

@ -24,6 +24,7 @@ function createServer(options) {
options['server-port'] != null ? options['server-port'] != null ?
options['server-port'] : options['server-port'] :
25565 ; 25565 ;
var maxPlayers = options['max-players'] || 20;
var host = options.host || '0.0.0.0'; var host = options.host || '0.0.0.0';
var kickTimeout = options.kickTimeout || 10 * 1000; var kickTimeout = options.kickTimeout || 10 * 1000;
var checkTimeoutInterval = options.checkTimeoutInterval || 4 * 1000; var checkTimeoutInterval = options.checkTimeoutInterval || 4 * 1000;
@ -32,6 +33,7 @@ function createServer(options) {
assert.ok(! onlineMode, "online mode for servers is not yet supported"); assert.ok(! onlineMode, "online mode for servers is not yet supported");
var server = new Server(options); var server = new Server(options);
server.maxPlayers = maxPlayers;
server.on("connection", function(client) { server.on("connection", function(client) {
client.once(0xfe, onPing); client.once(0xfe, onPing);
client.on(0x02, onHandshake); client.on(0x02, onHandshake);
@ -87,7 +89,7 @@ function createServer(options) {
protocol.minecraftVersion, protocol.minecraftVersion,
motd, motd,
server.playerCount, server.playerCount,
server.maxPlayers, maxPlayers,
].join('\u0000') ].join('\u0000')
}); });
} }
@ -118,9 +120,7 @@ function createClient(options) {
var email = options.email; var email = options.email;
var password = options.password; var password = options.password;
var client = new Client({ var client = new Client(false);
isServer: false
});
client.username = options.username; client.username = options.username;
client.on('connect', function() { client.on('connect', function() {
client.write(0x02, { client.write(0x02, {

View file

@ -7,12 +7,10 @@ var net = require('net')
module.exports = Client; module.exports = Client;
function Client(options) { function Client(isServer) {
EventEmitter.call(this); EventEmitter.call(this);
options = options || {}; this.isServer = !!isServer;
this.isServer = !!options.isServer;
this.socket = null; this.socket = null;
this.encryptionEnabled = false; this.encryptionEnabled = false;
this.cipher = null; this.cipher = null;

View file

@ -2,15 +2,6 @@ var assert = require('assert');
var STRING_MAX_LENGTH = 240; var STRING_MAX_LENGTH = 240;
module.exports = {
version: 51,
minecraftVersion: '1.4.7',
sessionVersion: 13,
parsePacket: parsePacket,
createPacketBuffer: createPacketBuffer,
STRING_MAX_LENGTH: STRING_MAX_LENGTH,
};
var packets = { var packets = {
0x00: [ 0x00: [
{ name: "keepAliveId", type: "int" } { name: "keepAliveId", type: "int" }
@ -459,10 +450,10 @@ var types = {
'double': [readDouble, DoubleWriter], 'double': [readDouble, DoubleWriter],
'float': [readFloat, FloatWriter], 'float': [readFloat, FloatWriter],
'slot': [readSlot, SlotWriter], 'slot': [readSlot, SlotWriter],
'long': [readLong, LongWriter],
'ascii': [readAscii, AsciiWriter],
'ascii': [readAscii],
'byteArray32': [readByteArray32], 'byteArray32': [readByteArray32],
'long': [readLong],
'slotArray': [readSlotArray], 'slotArray': [readSlotArray],
'mapChunkBulk': [readMapChunkBulk], 'mapChunkBulk': [readMapChunkBulk],
'entityMetadata': [readEntityMetadata], 'entityMetadata': [readEntityMetadata],
@ -886,6 +877,22 @@ StringWriter.prototype.write = function(buffer, offset) {
} }
}; };
function AsciiWriter(value) {
this.value = value;
this.size = 2 + value.length;
}
AsciiWriter.prototype.write = function(buffer, offset) {
var cursor = offset;
buffer.writeInt16BE(this.value.length, cursor);
cursor += 2;
for (var i = 0; i < this.value.length; ++i) {
buffer.writeUInt8(this.value.charCodeAt(i), cursor);
cursor += 1;
}
};
function ByteArray16Writer(value) { function ByteArray16Writer(value) {
assert.ok(Buffer.isBuffer(value), "non buffer passed to ByteArray16Writer"); assert.ok(Buffer.isBuffer(value), "non buffer passed to ByteArray16Writer");
this.value = value; this.value = value;
@ -969,6 +976,16 @@ IntWriter.prototype.write = function(buffer, offset) {
buffer.writeInt32BE(this.value, offset); buffer.writeInt32BE(this.value, offset);
} }
function LongWriter(value) {
this.value = value;
this.size = 8;
}
LongWriter.prototype.write = function(buffer, offset) {
buffer.writeInt32BE(this.value[0], offset);
buffer.writeInt32BE(this.value[1], offset + 4);
}
function get(packetId, toServer) { function get(packetId, toServer) {
var packetInfo = packets[packetId]; var packetInfo = packets[packetId];
return Array.isArray(packetInfo) ? return Array.isArray(packetInfo) ?
@ -1025,3 +1042,14 @@ function parsePacket(buffer, isServer) {
results: results, results: results,
}; };
} }
module.exports = {
version: 51,
minecraftVersion: '1.4.7',
sessionVersion: 13,
parsePacket: parsePacket,
createPacketBuffer: createPacketBuffer,
STRING_MAX_LENGTH: STRING_MAX_LENGTH,
packets: packets,
get: get,
};

View file

@ -6,10 +6,9 @@ var net = require('net')
module.exports = Server; module.exports = Server;
function Server(options) { function Server() {
EventEmitter.call(this); EventEmitter.call(this);
this.maxPlayers = options['max-players'] || 20;
this.playerCount = 0 this.playerCount = 0
this.socketServer = null; this.socketServer = null;
@ -24,9 +23,7 @@ Server.prototype.listen = function(port, host) {
var nextId = 0; var nextId = 0;
self.socketServer = net.createServer(); self.socketServer = net.createServer();
self.socketServer.on('connection', function(socket) { self.socketServer.on('connection', function(socket) {
var client = new Client({ var client = new Client(true);
isServer: true,
});
client.id = nextId++; client.id = nextId++;
self.clients[client.id] = client; self.clients[client.id] = client;
client.on('error', function(err) { client.on('error', function(err) {

View file

@ -28,7 +28,8 @@
"devDependencies": { "devDependencies": {
"mocha": "~1.7.4", "mocha": "~1.7.4",
"mkdirp": "~0.3.4", "mkdirp": "~0.3.4",
"rimraf": "~2.1.1" "rimraf": "~2.1.1",
"zfill": "0.0.1"
}, },
"dependencies": { "dependencies": {
"ursa": "~0.8.0", "ursa": "~0.8.0",

View file

@ -1,12 +1,16 @@
var mc = require('../') var mc = require('../')
, protocol = require('../lib/protocol') , protocol = mc.protocol
, Client = mc.Client
, Server = mc.Server
, spawn = require('child_process').spawn , spawn = require('child_process').spawn
, path = require('path') , path = require('path')
, fs = require('fs') , fs = require('fs')
, net = require('net')
, assert = require('assert') , assert = require('assert')
, mkdirp = require('mkdirp') , mkdirp = require('mkdirp')
, rimraf = require('rimraf') , rimraf = require('rimraf')
, Batch = require('batch') , Batch = require('batch')
, zfill = require('zfill')
, MC_SERVER_JAR = process.env.MC_SERVER_JAR , MC_SERVER_JAR = process.env.MC_SERVER_JAR
, SURVIVE_TIME = 10000 , SURVIVE_TIME = 10000
, MC_SERVER_PATH = path.join(__dirname, 'server') , MC_SERVER_PATH = path.join(__dirname, 'server')
@ -41,6 +45,125 @@ var defaultServerProps = {
'motd': 'A Minecraft Server', 'motd': 'A Minecraft Server',
}; };
var values = {
'int': Math.floor(Math.random() * Math.pow(2, 16)),
'short': Math.floor(Math.random() * Math.pow(2, 8)),
'ushort': Math.floor(Math.random() * Math.pow(2, 16)),
'byte': Math.floor(Math.random() * Math.pow(2, 4)),
'ubyte': Math.floor(Math.random() * Math.pow(2, 8)),
'string': "hi hi this is my string",
'byteArray16': new Buffer(8),
'bool': Math.random() < 0.5,
'double': Math.random() * Math.pow(2, 64),
'float': Math.random() * Math.pow(2, 32),
'slot': {
id: 5,
itemCount: 56,
itemDamage: 2,
nbtData: new Buffer(90),
},
'ascii': "hello",
'byteArray32': new Buffer(10),
'long': [0, 1],
'slotArray': [{
id: 41,
itemCount: 2,
itemDamage: 3,
nbtData: new Buffer(0),
}],
'mapChunkBulk': {
skyLightSent: true,
compressedChunkData: new Buffer(1234),
meta: [{
x: 23,
z: 64,
bitMap: 3,
addBitMap: 10,
}],
},
'entityMetadata': {},
'objectData': {
intField: 9,
velocityX: 1,
velocityY: 2,
velocityZ: 3,
},
'intArray8': [1, 2, 3, 4],
'intVector': {x: 1, y: 2, z: 3},
'byteVector': {x: 1, y: 2, z: 3},
'byteVectorArray': [{x: 1, y: 2, z: 3}],
}
describe("packets", function() {
var client, server, serverClient;
before(function(done) {
server = new Server();
server.once('listening', function() {
server.once('connection', function(c) {
serverClient = c;
done();
});
client = new Client();
client.setSocket(net.connect(25565, 'localhost'));
});
server.listen(25565, 'localhost');
});
after(function(done) {
client.on('end', function() {
server.on('close', done);
server.close();
});
client.end();
});
var packetId, packetInfo, field;
for(packetId in protocol.packets) {
packetId = parseInt(packetId, 10);
packetInfo = protocol.packets[packetId];
it("0x" + zfill(parseInt(packetId, 10).toString(16), 2), callTestPacket(packetId, packetInfo));
}
function callTestPacket(packetId, packetInfo) {
return function(done) {
var batch = new Batch();
batch.push(function(done) {
testPacket(packetId, protocol.get(packetId, false), done);
});
batch.push(function(done) {
testPacket(packetId, protocol.get(packetId, true), done);
});
batch.end(done);
};
}
function testPacket(packetId, packetInfo, done) {
// empty object uses default values
var packet = {};
packetInfo.forEach(function(field) {
var value = field.type;
packet[field.name] = values[field.type];
});
serverClient.once(packetId, function(receivedPacket) {
delete receivedPacket.id;
assertPacketsMatch(packet, receivedPacket);
client.once(packetId, function(clientReceivedPacket) {
delete clientReceivedPacket.id;
assertPacketsMatch(receivedPacket, clientReceivedPacket);
done();
});
serverClient.write(packetId, receivedPacket);
});
client.write(packetId, packet);
}
function assertPacketsMatch(p1, p2) {
var field;
for (field in p1) {
assert.ok(field in p2, "field " + field + " missing in p2")
}
for (field in p2) {
assert.ok(field in p1, "field " + field + " missing in p1");
}
}
});
describe("client", function() { describe("client", function() {
this.timeout(20000); this.timeout(20000);