mirror of
https://github.com/unmojang/node-minecraft-protocol.git
synced 2025-09-28 21:52:17 -04:00
Merge pull request #177 from rom1504/move_functions_to_serializer
move createPacketBuffer and parsePacketData functions to serializer
This commit is contained in:
commit
22a6906b93
@ -1,4 +1 @@
|
|||||||
module.exports = {
|
module.exports = require('./dist/browser.js');
|
||||||
Client: require('./dist/client'),
|
|
||||||
protocol: require('./dist/protocol')
|
|
||||||
};
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
var readline = require('readline');
|
var readline = require('readline');
|
||||||
var color = require("ansi-color").set;
|
var color = require("ansi-color").set;
|
||||||
var mc = require('../../');
|
var mc = require('../../');
|
||||||
var states = mc.protocol.states;
|
var states = mc.states;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var colors = new Array();
|
var colors = new Array();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
var mc = require('../../')
|
var mc = require('../../')
|
||||||
, states = mc.protocol.states
|
, states = mc.states
|
||||||
|
|
||||||
var client = mc.createClient({
|
var client = mc.createClient({
|
||||||
username: process.env.MC_USERNAME,
|
username: process.env.MC_USERNAME,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
var mc = require('../../');
|
var mc = require('../../');
|
||||||
|
|
||||||
var states = mc.protocol.states;
|
var states = mc.states;
|
||||||
function printHelpAndExit(exitCode) {
|
function printHelpAndExit(exitCode) {
|
||||||
console.log("usage: node proxy.js [<options>...] <target_srv> <user> [<password>]");
|
console.log("usage: node proxy.js [<options>...] <target_srv> <user> [<password>]");
|
||||||
console.log("options:");
|
console.log("options:");
|
||||||
@ -133,9 +133,9 @@ srv.on('login', function(client) {
|
|||||||
targetClient.on('raw', function(buffer, state) {
|
targetClient.on('raw', function(buffer, state) {
|
||||||
if(client.state != states.PLAY || state != states.PLAY)
|
if(client.state != states.PLAY || state != states.PLAY)
|
||||||
return;
|
return;
|
||||||
var packetId = mc.protocol.types.varint[0](buffer, 0);
|
var packetId = mc.types.varint[0](buffer, 0);
|
||||||
var packetData = mc.protocol.parsePacketData(buffer, state, false, {"packet": 1}).results;
|
var packetData = mc.parsePacketData(buffer, state, false, {"packet": 1}).results;
|
||||||
var packetBuff = mc.protocol.createPacketBuffer(packetData.id, packetData.state, packetData, true);
|
var packetBuff = mc.createPacketBuffer(packetData.id, packetData.state, packetData, true);
|
||||||
if(buffertools.compare(buffer, packetBuff) != 0) {
|
if(buffertools.compare(buffer, packetBuff) != 0) {
|
||||||
console.log("client<-server: Error in packetId " + state + ".0x" + packetId.value.toString(16));
|
console.log("client<-server: Error in packetId " + state + ".0x" + packetId.value.toString(16));
|
||||||
console.log(buffer.toString('hex'));
|
console.log(buffer.toString('hex'));
|
||||||
@ -152,9 +152,9 @@ srv.on('login', function(client) {
|
|||||||
client.on('raw', function(buffer, state) {
|
client.on('raw', function(buffer, state) {
|
||||||
if(state != states.PLAY || targetClient.state != states.PLAY)
|
if(state != states.PLAY || targetClient.state != states.PLAY)
|
||||||
return;
|
return;
|
||||||
var packetId = mc.protocol.types.varint[0](buffer, 0);
|
var packetId = mc.types.varint[0](buffer, 0);
|
||||||
var packetData = mc.protocol.parsePacketData(buffer, state, true, {"packet": 1}).results;
|
var packetData = mc.parsePacketData(buffer, state, true, {"packet": 1}).results;
|
||||||
var packetBuff = mc.protocol.createPacketBuffer(packetData.id, packetData.state, packetData, false);
|
var packetBuff = mc.createPacketBuffer(packetData.id, packetData.state, packetData, false);
|
||||||
if(buffertools.compare(buffer, packetBuff) != 0) {
|
if(buffertools.compare(buffer, packetBuff) != 0) {
|
||||||
console.log("client->server: Error in packetId " + state + ".0x" + packetId.value.toString(16));
|
console.log("client->server: Error in packetId " + state + ".0x" + packetId.value.toString(16));
|
||||||
console.log(buffer.toString('hex'));
|
console.log(buffer.toString('hex'));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
var mc = require('../../');
|
var mc = require('../../');
|
||||||
var states = mc.protocol.states;
|
var states = mc.states;
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
motd: 'Vox Industries',
|
motd: 'Vox Industries',
|
||||||
|
19
src/browser.js
Normal file
19
src/browser.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
var packets = require("../protocol/protocol");
|
||||||
|
var readPackets = require("./packets").readPackets;
|
||||||
|
var packetIndexes = readPackets(packets, states);
|
||||||
|
var utils = require("./utils");
|
||||||
|
var serializer = require("./transforms/serializer");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Client: require('./client'),
|
||||||
|
protocol: require('./protocol'),
|
||||||
|
createPacketBuffer: serializer.createPacketBuffer,
|
||||||
|
parsePacketData: serializer.parsePacketData,
|
||||||
|
packetFields: packetIndexes.packetFields,
|
||||||
|
packetNames: packetIndexes.packetNames,
|
||||||
|
packetIds: packetIndexes.packetIds,
|
||||||
|
packetStates: packetIndexes.packetStates,
|
||||||
|
types: serializer.types,
|
||||||
|
get: serializer.get,
|
||||||
|
evalCondition: utils.evalCondition,
|
||||||
|
};
|
@ -1,22 +1,17 @@
|
|||||||
var EventEmitter = require('events').EventEmitter
|
var EventEmitter = require('events').EventEmitter
|
||||||
, util = require('util')
|
, util = require('util')
|
||||||
, protocol = require('./protocol')
|
|
||||||
, createPacketBuffer = protocol.createPacketBuffer
|
|
||||||
, compressPacketBuffer = protocol.compressPacketBuffer
|
|
||||||
, oldStylePacket = protocol.oldStylePacket
|
|
||||||
, newStylePacket = protocol.newStylePacket
|
|
||||||
, parsePacketData = protocol.parsePacketData
|
|
||||||
, parseNewStylePacket = protocol.parseNewStylePacket
|
|
||||||
, packetIds = protocol.packetIds
|
|
||||||
, packetNames = protocol.packetNames
|
|
||||||
, states = protocol.states
|
|
||||||
, debug = require('./debug')
|
, debug = require('./debug')
|
||||||
, serializer = require('./transforms/serializer')
|
, serializer = require('./transforms/serializer')
|
||||||
, compression = require('./transforms/compression')
|
, compression = require('./transforms/compression')
|
||||||
, framing = require('./transforms/framing')
|
, framing = require('./transforms/framing')
|
||||||
, crypto = require('crypto')
|
, crypto = require('crypto')
|
||||||
|
, states = serializer.states
|
||||||
;
|
;
|
||||||
|
|
||||||
|
var packets = require("../protocol/protocol");
|
||||||
|
var readPackets = require("./packets").readPackets;
|
||||||
|
var packetIndexes = readPackets(packets, states);
|
||||||
|
|
||||||
module.exports = Client;
|
module.exports = Client;
|
||||||
|
|
||||||
function Client(isServer) {
|
function Client(isServer) {
|
||||||
@ -56,14 +51,14 @@ function Client(isServer) {
|
|||||||
|
|
||||||
this.on('newListener', function(event, listener) {
|
this.on('newListener', function(event, listener) {
|
||||||
var direction = this.isServer ? 'toServer' : 'toClient';
|
var direction = this.isServer ? 'toServer' : 'toClient';
|
||||||
if(protocol.packetStates[direction].hasOwnProperty(event) || event === "packet") {
|
if(packetIndexes.packetStates[direction].hasOwnProperty(event) || event === "packet") {
|
||||||
if(typeof this.packetsToParse[event] === "undefined") this.packetsToParse[event] = 1;
|
if(typeof this.packetsToParse[event] === "undefined") this.packetsToParse[event] = 1;
|
||||||
else this.packetsToParse[event] += 1;
|
else this.packetsToParse[event] += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.on('removeListener', function(event, listener) {
|
this.on('removeListener', function(event, listener) {
|
||||||
var direction = this.isServer ? 'toServer' : 'toClient';
|
var direction = this.isServer ? 'toServer' : 'toClient';
|
||||||
if(protocol.packetStates[direction].hasOwnProperty(event) || event === "packet") {
|
if(packetIndexes.packetStates[direction].hasOwnProperty(event) || event === "packet") {
|
||||||
this.packetsToParse[event] -= 1;
|
this.packetsToParse[event] -= 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -75,9 +70,9 @@ util.inherits(Client, EventEmitter);
|
|||||||
Client.prototype.on = function(type, func) {
|
Client.prototype.on = function(type, func) {
|
||||||
var direction = this.isServer ? 'toServer' : 'toClient';
|
var direction = this.isServer ? 'toServer' : 'toClient';
|
||||||
if(Array.isArray(type)) {
|
if(Array.isArray(type)) {
|
||||||
arguments[0] = protocol.packetNames[type[0]][direction][type[1]];
|
arguments[0] = packetIndexes.packetNames[type[0]][direction][type[1]];
|
||||||
} else if(typeof type === "number") {
|
} else if(typeof type === "number") {
|
||||||
arguments[0] = protocol.packetNames[this.state][direction][type];
|
arguments[0] = packetIndexes.packetNames[this.state][direction][type];
|
||||||
}
|
}
|
||||||
EventEmitter.prototype.on.apply(this, arguments);
|
EventEmitter.prototype.on.apply(this, arguments);
|
||||||
};
|
};
|
||||||
@ -85,9 +80,9 @@ Client.prototype.on = function(type, func) {
|
|||||||
Client.prototype.onRaw = function(type, func) {
|
Client.prototype.onRaw = function(type, func) {
|
||||||
var arg = "raw.";
|
var arg = "raw.";
|
||||||
if(Array.isArray(type)) {
|
if(Array.isArray(type)) {
|
||||||
arg += protocol.packetNames[type[0]][direction][type[1]];
|
arg += packetIndexes.packetNames[type[0]][direction][type[1]];
|
||||||
} else if(typeof type === "number") {
|
} else if(typeof type === "number") {
|
||||||
arg += protocol.packetNames[this.state][direction][type];
|
arg += packetIndexes.packetNames[this.state][direction][type];
|
||||||
} else {
|
} else {
|
||||||
arg += type;
|
arg += type;
|
||||||
}
|
}
|
||||||
@ -136,7 +131,7 @@ Client.prototype.setSocket = function(socket) {
|
|||||||
|
|
||||||
this.deserializer.on('data', (parsed) => {
|
this.deserializer.on('data', (parsed) => {
|
||||||
var packet = parsed.results;
|
var packet = parsed.results;
|
||||||
var packetName = protocol.packetNames[packet.state][this.isServer ? 'toServer' : 'toClient'][packet.id];
|
var packetName = packetIndexes.packetNames[packet.state][this.isServer ? 'toServer' : 'toClient'][packet.id];
|
||||||
this.emit('packet', packet);
|
this.emit('packet', packet);
|
||||||
this.emit(packetName, packet);
|
this.emit(packetName, packet);
|
||||||
this.emit('raw.' + packetName, parsed.buffer, packet.state);
|
this.emit('raw.' + packetName, parsed.buffer, packet.state);
|
||||||
@ -185,8 +180,8 @@ Client.prototype.write = function(packetId, params) {
|
|||||||
packetId = packetId[1];
|
packetId = packetId[1];
|
||||||
}
|
}
|
||||||
if(typeof packetId === "string")
|
if(typeof packetId === "string")
|
||||||
packetId = protocol.packetIds[this.state][this.isServer ? "toClient" : "toServer"][packetId];
|
packetId = packetIndexes.packetIds[this.state][this.isServer ? "toClient" : "toServer"][packetId];
|
||||||
var packetName = protocol.packetNames[this.state][this.isServer ? "toClient" : "toServer"][packetId];
|
var packetName = packetIndexes.packetNames[this.state][this.isServer ? "toClient" : "toServer"][packetId];
|
||||||
debug("writing packetId " + this.state + "." + packetName + " (0x" + packetId.toString(16) + ")");
|
debug("writing packetId " + this.state + "." + packetName + " (0x" + packetId.toString(16) + ")");
|
||||||
debug(params);
|
debug(params);
|
||||||
this.serializer.write({ packetId, params });
|
this.serializer.write({ packetId, params });
|
||||||
|
31
src/index.js
31
src/index.js
@ -1,7 +1,6 @@
|
|||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
, crypto = require('crypto')
|
, crypto = require('crypto')
|
||||||
, bufferEqual = require('buffer-equal')
|
, bufferEqual = require('buffer-equal')
|
||||||
, protocol = require('./protocol')
|
|
||||||
, Client = require('./client')
|
, Client = require('./client')
|
||||||
, dns = require('dns')
|
, dns = require('dns')
|
||||||
, net = require('net')
|
, net = require('net')
|
||||||
@ -10,8 +9,10 @@ var assert = require('assert')
|
|||||||
, getSession = Yggdrasil.getSession
|
, getSession = Yggdrasil.getSession
|
||||||
, validateSession = Yggdrasil.validateSession
|
, validateSession = Yggdrasil.validateSession
|
||||||
, joinServer = Yggdrasil.joinServer
|
, joinServer = Yggdrasil.joinServer
|
||||||
, states = protocol.states
|
, serializer = require("./transforms/serializer")
|
||||||
|
, states = serializer.states
|
||||||
, debug = require("./debug")
|
, debug = require("./debug")
|
||||||
|
, utils = require("./utils")
|
||||||
;
|
;
|
||||||
|
|
||||||
var ursa;
|
var ursa;
|
||||||
@ -23,14 +24,32 @@ try {
|
|||||||
ursa = require("ursa-purejs");
|
ursa = require("ursa-purejs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var version = 47;
|
||||||
|
var minecraftVersion = '1.8.1';
|
||||||
|
|
||||||
|
var packets = require("../protocol/protocol");
|
||||||
|
var readPackets = require("./packets").readPackets;
|
||||||
|
var packetIndexes = readPackets(packets, states);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createClient: createClient,
|
createClient: createClient,
|
||||||
createServer: createServer,
|
createServer: createServer,
|
||||||
Client: Client,
|
Client: Client,
|
||||||
Server: Server,
|
Server: Server,
|
||||||
|
states: states,
|
||||||
|
createPacketBuffer: serializer.createPacketBuffer,
|
||||||
|
parsePacketData: serializer.parsePacketData,
|
||||||
|
packetFields: packetIndexes.packetFields,
|
||||||
|
packetNames: packetIndexes.packetNames,
|
||||||
|
packetIds: packetIndexes.packetIds,
|
||||||
|
packetStates: packetIndexes.packetStates,
|
||||||
|
types: serializer.types,
|
||||||
|
get: serializer.get,
|
||||||
|
evalCondition: utils.evalCondition,
|
||||||
ping: require('./ping'),
|
ping: require('./ping'),
|
||||||
protocol: protocol,
|
|
||||||
yggdrasil: Yggdrasil,
|
yggdrasil: Yggdrasil,
|
||||||
|
version: version,
|
||||||
|
minecraftVersion: minecraftVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
function createServer(options) {
|
function createServer(options) {
|
||||||
@ -108,8 +127,8 @@ function createServer(options) {
|
|||||||
function onPing(packet) {
|
function onPing(packet) {
|
||||||
var response = {
|
var response = {
|
||||||
"version": {
|
"version": {
|
||||||
"name": protocol.minecraftVersion,
|
"name": minecraftVersion,
|
||||||
"protocol": protocol.version
|
"protocol": version
|
||||||
},
|
},
|
||||||
"players": {
|
"players": {
|
||||||
"max": server.maxPlayers,
|
"max": server.maxPlayers,
|
||||||
@ -291,7 +310,7 @@ function createClient(options) {
|
|||||||
|
|
||||||
function onConnect() {
|
function onConnect() {
|
||||||
client.write(0x00, {
|
client.write(0x00, {
|
||||||
protocolVersion: protocol.version,
|
protocolVersion: version,
|
||||||
serverHost: host,
|
serverHost: host,
|
||||||
serverPort: port,
|
serverPort: port,
|
||||||
nextState: 2
|
nextState: 2
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
var net = require('net')
|
var net = require('net')
|
||||||
, Client = require('./client')
|
, Client = require('./client')
|
||||||
, protocol = require('./protocol')
|
, states = require('./transforms/serializer').states
|
||||||
, states = protocol.states
|
|
||||||
;
|
;
|
||||||
|
|
||||||
module.exports = ping;
|
module.exports = ping;
|
||||||
|
164
src/protocol.js
164
src/protocol.js
@ -1,26 +1,3 @@
|
|||||||
var assert = require('assert');
|
|
||||||
var zlib = require('zlib');
|
|
||||||
|
|
||||||
var evalCondition = require("./utils").evalCondition;
|
|
||||||
var readPackets = require("./packets").readPackets;
|
|
||||||
var debug = require("./debug");
|
|
||||||
|
|
||||||
|
|
||||||
// This is really just for the client.
|
|
||||||
var states = {
|
|
||||||
"HANDSHAKING": "handshaking",
|
|
||||||
"STATUS": "status",
|
|
||||||
"LOGIN": "login",
|
|
||||||
"PLAY": "play"
|
|
||||||
};
|
|
||||||
var packets = require("../protocol/protocol");
|
|
||||||
var packetIndexes = readPackets(packets, states);
|
|
||||||
|
|
||||||
var packetFields = packetIndexes.packetFields;
|
|
||||||
var packetNames = packetIndexes.packetNames;
|
|
||||||
var packetIds = packetIndexes.packetIds;
|
|
||||||
var packetStates = packetIndexes.packetStates;
|
|
||||||
|
|
||||||
function NMProtocols() {
|
function NMProtocols() {
|
||||||
this.types = {};
|
this.types = {};
|
||||||
}
|
}
|
||||||
@ -74,144 +51,5 @@ NMProtocols.prototype.sizeOf = function(value, fieldInfo, rootNode) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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 proto = new NMProtocols();
|
module.exports = NMProtocols;
|
||||||
proto.addTypes(numeric);
|
|
||||||
proto.addTypes(utils);
|
|
||||||
proto.addTypes(minecraft);
|
|
||||||
proto.addTypes(structures);
|
|
||||||
proto.addTypes(conditional);
|
|
||||||
|
|
||||||
function get(packetId, state, toServer) {
|
|
||||||
var direction = toServer ? "toServer" : "toClient";
|
|
||||||
var packetInfo = packetFields[state][direction][packetId];
|
|
||||||
if(!packetInfo) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return packetInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO : This does NOT contain the length prefix anymore.
|
|
||||||
function createPacketBuffer(packetId, state, params, isServer) {
|
|
||||||
var length = 0;
|
|
||||||
if(typeof packetId === 'string' && typeof state !== 'string' && !params) {
|
|
||||||
// simplified two-argument usage, createPacketBuffer(name, params)
|
|
||||||
params = state;
|
|
||||||
state = packetStates[!isServer ? 'toServer' : 'toClient'][packetId];
|
|
||||||
}
|
|
||||||
if(typeof packetId === 'string') packetId = packetIds[state][!isServer ? 'toServer' : 'toClient'][packetId];
|
|
||||||
assert.notEqual(packetId, undefined);
|
|
||||||
|
|
||||||
var packet = get(packetId, state, !isServer);
|
|
||||||
assert.notEqual(packet, null);
|
|
||||||
packet.forEach(function(fieldInfo) {
|
|
||||||
try {
|
|
||||||
length += proto.sizeOf(params[fieldInfo.name], fieldInfo, params);
|
|
||||||
} catch(e) {
|
|
||||||
console.log("fieldInfo : " + JSON.stringify(fieldInfo));
|
|
||||||
console.log("params : " + JSON.stringify(params));
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
length += utils.varint[2](packetId);
|
|
||||||
var size = length;// + utils.varint[2](length);
|
|
||||||
var buffer = new Buffer(size);
|
|
||||||
var offset = 0;//utils.varint[1](length, buffer, 0);
|
|
||||||
offset = utils.varint[1](packetId, buffer, offset);
|
|
||||||
packet.forEach(function(fieldInfo) {
|
|
||||||
var value = params[fieldInfo.name];
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 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
|
|
||||||
var name = packetNames[state][isServer ? "toServer" : "toClient"][packetId];
|
|
||||||
var shouldParse = (!packetsToParse.hasOwnProperty(name) || packetsToParse[name] <= 0)
|
|
||||||
&& (!packetsToParse.hasOwnProperty("packet") || packetsToParse["packet"] <= 0);
|
|
||||||
if(shouldParse) {
|
|
||||||
return {
|
|
||||||
buffer: buffer,
|
|
||||||
results: results
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var packetInfo = get(packetId, state, isServer);
|
|
||||||
if(packetInfo === null) {
|
|
||||||
return {
|
|
||||||
error: new Error("Unrecognized packetId: " + packetId + " (0x" + packetId.toString(16) + ")"),
|
|
||||||
buffer: buffer,
|
|
||||||
results: results
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
var packetName = packetNames[state][isServer ? "toServer" : "toClient"][packetId];
|
|
||||||
debug("read packetId " + state + "." + packetName + " (0x" + packetId.toString(16) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
var i, fieldInfo, readResults;
|
|
||||||
for(i = 0; i < packetInfo.length; ++i) {
|
|
||||||
fieldInfo = packetInfo[i];
|
|
||||||
readResults = proto.read(buffer, cursor, fieldInfo, results);
|
|
||||||
/* A deserializer cannot return null anymore. Besides, proto.read() returns
|
|
||||||
* null when the condition is not fulfilled.
|
|
||||||
if (!!!readResults) {
|
|
||||||
var error = new Error("A deserializer returned null");
|
|
||||||
error.packetId = packetId;
|
|
||||||
error.fieldInfo = fieldInfo.name;
|
|
||||||
return {
|
|
||||||
size: length + lengthField.size,
|
|
||||||
error: error,
|
|
||||||
results: results
|
|
||||||
};
|
|
||||||
}*/
|
|
||||||
// TODO : investigate readResults returning null : shouldn't happen.
|
|
||||||
// When there is not enough data to read, we should return an error.
|
|
||||||
// As a general rule, it would be a good idea to introduce a whole bunch
|
|
||||||
// of new error classes to differenciate the errors.
|
|
||||||
if(readResults === null || readResults.value == null) continue;
|
|
||||||
if(readResults.error) {
|
|
||||||
return readResults;
|
|
||||||
}
|
|
||||||
results[fieldInfo.name] = readResults.value;
|
|
||||||
cursor += readResults.size;
|
|
||||||
}
|
|
||||||
if(buffer.length > cursor)
|
|
||||||
debug("Too much data to read for packetId: " + packetId + " (0x" + packetId.toString(16) + ")");
|
|
||||||
debug(results);
|
|
||||||
return {
|
|
||||||
results: results,
|
|
||||||
buffer: buffer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
version: 47,
|
|
||||||
minecraftVersion: '1.8.1',
|
|
||||||
sessionVersion: 13,
|
|
||||||
parsePacketData: parsePacketData,
|
|
||||||
createPacketBuffer: createPacketBuffer,
|
|
||||||
packetIds: packetIds,
|
|
||||||
packetNames: packetNames,
|
|
||||||
packetFields: packetFields,
|
|
||||||
packetStates: packetStates,
|
|
||||||
types: proto.types,
|
|
||||||
states: states,
|
|
||||||
get: get,
|
|
||||||
evalCondition: evalCondition
|
|
||||||
};
|
|
||||||
|
@ -2,7 +2,7 @@ var net = require('net')
|
|||||||
, EventEmitter = require('events').EventEmitter
|
, EventEmitter = require('events').EventEmitter
|
||||||
, util = require('util')
|
, util = require('util')
|
||||||
, Client = require('./client')
|
, Client = require('./client')
|
||||||
, states = require('./protocol').states
|
, states = require('./transforms/serializer').states
|
||||||
;
|
;
|
||||||
|
|
||||||
module.exports = Server;
|
module.exports = Server;
|
||||||
|
@ -1,17 +1,179 @@
|
|||||||
var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint;
|
var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint;
|
||||||
var protocol = require("../protocol");
|
var protocol = require("../protocol");
|
||||||
var Transform = require("readable-stream").Transform;
|
var Transform = require("readable-stream").Transform;
|
||||||
|
var debug = require("../debug");
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
module.exports.createSerializer = function(obj) {
|
module.exports.createSerializer = function(obj) {
|
||||||
return new Serializer(obj);
|
return new Serializer(obj);
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports.createDeserializer = function(obj) {
|
module.exports.createDeserializer = function(obj) {
|
||||||
return new Deserializer(obj);
|
return new Deserializer(obj);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.createPacketBuffer=createPacketBuffer;
|
||||||
|
module.exports.parsePacketData=parsePacketData;
|
||||||
|
|
||||||
|
// This is really just for the client.
|
||||||
|
var states = {
|
||||||
|
"HANDSHAKING": "handshaking",
|
||||||
|
"STATUS": "status",
|
||||||
|
"LOGIN": "login",
|
||||||
|
"PLAY": "play"
|
||||||
|
};
|
||||||
|
module.exports.states = states;
|
||||||
|
|
||||||
|
module.exports.get = get;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 proto = new NMProtocols();
|
||||||
|
proto.addTypes(numeric);
|
||||||
|
proto.addTypes(utils);
|
||||||
|
proto.addTypes(minecraft);
|
||||||
|
proto.addTypes(structures);
|
||||||
|
proto.addTypes(conditional);
|
||||||
|
|
||||||
|
|
||||||
|
module.exports.types = proto.types;
|
||||||
|
|
||||||
|
var evalCondition = require("../utils").evalCondition;
|
||||||
|
var packets = require("../../protocol/protocol");
|
||||||
|
var readPackets = require("../packets").readPackets;
|
||||||
|
var packetIndexes = readPackets(packets, states);
|
||||||
|
|
||||||
|
var packetFields = packetIndexes.packetFields;
|
||||||
|
var packetNames = packetIndexes.packetNames;
|
||||||
|
var packetIds = packetIndexes.packetIds;
|
||||||
|
var packetStates = packetIndexes.packetStates;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO : This does NOT contain the length prefix anymore.
|
||||||
|
function createPacketBuffer(packetId, state, params, isServer) {
|
||||||
|
var length = 0;
|
||||||
|
if(typeof packetId === 'string' && typeof state !== 'string' && !params) {
|
||||||
|
// simplified two-argument usage, createPacketBuffer(name, params)
|
||||||
|
params = state;
|
||||||
|
state = packetStates[!isServer ? 'toServer' : 'toClient'][packetId];
|
||||||
|
}
|
||||||
|
if(typeof packetId === 'string') packetId = packetIds[state][!isServer ? 'toServer' : 'toClient'][packetId];
|
||||||
|
assert.notEqual(packetId, undefined);
|
||||||
|
|
||||||
|
var packet = get(packetId, state, !isServer);
|
||||||
|
assert.notEqual(packet, null);
|
||||||
|
packet.forEach(function(fieldInfo) {
|
||||||
|
try {
|
||||||
|
length += proto.sizeOf(params[fieldInfo.name], fieldInfo, params);
|
||||||
|
} catch(e) {
|
||||||
|
console.log("fieldInfo : " + JSON.stringify(fieldInfo));
|
||||||
|
console.log("params : " + JSON.stringify(params));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
length += utils.varint[2](packetId);
|
||||||
|
var size = length;// + utils.varint[2](length);
|
||||||
|
var buffer = new Buffer(size);
|
||||||
|
var offset = 0;//utils.varint[1](length, buffer, 0);
|
||||||
|
offset = utils.varint[1](packetId, buffer, offset);
|
||||||
|
packet.forEach(function(fieldInfo) {
|
||||||
|
var value = params[fieldInfo.name];
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get(packetId, state, toServer) {
|
||||||
|
var direction = toServer ? "toServer" : "toClient";
|
||||||
|
var packetInfo = packetFields[state][direction][packetId];
|
||||||
|
if(!packetInfo) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return packetInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 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
|
||||||
|
var name = packetNames[state][isServer ? "toServer" : "toClient"][packetId];
|
||||||
|
var shouldParse = (!packetsToParse.hasOwnProperty(name) || packetsToParse[name] <= 0)
|
||||||
|
&& (!packetsToParse.hasOwnProperty("packet") || packetsToParse["packet"] <= 0);
|
||||||
|
if(shouldParse) {
|
||||||
|
return {
|
||||||
|
buffer: buffer,
|
||||||
|
results: results
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var packetInfo = get(packetId, state, isServer);
|
||||||
|
if(packetInfo === null) {
|
||||||
|
return {
|
||||||
|
error: new Error("Unrecognized packetId: " + packetId + " (0x" + packetId.toString(16) + ")"),
|
||||||
|
buffer: buffer,
|
||||||
|
results: results
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var packetName = packetNames[state][isServer ? "toServer" : "toClient"][packetId];
|
||||||
|
debug("read packetId " + state + "." + packetName + " (0x" + packetId.toString(16) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
var i, fieldInfo, readResults;
|
||||||
|
for(i = 0; i < packetInfo.length; ++i) {
|
||||||
|
fieldInfo = packetInfo[i];
|
||||||
|
readResults = proto.read(buffer, cursor, fieldInfo, results);
|
||||||
|
/* A deserializer cannot return null anymore. Besides, proto.read() returns
|
||||||
|
* null when the condition is not fulfilled.
|
||||||
|
if (!!!readResults) {
|
||||||
|
var error = new Error("A deserializer returned null");
|
||||||
|
error.packetId = packetId;
|
||||||
|
error.fieldInfo = fieldInfo.name;
|
||||||
|
return {
|
||||||
|
size: length + lengthField.size,
|
||||||
|
error: error,
|
||||||
|
results: results
|
||||||
|
};
|
||||||
|
}*/
|
||||||
|
// TODO : investigate readResults returning null : shouldn't happen.
|
||||||
|
// When there is not enough data to read, we should return an error.
|
||||||
|
// As a general rule, it would be a good idea to introduce a whole bunch
|
||||||
|
// of new error classes to differenciate the errors.
|
||||||
|
if(readResults === null || readResults.value == null) continue;
|
||||||
|
if(readResults.error) {
|
||||||
|
return readResults;
|
||||||
|
}
|
||||||
|
results[fieldInfo.name] = readResults.value;
|
||||||
|
cursor += readResults.size;
|
||||||
|
}
|
||||||
|
if(buffer.length > cursor)
|
||||||
|
debug("Too much data to read for packetId: " + packetId + " (0x" + packetId.toString(16) + ")");
|
||||||
|
debug(results);
|
||||||
|
return {
|
||||||
|
results: results,
|
||||||
|
buffer: buffer
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class Serializer extends Transform {
|
class Serializer extends Transform {
|
||||||
constructor({ state = protocol.states.HANDSHAKING, isServer = false } = {}) {
|
constructor({ state = states.HANDSHAKING, isServer = false } = {}) {
|
||||||
super({ writableObjectMode: true });
|
super({ writableObjectMode: true });
|
||||||
this.protocolState = state;
|
this.protocolState = state;
|
||||||
this.isServer = isServer;
|
this.isServer = isServer;
|
||||||
@ -20,7 +182,7 @@ class Serializer extends Transform {
|
|||||||
// TODO : Might make sense to make createPacketBuffer async.
|
// TODO : Might make sense to make createPacketBuffer async.
|
||||||
_transform(chunk, enc, cb) {
|
_transform(chunk, enc, cb) {
|
||||||
try {
|
try {
|
||||||
var buf = protocol.createPacketBuffer(chunk.packetId, this.protocolState, chunk.params, this.isServer);
|
var buf = createPacketBuffer(chunk.packetId, this.protocolState, chunk.params, this.isServer);
|
||||||
this.push(buf);
|
this.push(buf);
|
||||||
return cb();
|
return cb();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -30,7 +192,7 @@ class Serializer extends Transform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Deserializer extends Transform {
|
class Deserializer extends Transform {
|
||||||
constructor({ state = protocol.states.HANDSHAKING, isServer = false, packetsToParse = {"packet": true} } = {}) {
|
constructor({ state = states.HANDSHAKING, isServer = false, packetsToParse = {"packet": true} } = {}) {
|
||||||
super({ readableObjectMode: true });
|
super({ readableObjectMode: true });
|
||||||
this.protocolState = state;
|
this.protocolState = state;
|
||||||
this.isServer = isServer;
|
this.isServer = isServer;
|
||||||
@ -40,7 +202,7 @@ class Deserializer extends Transform {
|
|||||||
_transform(chunk, enc, cb) {
|
_transform(chunk, enc, cb) {
|
||||||
var packet;
|
var packet;
|
||||||
try {
|
try {
|
||||||
packet = protocol.parsePacketData(chunk, this.protocolState, this.isServer, this.packetsToParse);
|
packet = parsePacketData(chunk, this.protocolState, this.isServer, this.packetsToParse);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return cb(e);
|
return cb(e);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
var ITERATIONS = 100000;
|
var ITERATIONS = 100000;
|
||||||
|
|
||||||
var protocol = require('../dist/protocol'),
|
var mc = require("../");
|
||||||
util = require('util'),
|
util = require('util'),
|
||||||
states = protocol.states;
|
states = mc.states;
|
||||||
|
|
||||||
var testDataWrite = [
|
var testDataWrite = [
|
||||||
{id: 0x00, params: {keepAliveId: 957759560}},
|
{id: 0x00, params: {keepAliveId: 957759560}},
|
||||||
@ -18,7 +18,7 @@ console.log('Beginning write test');
|
|||||||
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(protocol.createPacketBuffer(testDataWrite[j].id, states.PLAY, testDataWrite[j].params, false));
|
inputData.push(mc.createPacketBuffer(testDataWrite[j].id, states.PLAY, testDataWrite[j].params, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log('Finished write test in ' + (Date.now() - start) / 1000 + ' seconds');
|
console.log('Finished write test in ' + (Date.now() - start) / 1000 + ' seconds');
|
||||||
@ -26,6 +26,6 @@ console.log('Finished write test in ' + (Date.now() - start) / 1000 + ' seconds'
|
|||||||
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++) {
|
||||||
protocol.parsePacketData(inputData[j], states.PLAY, true);
|
mc.parsePacketData(inputData[j], states.PLAY, true);
|
||||||
}
|
}
|
||||||
console.log('Finished read test in ' + (Date.now() - start) / 1000 + ' seconds');
|
console.log('Finished read test in ' + (Date.now() - start) / 1000 + ' seconds');
|
||||||
|
21
test/test.js
21
test/test.js
@ -1,6 +1,5 @@
|
|||||||
var mc = require('../')
|
var mc = require('../')
|
||||||
, protocol = mc.protocol
|
, states = mc.states
|
||||||
, states = protocol.states
|
|
||||||
, Client = mc.Client
|
, Client = mc.Client
|
||||||
, Server = mc.Server
|
, Server = mc.Server
|
||||||
, spawn = require('child_process').spawn
|
, spawn = require('child_process').spawn
|
||||||
@ -152,19 +151,19 @@ describe("packets", function() {
|
|||||||
client.end();
|
client.end();
|
||||||
});
|
});
|
||||||
var packetId, packetInfo, field;
|
var packetId, packetInfo, field;
|
||||||
for(state in protocol.packetFields) {
|
for(state in mc.packetFields) {
|
||||||
if(!protocol.packetFields.hasOwnProperty(state)) continue;
|
if(!mc.packetFields.hasOwnProperty(state)) continue;
|
||||||
for(packetId in protocol.packetFields[state].toServer) {
|
for(packetId in mc.packetFields[state].toServer) {
|
||||||
if(!protocol.packetFields[state].toServer.hasOwnProperty(packetId)) continue;
|
if(!mc.packetFields[state].toServer.hasOwnProperty(packetId)) continue;
|
||||||
packetId = parseInt(packetId, 10);
|
packetId = parseInt(packetId, 10);
|
||||||
packetInfo = protocol.get(packetId, state, true);
|
packetInfo = mc.get(packetId, state, true);
|
||||||
it(state + ",ServerBound,0x" + zfill(parseInt(packetId, 10).toString(16), 2),
|
it(state + ",ServerBound,0x" + zfill(parseInt(packetId, 10).toString(16), 2),
|
||||||
callTestPacket(packetId, packetInfo, state, true));
|
callTestPacket(packetId, packetInfo, state, true));
|
||||||
}
|
}
|
||||||
for(packetId in protocol.packetFields[state].toClient) {
|
for(packetId in mc.packetFields[state].toClient) {
|
||||||
if(!protocol.packetFields[state].toClient.hasOwnProperty(packetId)) continue;
|
if(!mc.packetFields[state].toClient.hasOwnProperty(packetId)) continue;
|
||||||
packetId = parseInt(packetId, 10);
|
packetId = parseInt(packetId, 10);
|
||||||
packetInfo = protocol.get(packetId, state, false);
|
packetInfo = mc.get(packetId, state, false);
|
||||||
it(state + ",ClientBound,0x" + zfill(parseInt(packetId, 10).toString(16), 2),
|
it(state + ",ClientBound,0x" + zfill(parseInt(packetId, 10).toString(16), 2),
|
||||||
callTestPacket(packetId, packetInfo, state, false));
|
callTestPacket(packetId, packetInfo, state, false));
|
||||||
}
|
}
|
||||||
@ -181,7 +180,7 @@ 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" || protocol.evalCondition(field.typeArgs, packet)) {
|
if(field.type !== "condition" || mc.evalCondition(field.typeArgs, packet)) {
|
||||||
var fieldVal = values[field.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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user