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
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
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

View File

@ -2,7 +2,7 @@ var mc = require('../../');
var states = mc.states;
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(" --dump 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 host;
var port = 25565;
var user;
var passwd;
var version;
var printAllNames = false;
@ -62,8 +60,6 @@ var printNameBlacklist = {};
}
if(!(i + 2 <= args.length && args.length <= i + 4)) printHelpAndExit(1);
host = args[i++];
user = args[i++];
passwd = args[i++];
version = args[i++];
})();
@ -98,9 +94,7 @@ srv.on('login', function(client) {
var targetClient = mc.createClient({
host: host,
port: port,
username: user,
password: passwd,
'online-mode': passwd != null ? true : false,
username: client.username,
keepAlive:false,
version:version
});
@ -108,7 +102,7 @@ srv.on('login', function(client) {
if(targetClient.state == states.PLAY && meta.state == states.PLAY) {
if(shouldDump(meta.name, "o")) {
console.log("client->server:",
client.state + ".0x" + meta.id.toString(16) + " :",
client.state + " "+ meta.name + " :",
JSON.stringify(data));
}
if(!endedTargetClient)
@ -122,18 +116,19 @@ srv.on('login', function(client) {
targetClient.state + "." + meta.name + " :" +
JSON.stringify(data));
}
if(!endedClient)
if(!endedClient) {
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;
}
}
});
var bufferEqual = require('buffer-equal');
targetClient.on('raw', function(buffer, meta) {
if(client.state != states.PLAY || meta.state != states.PLAY)
return;
var packetData = targetClient.deserializer.parsePacketData(buffer).data;
var packetBuff = client.serializer.createPacketBuffer(meta.name, packetData);
var packetData = targetClient.deserializer.parsePacketBuffer(buffer).data.params;
var packetBuff = client.serializer.createPacketBuffer({name:meta.name, params:packetData});
if(!bufferEqual(buffer, packetBuff)) {
console.log("client<-server: Error in packet " + state + "." + meta.name);
console.log(buffer.toString('hex'));
@ -152,8 +147,8 @@ srv.on('login', function(client) {
client.on('raw', function(buffer, meta) {
if(meta.state != states.PLAY || targetClient.state != states.PLAY)
return;
var packetData = client.deserializer.parsePacketData(buffer).data;
var packetBuff = targetClient.serializer.createPacketBuffer(meta.name, packetData);
var packetData = client.deserializer.parsePacketBuffer(buffer).data.params;
var packetBuff = targetClient.serializer.createPacketBuffer({name:meta.name, params:packetData});
if(!bufferEqual(buffer, packetBuff)) {
console.log("client->server: Error in packet " + state + "." + meta.name);
console.log(buffer.toString('hex'));

View File

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

View File

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

View File

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

View File

@ -1,11 +1,12 @@
var EventEmitter = require('events').EventEmitter;
var debug = require('./debug');
var serializer = require('./transforms/serializer');
var compression = require('./transforms/compression');
var framing = require('./transforms/framing');
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
{
@ -19,12 +20,14 @@ class Client extends EventEmitter
decompressor=null;
deserializer;
isServer;
version;
protocolState=states.HANDSHAKING;
constructor(isServer,version) {
super();
this.serializer = serializer.createSerializer({ isServer, version:version});
this.deserializer = serializer.createDeserializer({ isServer, packetsToParse: this.packetsToParse, version:version});
this.version=version;
this.isServer = !!isServer;
this.setSerializer(states.HANDSHAKING);
this.on('newListener', function(event, listener) {
var direction = this.isServer ? 'toServer' : 'toClient';
@ -38,13 +41,76 @@ class Client extends EventEmitter
}
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) {
var oldProperty = this.serializer.protocolState;
this.serializer.protocolState = newProperty;
this.deserializer.protocolState = newProperty;
var oldProperty = this.protocolState;
this.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);
}
@ -87,20 +153,11 @@ class Client extends EventEmitter
this.socket.on('close', endSocket);
this.socket.on('end', endSocket);
this.socket.on('timeout', endSocket);
this.serializer.on('error', onError);
this.deserializer.on('error', onError);
this.framer.on('error', onError);
this.splitter.on('error', onError);
this.socket.pipe(this.splitter).pipe(this.deserializer);
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) {
@ -137,10 +194,10 @@ class Client extends EventEmitter
}
}
write(packetName, params) {
debug("writing packet " + this.state + "." + packetName);
write(name, params) {
debug("writing packet " + this.state + "." + name);
debug(params);
this.serializer.write({ packetName, params });
this.serializer.write({ name, params });
}
writeRaw(buffer) {

View File

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

View File

@ -1,10 +1,10 @@
var ursa=require("./ursa");
var crypto = require('crypto');
var yggserver = require('yggdrasil').server({});
var serializer = require("./transforms/serializer");
var states = serializer.states;
var states = require("./states");
var bufferEqual = require('buffer-equal');
var Server = require('./server');
var UUID = require('uuid-1345');
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() {
var isException = !!server.onlineModeExceptions[client.username.toLowerCase()];
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.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 utils = require("./utils");
var numeric = require("./numeric");
var types=require("protodef").types;
var uuid = require('node-uuid');
// TODO : remove type-specific, replace with generic containers and arrays.
module.exports = {
'UUID': [readUUID, writeUUID, 16],
'slot': [readSlot, writeSlot, sizeOfSlot],
'nbt': [readNbt, utils.buffer[1], utils.buffer[2]],
'nbt': [readNbt, types.buffer[1], types.buffer[2]],
'restBuffer': [readRestBuffer, writeRestBuffer, sizeOfRestBuffer],
'entityMetadataLoop': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata]
};
@ -26,7 +25,7 @@ function writeUUID(value, buffer, offset) {
function readSlot(buffer, offset) {
var value = {};
var results = numeric.short[0](buffer, offset);
var results = types.short[0](buffer, offset);
if(!results) return null;
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 serializer = require("./transforms/serializer");
var utils = require("./utils");
var readPackets = require("./packets").readPackets;
var createClient = require("./createClient");
var createServer = require("./createServer");
@ -11,10 +10,9 @@ module.exports = {
createServer: createServer,
Client: Client,
Server: Server,
states: serializer.states,
states: require("./states"),
createSerializer:serializer.createSerializer,
createDeserializer:serializer.createDeserializer,
readPackets:readPackets,
ping: require('./ping'),
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 Client = require('./client');
var states = require('./transforms/serializer').states;
var states = require("./states");
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 EventEmitter = require('events').EventEmitter;
var Client = require('./client');
var states = require('./transforms/serializer').states;
var states = require("./states");
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 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;
module.exports.createSplitter = function() {

View File

@ -1,185 +1,61 @@
var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint;
var protocol = require("../protocol");
var Transform = require("readable-stream").Transform;
var debug = require("../debug");
var assert = require('assert');
var { getFieldInfo, tryCatch, addErrorField } = require('../utils');
var ProtoDef = require("protodef").ProtoDef;
var Serializer = require("protodef").Serializer;
var Parser = require("protodef").Parser;
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 structures = require("../datatypes/structures");
var conditional = require("../datatypes/conditional");
var readPackets = require("../packets").readPackets;
var states = require("../states");
function createProtocol(types)
function createProtocol(types,packets)
{
var proto = new NMProtocols();
proto.addTypes(numeric);
proto.addTypes(utils);
var proto = new ProtoDef();
proto.addType("string",["pstring",{
countType:"varint"
}]);
proto.addTypes(minecraft);
proto.addTypes(structures);
proto.addTypes(conditional);
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;
}
class Serializer extends Transform {
constructor({ state = states.HANDSHAKING, isServer = false , version} = {}) {
super({ writableObjectMode: true });
this.protocolState = state;
this.isServer = isServer;
this.version = version;
function createSerializer({ state = states.HANDSHAKING, isServer = false , 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}.${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);
}
}
var direction = !isServer ? 'toServer' : 'toClient';
var packets = mcData.protocol.states[state][direction];
var proto=createProtocol(mcData.protocol.types,packets);
return new Serializer(proto,"packet");
}
class Deserializer extends Transform {
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;
function createDeserializer({ state = states.HANDSHAKING, isServer = false,
packetsToParse = {"packet": true}, 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 = this.parsePacketData(chunk);
} catch (e) {
return cb(e);
}
this.push(packet);
return cb();
}
var direction = isServer ? "toServer" : "toClient";
var packets = mcData.protocol.states[state][direction];
var proto=createProtocol(mcData.protocol.types,packets);
return new Parser(proto,"packet");
}
module.exports = {
createSerializer:createSerializer,
createDeserializer:createDeserializer
};

View File

@ -1,4 +1,4 @@
var ITERATIONS = 100000;
var ITERATIONS = 10000;
var mc = require("../");
var util = require('util');
@ -15,7 +15,7 @@ mc.supportedVersions.forEach(function(supportedVersion){
var mcData=require("minecraft-data")(supportedVersion);
var version=mcData.version;
describe("benchmark "+version.minecraftVersion,function(){
this.timeout(20 * 1000);
this.timeout(60 * 1000);
var inputData = [];
it("bench serializing",function(done){
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();
for(i = 0; i < ITERATIONS; i++) {
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;
@ -37,7 +37,7 @@ mc.supportedVersions.forEach(function(supportedVersion){
console.log('Beginning read test');
start = Date.now();
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');
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 version=mcData.version;
var packets = mcData.protocol.states;
var packetIndexes = mc.readPackets(packets, states);
var packetFields = packetIndexes.packetFields;
describe("packets "+version.minecraftVersion, function() {
var client, server, serverClient;
@ -132,18 +130,18 @@ mc.supportedVersions.forEach(function(supportedVersion){
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];
for(state in packets) {
if(!packets.hasOwnProperty(state)) continue;
for(packetName in packets[state].toServer) {
if(!packets[state].toServer.hasOwnProperty(packetName)) continue;
packetInfo = packets[state]["toServer"][packetName].fields;
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];
for(packetName in packets[state].toClient) {
if(!packets[state].toClient.hasOwnProperty(packetName)) continue;
packetInfo = packets[state]["toClient"][packetName].fields;
packetInfo=packetInfo ? packetInfo : null;
it(state + ",ClientBound," + packetName,
callTestPacket(packetName, packetInfo, state, false));