UNPKG

minecraft.js

Version:

Minecraft data serialization/deserialization and networking

205 lines (170 loc) 6.12 kB
var net = require('net'), events = require('events'), util = require('util'); var Packet = require(__dirname + '/packet.js'), protocol = require(__dirname + '/protocol.js'), items = require(__dirname + '/../items.js'), Item = require(__dirname + '/../item.js'); /** @constructor */ var Socket = module.exports = function() { events.EventEmitter.call(this); this._connected = false; this._writeQueue = []; this._writeLock = false; this._isServer = false; this._readQueue = []; this._readAppend = false; this._readLock = false; this._receivedKicks = false; this.read = 0; this.written = 0; this.socket = new net.Socket(); this.socket.on('error', function(err) { console.log('error: ' + err); }); this.socket.on('data', function(data) { this.socket.pause(); if(this._readAppend) { this._readQueue[0] = cat(this._readQueue[0], data); this._readAppend = false; } else { this._readQueue.push(data); } this.socket.resume(); if(!this._readLock) { this._readLock = true; var read = function() { if(this._readQueue.length > 0) { if(this._readQueue[0].readUInt8(0) === 0xff && this._readQueue[0].readUInt8(1) === 0xff && this._readQueue[0].readUInt8(2) === 0xff && !this._receivedKicks) { this._receivedKicks = true; this._readQueue.shift(); } else { var packet = this.decode(this._readQueue[0]); if(packet === true) { if(this._readQueue.length > 1) { this.socket.pause(); var head = this._readQueue.shift(); this.socket.resume(); this._readQueue[0] = cat(head, this._readQueue[0]); } else { this._readAppend = true; } } else if(packet === false) { var id = this._readQueue[0].readUInt8(0); this.emit('error', 'Error reading incoming data with id 0x' + id.toString(16) + ', (are we out of sync?)'); this.socket.pause(); this._readQueue.shift(); this.socket.resume(); } else { this.emit('data', packet); this.emit(packet.id, packet.data); this.read += packet._size; if(this._readQueue[0].length > packet._size) { this._readQueue[0] = this._readQueue[0].slice(packet._size); } else { this.socket.pause(); this._readQueue.shift(); this.socket.resume(); } } } process.nextTick(read); } else { this._readLock = false; } }.bind(this); read(); } }.bind(this)); this.socket.on('end', function() { if(this.connected) { this.connected = false; this.emit('end', 'Connection closed by server'); } }.bind(this)); this.socket.on('drain', function() { this._writeLock = false; while(this._writeQueue.length > 0) { this.write(this._writeQueue.shift()); } }.bind(this)); }; util.inherits(Socket, events.EventEmitter); Socket.prototype.write = function(packet, data) { if( typeof data != 'undefined') { var format = protocol.get(packet, !this._isServer); if( typeof format != 'undefined') { this.write(new Packet(packet, data, !this._isServer)); } else { this.error('Invalid packet id (0x' + packet.toString(16) + ')'); } } else { if(this._writeLock) { this._writeQueue.push(packet); } else { if(!this.socket.write(packet.toString('buffer'))) { this._writeLock = true; this._writeQueue.push(packet); } else { this.written += packet.getSize(); } } } return this; }; Socket.prototype.decode = function(buf) { if(typeof buf !== 'undefined' && buf.length > 0) { var id = buf.readUInt8(0); var packet = {}; var format = protocol.get(id, this._isServer); if(typeof format !== 'undefined') { try { buf = buf.slice(1); var size = 1; for(var i = 0; i < format.length; i++) { var unpack = protocol.datatypes[format[i].type.toUpperCase()].unpack(buf); if(unpack === false) { return false; } else { buf = buf.slice(unpack.bytes); size += unpack.bytes; packet[format[i].name] = unpack.value; } } packet = new Packet(id, packet, this._isServer); packet._size = size; return packet; } catch(e) { return true; } } else { return false; } } else { return true; } }; Socket.prototype.connect = function(host, port, callback) { this.socket.connect(port, host, function() { this._connected = true; if(callback) callback(); }.bind(this)); return this; }; Socket.prototype.destroy = function() { this.socket.destroy(); }; Socket.prototype.error = function(message) { this.emit('error', message); return this; }; var cat = function(b1, b2) { var newBuffer = new Buffer(b1.length + b2.length); b1.copy(newBuffer); b2.copy(newBuffer, b1.length); return newBuffer; };