Add the new type syntax support. Old type syntax doesn't work

This commit is contained in:
roblabla 2015-08-24 11:49:34 +00:00
parent 5ca49a21ab
commit ba8a3790aa
8 changed files with 97 additions and 70 deletions

View file

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

View file

@ -7,19 +7,19 @@ module.exports = {
function readCondition(buffer, offset, typeArgs, rootNode) {
if(!evalCondition(typeArgs, rootNode))
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) {
if(!evalCondition(typeArgs, rootNode))
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) {
if(!evalCondition(fieldInfo, rootNode))
function sizeOfCondition(value, typeArgs, rootNode) {
if(!evalCondition(typeArgs, rootNode))
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],
'slot': [readSlot, writeSlot, sizeOfSlot],
'nbt': [readNbt, utils.buffer[1], utils.buffer[2]],
'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]],
'restBuffer': [readRestBuffer, writeRestBuffer, sizeOfRestBuffer],
'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata]
};
@ -131,13 +131,21 @@ function sizeOfNbt(value) {
}
function readRestBuffer(buffer, offset, typeArgs, rootNode) {
function readRestBuffer(buffer, offset) {
return {
value: buffer.slice(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 = {
0: {type: 'byte'},

View file

@ -33,7 +33,7 @@ function readArray(buffer, offset, typeArgs, rootNode) {
} else // TODO : broken schema, should probably error out.
count = 0;
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;
offset += readResults.size;
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
}
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;
}
@ -60,7 +60,7 @@ function sizeOfArray(value, typeArgs, rootNode) {
size = this.sizeOf(value.length, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}
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;
}
@ -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.
var backupThis = rootNode.this;
rootNode.this = results.value;
for(var index in typeArgs.fields) {
var readResults = this.read(buffer, offset, typeArgs.fields[index], rootNode);
for(var index in typeArgs) {
var readResults = this.read(buffer, offset, typeArgs[index].type, rootNode);
if(readResults == null || readResults.value == null) {
continue;
}
results.size += readResults.size;
offset += readResults.size;
results.value[typeArgs.fields[index].name] = readResults.value;
results.value[typeArgs[index].name] = readResults.value;
}
rootNode.this = backupThis;
return results;
}
function writeContainer(value, buffer, offset, typeArgs, rootNode) {
var context = value.this ? value.this : value;
var backupThis = rootNode.this;
rootNode.this = value;
for(var index in typeArgs.fields) {
if(!context.hasOwnProperty(typeArgs.fields[index].name) && typeArgs.fields[index].type != "count" &&
(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);
for(var index in typeArgs) {
offset = this.write(value[typeArgs[index].name], buffer, offset, typeArgs[index].type, rootNode);
}
rootNode.this = backupThis;;
rootNode.this = backupThis;
return offset;
}
function sizeOfContainer(value, typeArgs, rootNode) {
var size = 0;
var context = value.this ? value.this : value;
var backupThis = rootNode.this;
rootNode.this = value;
for(var index in typeArgs.fields) {
size += this.sizeOf(context[typeArgs.fields[index].name], typeArgs.fields[index], rootNode);
for(var index in typeArgs) {
size += this.sizeOf(value[typeArgs[index].name], typeArgs[index].type, rootNode);
}
rootNode.this = backupThis;
return size;
}
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) {
// 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, {type: typeArgs.type}, rootNode);
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, {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() {
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];
if(!type) {
return {
error: new Error("missing data type: " + fieldInfo.type)
};
}
var typeArgs = fieldInfo.typeArgs || {};
var readResults = type[0].call(this, buffer, cursor, typeArgs, rootNodes);
var readResults = type[0].call(this, buffer, cursor, fieldInfo.typeArgs, rootNodes);
if(readResults == null) {
throw new Error("Reader returned null : " + JSON.stringify(fieldInfo));
}
@ -29,25 +31,25 @@ NMProtocols.prototype.read = function(buffer, cursor, fieldInfo, rootNodes) {
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];
if(!type) {
return {
error: new Error("missing data type: " + fieldInfo.type)
};
}
var typeArgs = fieldInfo.typeArgs || {};
return type[1].call(this, value, buffer, offset, typeArgs, rootNode);
return type[1].call(this, value, buffer, offset, fieldInfo.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];
if(!type) {
throw new Error("missing data type: " + fieldInfo.type);
}
if(typeof type[2] === 'function') {
var typeArgs = fieldInfo.typeArgs || {};
return type[2].call(this, value, typeArgs, rootNode);
return type[2].call(this, value, fieldInfo.typeArgs, rootNode);
} else {
return type[2];
}

View file

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

View file

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