2015-03-29 02:24:23 -04:00
|
|
|
var EventEmitter = require('events').EventEmitter
|
2013-01-04 01:45:57 -05:00
|
|
|
, util = require('util')
|
2015-05-13 08:47:28 -04:00
|
|
|
, debug = require('./debug')
|
2015-05-21 16:35:03 -04:00
|
|
|
, serializer = require('./transforms/serializer')
|
|
|
|
, compression = require('./transforms/compression')
|
|
|
|
, framing = require('./transforms/framing')
|
|
|
|
, crypto = require('crypto')
|
2015-05-22 21:31:47 -04:00
|
|
|
, states = serializer.states
|
2015-05-14 16:08:49 -04:00
|
|
|
;
|
2015-08-21 09:44:06 -04:00
|
|
|
var version = require('./version');
|
2015-08-27 07:36:07 -04:00
|
|
|
var packets = require('minecraft-data')(version.majorVersion).protocol.states;
|
2015-05-22 21:31:47 -04:00
|
|
|
var readPackets = require("./packets").readPackets;
|
|
|
|
var packetIndexes = readPackets(packets, states);
|
|
|
|
|
2013-01-04 01:45:57 -05:00
|
|
|
module.exports = Client;
|
|
|
|
|
2013-01-07 23:36:14 -05:00
|
|
|
function Client(isServer) {
|
2013-01-04 01:45:57 -05:00
|
|
|
EventEmitter.call(this);
|
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
var socket;
|
2015-05-22 18:25:12 -04:00
|
|
|
this.packetsToParse = {};
|
2015-05-21 16:35:03 -04:00
|
|
|
|
|
|
|
this.serializer = serializer.createSerializer({ isServer });
|
|
|
|
this.compressor = null;
|
|
|
|
this.framer = framing.createFramer();
|
|
|
|
this.cipher = null;
|
|
|
|
|
|
|
|
this.decipher = null;
|
|
|
|
this.splitter = framing.createSplitter();
|
|
|
|
this.decompressor = null;
|
2015-05-22 18:25:12 -04:00
|
|
|
this.deserializer = serializer.createDeserializer({ isServer, packetsToParse: this.packetsToParse });
|
2015-05-21 16:35:03 -04:00
|
|
|
|
2013-12-30 10:05:22 -05:00
|
|
|
this._state = states.HANDSHAKING;
|
|
|
|
Object.defineProperty(this, "state", {
|
|
|
|
get: function() {
|
2015-05-21 16:35:03 -04:00
|
|
|
return this.serializer.protocolState;
|
2013-12-30 10:05:22 -05:00
|
|
|
},
|
|
|
|
set: function(newProperty) {
|
2015-05-21 16:35:03 -04:00
|
|
|
var oldProperty = this.serializer.protocolState;
|
|
|
|
this.serializer.protocolState = newProperty;
|
|
|
|
this.deserializer.protocolState = newProperty;
|
2013-12-30 10:05:22 -05:00
|
|
|
this.emit('state', newProperty, oldProperty);
|
|
|
|
}
|
|
|
|
});
|
2015-05-21 16:35:03 -04:00
|
|
|
Object.defineProperty(this, "compressionThreshold", {
|
|
|
|
get: () => this.compressor == null ? -2 : this.compressor.compressionThreshold,
|
|
|
|
set: (threshold) => this.setCompressionThreshold(threshold)
|
|
|
|
});
|
|
|
|
|
2013-01-07 23:36:14 -05:00
|
|
|
this.isServer = !!isServer;
|
2015-05-21 16:35:03 -04:00
|
|
|
|
2014-04-10 19:48:06 -04:00
|
|
|
this.on('newListener', function(event, listener) {
|
|
|
|
var direction = this.isServer ? 'toServer' : 'toClient';
|
2015-05-22 21:31:47 -04:00
|
|
|
if(packetIndexes.packetStates[direction].hasOwnProperty(event) || event === "packet") {
|
2015-05-14 16:08:49 -04:00
|
|
|
if(typeof this.packetsToParse[event] === "undefined") this.packetsToParse[event] = 1;
|
2014-04-10 19:48:06 -04:00
|
|
|
else this.packetsToParse[event] += 1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.on('removeListener', function(event, listener) {
|
|
|
|
var direction = this.isServer ? 'toServer' : 'toClient';
|
2015-05-22 21:31:47 -04:00
|
|
|
if(packetIndexes.packetStates[direction].hasOwnProperty(event) || event === "packet") {
|
2014-04-10 19:48:06 -04:00
|
|
|
this.packetsToParse[event] -= 1;
|
|
|
|
}
|
|
|
|
});
|
2013-01-04 01:45:57 -05:00
|
|
|
}
|
2014-04-10 19:48:06 -04:00
|
|
|
|
2013-01-04 01:45:57 -05:00
|
|
|
util.inherits(Client, EventEmitter);
|
|
|
|
|
|
|
|
Client.prototype.setSocket = function(socket) {
|
2015-05-21 16:35:03 -04:00
|
|
|
var ended = false;
|
2015-05-14 16:08:49 -04:00
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
// TODO : A lot of other things needs to be done.
|
|
|
|
var endSocket = () => {
|
|
|
|
if(ended) return;
|
|
|
|
ended = true;
|
|
|
|
this.socket.removeListener('close', endSocket);
|
|
|
|
this.socket.removeListener('end', endSocket);
|
|
|
|
this.socket.removeListener('timeout', endSocket);
|
|
|
|
this.emit('end', this._endReason);
|
|
|
|
};
|
2015-02-19 15:36:37 -05:00
|
|
|
|
2015-05-22 19:43:32 -04:00
|
|
|
var onFatalError = (err) => {
|
2015-05-21 16:35:03 -04:00
|
|
|
this.emit('error', err);
|
|
|
|
endSocket();
|
|
|
|
};
|
2015-02-19 15:36:37 -05:00
|
|
|
|
2015-05-22 19:43:32 -04:00
|
|
|
var onError = (err) => this.emit('error', err);
|
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
this.socket = socket;
|
2013-01-04 01:45:57 -05:00
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
if(this.socket.setNoDelay)
|
|
|
|
this.socket.setNoDelay(true);
|
2013-02-03 15:36:55 -05:00
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
this.socket.on('connect', () => this.emit('connect'));
|
2013-02-03 15:36:55 -05:00
|
|
|
|
2015-05-22 19:43:32 -04:00
|
|
|
this.socket.on('error', onFatalError);
|
2015-05-21 16:35:03 -04:00
|
|
|
this.socket.on('close', endSocket);
|
|
|
|
this.socket.on('end', endSocket);
|
|
|
|
this.socket.on('timeout', endSocket);
|
2015-05-22 19:43:32 -04:00
|
|
|
this.serializer.on('error', onError);
|
|
|
|
this.deserializer.on('error', onError);
|
|
|
|
this.framer.on('error', onError);
|
|
|
|
this.splitter.on('error', onError);
|
2013-02-03 15:36:55 -05:00
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
this.socket.pipe(this.splitter).pipe(this.deserializer);
|
|
|
|
this.serializer.pipe(this.framer).pipe(this.socket);
|
2015-05-14 16:08:49 -04:00
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
this.deserializer.on('data', (parsed) => {
|
2015-09-19 18:54:19 -04:00
|
|
|
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);
|
2015-05-21 16:35:03 -04:00
|
|
|
});
|
2013-01-04 01:45:57 -05:00
|
|
|
};
|
|
|
|
|
2013-01-04 20:55:53 -05:00
|
|
|
Client.prototype.end = function(reason) {
|
|
|
|
this._endReason = reason;
|
2013-01-04 01:45:57 -05:00
|
|
|
this.socket.end();
|
|
|
|
};
|
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
Client.prototype.setEncryption = function(sharedSecret) {
|
|
|
|
if (this.cipher != null)
|
|
|
|
throw new Error("Set encryption twice !");
|
|
|
|
this.cipher = crypto.createCipheriv('aes-128-cfb8', sharedSecret, sharedSecret);
|
2015-05-22 19:43:32 -04:00
|
|
|
this.cipher.on('error', (err) => this.emit('error', err));
|
2015-05-21 16:35:03 -04:00
|
|
|
this.framer.unpipe(this.socket);
|
|
|
|
this.framer.pipe(this.cipher).pipe(this.socket);
|
|
|
|
this.decipher = crypto.createDecipheriv('aes-128-cfb8', sharedSecret, sharedSecret);
|
2015-05-22 19:43:32 -04:00
|
|
|
this.decipher.on('error', (err) => this.emit('error', err));
|
2015-05-21 16:35:03 -04:00
|
|
|
this.socket.unpipe(this.splitter);
|
|
|
|
this.socket.pipe(this.decipher).pipe(this.splitter);
|
2015-05-18 13:01:23 -04:00
|
|
|
}
|
|
|
|
|
2015-05-21 16:35:03 -04:00
|
|
|
Client.prototype.setCompressionThreshold = function(threshold) {
|
|
|
|
if (this.compressor == null) {
|
|
|
|
this.compressor = compression.createCompressor(threshold);
|
2015-05-22 19:43:32 -04:00
|
|
|
this.compressor.on('error', (err) => this.emit('error', err));
|
2015-05-21 16:35:03 -04:00
|
|
|
this.serializer.unpipe(this.framer);
|
|
|
|
this.serializer.pipe(this.compressor).pipe(this.framer);
|
|
|
|
this.decompressor = compression.createDecompressor(threshold);
|
2015-05-22 19:43:32 -04:00
|
|
|
this.decompressor.on('error', (err) => this.emit('error', err));
|
2015-05-21 16:35:03 -04:00
|
|
|
this.splitter.unpipe(this.deserializer);
|
|
|
|
this.splitter.pipe(this.decompressor).pipe(this.deserializer);
|
2014-12-30 03:55:04 -05:00
|
|
|
} else {
|
2015-05-21 16:35:03 -04:00
|
|
|
this.decompressor.threshold = threshold;
|
|
|
|
this.compressor.threshold = threshold;
|
2014-12-30 03:55:04 -05:00
|
|
|
}
|
2015-05-21 16:35:03 -04:00
|
|
|
}
|
|
|
|
|
2015-09-19 18:54:19 -04:00
|
|
|
Client.prototype.write = function(packetName, params) {
|
|
|
|
debug("writing packet " + this.state + "." + packetName);
|
2015-05-21 16:35:03 -04:00
|
|
|
debug(params);
|
2015-09-19 18:54:19 -04:00
|
|
|
this.serializer.write({ packetName, params });
|
2013-01-04 01:45:57 -05:00
|
|
|
};
|
2014-04-10 19:48:06 -04:00
|
|
|
|
2015-03-02 21:01:04 -05:00
|
|
|
Client.prototype.writeRaw = function(buffer) {
|
2015-05-22 15:22:21 -04:00
|
|
|
if (this.compressor === null)
|
|
|
|
this.framer.write(buffer);
|
|
|
|
else
|
|
|
|
this.compressor.write(buffer);
|
2014-10-28 19:30:33 -04:00
|
|
|
};
|