From 917b6adda176f81baecdb29980d11a3d35fccb7c Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Tue, 29 Sep 2015 22:41:41 +0200 Subject: [PATCH 1/4] enable cross version with an option in createClient and createServer : * put parsePacketData in deserializer and createPacketBuffer in serializer * remove packets from the index and expose readPacket instead * load packets when needed in various files * make tests test every supported version static cross version of #234, fix #65, fix #240 --- README.md | 5 +- circle.yml | 10 +- doc/README.md | 1 + package.json | 2 +- src/browser.js | 11 +- src/client.js | 15 +- src/createClient.js | 7 +- src/createServer.js | 7 +- src/index.js | 17 +- src/ping.js | 12 +- src/server.js | 7 +- src/transforms/serializer.js | 232 ++++---- src/version.js | 7 +- test/benchmark.js | 35 +- test/test.js | 1027 ++++++++++++++++++---------------- 15 files changed, 726 insertions(+), 669 deletions(-) diff --git a/README.md b/README.md index bc77748..d5032b9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Parse and serialize minecraft packets, plus authentication and encryption. ## Features - * Supports Minecraft version 1.8.8 + * Supports Minecraft version 1.8.8 and 1.9 * Parses all packets and emits events with packet fields as JavaScript objects. * Send a packet by supplying fields as a JavaScript object. @@ -121,8 +121,7 @@ See [doc](doc/README.md) ## Testing * Ensure your system has the `java` executable in `PATH`. -* Download the appropriate version of `minecraft_server.jar`. -* `MC_SERVER_JAR=path/to/minecraft_server.jar MC_USERNAME=email@example.com MC_PASSWORD=password npm test` +* `MC_SERVER_JAR_DIR=some/path/to/store/minecraft/server/ MC_USERNAME=email@example.com MC_PASSWORD=password npm test` ## Debugging diff --git a/circle.yml b/circle.yml index 97265a8..89951c8 100644 --- a/circle.yml +++ b/circle.yml @@ -1,12 +1,10 @@ machine: environment: - MC_SERVER_JAR: /home/ubuntu/node-minecraft-protocol/minecraft-server/minecraft_server.jar + MC_SERVER_JAR_DIR: /home/ubuntu/node-minecraft-protocol/minecraft-server/ node: version: 0.10.28 java: version: openjdk7 -test: - override: - - mkdir -p /home/ubuntu/node-minecraft-protocol/minecraft-server/ - - node_modules/.bin/downloadMinecraft `node -e 'console.log(require("./src/version").minecraftVersion)'` $MC_SERVER_JAR - - npm test +dependencies: + pre: + - mkdir minecraft-server diff --git a/doc/README.md b/doc/README.md index 4c2f8a6..0c60e1c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -84,6 +84,7 @@ Returns a `Client` instance and perform login. * clientToken : generated if a password is given * accessToken : generated if a password is given * keepAlive : send keep alive packets : default to true + * version : 1.8 or 1.9 ## Client diff --git a/package.json b/package.json index 3645273..7ed1bb3 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "gulp-plumber": "^1.0.1", "gulp-sourcemaps": "^1.3.0", "intelli-espower-loader": "^1.0.0", - "mocha": "~1.8.2", + "mocha": "~2.3.3", "power-assert": "^1.0.0", "source-map-support": "^0.3.2", "minecraft-wrap": "~0.5.4" diff --git a/src/browser.js b/src/browser.js index f0cc6f6..10a29fc 100644 --- a/src/browser.js +++ b/src/browser.js @@ -1,19 +1,12 @@ -var version = require('./version'); -var packets = require('minecraft-data')(version.majorVersion).protocol.states; var readPackets = require("./packets").readPackets; -var packetIndexes = readPackets(packets, states); var utils = require("./utils"); var serializer = require("./transforms/serializer"); module.exports = { Client: require('./client'), protocol: require('./protocol'), - createPacketBuffer: serializer.createPacketBuffer, - parsePacketData: serializer.parsePacketData, - packetFields: packetIndexes.packetFields, - packetNames: packetIndexes.packetNames, - packetIds: packetIndexes.packetIds, - packetStates: packetIndexes.packetStates, types: serializer.types, get: serializer.get, + readPackets:readPackets, + supportedVersions:require("./version").supportedVersions }; diff --git a/src/client.js b/src/client.js index 7a73438..7bb68da 100644 --- a/src/client.js +++ b/src/client.js @@ -5,10 +5,6 @@ var compression = require('./transforms/compression'); var framing = require('./transforms/framing'); var crypto = require('crypto'); var states = serializer.states; -var version = require('./version'); -var packets = require('minecraft-data')(version.majorVersion).protocol.states; -var readPackets = require("./packets").readPackets; -var packetIndexes = readPackets(packets, states); class Client extends EventEmitter @@ -24,11 +20,16 @@ class Client extends EventEmitter deserializer; isServer; - constructor(isServer) { + constructor(isServer,version) { super(); - this.serializer = serializer.createSerializer({ isServer }); - this.deserializer = serializer.createDeserializer({ isServer, packetsToParse: this.packetsToParse }); + var mcData=require("minecraft-data")(version); + var packets = mcData.protocol.states; + var readPackets = require("./packets").readPackets; + var packetIndexes = readPackets(packets, states); + + this.serializer = serializer.createSerializer({ isServer, version:version}); + this.deserializer = serializer.createDeserializer({ isServer, packetsToParse: this.packetsToParse, version:version}); this.isServer = !!isServer; this.on('newListener', function(event, listener) { diff --git a/src/createClient.js b/src/createClient.js index 2efefff..4ed201d 100644 --- a/src/createClient.js +++ b/src/createClient.js @@ -1,6 +1,5 @@ var mcHexDigest=require("./mcHexDigest"); var ursa=require("./ursa"); -var version = require("./version"); var net = require('net'); var dns = require('dns'); var Client = require('./client'); @@ -42,8 +41,12 @@ function createClient(options) { var haveCredentials = options.password != null || (clientToken != null && accessToken != null); var keepAlive = options.keepAlive == null ? true : options.keepAlive; + var optVersion = options.version || require("./version").defaultVersion; + var mcData=require("minecraft-data")(optVersion); + var version = mcData.version; - var client = new Client(false); + + var client = new Client(false,version.majorVersion); client.on('connect', onConnect); if(keepAlive) client.on('keep_alive', onKeepAlive); client.once('encryption_begin', onEncryptionKeyRequest); diff --git a/src/createServer.js b/src/createServer.js index ba79c9b..428c8d6 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -1,6 +1,5 @@ var mcHexDigest=require("./mcHexDigest"); var ursa=require("./ursa"); -var version = require("./version"); var crypto = require('crypto'); var Yggdrasil = require('./yggdrasil.js'); var validateSession = Yggdrasil.validateSession; @@ -28,9 +27,13 @@ function createServer(options) { var enableKeepAlive = options.keepAlive == null ? true : options.keepAlive; + var optVersion = options.version || require("./version").defaultVersion; + var mcData=require("minecraft-data")(optVersion); + var version = mcData.version; + var serverKey = ursa.generatePrivateKey(1024); - var server = new Server(options); + var server = new Server(version.majorVersion); server.motd = options.motd || "A Minecraft server"; server.maxPlayers = options['max-players'] || 20; server.playerCount = 0; diff --git a/src/index.js b/src/index.js index ff5270b..1f61ae1 100644 --- a/src/index.js +++ b/src/index.js @@ -3,10 +3,7 @@ var Server = require('./server'); var Yggdrasil = require('./yggdrasil.js'); var serializer = require("./transforms/serializer"); var utils = require("./utils"); -var version = require("./version"); -var packets = require('minecraft-data')(version.majorVersion).protocol.states; var readPackets = require("./packets").readPackets; -var packetIndexes = readPackets(packets, serializer.states); var createClient = require("./createClient"); var createServer = require("./createServer"); @@ -16,16 +13,10 @@ module.exports = { Client: Client, Server: Server, states: serializer.states, - createPacketBuffer: serializer.createPacketBuffer, - parsePacketData: serializer.parsePacketData, - packetFields: packetIndexes.packetFields, - packetNames: packetIndexes.packetNames, - packetIds: packetIndexes.packetIds, - packetStates: packetIndexes.packetStates, - types: serializer.types, - get: serializer.get, + createSerializer:serializer.createSerializer, + createDeserializer:serializer.createDeserializer, + readPackets:readPackets, ping: require('./ping'), yggdrasil: Yggdrasil, - version: version.version, - minecraftVersion: version.minecraftVersion + supportedVersions:require("./version").supportedVersions }; diff --git a/src/ping.js b/src/ping.js index 4fa3416..77c2e3f 100644 --- a/src/ping.js +++ b/src/ping.js @@ -1,15 +1,17 @@ -var net = require('net') - , Client = require('./client') - , states = require('./transforms/serializer').states; -var version = require('./version'); +var net = require('net'); +var Client = require('./client'); +var states = require('./transforms/serializer').states; module.exports = ping; function ping(options, cb) { var host = options.host || 'localhost'; var port = options.port || 25565; + var optVersion = options.version || require("./version").defaultVersion; + var mcData=require("minecraft-data")(optVersion); + var version = mcData.version; - var client = new Client(); + var client = new Client(false,version.majorVersion); client.on('error', function(err) { cb(err); }); diff --git a/src/server.js b/src/server.js index 6b953d0..81471b8 100644 --- a/src/server.js +++ b/src/server.js @@ -10,16 +10,17 @@ class Server extends EventEmitter decipher=null; clients={}; - constructor() { + constructor(version) { super(); + this.version=version; } listen(port, host) { var self = this; var nextId = 0; self.socketServer = net.createServer(); - self.socketServer.on('connection', function(socket) { - var client = new Client(true); + self.socketServer.on('connection', socket => { + var client = new Client(true,this.version); client._end = client.end; client.end = function end(endReason) { endReason='{"text":"'+endReason+'"}'; diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js index 0f2dd6f..2d98bee 100644 --- a/src/transforms/serializer.js +++ b/src/transforms/serializer.js @@ -13,9 +13,6 @@ module.exports.createDeserializer = function(obj) { return new Deserializer(obj); }; -module.exports.createPacketBuffer=createPacketBuffer; -module.exports.parsePacketData=parsePacketData; - // This is really just for the client. var states = { "HANDSHAKING": "handshaking", @@ -25,137 +22,81 @@ var states = { }; module.exports.states = states; -module.exports.get = get; - - - var NMProtocols = require("../protocol"); - var numeric = require("../datatypes/numeric"); var utils = require("../datatypes/utils"); var minecraft = require("../datatypes/minecraft"); var structures = require("../datatypes/structures"); var conditional = require("../datatypes/conditional"); - -var proto = new NMProtocols(); -proto.addTypes(numeric); -proto.addTypes(utils); -proto.addTypes(minecraft); -proto.addTypes(structures); -proto.addTypes(conditional); - -module.exports.types = proto.types; - -var version = require('../version'); -var packets = require('minecraft-data')(version.majorVersion).protocol; -proto.addTypes(packets.types); - var readPackets = require("../packets").readPackets; -var packetIndexes = readPackets(packets.states, states); - -var packetFields = packetIndexes.packetFields; -var packetNames = packetIndexes.packetNames; -var packetIds = packetIndexes.packetIds; -var packetStates = packetIndexes.packetStates; -// TODO : This does NOT contain the length prefix anymore. -function createPacketBuffer(packetName, state, params, isServer) { - var direction = !isServer ? 'toServer' : 'toClient'; - var packetId = packetIds[state][direction][packetName]; - assert.notEqual(packetId, undefined, `${state}.${isServer}.${packetName} : ${packetId}`); - var packet = get(packetName, state, !isServer); - assert.notEqual(packet, null); - - var length = utils.varint[2](packetId); - tryCatch(() => { - length += structures.container[2].call(proto, params, packet, {}); - //length += proto.sizeOf(params, ["container", packet], {}); - }, (e) => { - e.field = [state, direction, packetName, e.field].join("."); - e.message = `SizeOf error for ${e.field} : ${e.message}`; - throw e; - }); - - var buffer = new Buffer(length); - var offset = utils.varint[1](packetId, buffer, 0); - tryCatch(() => { - offset = structures.container[1].call(proto, params, buffer, offset, packet, {}); - //offset = proto.write(params, buffer, offset, ["container", packet], {}); - }, (e) => { - e.field = [state, direction, packetName, e.field].join("."); - e.message = `Write error for ${e.field} : ${e.message}`; - throw e; - }); - return buffer; +function createProtocol(types) +{ + var proto = new NMProtocols(); + proto.addTypes(numeric); + proto.addTypes(utils); + proto.addTypes(minecraft); + proto.addTypes(structures); + proto.addTypes(conditional); + proto.addTypes(types); + return proto; } -function get(packetName, state, toServer) { - var direction = toServer ? "toServer" : "toClient"; - var packetInfo = packetFields[state][direction][packetName]; - if(!packetInfo) { - return null; - } - return packetInfo; -} - -function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": true}) { - var { value: packetId, size: cursor } = utils.varint[0](buffer, 0); - - var direction = isServer ? "toServer" : "toClient"; - var packetName = packetNames[state][direction][packetId]; - var results = { - metadata: { - name: packetName, - id: packetId, - state - }, - data: {}, - buffer - }; - - // Only parse the packet if there is a need for it, AKA if there is a listener - // attached to it. - var shouldParse = - (packetsToParse.hasOwnProperty(packetName) && packetsToParse[packetName] > 0) || - (packetsToParse.hasOwnProperty("packet") && packetsToParse["packet"] > 0); - if (!shouldParse) - return results; - - var packetInfo = get(packetName, state, isServer); - if(packetInfo === null) - throw new Error("Unrecognized packetId: " + packetId + " (0x" + packetId.toString(16) + ")") - else - debug("read packetId " + state + "." + packetName + " (0x" + packetId.toString(16) + ")"); - - var res; - tryCatch(() => { - res = proto.read(buffer, cursor, ["container", packetInfo], {}); - }, (e) => { - e.field = [state, direction, packetName, e.field].join("."); - e.message = `Read error for ${e.field} : ${e.message}`; - throw e; - }); - results.data = res.value; - cursor += res.size; - if(buffer.length > cursor) - throw new Error(`Read error for ${packetName} : Packet data not entirely read : - ${JSON.stringify(results)}`); - debug(results); - return results; -} - class Serializer extends Transform { - constructor({ state = states.HANDSHAKING, isServer = false } = {}) { + constructor({ state = states.HANDSHAKING, isServer = false , version} = {}) { super({ writableObjectMode: true }); this.protocolState = state; this.isServer = isServer; + this.version = version; + + var mcData=require("minecraft-data")(version); + var packets = mcData.protocol.states; + var packetIndexes = readPackets(packets, states); + + this.proto=createProtocol(mcData.protocol.types); + + this.packetFields = packetIndexes.packetFields; + this.packetIds = packetIndexes.packetIds; + } + + // TODO : This does NOT contain the length prefix anymore. + createPacketBuffer(packetName, params) { + var direction = !this.isServer ? 'toServer' : 'toClient'; + var packetId = this.packetIds[this.protocolState][direction][packetName]; + assert.notEqual(packetId, undefined, `${this.protocolState}.${this.isServer}.${packetName} : ${packetId}`); + var packet = this.packetFields[this.protocolState][direction][packetName]; + packet=packet ? packet : null; + + assert.notEqual(packet, null); + + var length = utils.varint[2](packetId); + tryCatch(() => { + length += structures.container[2].call(this.proto, params, packet, {}); + //length += proto.sizeOf(params, ["container", packet], {}); + }, (e) => { + e.field = [this.protocolState, direction, packetName, e.field].join("."); + e.message = `SizeOf error for ${e.field} : ${e.message}`; + throw e; + }); + + var buffer = new Buffer(length); + var offset = utils.varint[1](packetId, buffer, 0); + tryCatch(() => { + offset = structures.container[1].call(this.proto, params, buffer, offset, packet, {}); + //offset = proto.write(params, buffer, offset, ["container", packet], {}); + }, (e) => { + e.field = [this.protocolState, direction, packetName, e.field].join("."); + e.message = `Write error for ${e.field} : ${e.message}`; + throw e; + }); + return buffer; } _transform(chunk, enc, cb) { try { - var buf = createPacketBuffer(chunk.packetName, this.protocolState, chunk.params, this.isServer); + var buf = this.createPacketBuffer(chunk.packetName, chunk.params); this.push(buf); return cb(); } catch (e) { @@ -165,17 +106,76 @@ class Serializer extends Transform { } class Deserializer extends Transform { - constructor({ state = states.HANDSHAKING, isServer = false, packetsToParse = {"packet": true} } = {}) { + constructor({ state = states.HANDSHAKING, isServer = false, packetsToParse = {"packet": true}, version } = {}) { super({ readableObjectMode: true }); this.protocolState = state; this.isServer = isServer; this.packetsToParse = packetsToParse; + this.version = version; + + + var mcData=require("minecraft-data")(version); + var packets = mcData.protocol.states; + var packetIndexes = readPackets(packets, states); + + this.proto=createProtocol(mcData.protocol.types); + + this.packetFields = packetIndexes.packetFields; + this.packetNames = packetIndexes.packetNames; } + parsePacketData(buffer) { + var { value: packetId, size: cursor } = utils.varint[0](buffer, 0); + + var direction = this.isServer ? "toServer" : "toClient"; + var packetName = this.packetNames[this.protocolState][direction][packetId]; + var results = { + metadata: { + name: packetName, + id: packetId, + state:this.protocolState + }, + data: {}, + buffer + }; + + // Only parse the packet if there is a need for it, AKA if there is a listener + // attached to it. + var shouldParse = + (this.packetsToParse.hasOwnProperty(packetName) && this.packetsToParse[packetName] > 0) || + (this.packetsToParse.hasOwnProperty("packet") && this.packetsToParse["packet"] > 0); + if (!shouldParse) + return results; + + var packetInfo = this.packetFields[this.protocolState][direction][packetName]; + packetInfo=packetInfo ? packetInfo : null; + if(packetInfo === null) + throw new Error("Unrecognized packetId: " + packetId + " (0x" + packetId.toString(16) + ")") + else + debug("read packetId " + this.protocolState + "." + packetName + " (0x" + packetId.toString(16) + ")"); + + var res; + tryCatch(() => { + res = this.proto.read(buffer, cursor, ["container", packetInfo], {}); + }, (e) => { + e.field = [this.protocolState, direction, packetName, e.field].join("."); + e.message = `Read error for ${e.field} : ${e.message}`; + throw e; + }); + results.data = res.value; + cursor += res.size; + if(buffer.length > cursor) + throw new Error(`Read error for ${packetName} : Packet data not entirely read : + ${JSON.stringify(results)}`); + debug(results); + return results; + } + + _transform(chunk, enc, cb) { var packet; try { - packet = parsePacketData(chunk, this.protocolState, this.isServer, this.packetsToParse); + packet = this.parsePacketData(chunk); } catch (e) { return cb(e); } diff --git a/src/version.js b/src/version.js index ee795d9..2a82160 100644 --- a/src/version.js +++ b/src/version.js @@ -1,3 +1,4 @@ -var majorVersion='1.8'; -var mcData=require("minecraft-data")(majorVersion); -module.exports=mcData.version; +module.exports={ + defaultVersion:'1.8', + supportedVersions:['1.8','1.9'] +}; diff --git a/test/benchmark.js b/test/benchmark.js index d1fcd97..c85816f 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -11,21 +11,24 @@ var testDataWrite = [ // TODO: add more packets for better quality data ]; -var inputData = []; - -var start, i, j; -console.log('Beginning write test'); -start = Date.now(); -for(i = 0; i < ITERATIONS; i++) { - for(j = 0; j < testDataWrite.length; j++) { - inputData.push(mc.createPacketBuffer(testDataWrite[j].name, states.PLAY, testDataWrite[j].params, false)); +mc.supportedVersions.forEach(function(supportedVersion){ + var inputData = []; + var serializer=new mc.createSerializer({state:states.PLAY,isServer:false,version:supportedVersion}); + var start, i, j; + console.log('Beginning write test for '+supportedVersion); + start = Date.now(); + for(i = 0; i < ITERATIONS; i++) { + for(j = 0; j < testDataWrite.length; j++) { + inputData.push(serializer.createPacketBuffer(testDataWrite[j].name, testDataWrite[j].params)); + } } -} -console.log('Finished write test in ' + (Date.now() - start) / 1000 + ' seconds'); + console.log('Finished write test in ' + (Date.now() - start) / 1000 + ' seconds'); -console.log('Beginning read test'); -start = Date.now(); -for (j = 0; j < inputData.length; j++) { - mc.parsePacketData(inputData[j], states.PLAY, true); -} -console.log('Finished read test in ' + (Date.now() - start) / 1000 + ' seconds'); + var deserializer=new mc.createDeserializer({state:states.PLAY,isServer:true,version:supportedVersion}); + console.log('Beginning read test for '+supportedVersion); + start = Date.now(); + for (j = 0; j < inputData.length; j++) { + deserializer.parsePacketData(inputData[j]); + } + console.log('Finished read test in ' + (Date.now() - start) / 1000 + ' seconds'); +}); diff --git a/test/test.js b/test/test.js index a7d7ba0..8eccbc9 100644 --- a/test/test.js +++ b/test/test.js @@ -1,20 +1,17 @@ -var mc = require('../') - , states = mc.states - , Client = mc.Client - , Server = mc.Server - , path = require('path') - , fs = require('fs') - , net = require('net') - , assert = require('power-assert') - , MC_SERVER_JAR = process.env.MC_SERVER_JAR - , SURVIVE_TIME = 10000 - , MC_SERVER_PATH = path.join(__dirname, 'server') - , getFieldInfo = require('../dist/utils').getFieldInfo - , getField = require('../dist/utils').getField - ; +var mc = require('../'); +var states = mc.states; +var Client = mc.Client; +var Server = mc.Server; +var path = require('path'); +var fs = require('fs'); +var net = require('net'); +var assert = require('power-assert'); +var SURVIVE_TIME = 10000; +var getFieldInfo = require('../dist/utils').getFieldInfo; +var getField = require('../dist/utils').getField; +var MC_SERVER_PATH = path.join(__dirname, 'server'); var Wrap = require('minecraft-wrap').Wrap; -var wrap=new Wrap(MC_SERVER_JAR,MC_SERVER_PATH); function evalCount(count, fields) { if(fields[count["field"]] in count["map"]) @@ -48,7 +45,7 @@ var values = { }, 'container': function(typeArgs, context) { var results = { - "..": context; + "..": context }; for(var index in typeArgs) { results[typeArgs[index].name] = getValue(typeArgs[index].type, results); @@ -111,497 +108,561 @@ function getValue(_type, packet) { throw new Error("No value for type " + fieldInfo.type); } -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 packetName, packetInfo, field; - for(state in mc.packetFields) { - if(!mc.packetFields.hasOwnProperty(state)) continue; - for(packetName in mc.packetFields[state].toServer) { - if(!mc.packetFields[state].toServer.hasOwnProperty(packetName)) continue; - packetInfo = mc.get(packetName, state, true); - it(state + ",ServerBound," + packetName, - callTestPacket(packetName, packetInfo, state, true)); - } - for(packetName in mc.packetFields[state].toClient) { - if(!mc.packetFields[state].toClient.hasOwnProperty(packetName)) continue; - packetInfo = mc.get(packetName, state, false); - it(state + ",ClientBound," + packetName, - callTestPacket(packetName, packetInfo, state, false)); - } - } - function callTestPacket(packetName, packetInfo, state, toServer) { - return function(done) { - client.state = state; - serverClient.state = state; - testPacket(packetName, packetInfo, state, toServer, done); - }; - } +var download = require('minecraft-wrap').download; - function testPacket(packetName, packetInfo, state, toServer, done) { - // empty object uses default values - var packet = {}; - packetInfo.forEach(function(field) { - packet[field.name] = getValue(field.type, packet); - }); - if(toServer) { - serverClient.once(packetName, function(receivedPacket) { - try { - assertPacketsMatch(packet, receivedPacket); - } catch (e) { - console.log(packet, receivedPacket); - throw e; - } - done(); - }); - client.write(packetName, packet); - } else { - client.once(packetName, function(receivedPacket) { - assertPacketsMatch(packet, receivedPacket); - done(); - }); - serverClient.write(packetName, packet); - } - } +mc.supportedVersions.forEach(function(supportedVersion){ + var mcData=require("minecraft-data")(supportedVersion); + var version=mcData.version; + var MC_SERVER_JAR_DIR = process.env.MC_SERVER_JAR_DIR; + var MC_SERVER_JAR = MC_SERVER_JAR_DIR+"/minecraft_server."+version.minecraftVersion+".jar"; + var wrap=new Wrap(MC_SERVER_JAR,MC_SERVER_PATH); - function assertPacketsMatch(p1, p2) { - packetInfo.forEach(function(field) { - assert.deepEqual(p1[field], p2[field]); - }); - var field; - for(field in p1) { - if (p1[field] !== undefined) - assert.ok(field in p2, "field " + field + " missing in p2, in p1 it has value " + JSON.stringify(p1[field])); - } - for(field in p2) { - assert.ok(field in p1, "field " + field + " missing in p1, in p2 it has value " + JSON.stringify(p2[field])); - } - } -}); + var packets = mcData.protocol.states; + var packetIndexes = mc.readPackets(packets, states); + var packetFields = packetIndexes.packetFields; -describe("client", function() { - this.timeout(10 * 60 * 1000); - afterEach(function(done) { - wrap.stopServer(function(err){ - if(err) - console.log(err); - done(err); - }); - }); - after(function(done) { - wrap.deleteServerData(function(err){ - if(err) - console.log(err); - done(err); - }); - }); - it("pings the server", function(done) { - wrap.on('line',function(line){ - console.log(line); - }); - wrap.startServer({ - motd: 'test1234', - 'max-players': 120, - }, function(err) { - if(err) - return done(err); - mc.ping({}, function(err, results) { - if(err) return done(err); - assert.ok(results.latency >= 0); - assert.ok(results.latency <= 1000); - delete results.latency; - delete results.favicon; // too lazy to figure it out - /* assert.deepEqual(results, { - version: { - name: '1.7.4', - protocol: 4 - }, - description: { text: "test1234" } - });*/ - done(); - }); - }); - }); - it.skip("connects successfully - online mode (STUBBED)", function(done) { - wrap.startServer({'online-mode': 'true'}, function(err) { - if(err) - return done(err); - var client = mc.createClient({ - username: process.env.MC_USERNAME, - password: process.env.MC_PASSWORD, - }); - wrap.on('line', function(line) { - var match = line.match(/\[Server thread\/INFO\]: <(.+?)> (.+)/); - if(!match) return; - assert.strictEqual(match[1], client.session.username); - assert.strictEqual(match[2], "hello everyone; I have logged in."); - wrap.writeServer("say hello\n"); - }); - var chatCount = 0; - client.on('login', function(packet) { - assert.strictEqual(packet.levelType, 'default'); - assert.strictEqual(packet.difficulty, 1); - assert.strictEqual(packet.dimension, 0); - assert.strictEqual(packet.gameMode, 0); - client.write('chat', { - message: "hello everyone; I have logged in." - }); - }); - client.on('chat', function(packet) { - done(); - }); - }); - done(); - }); - it.skip("connects successfully - offline mode (STUBBED)", function(done) { - wrap.startServer({'online-mode': 'false'}, function(err) { - if(err) - return done(err); - var client = mc.createClient({ - username: 'Player', - }); - wrap.on('line', function(line) { - var match = line.match(/\[Server thread\/INFO\]: <(.+?)> (.+)/); - if(!match) return; - assert.strictEqual(match[1], 'Player'); - assert.strictEqual(match[2], "hello everyone; I have logged in."); - wrap.writeServer("say hello\n"); - }); - var chatCount = 0; - client.on('login', function(packet) { - assert.strictEqual(packet.levelType, 'default'); - assert.strictEqual(packet.difficulty, 1); - assert.strictEqual(packet.dimension, 0); - assert.strictEqual(packet.gameMode, 0); - client.write('chat', { - message: "hello everyone; I have logged in." - }); - }); - client.on('chat', function(packet) { - chatCount += 1; - assert.ok(chatCount <= 2); - var message = JSON.parse(packet.message); - if(chatCount === 1) { - assert.strictEqual(message.translate, "chat.type.text"); - assert.deepEqual(message["with"][0], { - clickEvent: { - action: "suggest_command", - value: "/msg Player " - }, - text: "Player" - }); - assert.strictEqual(message["with"][1], "hello everyone; I have logged in."); - } else if(chatCount === 2) { - assert.strictEqual(message.translate, "chat.type.announcement"); - assert.strictEqual(message["with"][0], "Server"); - assert.deepEqual(message["with"][1], { - text: "", - extra: ["hello"] - }); + describe("packets "+version.minecraftVersion, function() { + var client, server, serverClient; + before(function(done) { + server = new Server(version.majorVersion); + server.once('listening', function() { + server.once('connection', function(c) { + serverClient = c; done(); - } - }); - }); - done(); - }); - it("gets kicked when no credentials supplied in online mode", function(done) { - wrap.startServer({'online-mode': 'true'}, function(err) { - if(err) - return done(err); - var client = mc.createClient({ - username: 'Player', - }); - var gotKicked = false; - client.on('disconnect', function(packet) { - assert.ok(packet.reason.indexOf('"Failed to verify username!"')!=-1); - gotKicked = true; - }); - client.on('end', function() { - assert.ok(gotKicked); - done(); - }); - }); - }); - it("does not crash for " + SURVIVE_TIME + "ms", function(done) { - wrap.startServer({'online-mode': 'false'}, function(err) { - if(err) - return done(err); - var client = mc.createClient({ - username: 'Player', - }); - client.on("login", function(packet) { - client.write("chat", { - message: "hello everyone; I have logged in." }); + client = new Client(false,version.majorVersion); + client.setSocket(net.connect(25565, 'localhost')); }); - client.on("chat", function(packet) { - var message = JSON.parse(packet.message); - assert.strictEqual(message.translate, "chat.type.text"); - /*assert.deepEqual(message["with"][0], { - clickEvent: { - action: "suggest_command", - value: "/msg Player " - }, - text: "Player" - });*/ - assert.strictEqual(message["with"][1], "hello everyone; I have logged in."); - setTimeout(function() { + server.listen(25565, 'localhost'); + }); + after(function(done) { + client.on('end', function() { + server.on('close', done); + server.close(); + }); + client.end(); + }); + var packetName, packetInfo, field; + for(state in packetFields) { + if(!packetFields.hasOwnProperty(state)) continue; + for(packetName in packetFields[state].toServer) { + if(!packetFields[state].toServer.hasOwnProperty(packetName)) continue; + packetInfo = packetFields[state]["toServer"][packetName]; + packetInfo=packetInfo ? packetInfo : null; + it(state + ",ServerBound," + packetName, + callTestPacket(packetName, packetInfo, state, true)); + } + for(packetName in packetFields[state].toClient) { + if(!packetFields[state].toClient.hasOwnProperty(packetName)) continue; + packetInfo = packetFields[state]["toClient"][packetName]; + packetInfo=packetInfo ? packetInfo : null; + it(state + ",ClientBound," + packetName, + callTestPacket(packetName, packetInfo, state, false)); + } + } + function callTestPacket(packetName, packetInfo, state, toServer) { + return function(done) { + client.state = state; + serverClient.state = state; + testPacket(packetName, packetInfo, state, toServer, done); + }; + } + + function testPacket(packetName, packetInfo, state, toServer, done) { + // empty object uses default values + var packet = {}; + packetInfo.forEach(function(field) { + packet[field.name] = getValue(field.type, packet); + }); + if(toServer) { + serverClient.once(packetName, function(receivedPacket) { + try { + assertPacketsMatch(packet, receivedPacket); + } catch (e) { + console.log(packet, receivedPacket); + throw e; + } done(); - }, SURVIVE_TIME); - }); - }); - }); -}); -describe("mc-server", function() { - it("starts listening and shuts down cleanly", function(done) { - var server = mc.createServer({'online-mode': false}); - var listening = false; - server.on('listening', function() { - listening = true; - server.close(); - }); - server.on('close', function() { - assert.ok(listening); - done(); - }); - }); - it("kicks clients that do not log in", function(done) { - var server = mc.createServer({ - 'online-mode': false, - kickTimeout: 100, - checkTimeoutInterval: 10, - }); - var count = 2; - server.on('connection', function(client) { - client.on('end', function(reason) { - assert.strictEqual(reason, '{"text":"LoginTimeout"}'); - server.close(); - }); - }); - server.on('close', function() { - resolve(); - }); - server.on('listening', function() { - var client = new mc.Client(); - client.on('end', function() { - resolve(); - }); - client.connect(25565, '127.0.0.1'); - }); - - function resolve() { - count -= 1; - if(count <= 0) done(); - } - }); - it("kicks clients that do not send keepalive packets", function(done) { - var server = mc.createServer({ - 'online-mode': false, - kickTimeout: 100, - checkTimeoutInterval: 10, - }); - var count = 2; - server.on('connection', function(client) { - client.on('end', function(reason) { - assert.strictEqual(reason, '{"text":"KeepAliveTimeout"}'); - server.close(); - }); - }); - server.on('close', function() { - resolve(); - }); - server.on('listening', function() { - var client = mc.createClient({ - username: 'superpants', - host: '127.0.0.1', - port: 25565, - keepAlive: false, - }); - client.on('end', function() { - resolve(); - }); - }); - function resolve() { - count -= 1; - if(count <= 0) done(); - } - }); - it("responds to ping requests", function(done) { - var server = mc.createServer({ - 'online-mode': false, - motd: 'test1234', - 'max-players': 120, - }); - server.on('listening', function() { - mc.ping({host: '127.0.0.1'}, function(err, results) { - if(err) return done(err); - assert.ok(results.latency >= 0); - assert.ok(results.latency <= 1000); - delete results.latency; - assert.deepEqual(results, { - version: { - name: mc.minecraftVersion, - protocol: mc.version - }, - players: { - max: 120, - online: 0, - sample: [] - }, - description: {text: "test1234"} }); - server.close(); - }); - }); - server.on('close', done); - }); - it("clients can log in and chat", function(done) { - var server = mc.createServer({'online-mode': false,}); - var username = ['player1', 'player2']; - var index = 0; - server.on('login', function(client) { - assert.notEqual(client.id, null); - assert.strictEqual(client.username, username[index++]); - broadcast(client.username + ' joined the game.'); - client.on('end', function() { - broadcast(client.username + ' left the game.', client); - if(client.username === 'player2') server.close(); - }); - client.write('login', { - entityId: client.id, - levelType: 'default', - gameMode: 1, - dimension: 0, - difficulty: 2, - maxPlayers: server.maxPlayers, - reducedDebugInfo: 0 - }); - client.on('chat', function(packet) { - var message = '<' + client.username + '>' + ' ' + packet.message; - broadcast(message); - }); - }); - server.on('close', done); - server.on('listening', function() { - var player1 = mc.createClient({username: 'player1', host: '127.0.0.1'}); - player1.on('login', function(packet) { - assert.strictEqual(packet.gameMode, 1); - assert.strictEqual(packet.levelType, 'default'); - assert.strictEqual(packet.dimension, 0); - assert.strictEqual(packet.difficulty, 2); - player1.once('chat', function(packet) { - assert.strictEqual(packet.message, '{"text":"player2 joined the game."}'); - player1.once('chat', function(packet) { - assert.strictEqual(packet.message, '{"text":" hi"}'); - player2.once('chat', fn); - function fn(packet) { - if(//.test(packet.message)) { - player2.once('chat', fn); - return; - } - assert.strictEqual(packet.message, '{"text":" hello"}'); - player1.once('chat', function(packet) { - assert.strictEqual(packet.message, '{"text":"player2 left the game."}'); - player1.end(); - }); - player2.end(); - } - - player1.write('chat', {message: "hello"}); - }); - player2.write('chat', {message: "hi"}); + client.write(packetName, packet); + } else { + client.once(packetName, function(receivedPacket) { + assertPacketsMatch(packet, receivedPacket); + done(); }); - var player2 = mc.createClient({username: 'player2', host: '127.0.0.1'}); + serverClient.write(packetName, packet); + } + } + + function assertPacketsMatch(p1, p2) { + packetInfo.forEach(function(field) { + assert.deepEqual(p1[field], p2[field]); }); - }); - - function broadcast(message, exclude) { - var client; - for(var clientId in server.clients) { - if(!server.clients.hasOwnProperty(clientId)) continue; - - client = server.clients[clientId]; - if(client !== exclude) client.write('chat', {message: JSON.stringify({text: message}), position: 0}); + var field; + for(field in p1) { + if (p1[field] !== undefined) + assert.ok(field in p2, "field " + field + " missing in p2, in p1 it has value " + JSON.stringify(p1[field])); + } + for(field in p2) { + assert.ok(field in p1, "field " + field + " missing in p1, in p2 it has value " + JSON.stringify(p2[field])); } } }); - it("kicks clients when invalid credentials", function(done) { - this.timeout(10000); - var server = mc.createServer(); - var count = 4; - server.on('connection', function(client) { - client.on('end', function(reason) { - resolve(); + + describe("client "+version.minecraftVersion, function() { + this.timeout(10 * 60 * 1000); + + before(function(done){ + console.log(MC_SERVER_JAR); + fs.exists(MC_SERVER_JAR,function(exists){ + if(exists) + done(); + else + download(version.minecraftVersion,MC_SERVER_JAR,done); + }); + }); + + afterEach(function(done) { + wrap.stopServer(function(err){ + if(err) + console.log(err); + done(err); + }); + }); + after(function(done) { + wrap.deleteServerData(function(err){ + if(err) + console.log(err); + done(err); + }); + }); + it("pings the server", function(done) { + wrap.on('line',function(line){ + console.log(line); + }); + wrap.startServer({ + motd: 'test1234', + 'max-players': 120, + }, function(err) { + if(err) + return done(err); + mc.ping({ + version: version.majorVersion + }, function(err, results) { + if(err) return done(err); + assert.ok(results.latency >= 0); + assert.ok(results.latency <= 1000); + delete results.latency; + delete results.favicon; // too lazy to figure it out + /* assert.deepEqual(results, { + version: { + name: '1.7.4', + protocol: 4 + }, + description: { text: "test1234" } + });*/ + done(); + }); + }); + }); + it.skip("connects successfully - online mode (STUBBED)", function(done) { + wrap.startServer({'online-mode': 'true'}, function(err) { + if(err) + return done(err); + var client = mc.createClient({ + username: process.env.MC_USERNAME, + password: process.env.MC_PASSWORD, + version: version.majorVersion + }); + wrap.on('line', function(line) { + var match = line.match(/\[Server thread\/INFO\]: <(.+?)> (.+)/); + if(!match) return; + assert.strictEqual(match[1], client.session.username); + assert.strictEqual(match[2], "hello everyone; I have logged in."); + wrap.writeServer("say hello\n"); + }); + var chatCount = 0; + client.on('login', function(packet) { + assert.strictEqual(packet.levelType, 'default'); + assert.strictEqual(packet.difficulty, 1); + assert.strictEqual(packet.dimension, 0); + assert.strictEqual(packet.gameMode, 0); + client.write('chat', { + message: "hello everyone; I have logged in." + }); + }); + client.on('chat', function(packet) { + done(); + }); + }); + done(); + }); + it.skip("connects successfully - offline mode (STUBBED)", function(done) { + wrap.startServer({'online-mode': 'false'}, function(err) { + if(err) + return done(err); + var client = mc.createClient({ + username: 'Player', + version: version.majorVersion + }); + wrap.on('line', function(line) { + var match = line.match(/\[Server thread\/INFO\]: <(.+?)> (.+)/); + if(!match) return; + assert.strictEqual(match[1], 'Player'); + assert.strictEqual(match[2], "hello everyone; I have logged in."); + wrap.writeServer("say hello\n"); + }); + var chatCount = 0; + client.on('login', function(packet) { + assert.strictEqual(packet.levelType, 'default'); + assert.strictEqual(packet.difficulty, 1); + assert.strictEqual(packet.dimension, 0); + assert.strictEqual(packet.gameMode, 0); + client.write('chat', { + message: "hello everyone; I have logged in." + }); + }); + client.on('chat', function(packet) { + chatCount += 1; + assert.ok(chatCount <= 2); + var message = JSON.parse(packet.message); + if(chatCount === 1) { + assert.strictEqual(message.translate, "chat.type.text"); + assert.deepEqual(message["with"][0], { + clickEvent: { + action: "suggest_command", + value: "/msg Player " + }, + text: "Player" + }); + assert.strictEqual(message["with"][1], "hello everyone; I have logged in."); + } else if(chatCount === 2) { + assert.strictEqual(message.translate, "chat.type.announcement"); + assert.strictEqual(message["with"][0], "Server"); + assert.deepEqual(message["with"][1], { + text: "", + extra: ["hello"] + }); + done(); + } + }); + }); + done(); + }); + it("gets kicked when no credentials supplied in online mode", function(done) { + wrap.startServer({'online-mode': 'true'}, function(err) { + if(err) + return done(err); + var client = mc.createClient({ + username: 'Player', + version: version.majorVersion + }); + var gotKicked = false; + client.on('disconnect', function(packet) { + assert.ok(packet.reason.indexOf('"Failed to verify username!"')!=-1); + gotKicked = true; + }); + client.on('end', function() { + assert.ok(gotKicked); + done(); + }); + }); + }); + it("does not crash for " + SURVIVE_TIME + "ms", function(done) { + wrap.startServer({'online-mode': 'false'}, function(err) { + if(err) + return done(err); + var client = mc.createClient({ + username: 'Player', + version: version.majorVersion + }); + client.on("login", function(packet) { + client.write("chat", { + message: "hello everyone; I have logged in." + }); + }); + client.on("chat", function(packet) { + var message = JSON.parse(packet.message); + assert.strictEqual(message.translate, "chat.type.text"); + /*assert.deepEqual(message["with"][0], { + clickEvent: { + action: "suggest_command", + value: "/msg Player " + }, + text: "Player" + });*/ + assert.strictEqual(message["with"][1], "hello everyone; I have logged in."); + setTimeout(function() { + done(); + }, SURVIVE_TIME); + }); + }); + }); + }); + describe("mc-server "+version.minecraftVersion, function() { + it("starts listening and shuts down cleanly", function(done) { + var server = mc.createServer({ + 'online-mode': false, + version: version.majorVersion + }); + var listening = false; + server.on('listening', function() { + listening = true; server.close(); }); - }); - server.on('close', function() { - resolve(); - }); - server.on('listening', function() { - resolve(); - var client = mc.createClient({ - username: 'lalalal', - host: "127.0.0.1" + server.on('close', function() { + assert.ok(listening); + done(); }); - client.on('end', function() { + }); + it("kicks clients that do not log in", function(done) { + var server = mc.createServer({ + 'online-mode': false, + kickTimeout: 100, + checkTimeoutInterval: 10, + version: version.majorVersion + }); + var count = 2; + server.on('connection', function(client) { + client.on('end', function(reason) { + assert.strictEqual(reason, '{"text":"LoginTimeout"}'); + server.close(); + }); + }); + server.on('close', function() { resolve(); }); + server.on('listening', function() { + var client = new mc.Client(false,version.majorVersion); + client.on('end', function() { + resolve(); + }); + client.connect(25565, '127.0.0.1'); + }); + + function resolve() { + count -= 1; + if(count <= 0) done(); + } }); - function resolve() { - count -= 1; - if(count <= 0) done(); - } - }); - it("gives correct reason for kicking clients when shutting down", function(done) { - var server = mc.createServer({'online-mode': false,}); - var count = 2; - server.on('login', function(client) { - client.on('end', function(reason) { - assert.strictEqual(reason, '{"text":"ServerShutdown"}'); + it("kicks clients that do not send keepalive packets", function(done) { + var server = mc.createServer({ + 'online-mode': false, + kickTimeout: 100, + checkTimeoutInterval: 10, + version: version.majorVersion + }); + var count = 2; + server.on('connection', function(client) { + client.on('end', function(reason) { + assert.strictEqual(reason, '{"text":"KeepAliveTimeout"}'); + server.close(); + }); + }); + server.on('close', function() { resolve(); }); - client.write('login', { - entityId: client.id, - levelType: 'default', - gameMode: 1, - dimension: 0, - difficulty: 2, - maxPlayers: server.maxPlayers, - reducedDebugInfo: 0 + server.on('listening', function() { + var client = mc.createClient({ + username: 'superpants', + host: '127.0.0.1', + port: 25565, + keepAlive: false, + version: version.majorVersion + }); + client.on('end', function() { + resolve(); + }); }); + function resolve() { + count -= 1; + if(count <= 0) done(); + } }); - server.on('close', function() { - resolve(); - }); - server.on('listening', function() { - var client = mc.createClient({username: 'lalalal', host: '127.0.0.1'}); - client.on('login', function() { - server.close(); + it("responds to ping requests", function(done) { + var server = mc.createServer({ + 'online-mode': false, + motd: 'test1234', + 'max-players': 120, + version: version.majorVersion }); + server.on('listening', function() { + mc.ping({ + host: '127.0.0.1', + version: version.majorVersion + }, function(err, results) { + if(err) return done(err); + assert.ok(results.latency >= 0); + assert.ok(results.latency <= 1000); + delete results.latency; + assert.deepEqual(results, { + version: { + name: version.minecraftVersion, + protocol: version.version + }, + players: { + max: 120, + online: 0, + sample: [] + }, + description: {text: "test1234"} + }); + server.close(); + }); + }); + server.on('close', done); + }); + it("clients can log in and chat", function(done) { + var server = mc.createServer({ + 'online-mode': false, + version: version.majorVersion + }); + var username = ['player1', 'player2']; + var index = 0; + server.on('login', function(client) { + assert.notEqual(client.id, null); + assert.strictEqual(client.username, username[index++]); + broadcast(client.username + ' joined the game.'); + client.on('end', function() { + broadcast(client.username + ' left the game.', client); + if(client.username === 'player2') server.close(); + }); + client.write('login', { + entityId: client.id, + levelType: 'default', + gameMode: 1, + dimension: 0, + difficulty: 2, + maxPlayers: server.maxPlayers, + reducedDebugInfo: 0 + }); + client.on('chat', function(packet) { + var message = '<' + client.username + '>' + ' ' + packet.message; + broadcast(message); + }); + }); + server.on('close', done); + server.on('listening', function() { + var player1 = mc.createClient({ + username: 'player1', + host: '127.0.0.1', + version: version.majorVersion + }); + player1.on('login', function(packet) { + assert.strictEqual(packet.gameMode, 1); + assert.strictEqual(packet.levelType, 'default'); + assert.strictEqual(packet.dimension, 0); + assert.strictEqual(packet.difficulty, 2); + player1.once('chat', function(packet) { + assert.strictEqual(packet.message, '{"text":"player2 joined the game."}'); + player1.once('chat', function(packet) { + assert.strictEqual(packet.message, '{"text":" hi"}'); + player2.once('chat', fn); + function fn(packet) { + if(//.test(packet.message)) { + player2.once('chat', fn); + return; + } + assert.strictEqual(packet.message, '{"text":" hello"}'); + player1.once('chat', function(packet) { + assert.strictEqual(packet.message, '{"text":"player2 left the game."}'); + player1.end(); + }); + player2.end(); + } + + player1.write('chat', {message: "hello"}); + }); + player2.write('chat', {message: "hi"}); + }); + var player2 = mc.createClient({ + username: 'player2', + host: '127.0.0.1', + version: version.majorVersion + }); + }); + }); + + function broadcast(message, exclude) { + var client; + for(var clientId in server.clients) { + if(!server.clients.hasOwnProperty(clientId)) continue; + + client = server.clients[clientId]; + if(client !== exclude) client.write('chat', {message: JSON.stringify({text: message}), position: 0}); + } + } + }); + it("kicks clients when invalid credentials", function(done) { + this.timeout(10000); + var server = mc.createServer({ + version: version.majorVersion + }); + var count = 4; + server.on('connection', function(client) { + client.on('end', function(reason) { + resolve(); + server.close(); + }); + }); + server.on('close', function() { + resolve(); + }); + server.on('listening', function() { + resolve(); + var client = mc.createClient({ + username: 'lalalal', + host: "127.0.0.1", + version: version.majorVersion + }); + client.on('end', function() { + resolve(); + }); + }); + function resolve() { + count -= 1; + if(count <= 0) done(); + } + }); + it("gives correct reason for kicking clients when shutting down", function(done) { + var server = mc.createServer({ + 'online-mode': false, + version: version.majorVersion + }); + var count = 2; + server.on('login', function(client) { + client.on('end', function(reason) { + assert.strictEqual(reason, '{"text":"ServerShutdown"}'); + resolve(); + }); + client.write('login', { + entityId: client.id, + levelType: 'default', + gameMode: 1, + dimension: 0, + difficulty: 2, + maxPlayers: server.maxPlayers, + reducedDebugInfo: 0 + }); + }); + server.on('close', function() { + resolve(); + }); + server.on('listening', function() { + var client = mc.createClient({ + username: 'lalalal', + host: '127.0.0.1', + version: version.majorVersion + }); + client.on('login', function() { + server.close(); + }); + }); + function resolve() { + count -= 1; + if(count <= 0) done(); + } }); - function resolve() { - count -= 1; - if(count <= 0) done(); - } }); -}); +}) From 7871ac446aa339a711eb985ed442e8b259c6e6b0 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 30 Sep 2015 00:03:42 +0200 Subject: [PATCH 2/4] fix proxy relatively to the unexposing of createPacketBuffer also fix a small bug in serializer.js --- examples/proxy/proxy.js | 8 ++++---- src/transforms/serializer.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/proxy/proxy.js b/examples/proxy/proxy.js index ea92d21..4bd71a0 100644 --- a/examples/proxy/proxy.js +++ b/examples/proxy/proxy.js @@ -128,8 +128,8 @@ srv.on('login', function(client) { targetClient.on('raw', function(buffer, meta) { if(client.state != states.PLAY || meta.state != states.PLAY) return; - var packetData = mc.parsePacketData(buffer, meta.state, false, {"packet": 1}).data; - var packetBuff = mc.createPacketBuffer(meta.name, meta.state, packetData, true); + var packetData = targetClient.deserializer.parsePacketData(buffer).data; + var packetBuff = client.serializer.createPacketBuffer(meta.name, packetData); if(buffertools.compare(buffer, packetBuff) != 0) { console.log("client<-server: Error in packet " + state + "." + meta.name); console.log(buffer.toString('hex')); @@ -148,8 +148,8 @@ srv.on('login', function(client) { client.on('raw', function(buffer, meta) { if(meta.state != states.PLAY || targetClient.state != states.PLAY) return; - var packetData = mc.parsePacketData(buffer, meta.state, true, {"packet": 1}).data; - var packetBuff = mc.createPacketBuffer(meta.name, meta.state, packetData, false); + var packetData = client.deserializer.parsePacketData(buffer).data; + var packetBuff = targetClient.serializer.createPacketBuffer(meta.name, packetData); if(buffertools.compare(buffer, packetBuff) != 0) { console.log("client->server: Error in packet " + state + "." + meta.name); console.log(buffer.toString('hex')); diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js index 2d98bee..1f030ff 100644 --- a/src/transforms/serializer.js +++ b/src/transforms/serializer.js @@ -65,7 +65,7 @@ class Serializer extends Transform { createPacketBuffer(packetName, params) { var direction = !this.isServer ? 'toServer' : 'toClient'; var packetId = this.packetIds[this.protocolState][direction][packetName]; - assert.notEqual(packetId, undefined, `${this.protocolState}.${this.isServer}.${packetName} : ${packetId}`); + assert.notEqual(packetId, undefined, `${this.protocolState}.${direction}.${packetName} : ${packetId}`); var packet = this.packetFields[this.protocolState][direction][packetName]; packet=packet ? packet : null; From 3f50a6f19a53df902c96f1be01a37c07e3da5c06 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 30 Sep 2015 00:16:39 +0200 Subject: [PATCH 3/4] add an optional argument to the proxy example to specify the version --- examples/proxy/proxy.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/proxy/proxy.js b/examples/proxy/proxy.js index 4bd71a0..14792d9 100644 --- a/examples/proxy/proxy.js +++ b/examples/proxy/proxy.js @@ -2,7 +2,7 @@ var mc = require('../../'); var states = mc.states; function printHelpAndExit(exitCode) { - console.log("usage: node proxy.js [...] []"); + console.log("usage: node proxy.js [...] [] []"); console.log("options:"); console.log(" --dump name"); console.log(" print to stdout messages with the specified name."); @@ -37,6 +37,7 @@ var host; var port = 25565; var user; var passwd; +var version; var printAllNames = false; var printNameWhitelist = {}; @@ -59,10 +60,11 @@ var printNameBlacklist = {}; printHelpAndExit(1); } } - if(!(i + 2 <= args.length && args.length <= i + 3)) printHelpAndExit(1); + if(!(i + 2 <= args.length && args.length <= i + 4)) printHelpAndExit(1); host = args[i++]; user = args[i++]; passwd = args[i++]; + version = args[i++]; })(); if(host.indexOf(':') != -1) { @@ -73,7 +75,8 @@ if(host.indexOf(':') != -1) { var srv = mc.createServer({ 'online-mode': false, port: 25566, - keepAlive: false + keepAlive: false, + version:version }); srv.on('login', function(client) { var addr = client.socket.remoteAddress; @@ -98,7 +101,8 @@ srv.on('login', function(client) { username: user, password: passwd, 'online-mode': passwd != null ? true : false, - keepAlive:false + keepAlive:false, + version:version }); client.on('packet', function(data, meta) { if(targetClient.state == states.PLAY && meta.state == states.PLAY) { From c9c537e3930f782074855dfd96f435b3815e1631 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 30 Sep 2015 12:45:38 +0200 Subject: [PATCH 4/4] remove some stuff I forgot in browser.js --- src/browser.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/browser.js b/src/browser.js index 10a29fc..91c7e13 100644 --- a/src/browser.js +++ b/src/browser.js @@ -5,8 +5,6 @@ var serializer = require("./transforms/serializer"); module.exports = { Client: require('./client'), protocol: require('./protocol'), - types: serializer.types, - get: serializer.get, readPackets:readPackets, supportedVersions:require("./version").supportedVersions };