Merge pull request #221 from roblabla/feature-newTypeSyntax

Add the new type syntax support. Old type syntax doesn't work
This commit is contained in:
Robin Lambertz 2015-08-24 20:30:19 +02:00
commit ccbb60b587
8 changed files with 97 additions and 70 deletions

View File

@ -9,7 +9,7 @@
}, },
"scripts": { "scripts": {
"prepublish": "gulp", "prepublish": "gulp",
"test": "mocha --reporter spec" "test": "mocha --require source-map-support/register --reporter spec"
}, },
"keywords": [ "keywords": [
"minecraft", "minecraft",
@ -28,14 +28,15 @@
}, },
"browser": "browser.js", "browser": "browser.js",
"devDependencies": { "devDependencies": {
"batch": "~0.3.1",
"gulp": "^3.8.11", "gulp": "^3.8.11",
"gulp-plumber": "^1.0.1",
"gulp-babel": "^5.1.0", "gulp-babel": "^5.1.0",
"gulp-plumber": "^1.0.1",
"gulp-sourcemaps": "^1.3.0", "gulp-sourcemaps": "^1.3.0",
"mkdirp": "~0.3.4", "mkdirp": "~0.3.4",
"mocha": "~1.8.2", "mocha": "~1.8.2",
"rimraf": "~2.1.1", "rimraf": "~2.1.1",
"batch": "~0.3.1", "source-map-support": "^0.3.2",
"zfill": "0.0.1" "zfill": "0.0.1"
}, },
"dependencies": { "dependencies": {
@ -46,7 +47,7 @@
"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",
"minecraft-data": "0.5.1" "minecraft-data": "0.6.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"ursa": "~0.8.0" "ursa": "~0.8.0"

View File

@ -7,19 +7,19 @@ module.exports = {
function readCondition(buffer, offset, typeArgs, rootNode) { function readCondition(buffer, offset, typeArgs, rootNode) {
if(!evalCondition(typeArgs, rootNode)) if(!evalCondition(typeArgs, rootNode))
return {value: null, size: 0}; return {value: null, size: 0};
return this.read(buffer, offset, {type: typeArgs.type, typeArgs: typeArgs.typeArgs}, rootNode); return this.read(buffer, offset, typeArgs.type, rootNode);
} }
function writeCondition(value, buffer, offset, typeArgs, rootNode) { function writeCondition(value, buffer, offset, typeArgs, rootNode) {
if(!evalCondition(typeArgs, rootNode)) if(!evalCondition(typeArgs, rootNode))
return offset; return offset;
return this.write(value, buffer, offset, {type: typeArgs.type, typeArgs: typeArgs.typeArgs}, rootNode); return this.write(value, buffer, offset, typeArgs.type, rootNode);
} }
function sizeOfCondition(value, fieldInfo, rootNode) { function sizeOfCondition(value, typeArgs, rootNode) {
if(!evalCondition(fieldInfo, rootNode)) if(!evalCondition(typeArgs, rootNode))
return 0; return 0;
return this.sizeOf(value, fieldInfo, rootNode); return this.sizeOf(value, typeArgs.type, rootNode);
} }

View File

@ -9,7 +9,7 @@ module.exports = {
'position': [readPosition, writePosition, 8], 'position': [readPosition, writePosition, 8],
'slot': [readSlot, writeSlot, sizeOfSlot], 'slot': [readSlot, writeSlot, sizeOfSlot],
'nbt': [readNbt, utils.buffer[1], utils.buffer[2]], 'nbt': [readNbt, utils.buffer[1], utils.buffer[2]],
'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]], 'restBuffer': [readRestBuffer, writeRestBuffer, sizeOfRestBuffer],
'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata] 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata]
}; };
@ -131,13 +131,21 @@ function sizeOfNbt(value) {
} }
function readRestBuffer(buffer, offset, typeArgs, rootNode) { function readRestBuffer(buffer, offset) {
return { return {
value: buffer.slice(offset), value: buffer.slice(offset),
size: buffer.length - offset size: buffer.length - offset
}; };
} }
function writeRestBuffer(value, buffer, offset) {
value.copy(buffer, offset);
return offset + value.length;
}
function sizeOfRestBuffer(value) {
return value.length;
}
var entityMetadataTypes = { var entityMetadataTypes = {
0: {type: 'byte'}, 0: {type: 'byte'},

View File

@ -33,7 +33,7 @@ function readArray(buffer, offset, typeArgs, rootNode) {
} else // TODO : broken schema, should probably error out. } else // TODO : broken schema, should probably error out.
count = 0; count = 0;
for(var i = 0; i < count; i++) { for(var i = 0; i < count; i++) {
var readResults = this.read(buffer, offset, {type: typeArgs.type, typeArgs: typeArgs.typeArgs}, rootNode); var readResults = this.read(buffer, offset, typeArgs.type, rootNode);
results.size += readResults.size; results.size += readResults.size;
offset += readResults.size; offset += readResults.size;
results.value.push(readResults.value); results.value.push(readResults.value);
@ -48,7 +48,7 @@ function writeArray(value, buffer, offset, typeArgs, rootNode) {
} else if (typeof typeArgs.count === "undefined") { // Broken schema, should probably error out } else if (typeof typeArgs.count === "undefined") { // Broken schema, should probably error out
} }
for(var index in value) { for(var index in value) {
offset = this.write(value[index], buffer, offset, {type: typeArgs.type, typeArgs: typeArgs.typeArgs}, rootNode); offset = this.write(value[index], buffer, offset, typeArgs.type, rootNode);
} }
return offset; return offset;
} }
@ -60,7 +60,7 @@ function sizeOfArray(value, typeArgs, rootNode) {
size = this.sizeOf(value.length, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode); size = this.sizeOf(value.length, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
} }
for(var index in value) { for(var index in value) {
size += this.sizeOf(value[index], {type: typeArgs.type, typeArgs: typeArgs.typeArgs}, rootNode); size += this.sizeOf(value[index], typeArgs.type, rootNode);
} }
return size; return size;
} }
@ -76,58 +76,51 @@ function readContainer(buffer, offset, typeArgs, rootNode) {
// TODO : what I do inside of roblabla/Protocols is have each "frame" create a new empty slate with just a "super" object pointing to the parent. // TODO : what I do inside of roblabla/Protocols is have each "frame" create a new empty slate with just a "super" object pointing to the parent.
var backupThis = rootNode.this; var backupThis = rootNode.this;
rootNode.this = results.value; rootNode.this = results.value;
for(var index in typeArgs.fields) { for(var index in typeArgs) {
var readResults = this.read(buffer, offset, typeArgs.fields[index], rootNode); var readResults = this.read(buffer, offset, typeArgs[index].type, rootNode);
if(readResults == null || readResults.value == null) { if(readResults == null || readResults.value == null) {
continue; continue;
} }
results.size += readResults.size; results.size += readResults.size;
offset += readResults.size; offset += readResults.size;
results.value[typeArgs.fields[index].name] = readResults.value; results.value[typeArgs[index].name] = readResults.value;
} }
rootNode.this = backupThis; rootNode.this = backupThis;
return results; return results;
} }
function writeContainer(value, buffer, offset, typeArgs, rootNode) { function writeContainer(value, buffer, offset, typeArgs, rootNode) {
var context = value.this ? value.this : value;
var backupThis = rootNode.this; var backupThis = rootNode.this;
rootNode.this = value; rootNode.this = value;
for(var index in typeArgs.fields) { for(var index in typeArgs) {
if(!context.hasOwnProperty(typeArgs.fields[index].name) && typeArgs.fields[index].type != "count" && offset = this.write(value[typeArgs[index].name], buffer, offset, typeArgs[index].type, rootNode);
(typeArgs.fields[index].type != "condition" || evalCondition(typeArgs.fields[index].typeArgs, rootNode))) {
debug(new Error("Missing Property " + typeArgs.fields[index].name).stack);
console.log(context);
} }
offset = this.write(context[typeArgs.fields[index].name], buffer, offset, typeArgs.fields[index], rootNode); rootNode.this = backupThis;
}
rootNode.this = backupThis;;
return offset; return offset;
} }
function sizeOfContainer(value, typeArgs, rootNode) { function sizeOfContainer(value, typeArgs, rootNode) {
var size = 0; var size = 0;
var context = value.this ? value.this : value;
var backupThis = rootNode.this; var backupThis = rootNode.this;
rootNode.this = value; rootNode.this = value;
for(var index in typeArgs.fields) { for(var index in typeArgs) {
size += this.sizeOf(context[typeArgs.fields[index].name], typeArgs.fields[index], rootNode); size += this.sizeOf(value[typeArgs[index].name], typeArgs[index].type, rootNode);
} }
rootNode.this = backupThis; rootNode.this = backupThis;
return size; return size;
} }
function readCount(buffer, offset, typeArgs, rootNode) { function readCount(buffer, offset, typeArgs, rootNode) {
return this.read(buffer, offset, {type: typeArgs.type}, rootNode); return this.read(buffer, offset, typeArgs.type, rootNode);
} }
function writeCount(value, buffer, offset, typeArgs, rootNode) { function writeCount(value, buffer, offset, typeArgs, rootNode) {
// Actually gets the required field, and writes its length. Value is unused. // Actually gets the required field, and writes its length. Value is unused.
// TODO : a bit hackityhack. // TODO : a bit hackityhack.
return this.write(getField(typeArgs.countFor, rootNode).length, buffer, offset, {type: typeArgs.type}, rootNode); return this.write(getField(typeArgs.countFor, rootNode).length, buffer, offset, typeArgs.type, rootNode);
} }
function sizeOfCount(value, typeArgs, rootNode) { function sizeOfCount(value, typeArgs, rootNode) {
// TODO : should I use value or getField().length ? // TODO : should I use value or getField().length ?
return this.sizeOf(getField(typeArgs.countFor, rootNode).length, {type: typeArgs.type}, rootNode); return this.sizeOf(getField(typeArgs.countFor, rootNode).length, typeArgs.type, rootNode);
} }

View File

@ -1,3 +1,5 @@
var { getFieldInfo } = require('./utils');
function NMProtocols() { function NMProtocols() {
this.types = {}; this.types = {};
} }
@ -13,15 +15,15 @@ NMProtocols.prototype.addTypes = function(types) {
}); });
}; };
NMProtocols.prototype.read = function(buffer, cursor, fieldInfo, rootNodes) { NMProtocols.prototype.read = function(buffer, cursor, _fieldInfo, rootNodes) {
let fieldInfo = getFieldInfo(_fieldInfo);
var type = this.types[fieldInfo.type]; var type = this.types[fieldInfo.type];
if(!type) { if(!type) {
return { return {
error: new Error("missing data type: " + fieldInfo.type) error: new Error("missing data type: " + fieldInfo.type)
}; };
} }
var typeArgs = fieldInfo.typeArgs || {}; var readResults = type[0].call(this, buffer, cursor, fieldInfo.typeArgs, rootNodes);
var readResults = type[0].call(this, buffer, cursor, typeArgs, rootNodes);
if(readResults == null) { if(readResults == null) {
throw new Error("Reader returned null : " + JSON.stringify(fieldInfo)); throw new Error("Reader returned null : " + JSON.stringify(fieldInfo));
} }
@ -29,25 +31,25 @@ NMProtocols.prototype.read = function(buffer, cursor, fieldInfo, rootNodes) {
return readResults; return readResults;
}; };
NMProtocols.prototype.write = function(value, buffer, offset, fieldInfo, rootNode) { NMProtocols.prototype.write = function(value, buffer, offset, _fieldInfo, rootNode) {
let fieldInfo = getFieldInfo(_fieldInfo);
var type = this.types[fieldInfo.type]; var type = this.types[fieldInfo.type];
if(!type) { if(!type) {
return { return {
error: new Error("missing data type: " + fieldInfo.type) error: new Error("missing data type: " + fieldInfo.type)
}; };
} }
var typeArgs = fieldInfo.typeArgs || {}; return type[1].call(this, value, buffer, offset, fieldInfo.typeArgs, rootNode);
return type[1].call(this, value, buffer, offset, typeArgs, rootNode);
}; };
NMProtocols.prototype.sizeOf = function(value, fieldInfo, rootNode) { NMProtocols.prototype.sizeOf = function(value, _fieldInfo, rootNode) {
let fieldInfo = getFieldInfo(_fieldInfo);
var type = this.types[fieldInfo.type]; var type = this.types[fieldInfo.type];
if(!type) { if(!type) {
throw new Error("missing data type: " + fieldInfo.type); throw new Error("missing data type: " + fieldInfo.type);
} }
if(typeof type[2] === 'function') { if(typeof type[2] === 'function') {
var typeArgs = fieldInfo.typeArgs || {}; return type[2].call(this, value, fieldInfo.typeArgs, rootNode);
return type[2].call(this, value, typeArgs, rootNode);
} else { } else {
return type[2]; return type[2];
} }

View File

@ -3,6 +3,7 @@ var protocol = require("../protocol");
var Transform = require("readable-stream").Transform; var Transform = require("readable-stream").Transform;
var debug = require("../debug"); var debug = require("../debug");
var assert = require('assert'); var assert = require('assert');
var { getFieldInfo } = require('../utils');
module.exports.createSerializer = function(obj) { module.exports.createSerializer = function(obj) {
return new Serializer(obj); return new Serializer(obj);
@ -73,7 +74,7 @@ function createPacketBuffer(packetId, state, params, isServer) {
assert.notEqual(packet, null); assert.notEqual(packet, null);
packet.forEach(function(fieldInfo) { packet.forEach(function(fieldInfo) {
try { try {
length += proto.sizeOf(params[fieldInfo.name], fieldInfo, params); length += proto.sizeOf(params[fieldInfo.name], fieldInfo.type, params);
} catch(e) { } catch(e) {
console.log("fieldInfo : " + JSON.stringify(fieldInfo)); console.log("fieldInfo : " + JSON.stringify(fieldInfo));
console.log("params : " + JSON.stringify(params)); console.log("params : " + JSON.stringify(params));
@ -90,7 +91,7 @@ function createPacketBuffer(packetId, state, params, isServer) {
// TODO : A better check is probably needed // TODO : A better check is probably needed
if(typeof value === "undefined" && fieldInfo.type != "count" && (fieldInfo.type != "condition" || evalCondition(fieldInfo.typeArgs, params))) if(typeof value === "undefined" && fieldInfo.type != "count" && (fieldInfo.type != "condition" || evalCondition(fieldInfo.typeArgs, params)))
debug(new Error("Missing Property " + fieldInfo.name).stack); debug(new Error("Missing Property " + fieldInfo.name).stack);
offset = proto.write(value, buffer, offset, fieldInfo, params); offset = proto.write(value, buffer, offset, fieldInfo.type, params);
}); });
return buffer; return buffer;
} }
@ -109,9 +110,7 @@ function get(packetId, state, toServer) {
// By default, parse every packets. // By default, parse every packets.
function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": true}) { function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": true}) {
var cursor = 0; var cursor = 0;
var packetIdField = utils.varint[0](buffer, cursor); var { value: packetId, size: cursor } = utils.varint[0](buffer, cursor);
var packetId = packetIdField.value;
cursor += packetIdField.size;
var results = {id: packetId, state: state}; var results = {id: packetId, state: state};
// Only parse the packet if there is a need for it, AKA if there is a listener attached to it // Only parse the packet if there is a need for it, AKA if there is a listener attached to it
@ -140,7 +139,7 @@ function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": tr
var i, fieldInfo, readResults; var i, fieldInfo, readResults;
for(i = 0; i < packetInfo.length; ++i) { for(i = 0; i < packetInfo.length; ++i) {
fieldInfo = packetInfo[i]; fieldInfo = packetInfo[i];
readResults = proto.read(buffer, cursor, fieldInfo, results); readResults = proto.read(buffer, cursor, fieldInfo.type, results);
/* A deserializer cannot return null anymore. Besides, proto.read() returns /* A deserializer cannot return null anymore. Besides, proto.read() returns
* null when the condition is not fulfilled. * null when the condition is not fulfilled.
if (!!!readResults) { if (!!!readResults) {

View File

@ -1,5 +1,6 @@
module.exports = { module.exports = {
getField: getField, getField: getField,
getFieldInfo: getFieldInfo,
evalCondition: evalCondition evalCondition: evalCondition
}; };
@ -12,6 +13,16 @@ function getField(countField, rootNode) {
return count; return count;
} }
function getFieldInfo(fieldInfo) {
if (typeof fieldInfo === "string")
return { type: fieldInfo };
else if (Array.isArray(fieldInfo))
return { type: fieldInfo[0], typeArgs: fieldInfo[1] };
else if (typeof fieldInfo.type === "string")
return fieldInfo;
else
throw new Error("Not a fieldinfo");
}
function evalCondition(condition, field_values) { function evalCondition(condition, field_values) {
var field_value_to_test = "this" in condition && condition["this"] ? field_values["this"][condition.field] : field_values[condition.field]; var field_value_to_test = "this" in condition && condition["this"] ? field_values["this"][condition.field] : field_values[condition.field];

View File

@ -14,6 +14,8 @@ var mc = require('../')
, MC_SERVER_JAR = process.env.MC_SERVER_JAR , MC_SERVER_JAR = process.env.MC_SERVER_JAR
, SURVIVE_TIME = 10000 , SURVIVE_TIME = 10000
, MC_SERVER_PATH = path.join(__dirname, 'server') , MC_SERVER_PATH = path.join(__dirname, 'server')
, getFieldInfo = require('../dist/utils').getFieldInfo
, evalCondition = require('../dist/utils').evalCondition
; ;
var defaultServerProps = { var defaultServerProps = {
@ -60,25 +62,29 @@ var values = {
'ubyte': 8, 'ubyte': 8,
'string': "hi hi this is my client string", 'string': "hi hi this is my client string",
'buffer': new Buffer(8), 'buffer': new Buffer(8),
'array': function(typeArgs) { 'array': function(_typeArgs, packet) {
var typeArgs = getFieldInfo(_typeArgs.type)
if(typeof values[typeArgs.type] === "undefined") { if(typeof values[typeArgs.type] === "undefined") {
throw new Error("No data type for " + typeArgs.type); throw new Error("No data type for " + typeArgs.type);
} }
if(typeof values[typeArgs.type] === "function") { if(typeof values[typeArgs.type] === "function") {
return [values[typeArgs.type](typeArgs.typeArgs)]; return [values[typeArgs.type](typeArgs.typeArgs, packet)];
} }
return [values[typeArgs.type]]; return [values[typeArgs.type]];
}, },
'container': function(typeArgs) { 'container': function(typeArgs, packet) {
var results = {}; var results = {};
for(var index in typeArgs.fields) { for(var index in typeArgs) {
if(typeof values[typeArgs.fields[index].type] === "undefined") { if(typeof values[getFieldInfo(typeArgs[index].type).type] === "undefined") {
throw new Error("No data type for " + typeArgs.fields[index].type); throw new Error("No data type for " + typeArgs[index].type);
} }
if(typeof values[typeArgs.fields[index].type] === "function") { if(typeof values[getFieldInfo(typeArgs[index].type).type] === "function") {
results[typeArgs.fields[index].name] = values[typeArgs.fields[index].type](typeArgs.fields[index].typeArgs); var backupThis = packet.this;
packet.this = results;
results[typeArgs[index].name] = values[getFieldInfo(typeArgs[index].type).type](getFieldInfo(typeArgs[index].type).typeArgs, packet);
packet.this = backupThis;
} else { } else {
results[typeArgs.fields[index].name] = values[typeArgs.fields[index].type]; results[typeArgs[index].name] = values[getFieldInfo(typeArgs[index].type).type];
} }
} }
return results; return results;
@ -121,10 +127,13 @@ var values = {
'UUID': "00112233-4455-6677-8899-aabbccddeeff", 'UUID': "00112233-4455-6677-8899-aabbccddeeff",
'position': {x: 12, y: 332, z: 4382821}, 'position': {x: 12, y: 332, z: 4382821},
'restBuffer': new Buffer(0), 'restBuffer': new Buffer(0),
'condition': function(typeArgs) { 'condition': function(typeArgs, packet) {
// check whether this should return undefined if needed instead ? (using evalCondition) if (evalCondition(typeArgs, packet)) {
// probably not since we only send values that respect the condition if (typeof values[getFieldInfo(typeArgs.type).type] === "function")
return values[typeArgs.type]; return values[getFieldInfo(typeArgs.type).type](getFieldInfo(typeArgs.type).typeArgs, packet);
else
return values[getFieldInfo(typeArgs.type).type];
}
} }
}; };
@ -179,22 +188,25 @@ describe("packets", function() {
// empty object uses default values // empty object uses default values
var packet = {}; var packet = {};
packetInfo.forEach(function(field) { packetInfo.forEach(function(field) {
if(field.type !== "condition" || mc.evalCondition(field.typeArgs, packet)) { var fieldVal = values[getFieldInfo(field.type).type];
var fieldVal = values[field.type];
if(typeof fieldVal === "undefined") { if(typeof fieldVal === "undefined") {
throw new Error("No value for type " + field.type); throw new Error("No value for type " + field.type);
} }
if(typeof fieldVal === "function") { if(typeof fieldVal === "function") {
fieldVal = fieldVal(field.typeArgs); fieldVal = fieldVal(getFieldInfo(field.type).typeArgs, packet);
} }
packet[field.name] = fieldVal; packet[field.name] = fieldVal;
}
}); });
if(toServer) { if(toServer) {
serverClient.once([state, packetId], function(receivedPacket) { serverClient.once([state, packetId], function(receivedPacket) {
delete receivedPacket.id; delete receivedPacket.id;
delete receivedPacket.state; delete receivedPacket.state;
try {
assertPacketsMatch(packet, receivedPacket); assertPacketsMatch(packet, receivedPacket);
} catch (e) {
console.log(packet, receivedPacket);
throw e;
}
done(); done();
}); });
client.write(packetId, packet); client.write(packetId, packet);
@ -215,6 +227,7 @@ describe("packets", function() {
}); });
var field; var field;
for(field in p1) { for(field in p1) {
if (p1[field] !== undefined)
assert.ok(field in p2, "field " + field + " missing in p2, in p1 it has value " + JSON.stringify(p1[field])); assert.ok(field in p2, "field " + field + " missing in p2, in p1 it has value " + JSON.stringify(p1[field]));
} }
for(field in p2) { for(field in p2) {