diff --git a/README.md b/README.md index 91d41eb..c2a95ab 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,8 @@ client.on('packet', function(packet) { }); }); ``` + +## Testing + +* Ensure your system has the `java` executable in `PATH`. +* `MC_SERVER_JAR=path/to/minecraft_server.jar MC_USERNAME=username MC_EMAIL=email@example.com MC_PASSWORD=password npm test` diff --git a/index.js b/index.js index 1b271f1..ce976fc 100644 --- a/index.js +++ b/index.js @@ -294,7 +294,7 @@ function readIntVector(buffer, offset) { function readEntityMetadata(buffer, offset) { var cursor = offset; var metadata = {}; - var item, key, type, results; + var item, key, type, results, reader; while (true) { if (cursor + 1 > buffer.length) return null; item = buffer.readUInt8(cursor); diff --git a/package.json b/package.json index ccd11d5..1b065cf 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "url": "git://github.com/superjoe30/node-minecraft-protocol.git" }, "scripts": { - "test": "mocha" + "test": "mocha --reporter spec" }, "keywords": [ "minecraft", @@ -26,7 +26,9 @@ "node": ">=0.8.16" }, "devDependencies": { - "mocha": "~1.7.4" + "mocha": "~1.7.4", + "mkdirp": "~0.3.4", + "rimraf": "~2.1.1" }, "dependencies": { "ursa": "~0.8.0", diff --git a/test.js b/test.js deleted file mode 100644 index 6023f72..0000000 --- a/test.js +++ /dev/null @@ -1,13 +0,0 @@ -var mc = require('./'); -var client = mc.createClient({ - username: process.env.MC_USERNAME, - email: process.env.MC_EMAIL, - password: process.env.MC_PASSWORD, -}); -client.on('packet', function(packet) { - if (packet.id !== 0x03) return; - if (packet.message.indexOf(client.session.username) !== -1) return; - client.writePacket(0x03, { - message: packet.message, - }); -}); diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..e52ef96 --- /dev/null +++ b/test/test.js @@ -0,0 +1,152 @@ +var mc = require('../') + , spawn = require('child_process').spawn + , path = require('path') + , fs = require('fs') + , assert = require('assert') + , mkdirp = require('mkdirp') + , rimraf = require('rimraf') + , Batch = require('batch') + , MC_SERVER_JAR = process.env.MC_SERVER_JAR + , SURVIVE_TIME = 10000 + , MC_SERVER_PATH = path.join(__dirname, 'server') + +var defaultServerProps = { + 'generator-settings': "", + 'allow-nether': 'true', + 'level-name': 'world', + 'enable-query': 'false', + 'allow-flight': 'false', + 'server-port': '25565', + 'level-type': 'DEFAULT', + 'enable-rcon': 'false', + 'level-seed': "", + 'server-ip': "", + 'max-build-height': '256', + 'spawn-npcs': 'true', + 'white-list': 'false', + 'spawn-animals': 'true', + 'snooper-enabled': 'true', + 'hardcore': 'false', + 'texture-pack': '', + 'online-mode': 'true', + 'pvp': 'true', + 'difficulty': '1', + 'gamemode': '0', + 'max-players': '20', + 'spawn-monsters': 'true', + 'generate-structures': 'true', + 'view-distance': '10', + 'spawn-protection': '16', + 'motd': 'A Minecraft Server', +}; + +describe("minecraft protocol", function() { + this.timeout(20000); + + var mcServer; + function startServer(propOverrides, done) { + var props = {}; + var prop; + for (prop in defaultServerProps) { + props[prop] = defaultServerProps[prop]; + } + for (prop in propOverrides) { + props[prop] = propOverrides[prop]; + } + var batch = new Batch(); + batch.push(function(cb) { mkdirp(MC_SERVER_PATH, cb); }); + batch.push(function(cb) { + var str = ""; + for (var prop in props) { + str += prop + "=" + props[prop] + "\n"; + } + fs.writeFile(path.join(MC_SERVER_PATH, "server.properties"), str, cb); + }); + batch.end(function(err) { + if (err) return done(err); + mcServer = spawn('java', [ '-jar', MC_SERVER_JAR, 'nogui'], { + stdio: 'pipe', + cwd: MC_SERVER_PATH, + }); + mcServer.stdin.setEncoding('utf8'); + mcServer.stdout.setEncoding('utf8'); + mcServer.stderr.setEncoding('utf8'); + var buffer = ""; + mcServer.stdout.on('data', onData); + mcServer.stderr.on('data', onData); + function onData(data) { + buffer += data; + var lines = buffer.split("\n"); + var len = lines.length - 1; + for (var i = 0; i < len; ++i) { + mcServer.emit('line', lines[i]); + } + buffer = lines[lines.length - 1]; + } + mcServer.on('line', onLine); + mcServer.on('line', function(line) { + //process.stderr.write('.'); + console.error("[MC]", line); + }); + function onLine(line) { + if (/\[INFO\] Done/.test(line)) { + mcServer.removeListener('line', onLine); + done(); + } + } + }); + } + afterEach(function(done) { + mcServer.stdin.write("stop\n"); + mcServer.on('exit', function() { + mcServer = null; + done(); + }); + }); + after(function(done) { + rimraf(MC_SERVER_PATH, done); + }); + it("connects successfully - online mode", function(done) { + startServer({ 'online-mode': 'true' }, function() { + var client = mc.createClient({ + username: process.env.MC_USERNAME, + email: process.env.MC_EMAIL, + password: process.env.MC_PASSWORD, + }); + mcServer.on('line', function(line) { + var match = line.match(/\[INFO\] <(.+?)> (.+)$/); + if (! match) return; + assert.strictEqual(match[1], client.session.username); + assert.strictEqual(match[2], "hello everyone; I have logged in."); + mcServer.stdin.write("say hello\n"); + }); + var chatCount = 0; + client.on('packet', function(packet) { + if (packet.id === 0x01) { + assert.strictEqual(packet.levelType, 'default'); + assert.strictEqual(packet.difficulty, 1); + assert.strictEqual(packet.dimension, 0); + assert.strictEqual(packet.gameMode, 0); + client.writePacket(0x03, { + message: "hello everyone; I have logged in." + }); + } else if (packet.id === 0x03) { + chatCount += 1; + assert.ok(chatCount <= 2); + if (chatCount === 1) { + assert.strictEqual(packet.message, "<" + client.session.username + ">" + " hello everyone; I have logged in."); + } else if (chatCount === 2) { + assert.strictEqual(packet.message, "[Server] hello"); + done(); + } + } + }); + }); + }); + it("connects successfully - offline mode"); + it("emits error when no credentials supplied in online mode"); + it("survives for " + SURVIVE_TIME + "ms"); +}); + +function doIt() { +}