Merge pull request #274 from rom1504/use_protocols

use protodef
This commit is contained in:
Robin Lambertz 2015-11-09 02:42:45 +01:00
commit 3007740135
26 changed files with 194 additions and 1374 deletions

View File

@ -119,12 +119,12 @@ The user's session, as returned by the Yggdrasil system.
### `packet` event ### `packet` event
Called with every packet parsed. Takes two params, the JSON data we parsed, Called with every packet parsed. Takes two params, the JSON data we parsed,
and the packet metadata (name, id, state) and the packet metadata (name, state)
### `raw` event ### `raw` event
Called with every packet, but as a buffer. Takes two params, the buffer Called with every packet, but as a buffer. Takes two params, the buffer
and the packet metadata (name, id, state) and the packet metadata (name, state)
### `state` event ### `state` event

View File

@ -2,7 +2,7 @@ var mc = require('../../');
var states = mc.states; var states = mc.states;
function printHelpAndExit(exitCode) { function printHelpAndExit(exitCode) {
console.log("usage: node proxy.js [<options>...] <target_srv> <user> [<password>] [<version>]"); console.log("usage: node proxy.js [<options>...] <target_srv> [<version>]");
console.log("options:"); console.log("options:");
console.log(" --dump name"); console.log(" --dump name");
console.log(" print to stdout messages with the specified name."); console.log(" print to stdout messages with the specified name.");
@ -35,8 +35,6 @@ process.argv.forEach(function(val, index, array) {
var args = process.argv.slice(2); var args = process.argv.slice(2);
var host; var host;
var port = 25565; var port = 25565;
var user;
var passwd;
var version; var version;
var printAllNames = false; var printAllNames = false;
@ -62,8 +60,6 @@ var printNameBlacklist = {};
} }
if(!(i + 2 <= args.length && args.length <= i + 4)) printHelpAndExit(1); if(!(i + 2 <= args.length && args.length <= i + 4)) printHelpAndExit(1);
host = args[i++]; host = args[i++];
user = args[i++];
passwd = args[i++];
version = args[i++]; version = args[i++];
})(); })();
@ -98,9 +94,7 @@ srv.on('login', function(client) {
var targetClient = mc.createClient({ var targetClient = mc.createClient({
host: host, host: host,
port: port, port: port,
username: user, username: client.username,
password: passwd,
'online-mode': passwd != null ? true : false,
keepAlive:false, keepAlive:false,
version:version version:version
}); });
@ -108,7 +102,7 @@ srv.on('login', function(client) {
if(targetClient.state == states.PLAY && meta.state == states.PLAY) { if(targetClient.state == states.PLAY && meta.state == states.PLAY) {
if(shouldDump(meta.name, "o")) { if(shouldDump(meta.name, "o")) {
console.log("client->server:", console.log("client->server:",
client.state + ".0x" + meta.id.toString(16) + " :", client.state + " "+ meta.name + " :",
JSON.stringify(data)); JSON.stringify(data));
} }
if(!endedTargetClient) if(!endedTargetClient)
@ -122,18 +116,19 @@ srv.on('login', function(client) {
targetClient.state + "." + meta.name + " :" + targetClient.state + "." + meta.name + " :" +
JSON.stringify(data)); JSON.stringify(data));
} }
if(!endedClient) if(!endedClient) {
client.write(meta.name, data); client.write(meta.name, data);
if (meta.name === 'set_compression' || meta.name === 'compression') // Set compression if (meta.name === 'set_compression') // Set compression
client.compressionThreshold = data.threshold; client.compressionThreshold = data.threshold;
}
} }
}); });
var bufferEqual = require('buffer-equal'); var bufferEqual = require('buffer-equal');
targetClient.on('raw', function(buffer, meta) { targetClient.on('raw', function(buffer, meta) {
if(client.state != states.PLAY || meta.state != states.PLAY) if(client.state != states.PLAY || meta.state != states.PLAY)
return; return;
var packetData = targetClient.deserializer.parsePacketData(buffer).data; var packetData = targetClient.deserializer.parsePacketBuffer(buffer).data.params;
var packetBuff = client.serializer.createPacketBuffer(meta.name, packetData); var packetBuff = client.serializer.createPacketBuffer({name:meta.name, params:packetData});
if(!bufferEqual(buffer, packetBuff)) { if(!bufferEqual(buffer, packetBuff)) {
console.log("client<-server: Error in packet " + state + "." + meta.name); console.log("client<-server: Error in packet " + state + "." + meta.name);
console.log(buffer.toString('hex')); console.log(buffer.toString('hex'));
@ -152,8 +147,8 @@ srv.on('login', function(client) {
client.on('raw', function(buffer, meta) { client.on('raw', function(buffer, meta) {
if(meta.state != states.PLAY || targetClient.state != states.PLAY) if(meta.state != states.PLAY || targetClient.state != states.PLAY)
return; return;
var packetData = client.deserializer.parsePacketData(buffer).data; var packetData = client.deserializer.parsePacketBuffer(buffer).data.params;
var packetBuff = targetClient.serializer.createPacketBuffer(meta.name, packetData); var packetBuff = targetClient.serializer.createPacketBuffer({name:meta.name, params:packetData});
if(!bufferEqual(buffer, packetBuff)) { if(!bufferEqual(buffer, packetBuff)) {
console.log("client->server: Error in packet " + state + "." + meta.name); console.log("client->server: Error in packet " + state + "." + meta.name);
console.log(buffer.toString('hex')); console.log(buffer.toString('hex'));

View File

@ -14,6 +14,10 @@ server.on('login', function(client) {
console.log('Connection closed', '(' + addr + ')'); console.log('Connection closed', '(' + addr + ')');
}); });
client.on('error', function(error) {
console.log('Error:', error);
});
// send init data so client will start rendering world // send init data so client will start rendering world
client.write('login', { client.write('login', {
entityId: client.id, entityId: client.id,

View File

@ -46,10 +46,12 @@
"minecraft-data": "^0.13.0", "minecraft-data": "^0.13.0",
"node-uuid": "~1.4.1", "node-uuid": "~1.4.1",
"prismarine-nbt": "0.0.1", "prismarine-nbt": "0.0.1",
"protodef": "0.2.0",
"readable-stream": "^1.1.0", "readable-stream": "^1.1.0",
"superagent": "~0.10.0", "superagent": "~0.10.0",
"ursa-purejs": "0.0.3", "ursa-purejs": "0.0.3",
"uuid": "^2.0.1", "uuid": "^2.0.1",
"uuid-1345": "^0.99.6",
"yggdrasil": "0.1.0" "yggdrasil": "0.1.0"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@ -1,10 +1,6 @@
var readPackets = require("./packets").readPackets;
var utils = require("./utils"); var utils = require("./utils");
var serializer = require("./transforms/serializer");
module.exports = { module.exports = {
Client: require('./client'), Client: require('./client'),
protocol: require('./protocol'),
readPackets:readPackets,
supportedVersions:require("./version").supportedVersions supportedVersions:require("./version").supportedVersions
}; };

View File

@ -1,11 +1,12 @@
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var debug = require('./debug'); var debug = require('./debug');
var serializer = require('./transforms/serializer');
var compression = require('./transforms/compression'); var compression = require('./transforms/compression');
var framing = require('./transforms/framing'); var framing = require('./transforms/framing');
var crypto = require('crypto'); var crypto = require('crypto');
var states = serializer.states; var states = require("./states");
var createSerializer=require("./transforms/serializer").createSerializer;
var createDeserializer=require("./transforms/serializer").createDeserializer;
class Client extends EventEmitter class Client extends EventEmitter
{ {
@ -19,12 +20,14 @@ class Client extends EventEmitter
decompressor=null; decompressor=null;
deserializer; deserializer;
isServer; isServer;
version;
protocolState=states.HANDSHAKING;
constructor(isServer,version) { constructor(isServer,version) {
super(); super();
this.serializer = serializer.createSerializer({ isServer, version:version}); this.version=version;
this.deserializer = serializer.createDeserializer({ isServer, packetsToParse: this.packetsToParse, version:version});
this.isServer = !!isServer; this.isServer = !!isServer;
this.setSerializer(states.HANDSHAKING);
this.on('newListener', function(event, listener) { this.on('newListener', function(event, listener) {
var direction = this.isServer ? 'toServer' : 'toClient'; var direction = this.isServer ? 'toServer' : 'toClient';
@ -38,13 +41,76 @@ class Client extends EventEmitter
} }
get state(){ get state(){
return this.serializer.protocolState; return this.protocolState;
}
setSerializer(state) {
this.serializer = createSerializer({ isServer:this.isServer, version:this.version, state: state});
this.deserializer = createDeserializer({ isServer:this.isServer, version:this.version, state: state, packetsToParse:
this.packetsToParse});
this.serializer.on('error', (e) => {
var parts=e.field.split(".");
parts.shift();
var serializerDirection = !this.isServer ? 'toServer' : 'toClient';
e.field = [this.protocolState, serializerDirection].concat(parts).join(".");
e.message = `Serialization error for ${e.field} : ${e.message}`;
this.emit('error',e);
});
this.deserializer.on('error', (e) => {
var parts=e.field.split(".");
parts.shift();
var deserializerDirection = this.isServer ? 'toServer' : 'toClient';
e.field = [this.protocolState, deserializerDirection].concat(parts).join(".");
e.message = `Deserialization error for ${e.field} : ${e.message}`;
this.emit('error',e);
});
this.deserializer.on('data', (parsed) => {
parsed.metadata.name=parsed.data.name;
parsed.data=parsed.data.params;
parsed.metadata.state=state;
this.emit('packet', parsed.data, parsed.metadata);
this.emit(parsed.metadata.name, parsed.data, parsed.metadata);
this.emit('raw.' + parsed.metadata.name, parsed.buffer, parsed.metadata);
this.emit('raw', parsed.buffer, parsed.metadata);
});
} }
set state(newProperty) { set state(newProperty) {
var oldProperty = this.serializer.protocolState; var oldProperty = this.protocolState;
this.serializer.protocolState = newProperty; this.protocolState = newProperty;
this.deserializer.protocolState = newProperty;
if(!this.compressor)
{
this.serializer.unpipe(this.framer);
this.splitter.unpipe(this.deserializer);
}
else
{
this.serializer.unpipe(this.compressor);
this.decompressor.unpipe(this.deserializer);
}
this.serializer.removeAllListeners();
this.deserializer.removeAllListeners();
this.setSerializer(this.protocolState);
if(!this.compressor)
{
this.serializer.pipe(this.framer);
this.splitter.pipe(this.deserializer);
}
else
{
this.serializer.pipe(this.compressor);
this.decompressor.pipe(this.deserializer);
}
this.emit('state', newProperty, oldProperty); this.emit('state', newProperty, oldProperty);
} }
@ -87,20 +153,11 @@ class Client extends EventEmitter
this.socket.on('close', endSocket); this.socket.on('close', endSocket);
this.socket.on('end', endSocket); this.socket.on('end', endSocket);
this.socket.on('timeout', endSocket); this.socket.on('timeout', endSocket);
this.serializer.on('error', onError);
this.deserializer.on('error', onError);
this.framer.on('error', onError); this.framer.on('error', onError);
this.splitter.on('error', onError); this.splitter.on('error', onError);
this.socket.pipe(this.splitter).pipe(this.deserializer); this.socket.pipe(this.splitter).pipe(this.deserializer);
this.serializer.pipe(this.framer).pipe(this.socket); this.serializer.pipe(this.framer).pipe(this.socket);
this.deserializer.on('data', (parsed) => {
this.emit('packet', parsed.data, parsed.metadata);
this.emit(parsed.metadata.name, parsed.data, parsed.metadata);
this.emit('raw.' + parsed.metadata.name, parsed.buffer, parsed.metadata);
this.emit('raw', parsed.buffer, parsed.metadata);
});
} }
end(reason) { end(reason) {
@ -137,10 +194,10 @@ class Client extends EventEmitter
} }
} }
write(packetName, params) { write(name, params) {
debug("writing packet " + this.state + "." + packetName); debug("writing packet " + this.state + "." + name);
debug(params); debug(params);
this.serializer.write({ packetName, params }); this.serializer.write({ name, params });
} }
writeRaw(buffer) { writeRaw(buffer) {

View File

@ -6,8 +6,7 @@ var assert = require('assert');
var crypto = require('crypto'); var crypto = require('crypto');
var yggdrasil = require('yggdrasil')({}); var yggdrasil = require('yggdrasil')({});
var yggserver = require('yggdrasil').server({}); var yggserver = require('yggdrasil').server({});
var serializer = require("./transforms/serializer"); var states = require("./states");
var states = serializer.states;
var debug = require("./debug"); var debug = require("./debug");
var uuid = require('uuid'); var uuid = require('uuid');

View File

@ -1,10 +1,10 @@
var ursa=require("./ursa"); var ursa=require("./ursa");
var crypto = require('crypto'); var crypto = require('crypto');
var yggserver = require('yggdrasil').server({}); var yggserver = require('yggdrasil').server({});
var serializer = require("./transforms/serializer"); var states = require("./states");
var states = serializer.states;
var bufferEqual = require('buffer-equal'); var bufferEqual = require('buffer-equal');
var Server = require('./server'); var Server = require('./server');
var UUID = require('uuid-1345');
module.exports=createServer; module.exports=createServer;
@ -195,10 +195,27 @@ function createServer(options) {
} }
} }
// https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/java/util/UUID.java#L163
function javaUUID(s)
{
var hash = crypto.createHash("md5");
hash.update(s, 'utf8');
var buffer = hash.digest();
buffer[6] = (buffer[6] & 0x0f) | 0x30;
buffer[8] = (buffer[8] & 0x3f) | 0x80;
return buffer;
}
function nameToMcOfflineUUID(name)
{
return (new UUID(javaUUID("OfflinePlayer:"+name))).toString();
}
function loginClient() { function loginClient() {
var isException = !!server.onlineModeExceptions[client.username.toLowerCase()]; var isException = !!server.onlineModeExceptions[client.username.toLowerCase()];
if(onlineMode == false || isException) { if(onlineMode == false || isException) {
client.uuid = "0-0-0-0-0"; client.uuid = nameToMcOfflineUUID(client.username);
} }
client.write('compress', { threshold: 256 }); // Default threshold is 256 client.write('compress', { threshold: 256 }); // Default threshold is 256
client.compressionThreshold = 256; client.compressionThreshold = 256;

View File

@ -1,69 +0,0 @@
var { getField, getFieldInfo } = require('../utils');
module.exports = {
'switch': [readSwitch, writeSwitch, sizeOfSwitch],
'option': [readOption, writeOption, sizeOfOption],
};
function readSwitch(buffer, offset, typeArgs, rootNode) {
var compareTo = getField(typeArgs.compareTo, rootNode);
var fieldInfo;
if (typeof typeArgs.fields[compareTo] === 'undefined' && typeof typeArgs.default === "undefined")
throw new Error(compareTo + " has no associated fieldInfo in switch");
else if (typeof typeArgs.fields[compareTo] === 'undefined')
fieldInfo = getFieldInfo(typeArgs.default);
else
fieldInfo = getFieldInfo(typeArgs.fields[compareTo]);
return this.read(buffer, offset, fieldInfo, rootNode);
}
function writeSwitch(value, buffer, offset, typeArgs, rootNode) {
var compareTo = getField(typeArgs.compareTo, rootNode);
var fieldInfo;
if (typeof typeArgs.fields[compareTo] === 'undefined' && typeof typeArgs.default === "undefined")
throw new Error(compareTo + " has no associated fieldInfo in switch");
else if (typeof typeArgs.fields[compareTo] === 'undefined')
fieldInfo = getFieldInfo(typeArgs.default);
else
fieldInfo = getFieldInfo(typeArgs.fields[compareTo]);
return this.write(value, buffer, offset, fieldInfo, rootNode);
}
function sizeOfSwitch(value, typeArgs, rootNode) {
var compareTo = getField(typeArgs.compareTo, rootNode);
var fieldInfo;
if (typeof typeArgs.fields[compareTo] === 'undefined' && typeof typeArgs.default === "undefined")
throw new Error(compareTo + " has no associated fieldInfo in switch");
else if (typeof typeArgs.fields[compareTo] === 'undefined')
fieldInfo = getFieldInfo(typeArgs.default);
else
fieldInfo = getFieldInfo(typeArgs.fields[compareTo]);
return this.sizeOf(value, fieldInfo, rootNode);
}
function readOption(buffer, offset, typeArgs, context) {
var val = buffer.readUInt8(offset++);
if (val !== 0) {
var retval = this.read(buffer, offset, typeArgs, context);
retval.size++;
return retval;
} else {
return {
size: 1
};
}
}
function writeOption(value, buffer, offset, typeArgs, context) {
if (value != null) {
buffer.writeUInt8(1, offset++);
this.write(value, buffer, offset, typeArgs, context);
} else {
buffer.writeUInt8(0, offset++);
}
return offset;
}
function sizeOfOption(value, typeArgs, context) {
return value == null ? 1 : this.sizeOf(value, typeArgs, context) + 1;
}

View File

@ -1,13 +1,12 @@
var nbt = require('prismarine-nbt'); var nbt = require('prismarine-nbt');
var utils = require("./utils"); var types=require("protodef").types;
var numeric = require("./numeric");
var uuid = require('node-uuid'); var uuid = require('node-uuid');
// TODO : remove type-specific, replace with generic containers and arrays. // TODO : remove type-specific, replace with generic containers and arrays.
module.exports = { module.exports = {
'UUID': [readUUID, writeUUID, 16], 'UUID': [readUUID, writeUUID, 16],
'slot': [readSlot, writeSlot, sizeOfSlot], 'slot': [readSlot, writeSlot, sizeOfSlot],
'nbt': [readNbt, utils.buffer[1], utils.buffer[2]], 'nbt': [readNbt, types.buffer[1], types.buffer[2]],
'restBuffer': [readRestBuffer, writeRestBuffer, sizeOfRestBuffer], 'restBuffer': [readRestBuffer, writeRestBuffer, sizeOfRestBuffer],
'entityMetadataLoop': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata] 'entityMetadataLoop': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata]
}; };
@ -26,7 +25,7 @@ function writeUUID(value, buffer, offset) {
function readSlot(buffer, offset) { function readSlot(buffer, offset) {
var value = {}; var value = {};
var results = numeric.short[0](buffer, offset); var results = types.short[0](buffer, offset);
if(!results) return null; if(!results) return null;
value.blockId = results.value; value.blockId = results.value;

View File

@ -1,50 +0,0 @@
function readLong(buffer, offset) {
if(offset + 8 > buffer.length) return null;
return {
value: [buffer.readInt32BE(offset), buffer.readInt32BE(offset + 4)],
size: 8,
};
}
function writeLong(value, buffer, offset) {
buffer.writeInt32BE(value[0], offset);
buffer.writeInt32BE(value[1], offset + 4);
return offset + 8;
}
function generateFunctions(bufferReader,bufferWriter,size)
{
var reader=function(buffer, offset)
{
if(offset + size > buffer.length) return null;
var value = buffer[bufferReader](offset);
return {
value: value,
size: size
};
};
var writer=function(value, buffer, offset) {
buffer[bufferWriter](value, offset);
return offset + size;
};
return [reader, writer, size];
}
var nums= {
'byte': ["readInt8", "writeInt8", 1],
'ubyte': ["readUInt8", "writeUInt8", 1],
'short': ["readInt16BE", "writeInt16BE", 2],
'ushort': ["readUInt16BE", "writeUInt16BE", 2],
'int': ["readInt32BE", "writeInt32BE", 4],
'float': ["readFloatBE", "writeFloatBE", 4],
'double': ["readDoubleBE", "writeDoubleBE", 8]
};
var types=Object.keys(nums).reduce(function(types,num){
types[num]=generateFunctions(nums[num][0], nums[num][1], nums[num][2]);
return types;
},{});
types["long"]=[readLong, writeLong, 8];
module.exports = types;

View File

@ -1,182 +0,0 @@
var { getField, tryCatch, addErrorField } = require("../utils");
var debug = require("../debug");
module.exports = {
'array': [readArray, writeArray, sizeOfArray],
'count': [readCount, writeCount, sizeOfCount],
'container': [readContainer, writeContainer, sizeOfContainer]
};
function evalCount(count, fields) {
if(fields[count["field"]] in count["map"])
return count["map"][fields[count["field"]]];
return count["default"];
}
function readArray(buffer, offset, typeArgs, rootNode) {
var results = {
value: [],
size: 0
};
var count;
if(typeof typeArgs.count === "object")
count = evalCount(typeArgs.count, rootNode);
else if (typeof typeArgs.count !== "undefined")
count = getField(typeArgs.count, rootNode);
else if (typeof typeArgs.countType !== "undefined") {
var countResults;
tryCatch(() => {
countResults = this.read(buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}, (e) => {
addErrorField(e, "$count");
throw e;
});
results.size += countResults.size;
offset += countResults.size;
count = countResults.value;
} else // TODO : broken schema, should probably error out.
count = 0;
for(var i = 0; i < count; i++) {
var readResults;
tryCatch(() => {
readResults = this.read(buffer, offset, typeArgs.type, rootNode);
}, (e) => {
addErrorField(e, i);
throw e;
});
results.size += readResults.size;
offset += readResults.size;
results.value.push(readResults.value);
}
return results;
}
function writeArray(value, buffer, offset, typeArgs, rootNode) {
if (typeof typeArgs.count === "undefined" &&
typeof typeArgs.countType !== "undefined") {
tryCatch(() => {
offset = this.write(value.length, buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}, (e) => {
addErrorField(e, "$count");
throw e;
});
} else if (typeof typeArgs.count === "undefined") { // Broken schema, should probably error out
}
for(var index in value) {
tryCatch(() => {
offset = this.write(value[index], buffer, offset, typeArgs.type, rootNode);
}, (e) => {
addErrorField(e, index);
throw e;
});
}
return offset;
}
function sizeOfArray(value, typeArgs, rootNode) {
var size = 0;
if (typeof typeArgs.count === "undefined" &&
typeof typeArgs.countType !== "undefined") {
tryCatch(() => {
size = this.sizeOf(value.length, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}, (e) => {
addErrorField(e, "$count");
throw e;
});
}
for(var index in value) {
tryCatch(() => {
size += this.sizeOf(value[index], typeArgs.type, rootNode);
}, (e) => {
addErrorField(e, index);
throw e;
});
}
return size;
}
function readContainer(buffer, offset, typeArgs, context) {
var results = {
value: { "..": context },
size: 0
};
typeArgs.forEach((typeArg) => {
tryCatch(() => {
var readResults = this.read(buffer, offset, typeArg.type, results.value);
results.size += readResults.size;
offset += readResults.size;
if (typeArg.anon) {
Object.keys(readResults.value).forEach(function(key) {
results.value[key] = readResults.value[key];
});
} else
results.value[typeArg.name] = readResults.value;
}, (e) => {
if (typeArgs && typeArg && typeArg.name)
addErrorField(e, typeArg.name);
else
addErrorField(e, "unknown");
throw e;
});
});
delete results.value[".."];
return results;
}
function writeContainer(value, buffer, offset, typeArgs, context) {
value[".."] = context;
typeArgs.forEach((typeArg) => {
tryCatch(() => {
if (typeArg.anon)
offset = this.write(value, buffer, offset, typeArg.type, value);
else
offset = this.write(value[typeArg.name], buffer, offset, typeArg.type, value);
}, (e) => {
if (typeArgs && typeArg && typeArg.name)
addErrorField(e, typeArg.name);
else
addErrorField(e, "unknown");
throw e;
});
});
delete value[".."];
return offset;
}
function sizeOfContainer(value, typeArgs, context) {
value[".."] = context;
var size = 0;
typeArgs.forEach((typeArg) => {
tryCatch(() => {
if (typeArg.anon)
size += this.sizeOf(value, typeArg.type, value);
else
size += this.sizeOf(value[typeArg.name], typeArg.type, value);
}, (e) => {
if (typeArgs && typeArg && typeArg.name)
addErrorField(e, typeArg.name);
else
addErrorField(e, "unknown");
throw e;
});
});
delete value[".."];
return size;
}
function readCount(buffer, offset, typeArgs, rootNode) {
return this.read(buffer, offset, typeArgs.type, rootNode);
}
function writeCount(value, buffer, offset, typeArgs, rootNode) {
// Actually gets the required field, and writes its length. Value is unused.
// TODO : a bit hackityhack.
return this.write(getField(typeArgs.countFor, rootNode).length, buffer, offset, typeArgs.type, rootNode);
}
function sizeOfCount(value, typeArgs, rootNode) {
// TODO : should I use value or getField().length ?
return this.sizeOf(getField(typeArgs.countFor, rootNode).length, typeArgs.type, rootNode);
}

View File

@ -1,212 +0,0 @@
var assert = require('assert');
var getField = require("../utils").getField;
module.exports = {
'varint': [readVarInt, writeVarInt, sizeOfVarInt],
'bool': [readBool, writeBool, 1],
'string': [readString, writeString, sizeOfString],
'buffer': [readBuffer, writeBuffer, sizeOfBuffer],
'void': [readVoid, writeVoid, 0],
'bitfield': [readBitField, writeBitField, sizeOfBitField]
};
function readVarInt(buffer, offset) {
var result = 0;
var shift = 0;
var cursor = offset;
while(true) {
if(cursor + 1 > buffer.length) return null;
var b = buffer.readUInt8(cursor);
result |= ((b & 0x7f) << shift); // Add the bits to our number, except MSB
cursor++;
if(!(b & 0x80)) { // If the MSB is not set, we return the number
return {
value: result,
size: cursor - offset
};
}
shift += 7; // we only have 7 bits, MSB being the return-trigger
assert.ok(shift < 64, "varint is too big"); // Make sure our shift don't overflow.
}
}
function sizeOfVarInt(value) {
var cursor = 0;
while(value & ~0x7F) {
value >>>= 7;
cursor++;
}
return cursor + 1;
}
function writeVarInt(value, buffer, offset) {
var cursor = 0;
while(value & ~0x7F) {
buffer.writeUInt8((value & 0xFF) | 0x80, offset + cursor);
cursor++;
value >>>= 7;
}
buffer.writeUInt8(value, offset + cursor);
return offset + cursor + 1;
}
function readString(buffer, offset) {
var length = readVarInt(buffer, offset);
if(!!!length) return null;
var cursor = offset + length.size;
var stringLength = length.value;
var strEnd = cursor + stringLength;
if(strEnd > buffer.length) return null;
var value = buffer.toString('utf8', cursor, strEnd);
cursor = strEnd;
return {
value: value,
size: cursor - offset,
};
}
function writeString(value, buffer, offset) {
var length = Buffer.byteLength(value, 'utf8');
offset = writeVarInt(length, buffer, offset);
buffer.write(value, offset, length, 'utf8');
return offset + length;
}
function sizeOfString(value) {
var length = Buffer.byteLength(value, 'utf8');
return sizeOfVarInt(length) + length;
}
function readBool(buffer, offset) {
if(offset + 1 > buffer.length) return null;
var value = buffer.readInt8(offset);
return {
value: !!value,
size: 1,
};
}
function writeBool(value, buffer, offset) {
buffer.writeInt8(+value, offset);
return offset + 1;
}
function readBuffer(buffer, offset, typeArgs, rootNode) {
var size = 0;
var count;
if (typeof typeArgs.count !== "undefined")
count = getField(typeArgs.count, rootNode);
else if (typeof typeArgs.countType !== "undefined") {
var countResults = this.read(buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
size += countResults.size;
offset += countResults.size;
count = countResults.value;
}
return {
value: buffer.slice(offset, offset + count),
size: size + count
};
}
function writeBuffer(value, buffer, offset, typeArgs, rootNode) {
if (typeof typeArgs.count === "undefined" &&
typeof typeArgs.countType !== "undefined") {
offset = this.write(value.length, buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
} else if (typeof typeArgs.count === "undefined") { // Broken schema, should probably error out
}
value.copy(buffer, offset);
return offset + value.length;
}
function sizeOfBuffer(value, typeArgs, rootNode) {
var size = 0;
if (typeof typeArgs.count === "undefined" &&
typeof typeArgs.countType !== "undefined") {
size = this.sizeOf(value.length, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}
return size + value.length;
}
function readVoid() {
return {
value: undefined,
size: 0,
};
}
function writeVoid(value, buffer, offset) {
return offset;
}
function generateBitMask(n) {
return (1 << n) - 1;
}
function readBitField(buffer, offset, typeArgs, context) {
var beginOffset = offset;
var curVal = null;
var bits = 0;
var results = {};
results.value = typeArgs.reduce(function(acc, item) {
var size = item.size;
var val = 0;
while (size > 0) {
if (bits == 0) {
curVal = buffer[offset++];
bits = 8;
}
var bitsToRead = Math.min(size, bits);
val = (val << bitsToRead) | (curVal & generateBitMask(bits)) >> (bits - bitsToRead);
bits -= bitsToRead;
size -= bitsToRead;
}
if (item.signed && val >= 1 << (item.size - 1))
val -= 1 << item.size;
acc[item.name] = val;
return acc;
}, {});
results.size = offset - beginOffset;
return results;
}
function writeBitField(value, buffer, offset, typeArgs, context) {
var toWrite = 0;
var bits = 0;
typeArgs.forEach(function(item) {
var val = value[item.name];
var size = item.size;
var signed = item.signed;
if ((!item.signed && val < 0) || (item.signed && val < -(1 << (size - 1))))
throw new Error(value + " < " + item.signed ? (-(1 << (size - 1))) : 0);
else if ((!item.signed && val >= 1 << size)
|| (item.signed && val >= (1 << (size - 1)) - 1))
throw new Error(value + " >= " + iteme.signed ? (1 << size) : ((1 << (size - 1)) - 1));
while (size > 0) {
var writeBits = Math.min(8 - bits, size);
toWrite = toWrite << writeBits |
((val >> (size - writeBits)) & generateBitMask(writeBits));
size -= writeBits;
bits += writeBits;
if (bits === 8) {
buffer[offset++] = toWrite;
bits = 0;
toWrite = 0;
}
}
});
if (bits != 0)
buffer[offset++] = toWrite << (8 - bits);
return offset;
}
function sizeOfBitField(value, typeArgs, context) {
return Math.ceil(typeArgs.reduce(function(acc, item) {
return acc + item.size;
}, 0) / 8);
}

View File

@ -2,7 +2,6 @@ var Client = require('./client');
var Server = require('./server'); var Server = require('./server');
var serializer = require("./transforms/serializer"); var serializer = require("./transforms/serializer");
var utils = require("./utils"); var utils = require("./utils");
var readPackets = require("./packets").readPackets;
var createClient = require("./createClient"); var createClient = require("./createClient");
var createServer = require("./createServer"); var createServer = require("./createServer");
@ -11,10 +10,9 @@ module.exports = {
createServer: createServer, createServer: createServer,
Client: Client, Client: Client,
Server: Server, Server: Server,
states: serializer.states, states: require("./states"),
createSerializer:serializer.createSerializer, createSerializer:serializer.createSerializer,
createDeserializer:serializer.createDeserializer, createDeserializer:serializer.createDeserializer,
readPackets:readPackets,
ping: require('./ping'), ping: require('./ping'),
supportedVersions:require("./version").supportedVersions supportedVersions:require("./version").supportedVersions
}; };

View File

@ -1,43 +0,0 @@
var assert = require("assert");
module.exports = {readPackets: readPackets};
function readPackets(packets, states) {
var packetFields = {};
var packetNames = {};
var packetIds = {};
var packetStates = {toClient: {}, toServer: {}};
for(var stateName in states) {
var state = states[stateName];
packetFields[state] = {toClient: [], toServer: []};
packetNames[state] = {toClient: [], toServer: []};
packetIds[state] = {toClient: [], toServer: []};
['toClient', 'toServer'].forEach(function(direction) {
for(var name in packets[state][direction]) {
var info = packets[state][direction][name];
var id = parseInt(info.id);
var fields = info.fields;
assert(id !== undefined, 'missing id for packet ' + name);
assert(fields !== undefined, 'missing fields for packet ' + name);
assert(!packetNames[state][direction].hasOwnProperty(id), 'duplicate packet id ' + id + ' for ' + name);
assert(!packetIds[state][direction].hasOwnProperty(name), 'duplicate packet name ' + name + ' for ' + id);
assert(!packetFields[state][direction].hasOwnProperty(name), 'duplicate packet id ' + id + ' for ' + name);
assert(!packetStates[direction].hasOwnProperty(name), 'duplicate packet name ' + name + ' for ' + id + ', must be unique across all states');
packetNames[state][direction][id] = name;
packetIds[state][direction][name] = id;
packetFields[state][direction][name] = fields;
packetStates[direction][name] = state;
}
});
}
return {
packetFields: packetFields,
packetNames: packetNames,
packetIds: packetIds,
packetStates: packetStates
};
}

View File

@ -1,6 +1,6 @@
var net = require('net'); var net = require('net');
var Client = require('./client'); var Client = require('./client');
var states = require('./transforms/serializer').states; var states = require("./states");
module.exports = ping; module.exports = ping;

View File

@ -1,120 +0,0 @@
var { getFieldInfo } = require('./utils');
var reduce = require('lodash.reduce');
function isFieldInfo(type) {
return typeof type === "string"
|| (Array.isArray(type) && typeof type[0] === "string")
|| type.type;
}
function findArgs(acc, v, k) {
if (typeof v === "string" && v.charAt(0) === '$')
acc.push({ "path": k, "val": v.substr(1) });
else if (Array.isArray(v) || typeof v === "object")
acc = acc.concat(reduce(v, findArgs, []).map((v) => ({ "path": k + "." + v.path, "val": v.val })));
return acc;
}
function setField(path, val, into) {
var c = path.split('.').reverse();
while (c.length > 1) {
into = into[c.pop()];
}
into[c.pop()] = val;
}
function extendType(functions, defaultTypeArgs) {
var argPos = reduce(defaultTypeArgs, findArgs, []);
return [function read(buffer, offset, typeArgs, context) {
var args = JSON.parse(JSON.stringify(defaultTypeArgs));
argPos.forEach((v) => {
setField(v.path, typeArgs[v.val], args);
});
return functions[0].call(this, buffer, offset, args, context);
}, function write(value, buffer, offset, typeArgs, context) {
var args = JSON.parse(JSON.stringify(defaultTypeArgs));
argPos.forEach((v) => {
setField(v.path, typeArgs[v.val], args);
});
return functions[1].call(this, value, buffer, offset, args, context);
}, function sizeOf(value, typeArgs, context) {
var args = JSON.parse(JSON.stringify(defaultTypeArgs));
argPos.forEach((v) => {
setField(v.path, typeArgs[v.val], args);
});
if (typeof functions[2] === "function")
return functions[2].call(this, value, args, context);
else
return functions[2];
}];
}
class NMProtocols
{
types={};
constructor() {
}
addType(name, functions) {
if (functions === "native")
return;
else if (isFieldInfo(functions)) {
var fieldInfo = getFieldInfo(functions);
this.types[name] = extendType(this.types[fieldInfo.type], fieldInfo.typeArgs);
}
else
this.types[name] = functions;
}
addTypes(types) {
var self = this;
Object.keys(types).forEach(function(name) {
self.addType(name, types[name]);
});
}
read(buffer, cursor, _fieldInfo, rootNodes) {
let fieldInfo = getFieldInfo(_fieldInfo);
var type = this.types[fieldInfo.type];
if(!type) {
return {
error: new Error("missing data type: " + fieldInfo.type)
};
}
var readResults = type[0].call(this, buffer, cursor, fieldInfo.typeArgs, rootNodes);
if(readResults == null) {
throw new Error("Reader returned null : " + JSON.stringify(fieldInfo));
}
if(readResults && readResults.error) return {error: readResults.error};
return readResults;
}
write(value, buffer, offset, _fieldInfo, rootNode) {
let fieldInfo = getFieldInfo(_fieldInfo);
var type = this.types[fieldInfo.type];
if(!type) {
return {
error: new Error("missing data type: " + fieldInfo.type)
};
}
return type[1].call(this, value, buffer, offset, fieldInfo.typeArgs, rootNode);
}
sizeOf(value, _fieldInfo, rootNode) {
let fieldInfo = getFieldInfo(_fieldInfo);
var type = this.types[fieldInfo.type];
if(!type) {
throw new Error("missing data type: " + fieldInfo.type);
}
if(typeof type[2] === 'function') {
return type[2].call(this, value, fieldInfo.typeArgs, rootNode);
} else {
return type[2];
}
}
}
module.exports = NMProtocols;

View File

@ -1,7 +1,7 @@
var net = require('net'); var net = require('net');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var Client = require('./client'); var Client = require('./client');
var states = require('./transforms/serializer').states; var states = require("./states");
class Server extends EventEmitter class Server extends EventEmitter
{ {

8
src/states.js Normal file
View File

@ -0,0 +1,8 @@
var states = {
"HANDSHAKING": "handshaking",
"STATUS": "status",
"LOGIN": "login",
"PLAY": "play"
};
module.exports=states;

View File

@ -1,4 +1,4 @@
var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; var [readVarInt, writeVarInt, sizeOfVarInt] = require("protodef").types.varint;
var zlib = require("zlib"); var zlib = require("zlib");
var Transform = require("readable-stream").Transform; var Transform = require("readable-stream").Transform;

View File

@ -1,4 +1,4 @@
var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; var [readVarInt, writeVarInt, sizeOfVarInt] = require("protodef").types.varint;
var Transform = require("readable-stream").Transform; var Transform = require("readable-stream").Transform;
module.exports.createSplitter = function() { module.exports.createSplitter = function() {

View File

@ -1,185 +1,61 @@
var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; var ProtoDef = require("protodef").ProtoDef;
var protocol = require("../protocol"); var Serializer = require("protodef").Serializer;
var Transform = require("readable-stream").Transform; var Parser = require("protodef").Parser;
var debug = require("../debug");
var assert = require('assert');
var { getFieldInfo, tryCatch, addErrorField } = require('../utils');
module.exports.createSerializer = function(obj) {
return new Serializer(obj);
};
module.exports.createDeserializer = function(obj) {
return new Deserializer(obj);
};
// This is really just for the client.
var states = {
"HANDSHAKING": "handshaking",
"STATUS": "status",
"LOGIN": "login",
"PLAY": "play"
};
module.exports.states = states;
var NMProtocols = require("../protocol");
var numeric = require("../datatypes/numeric");
var utils = require("../datatypes/utils");
var minecraft = require("../datatypes/minecraft"); var minecraft = require("../datatypes/minecraft");
var structures = require("../datatypes/structures"); var states = require("../states");
var conditional = require("../datatypes/conditional");
var readPackets = require("../packets").readPackets;
function createProtocol(types,packets)
function createProtocol(types)
{ {
var proto = new NMProtocols(); var proto = new ProtoDef();
proto.addTypes(numeric); proto.addType("string",["pstring",{
proto.addTypes(utils); countType:"varint"
}]);
proto.addTypes(minecraft); proto.addTypes(minecraft);
proto.addTypes(structures);
proto.addTypes(conditional);
proto.addTypes(types); proto.addTypes(types);
Object.keys(packets).forEach(function(name) {
proto.addType("packet_"+name,["container",packets[name].fields]);
});
proto.addType("packet",["container", [
{ "name": "name", "type":["mapper",{"type": "varint" ,
"mappings":Object.keys(packets).reduce(function(acc,name){
acc[parseInt(packets[name].id)]=name;
return acc;
},{})
}]},
{ "name": "params", "type": ["switch", {
"compareTo": "name",
"fields": Object.keys(packets).reduce(function(acc,name){
acc[name]="packet_"+name;
return acc;
},{})
}]}
]]);
return proto; return proto;
} }
function createSerializer({ state = states.HANDSHAKING, isServer = false , version} = {})
class Serializer extends Transform { {
constructor({ state = states.HANDSHAKING, isServer = false , version} = {}) { var mcData=require("minecraft-data")(version);
super({ writableObjectMode: true }); var direction = !isServer ? 'toServer' : 'toClient';
this.protocolState = state; var packets = mcData.protocol.states[state][direction];
this.isServer = isServer; var proto=createProtocol(mcData.protocol.types,packets);
this.version = version; return new Serializer(proto,"packet");
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}.${direction}.${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 = this.createPacketBuffer(chunk.packetName, chunk.params);
this.push(buf);
return cb();
} catch (e) {
return cb(e);
}
}
} }
class Deserializer extends Transform { function createDeserializer({ state = states.HANDSHAKING, isServer = false,
constructor({ state = states.HANDSHAKING, isServer = false, packetsToParse = {"packet": true}, version } = {}) { packetsToParse = {"packet": true}, version } = {})
super({ readableObjectMode: true }); {
this.protocolState = state; var mcData=require("minecraft-data")(version);
this.isServer = isServer; var direction = isServer ? "toServer" : "toClient";
this.packetsToParse = packetsToParse; var packets = mcData.protocol.states[state][direction];
this.version = version; var proto=createProtocol(mcData.protocol.types,packets);
return new Parser(proto,"packet");
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 = this.parsePacketData(chunk);
} catch (e) {
return cb(e);
}
this.push(packet);
return cb();
}
} }
module.exports = {
createSerializer:createSerializer,
createDeserializer:createDeserializer
};

View File

@ -1,4 +1,4 @@
var ITERATIONS = 100000; var ITERATIONS = 10000;
var mc = require("../"); var mc = require("../");
var util = require('util'); var util = require('util');
@ -15,7 +15,7 @@ mc.supportedVersions.forEach(function(supportedVersion){
var mcData=require("minecraft-data")(supportedVersion); var mcData=require("minecraft-data")(supportedVersion);
var version=mcData.version; var version=mcData.version;
describe("benchmark "+version.minecraftVersion,function(){ describe("benchmark "+version.minecraftVersion,function(){
this.timeout(20 * 1000); this.timeout(60 * 1000);
var inputData = []; var inputData = [];
it("bench serializing",function(done){ it("bench serializing",function(done){
var serializer=new mc.createSerializer({state:states.PLAY,isServer:false,version:version.majorVersion}); var serializer=new mc.createSerializer({state:states.PLAY,isServer:false,version:version.majorVersion});
@ -24,7 +24,7 @@ mc.supportedVersions.forEach(function(supportedVersion){
start = Date.now(); start = Date.now();
for(i = 0; i < ITERATIONS; i++) { for(i = 0; i < ITERATIONS; i++) {
for(j = 0; j < testDataWrite.length; j++) { for(j = 0; j < testDataWrite.length; j++) {
inputData.push(serializer.createPacketBuffer(testDataWrite[j].name, testDataWrite[j].params)); inputData.push(serializer.createPacketBuffer(testDataWrite[j]));
} }
} }
var result=(Date.now() - start) / 1000; var result=(Date.now() - start) / 1000;
@ -37,7 +37,7 @@ mc.supportedVersions.forEach(function(supportedVersion){
console.log('Beginning read test'); console.log('Beginning read test');
start = Date.now(); start = Date.now();
for (j = 0; j < inputData.length; j++) { for (j = 0; j < inputData.length; j++) {
deserializer.parsePacketData(inputData[j]); deserializer.parsePacketBuffer(inputData[j]);
} }
console.log('Finished read test in ' + (Date.now() - start) / 1000 + ' seconds'); console.log('Finished read test in ' + (Date.now() - start) / 1000 + ' seconds');
done(); done();

View File

@ -1,248 +0,0 @@
var expect = require('chai').expect;
var numeric = require('../../dist/datatypes/numeric');
var getReader = function(dataType) { return dataType[0]; };
var getWriter = function(dataType) { return dataType[1]; };
var getSizeOf = function(dataType) { return dataType[2]; };
/*var getReader = require('../../lib/utils').getReader;
var getWriter = require('../../lib/utils').getWriter;
var getSizeOf = require('../../lib/utils').getSizeOf;*/
var testData = {
'byte': {
'readPos': {
'buffer': new Buffer([0x3d]),
'value': 61
},
'readNeg': {
'buffer': new Buffer([0x86]),
'value': -122
},
'writePos': {
'buffer': new Buffer([0x00]),
'value': 32,
'bufferAfter': new Buffer([0x20])
},
'writeNeg': {
'buffer': new Buffer([0x00]),
'value': -122,
'bufferAfter': new Buffer([0x86])
},
'sizeof': {
'value': 0x2d,
'size': 1,
}
},
'ubyte': {
'readPos': {
'buffer': new Buffer([0x3d]),
'value': 61
},
'readNeg': {
'buffer': new Buffer([0x86]),
'value': 134
},
'writePos': {
'buffer': new Buffer([0x00]),
'value': 61,
'bufferAfter': new Buffer([0x3d])
},
'writeNeg': {
'buffer': new Buffer([0x00]),
'value': 134,
'bufferAfter': new Buffer([0x86])
},
'sizeof': {
'value': 0x2d,
'size': 1,
}
},
'short': {
'readPos': {
'buffer': new Buffer([0x30, 0x87]),
'value': 12423
},
'readNeg': {
'buffer': new Buffer([0xef, 0x77]),
'value': -4233
},
'writePos': {
'buffer': new Buffer([0x00, 0x00]),
'value': 12423,
'bufferAfter': new Buffer([0x30, 0x87]),
},
'writeNeg': {
'buffer': new Buffer([0x00, 0x00]),
'value': -4233,
'bufferAfter': new Buffer([0xef, 0x77])
},
'sizeof': {
'value': 0x2d,
'size': 2,
}
},
'ushort': {
'readPos': {
'buffer': new Buffer([0x30, 0x87]),
'value': 12423
},
'readNeg': {
'buffer': new Buffer([0xef, 0x77]),
'value': 61303
},
'writePos': {
'buffer': new Buffer([0x00, 0x00]),
'value': 12423,
'bufferAfter': new Buffer([0x30, 0x87]),
},
'writeNeg': {
'buffer': new Buffer([0x00, 0x00]),
'value': 61303,
'bufferAfter': new Buffer([0xef, 0x77])
},
'sizeof': {
'value': 0x2d,
'size': 2,
}
},
'int': {
'readPos': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0xea]),
'value': 234
},
'readNeg': {
'buffer': new Buffer([0xff, 0xff, 0xfc, 0x00]),
'value': -1024
},
'writePos': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00]),
'value': 234,
'bufferAfter': new Buffer([0x00, 0x00, 0x00, 0xea])
},
'writeNeg': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00]),
'value': -1024,
'bufferAfter': new Buffer([0xff, 0xff, 0xfc, 0x00])
},
'sizeof': {
'value': 0x2d,
'size': 4
}
},
'uint': {
'readPos': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0xea]),
'value': 234
},
'readNeg': {
'buffer': new Buffer([0xff, 0xff, 0xfc, 0x00]),
'value': 4294966272
},
'writePos': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00]),
'value': 234,
'bufferAfter': new Buffer([0x00, 0x00, 0x00, 0xea])
},
'writeNeg': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00]),
'value': 4294966272,
'bufferAfter': new Buffer([0xff, 0xff, 0xfc, 0x00])
},
'sizeof': {
'value': 0x2d,
'size': 4
}
},
'float': {
'readPos': {
'buffer': new Buffer([0x47, 0x05, 0xc3, 0x00]),
'value': 34243
},
'readNeg': {
'buffer': new Buffer([0xc6, 0x42, 0x4c, 0x00]),
'value': -12435
},
'writePos': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00]),
'value': 34243,
'bufferAfter': new Buffer([0x47, 0x05, 0xc3, 0x00])
},
'writeNeg': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00]),
'value': -12435,
'bufferAfter': new Buffer([0xc6, 0x42, 0x4c, 0x00])
},
'sizeof': {
'value': 0x2d,
'size': 4
}
},
'double': {
'readPos': {
'buffer': new Buffer([0x40, 0xe0, 0xb8, 0x60, 0x00, 0x00, 0x00, 0x00]),
'value': 34243
},
'readNeg': {
'buffer': new Buffer([0xc0, 0xc8, 0x49, 0x80, 0x00, 0x00, 0x00, 0x00]),
'value': -12435
},
'writePos': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
'value': 34243,
'bufferAfter': new Buffer([0x40, 0xe0, 0xb8, 0x60, 0x00, 0x00, 0x00, 0x00]),
},
'writeNeg': {
'buffer': new Buffer([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
'value': -12435,
'bufferAfter': new Buffer([0xc0, 0xc8, 0x49, 0x80, 0x00, 0x00, 0x00, 0x00]),
},
'sizeof': {
'value': 0x2d,
'size': 8
}
}
};
describe('Numeric', function() {
for (var key in testData) {
if (testData.hasOwnProperty(key) && numeric.hasOwnProperty(key)) {
var value = testData[key];
describe('.' + key, function() {
var reader;
var writer;
var sizeof;
before(function() {
reader = getReader(numeric[key]);
writer = getWriter(numeric[key]);
sizeof = getSizeOf(numeric[key]);
});
it('Returns null if not enough data is provided', function() {
expect(reader(new Buffer(0), 0)).to.eql(null);
});
it('Reads positive values', function() {
expect(reader(value.readPos.buffer, 0).value).to.deep.eql(value.readPos.value);
});
it('Reads big/negative values', function() {
expect(reader(value.readNeg.buffer, 0).value).to.deep.eql(value.readNeg.value);
});
it('Writes positive values', function() {
writer(value.writePos.value, value.writePos.buffer, 0);
expect(value.writePos.buffer).to.deep.eql(value.writePos.bufferAfter);
});
it('Writes negative values', function() {
writer(value.writeNeg.value, value.writeNeg.buffer, 0);
expect(value.writeNeg.buffer).to.deep.eql(value.writeNeg.bufferAfter);
});
it('Calculates size', function() {
var size;
if (typeof sizeof === "function") {
size = sizeof(value.sizeof.value);
} else {
size = sizeof;
}
expect(size).to.eql(value.sizeof.size);
});
});
}
}
});

View File

@ -1,205 +0,0 @@
var assert = require('power-assert');
var expect = require('chai').expect;
var utils = require('../../dist/datatypes/utils');
var getReader = function(dataType) { return dataType[0]; };
var getWriter = function(dataType) { return dataType[1]; };
var getSizeOf = function(dataType) { return dataType[2]; };
describe('Utils', function() {
describe('.bool', function() {
it('Reads false value for binary 0', function() {
assert.deepEqual(getReader(utils.bool)(new Buffer([0]), 0), {value: false, size: 1});
});
it('Reads true for every other binary value', function() {
var buf = new Buffer([0]);
var i = 1;
while (i < 256) {
buf[0] = i++;
assert.deepEqual(getReader(utils.bool)(buf, 0), {value: true, size: 1});
}
});
it('Writes false', function() {
var buffer = new Buffer(1);
getWriter(utils.bool)(false, buffer, 0);
assert.deepEqual(buffer, new Buffer([0]));
});
it('Writes true', function() {
var buffer = new Buffer(1);
getWriter(utils.bool)(true, buffer, 0);
assert.notDeepEqual(buffer, new Buffer([0]));
});
it('Has a size of 1', function() {
assert.equal(typeof getSizeOf(utils.bool), "number");
assert.equal(getSizeOf(utils.bool), 1);
});
});
describe('.varint', function() {
it.skip('Has no tests', function() {
});
});
describe('.buffer', function() {
it.skip('Has no tests', function() {
});
});
describe('.string', function() {
it.skip('Has no tests', function() {
});
});
describe('.void', function() {
it.skip('Has no tests', function() {
});
});
describe('.bitfield', function() {
it('Reads an unsigned 8 bit number', function() {
var buf = new Buffer([0xff]);
var typeArgs = [
{ "name": "one", "size": 8, "signed": false }
];
expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({
value: { "one": 255 },
size: 1
});
});
it('Reads a signed 8 bit number', function() {
var buf = new Buffer([0xff]);
var typeArgs = [
{ "name": "one", "size": 8, "signed": true }
];
expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({
value: { "one": -1 },
size: 1
});
});
it('Reads multiple signed 8 bit numbers', function() {
var buf = new Buffer([0xff, 0x80, 0x12]);
var typeArgs = [
{ "name": "one", "size": 8, "signed": true },
{ "name": "two", "size": 8, "signed": true },
{ "name": "three", "size": 8, "signed": true }
];
expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({
value: { "one": -1, "two": -128, "three": 18 },
size: 3
});
});
it('Reads multiple unsigned 4 bit numbers', function() {
var buf = new Buffer([0xff, 0x80]);
var typeArgs = [
{ "name": "one", "size": 4, "signed": false },
{ "name": "two", "size": 4, "signed": false },
{ "name": "three", "size": 4, "signed": false }
];
expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({
value: { "one": 15, "two": 15, "three": 8 },
size: 2
});
});
it('Reads multiple signed 4 bit numbers', function() {
var buf = new Buffer([0xff, 0x80]);
var typeArgs = [
{ "name": "one", "size": 4, "signed": true },
{ "name": "two", "size": 4, "signed": true },
{ "name": "three", "size": 4, "signed": true }
];
expect(getReader(utils.bitfield)(buf, 0, typeArgs, {})).to.deep.equal({
value: { "one": -1, "two": -1, "three": -8 },
size: 2
});
});
it('Reads an unsigned 12 bit number', function() {
var buf = new Buffer([0xff, 0x80]);
var typeArgs = [
{ "name": "one", "size": 12, "signed": false }
];
assert.deepEqual(getReader(utils.bitfield)(buf, 0, typeArgs, {}), {
value: { "one": 4088 },
size: 2
});
});
it('Reads a complex structure', function() {
var buf = new Buffer([0x00, 0x00, 0x03, 0x05, 0x30, 0x42, 0xE0, 0x65]);
var typeArgs = [
{ "name": "x", "size": 26, "signed": true },
{ "name": "y", "size": 12, "signed": true },
{ "name": "z", "size": 26, "signed": true }
];
var value = { x: 12, y: 332, z: 4382821 };
assert.deepEqual(getReader(utils.bitfield)(buf, 0, typeArgs, {}), {
value: value,
size: 8
});
});
it('Writes an unsigned 8 bit number', function() {
var buf = new Buffer(1);
var typeArgs = [
{ "name": "one", "size": 8, "signed": false }
];
var value = { "one": 0xff };
assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 1);
assert.deepEqual(buf, new Buffer([0xff]));
});
it('Writes a signed 8 bit number', function() {
var buf = new Buffer(1);
var typeArgs = [
{ "name": "one", "size": 8, "signed": true }
];
var value = { "one": -1 };
assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 1);
assert.deepEqual(buf, new Buffer([0xff]));
});
it('Writes multiple signed 8 bit numbers', function() {
var buf = new Buffer(3);
var typeArgs = [
{ "name": "one", "size": 8, "signed": true },
{ "name": "two", "size": 8, "signed": true },
{ "name": "three", "size": 8, "signed": true }
];
var value = { "one": -1, "two": -128, "three": 18 };
assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 3);
assert.deepEqual(buf, new Buffer([0xff, 0x80, 0x12]));
});
it('Writes multiple unsigned 4 bit numbers', function() {
var buf = new Buffer(2);
var typeArgs = [
{ "name": "one", "size": 4, "signed": false },
{ "name": "two", "size": 4, "signed": false },
{ "name": "three", "size": 4, "signed": false }
];
var value = { "one": 15, "two": 15, "three": 8 };
assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 2);
assert.deepEqual(buf, new Buffer([0xff, 0x80]));
});
it('Writes multiple signed 4 bit numbers', function() {
var buf = new Buffer(2);
var typeArgs = [
{ "name": "one", "size": 4, "signed": true },
{ "name": "two", "size": 4, "signed": true },
{ "name": "three", "size": 4, "signed": true }
];
var value = { "one": -1, "two": -1, "three": -8 };
assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 2);
assert.deepEqual(buf, new Buffer([0xff, 0x80]));
});
it('Writes an unsigned 12 bit number', function() {
var buf = new Buffer(2);
var typeArgs = [
{ "name": "one", "size": 12, "signed": false }
];
var value = { "one": 4088 };
assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 2);
assert.deepEqual(buf, new Buffer([0xff, 0x80]));
});
it('Writes a complex structure', function() {
var buf = new Buffer(8);
var typeArgs = [
{ "name": "x", "size": 26, "signed": true },
{ "name": "y", "size": 12, "signed": true },
{ "name": "z", "size": 26, "signed": true }
];
var value = { x: 12, y: 332, z: 4382821 };
assert.equal(getWriter(utils.bitfield)(value, buf, 0, typeArgs, {}), 8);
assert.deepEqual(buf, new Buffer([0x00, 0x00, 0x03, 0x05, 0x30, 0x42, 0xE0, 0x65]));
});
});
});

View File

@ -107,8 +107,6 @@ mc.supportedVersions.forEach(function(supportedVersion){
var mcData=require("minecraft-data")(supportedVersion); var mcData=require("minecraft-data")(supportedVersion);
var version=mcData.version; var version=mcData.version;
var packets = mcData.protocol.states; var packets = mcData.protocol.states;
var packetIndexes = mc.readPackets(packets, states);
var packetFields = packetIndexes.packetFields;
describe("packets "+version.minecraftVersion, function() { describe("packets "+version.minecraftVersion, function() {
var client, server, serverClient; var client, server, serverClient;
@ -132,18 +130,18 @@ mc.supportedVersions.forEach(function(supportedVersion){
client.end(); client.end();
}); });
var packetName, packetInfo, field; var packetName, packetInfo, field;
for(state in packetFields) { for(state in packets) {
if(!packetFields.hasOwnProperty(state)) continue; if(!packets.hasOwnProperty(state)) continue;
for(packetName in packetFields[state].toServer) { for(packetName in packets[state].toServer) {
if(!packetFields[state].toServer.hasOwnProperty(packetName)) continue; if(!packets[state].toServer.hasOwnProperty(packetName)) continue;
packetInfo = packetFields[state]["toServer"][packetName]; packetInfo = packets[state]["toServer"][packetName].fields;
packetInfo=packetInfo ? packetInfo : null; packetInfo=packetInfo ? packetInfo : null;
it(state + ",ServerBound," + packetName, it(state + ",ServerBound," + packetName,
callTestPacket(packetName, packetInfo, state, true)); callTestPacket(packetName, packetInfo, state, true));
} }
for(packetName in packetFields[state].toClient) { for(packetName in packets[state].toClient) {
if(!packetFields[state].toClient.hasOwnProperty(packetName)) continue; if(!packets[state].toClient.hasOwnProperty(packetName)) continue;
packetInfo = packetFields[state]["toClient"][packetName]; packetInfo = packets[state]["toClient"][packetName].fields;
packetInfo=packetInfo ? packetInfo : null; packetInfo=packetInfo ? packetInfo : null;
it(state + ",ClientBound," + packetName, it(state + ",ClientBound," + packetName,
callTestPacket(packetName, packetInfo, state, false)); callTestPacket(packetName, packetInfo, state, false));