Created
September 13, 2011 18:21
-
-
Save eddyb/1214575 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var pack = require('jspack').jspack, zlib = require('zlib'); | |
(function() { | |
var ev = ''; | |
'byte short int long float double str8 str16 bool mobMetadata optionalItem itemList compressedChunk multiBlock'.split(' ').forEach(function(type) { | |
ev += 'var '+type+' = function '+type+'(n){return ["'+type+'",n||"~"];};'; | |
}); | |
eval(ev); | |
exports.client2server = { | |
0x00: [int('id')], | |
0x01: [int('protoVersion'), str16('username'), long(), int(), byte(), byte(), byte()], | |
0x02: [str16('username')], | |
0x03: [str16('message')], | |
0x05: [int('EID'), short('slot'), short('itemID'), short('dataValue')], | |
0x07: [int('playerEID'), int('targetEID'), bool('leftClick')], | |
0x09: [byte('world'), long('seed')], | |
0x0a: [bool('onGround')], | |
0x0b: [double('x'), double('y'), double('stance'), double('z'), bool('onGround')], | |
0x0c: [float('yaw'), float('pitch'), bool('onGround')], | |
0x0d: [double('x'), double('y'), double('stance'), double('z'), float('yaw'), float('pitch'), bool('onGround')], | |
0x0e: [byte('status'), int('x'), byte('y'), int('z'), byte('face')], | |
0x0f: [int('x'), byte('y'), int('z'), byte('direction'), short('itemID'), byte('count'), short('dataValue')], | |
0x10: [short('slot')], | |
0x12: [int('EID'), byte('animation')], | |
0x13: [int('EID'), byte('action')], | |
0x15: [int('EID'), short('item'), byte('count'), short('dataValue'), int('x'), int('y'), int('z'), byte('yaw'), byte('pitch'), byte('roll')], | |
0x1c: [int('EID'), short('vx'), short('vy'), short('vz')], | |
0x65: [byte('windowID')], | |
0x66: [byte('windowID'), short('slot'), bool('rightClick'), short('actionID'), bool('shift'), short('itemID'), byte('itemCount'), short('itemDataValue')], | |
0x6a: [byte('windowID'), short('actionID'), bool('accepted')], | |
0x82: [int('x'), short('y'), int('z'), str16('line1'), str16('line2'), str16('line3'), str16('line4')], | |
0xfe: [], | |
0xff: [str16('message')], | |
}; | |
exports.server2client = { | |
0x00: [int('id')], | |
0x01: [int('playerEID'), str16(), long('seed'), int('mode'), byte('world'), byte('height'), byte('maxPlayers')], | |
0x02: [str16('hash')], | |
0x03: [str16('message')], | |
0x04: [long('time')], | |
0x05: [int('entityID'), short('slot'), short('itemID'), short('dataVal')], | |
0x06: [int('x'), int('y'), int('z')], | |
0x08: [short('health'), short('food'), float('foodSaturation')], | |
0x09: [byte('world'), long('seed')], | |
0x0d: [double('x'), double('stance'), double('y'), double('z'), float('yaw'), float('pitch'), bool('onGround')], | |
0x0e: [byte('status'), int('x'), byte('y'), int('z'), byte('face')], | |
0x0f: [int('x'), byte('y'), int('z'), byte('direction'), short('itemID'), byte('count'), short('dataValue')], | |
0x10: [int('uid'), short('item')], | |
0x11: [int('EID'), byte(), int('x'), byte('y'), int('z')], | |
0x12: [int('EID'), byte('animation')], | |
0x14: [int('EID'), str16('playerName'), int('x'), int('y'), int('z'), byte('yaw'), byte('pitch'), short('itemHolding')], | |
0x15: [int('EID'), short('item'), byte('count'), short('dataValue'), int('x'), int('y'), int('z'), byte('yaw'), byte('pitch'), byte('roll')], | |
0x16: [int('collectedID'), int('collectorID')], | |
0x17: [int('EID'), byte('objType'), int('x'), int('y'), int('z'), int(), short(), short(), short()], | |
0x18: [int('EID'), byte('mobType'), int('x'), int('y'), int('z'), byte('rotation'), byte('pitch'), mobMetadata('metadata')], | |
0x19: [int('EID'), str16('title'), int('x'), int('y'), int('z'), int('direction')], | |
0x1c: [int('EID'), short('vx'), short('vy'), short('vz')], | |
0x1d: [int('EID')], | |
0x1e: [int('EID')], | |
0x1f: [int('EID'), byte('x'), byte('y'), byte('z')], | |
0x20: [int('EID'), byte('yaw'), byte('pitch')], | |
0x21: [int('EID'), byte('x'), byte('y'), byte('z'), byte('yaw'), byte('pitch')], | |
0x22: [int('EID'), int('x'), int('y'), int('z'), byte('yaw'), byte('pitch')], | |
0x26: [int('EID'), byte('status')], | |
0x27: [int('EID'), int('vehicleID')], | |
0x28: [int('EID'), mobMetadata('metadata')], | |
0x2b: [byte('experience'), byte('level'), short('totalExperience')], | |
0x32: [int('x'), int('z'), bool('load')], | |
0x33: [int('x'), short('y'), int('z'), byte('sizeX'), byte('sizeY'), byte('sizeZ'), compressedChunk('chunk')], | |
0x34: [int('x'), int('z'), multiBlock('blocks')], | |
0x35: [int('x'), byte('y'), int('z'), byte('blockType'), byte('blockDataValue')], | |
0x36: [int('x'), short('y'), int('z'), byte('a'), byte('b')], | |
0x3c: [double('x'), double('y'), double('z'), float('radius'), int('count'), /* explosion block array */], | |
0x3d: [int('effectID'), int('x'), byte('y'), int('z'), int('data')], | |
0x46: [byte('reason'), byte('gameMode')], | |
0x47: [int('EID'), bool(), int('x'), int('y'), int('z')], | |
0x64: [byte('windowID'), byte('inventoryType'), str8('title'), byte('slots')], | |
0x65: [byte('windowID')], | |
0x67: [byte('windowID'), short('slot'), optionalItem('item')], | |
0x68: [byte('windowID'), itemList('items')], | |
0x69: [byte('windowID'), short('progressBar'), short('value')], | |
0x6a: [byte('windowID'), short('actionID'), bool('accepted')], | |
0x82: [int('x'), short('y'), int('z'), str16('line1'), str16('line2'), str16('line3'), str16('line4')], | |
0x83: [short('itemType'), short('itemID'), str8('text')], | |
0xc8: [int('statisticID'), byte('ammount')], | |
0xc9: [str16('playerName'), bool('online'), short('ping')], | |
0xff: [str16('message')], | |
}; | |
exports[15] = {}; | |
exports[15].client2server = exports.client2server; | |
exports[15].server2client = exports.server2client; | |
function clone(x) { | |
var y = {}; | |
for(var i in x) | |
y[i] = x[i].slice(0); | |
return y; | |
} | |
exports[14] = {}; | |
exports[14].client2server = clone(exports.client2server); | |
exports[14].client2server[0x00].splice(0,1); | |
exports[14].server2client = clone(exports.server2client); | |
exports[14].server2client[0x00].splice(0,1); | |
exports[14].server2client[0x01].splice(3,1).splice(4,2); | |
exports[14].server2client[0x08].splice(1,2); | |
exports[14].server2client[0x09].splice(1,3); | |
exports[14].server2client[0x46].splice(1,1); | |
})(); | |
exports.packets = { | |
KEEPALIVE: 0x00, | |
LOGIN: 0x01, | |
HANDSHAKE: 0x02, | |
CHAT: 0x03, | |
TIME: 0x04, | |
ENTITY_EQUIP: 0x05, | |
SPAWN_POS: 0x06, | |
USE_ENTITY: 0x07, | |
HEALTH: 0x08, | |
RESPAWN: 0x09, | |
PLAYER: 0x0a, | |
PLAYER_POS: 0x0b, | |
PLAYER_LOOK: 0x0c, | |
PLAYER_POS_LOOK:0x0d, | |
DIG_BLOCK: 0x0e, | |
PLACE_BLOCK: 0x0f, | |
WIELD: 0x10, | |
USE_BED: 0x11, | |
ANIMATION: 0x12, | |
ENTITY_ACTION: 0x13, | |
PLAYER_SPAWN: 0x14, | |
PICKUP_SPAWN: 0x15, | |
COLLECT_ITEM: 0x16, | |
ADD_VEHICLE: 0x17, | |
MOB_SPAWN: 0x18, | |
PAINTING: 0x19, | |
ENTITY_SPEED: 0x1c, | |
ENTITY_DESTROY: 0x1d, | |
ENTITY_CREATE: 0x1e, | |
ENTITY_MOVE: 0x1f, | |
ENTITY_LOOK: 0x20, | |
ENTITY_MOVE_LOOK:0x21, | |
ENTITY_TELEPORT:0x22, | |
ENTITY_STATUS: 0x26, | |
ENTITY_ATTACH: 0x27, | |
ENTITY_METADATA:0x28, | |
ENTITY_EFFECT: 0x29, | |
ENTITY_RM_EFFECT:0x2a, | |
EXPERIENCE: 0x2b, | |
PRE_CHUNK: 0x32, | |
MAP_CHUNK: 0x33, | |
MULTI_BLOCK_CHANGE:0x34, | |
BLOCK_CHANGE: 0x35, | |
BLOCK_ACTION: 0x36, | |
EXPLOSION: 0x3c, | |
SOUND_EFFECT: 0x3d, | |
STATE_CHANGE: 0x46, | |
THUNDERBOLT: 0x47, | |
WINDOW_OPEN: 0x64, | |
WINDOW_CLOSE: 0x65, | |
WINDOW_CLICK: 0x66, | |
SET_SLOT: 0x67, | |
WINDOW_ITEMS: 0x68, | |
PROGRESS_BAR: 0x69, | |
TRANSACTION: 0x6a, | |
CREATIVE_ACTION:0x6b, | |
UPDATE_SIGN: 0x82, | |
ITEM_DATA: 0x83, | |
INCREMENT_STAT: 0xc8, | |
PLAYER_LIST_INFO:0xc9, | |
SERVER_PING: 0xfe, | |
DISCONNECT: 0xff, | |
}; | |
exports.packetNames = {}; | |
for(i in exports.packets) | |
exports.packetNames[exports.packets[i]] = i; | |
function concatBuffers(a, b) { | |
var buffer = new Buffer(a.length + b.length); | |
a.copy(buffer, 0, 0); | |
b.copy(buffer, a.length, 0); | |
return buffer; | |
} | |
function unpackFormat(fmt) { | |
return function(pkt) { | |
var len = pack.CalcLength(fmt); | |
pkt.needs(len); | |
var value = pack.Unpack(fmt, pkt.data, pkt.ptr); | |
pkt.ptr += len; | |
return value[0]; | |
}; | |
} | |
function unpackBool(pkt) { | |
pkt.needs(1); | |
var ret = pkt.data[pkt.ptr] != 0; | |
pkt.ptr += 1; | |
return ret; | |
} | |
function unpackLong(pkt) { | |
pkt.needs(8); | |
var value = pack.Unpack('ii', pkt.data, pkt.ptr); | |
pkt.ptr += 8; | |
return value; | |
} | |
function unpackString8(pkt) { | |
var len = unpackers.short(pkt); | |
pkt.needs(len); | |
var str = ''; | |
for(var i = pkt.ptr; i < pkt.ptr + len; i++) | |
str += String.fromCharCode(pkt.data[i]); | |
pkt.ptr += len; | |
return str; | |
} | |
function unpackString16(pkt) { | |
var len = unpackers.short(pkt) * 2; | |
pkt.needs(len); | |
var str = ''; | |
for(var i = pkt.ptr; i < pkt.ptr + len; i+=2) | |
str += String.fromCharCode((pkt.data[i] << 8) + pkt.data[i+1]); | |
pkt.ptr += len; | |
return str; | |
} | |
function unpackMobMetadata(pkt) { | |
var x, data = {}; | |
while((x = unpackers.byte(pkt)) != 0x7f) { | |
var id = x & 0x1f; | |
switch(x >> 5) { | |
case 0: | |
data[id] = unpackers.byte(pkt); | |
break; | |
case 1: | |
data[id] = unpackers.short(pkt); | |
break; | |
case 2: | |
data[id] = unpackers.int(pkt); | |
break; | |
case 3: | |
data[id] = unpackers.float(pkt); | |
break; | |
case 4: | |
data[id] = unpackers.str16(pkt); | |
break; | |
default: | |
throw new Error('Can\'t figure out what ' + (x >> 5) + ' means'); | |
} | |
} | |
return data; | |
} | |
function unpackOptionalItem(pkt) { | |
var id = unpackers.short(pkt); | |
if(id != -1) { | |
var count = unpackers.byte(pkt); | |
var dataValue = unpackers.short(pkt); | |
return {id:id, count:count, dataValue:dataValue}; | |
} | |
return; | |
} | |
function unpackItemList(pkt) { | |
var len = unpackers.short(pkt), data = {}; | |
for(var i = 0; i < len; i++) | |
data[i] = unpackOptionalItem(pkt); | |
return data; | |
} | |
function unpackCompressedChunk(pkt) { | |
var len = unpackers.int(pkt); | |
pkt.needs(len); | |
var data = zlib.inflate(pkt.data.slice(pkt.ptr, pkt.ptr + len)); | |
pkt.ptr += len; | |
return data; | |
} | |
function unpackMultiBlock(pkt) { | |
var len = unpackers.short(pkt), data = []; | |
for(var i = 0; i < len; i++) { | |
var coord = unpackers.short(pkt); | |
data[i] = {x: coord >> 12, y: coord & 0xff, z: (coord >> 8) & 0xf}; | |
} | |
for(var i = 0; i < len; i++) | |
data[i].blockType = unpackers.byte(pkt); | |
for(var i = 0; i < len; i++) | |
data[i].metadata = unpackers.byte(pkt); | |
return data; | |
} | |
var unpackers = { | |
byte: unpackFormat('b'), | |
short: unpackFormat('h'), | |
int: unpackFormat('i'), | |
long: unpackLong, | |
str8: unpackString8, | |
str16: unpackString16, | |
bool: unpackBool, | |
float: unpackFormat('f'), | |
double: unpackFormat('d'), | |
mobMetadata: unpackMobMetadata, | |
optionalItem: unpackOptionalItem, | |
itemList: unpackItemList, | |
compressedChunk: unpackCompressedChunk, | |
multiBlock: unpackMultiBlock, | |
}; | |
function packFormat(fmt) { | |
return function() { | |
return new Buffer(pack.Pack(fmt, arguments)); | |
} | |
} | |
function packBool(bool) { | |
return new Buffer([bool ? 1 : 0]); | |
} | |
function packLong(v) { | |
if(typeof v === 'undefined') | |
v = 0; | |
if(typeof v === 'number') | |
v = [v / 0x100000000, v % 0x100000000]; | |
return new Buffer(pack.Pack('ii', v[0], v[1])); | |
} | |
function packString8(str) { | |
if(!str) | |
return packers.short(0); | |
var buffer = new Buffer(str.length * 2); | |
for (var i = 0; i < str.length; i++) | |
packers.byte(str.charCodeAt(i)).copy(buffer, i * 2); | |
return concatBuffers(packers.short(str.length), buffer); | |
} | |
function packString16(str) { | |
if(!str) | |
return packers.short(0); | |
var buffer = new Buffer(str.length * 2); | |
for (var i = 0; i < str.length; i++) | |
packers.short(str.charCodeAt(i)).copy(buffer, i * 2); | |
return concatBuffers(packers.short(str.length), buffer); | |
} | |
function packCompressedChunk(data) { | |
if(!(data instanceof Buffer)) | |
data = new Buffer(data); | |
data = zlib.deflate(data); | |
return concatBuffers(makers.int(data.length), data); | |
} | |
var packers = { | |
byte: packFormat('b'), | |
short: packFormat('h'), | |
int: packFormat('i'), | |
long: packLong, | |
str8: packString8, | |
str16: packString16, | |
bool: packBool, | |
float: packFormat('f'), | |
double: packFormat('d'), | |
compressedChunk: packCompressedChunk, | |
}; | |
exports.unpack = function(buffer, start, structs) { | |
var pkt = {type: buffer[start], data: buffer, ptr: start+1, needs: function(nBytes) { | |
if((this.data.length - this.ptr) < nBytes) | |
throw new Error('Packet 0x'+this.type.toString(16)+' too small'); | |
}}; | |
var struct = (structs || exports.server2client)[pkt.type]; | |
if(!struct) | |
throw Error('Unknown packet 0x' + pkt.type.toString(16)); | |
var packet = {type: pkt.type}; | |
for(var i in struct) { | |
try { | |
if(struct[i][1] == '~') | |
unpackers[struct[i][0]](pkt); | |
else | |
packet[struct[i][1]] = unpackers[struct[i][0]](pkt); | |
} catch(e) { | |
return; | |
} | |
} | |
packet.length = pkt.ptr - start; | |
return packet; | |
}; | |
exports.pack = function(packet, structs) { | |
var struct = (structs || exports.client2server)[packet.type]; | |
if(!struct) | |
throw new Error('Unknown packet 0x' + packet.type.toString(16)); | |
var buffer = new Buffer([packet.type]); | |
for(var i in struct) | |
buffer = concatBuffers(buffer, packers[struct[i][0]](packet[struct[i][1]] || undefined)); | |
return buffer; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment