Advertisement
NickG

Untitled

Nov 7th, 2013
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.23 KB | None | 0 0
  1. import struct
  2. import zlib
  3. from spock import utils
  4. from spock.mcp import mcdata, nbt
  5.  
  6. # Minecraft varints are 32-bit signed values
  7. # packed into Google Protobuf varints
  8. # TODO: Check that value is no more than 32bits
  9. # TODO: Return meaningful error if it isn't (None?)
  10. def unpack_varint(bbuff):
  11.     total = 0
  12.     shift = 0
  13.     val = 0x80
  14.     while val&0x80:
  15.         val = struct.unpack('B', bbuff.read(1))[0]
  16.         total |= ((val&0x7F)<<shift)
  17.         shift += 7
  18.     if total >= (1<<32):
  19.         return None
  20.     if total&(1<<31):
  21.         total = total - (1<<32)
  22.     return total
  23.  
  24. def pack_varint(val):
  25.     if val >= (1<<31) or val < -(1<<31):
  26.         return None
  27.     o = b''
  28.     if val < 0:
  29.         val = (1<<32)+val
  30.     while val>=0x80:
  31.         bits = val&0x7F
  32.         val >>= 7
  33.         o += struct.pack('B', (0x80|bits))
  34.     bits = val&0x7F
  35.     o += struct.pack('B', bits)
  36.     return o
  37.  
  38. # Slots are dictionaries that hold info about
  39. # inventory items, they also have funky
  40. # enchantment data stored in gziped NBT structs
  41. def unpack_slot(bbuff):
  42.     slot = {}
  43.     slot['id'] = unpack('short', bbuff)
  44.     if slot[['id'] != -1:
  45.         slot['amount'] = unpack('byte', bbuff)
  46.         slot['damage'] = unpack('short', bbuff)
  47.         length = unpack('short', bbuff)
  48.         if length > 0:
  49.             data = bbuff.recv(length)
  50.             try:
  51.                 ench_bbuff = utils.BoundBuffer(
  52.                     #Adding 16 to the window bits field tells zlib
  53.                     #to take care of the gzip headers for us
  54.                     zlib.decompress(data, 16+zlib.MAX_WBITS)
  55.                 )
  56.                 assert(unpack('byte', ench_bbuff) == nbt.TAG_COMPOUND)
  57.                 name = nbt.TAG_String(buffer = ench_bbuff)
  58.                 ench = nbt.TAG_Compound(buffer = ench_bbuff)
  59.                 ench.name = name
  60.                 slot['enchants'] = ench
  61.             except:
  62.                 slot['enchant_data'] = data
  63.     return slot
  64.  
  65. def pack_slot(slot):
  66.     o = pack('short', data['id'])
  67.     if data['id'] != -1:
  68.         o += pack('byte', data['amount'])
  69.         o += pack('short', data['damage'])
  70.         if 'enchantment_data' in data:
  71.             o += pack('short', len(data['enchant_data']))
  72.             o += data['enchant_data']
  73.         elif 'enchants' in data:
  74.             ench = data['enchants']
  75.             bbuff = utils.BoundBuffer()
  76.             TAG_Byte(ench.id)._render_buffer(bbuff)
  77.             TAG_String(ench.name)._render_buffer(bbuff)
  78.             ench._render_buffer(bbuff)
  79.             #Python zlib.compress doesn't provide wbits for some reason
  80.             #So we'll use a compression object instead, no biggie
  81.             compress = zlib.compressobj(wbits = 16+zlib.MAX_WBITS)
  82.             ench = compress.compress(bbuff.flush())
  83.             ench += compress.flush()
  84.             o += pack('short', len(ench))
  85.             o += ench
  86.         else:
  87.             o += pack('short', -1)
  88.     return o
  89.  
  90. # Dictionary list thing that holds metadata
  91. # about entities. Currently implemented as
  92. # a list/tuple thing, might switch to dicts
  93. metadata_lookup = 'byte', 'short', 'int', 'float', 'string', 'slot'
  94.  
  95. def unpack_metadata(bbuff):
  96.     metadata = []
  97.     head = unpack(bbuff, 'ubyte')
  98.     while head != 127:
  99.         key = head & 0x1F # Lower 5 bits
  100.         typ = head >> 5 # Upper 3 bits
  101.         if typ < len(metadata_lookup) and typ >= 0:
  102.             val = unpack(metadata_lookup[typ], bbuff)
  103.         elif typ == 6:
  104.             val = [unpack(bbuff, 'int') for i in range(3)]
  105.         else:
  106.             return None
  107.         metadata.append((key, (typ, val)))
  108.         head = unpack(bbuff, 'ubyte')
  109.     return metadata
  110.  
  111. def pack_metadata(metadata):
  112.     o = b''
  113.     for key, tmp in data:
  114.         typ, val = tmp
  115.         o += pack('ubyte', (typ << 5)|key)
  116.         if typ >= 0 and typ < len(metadata_lookup):
  117.             o += pack(metadata_lookup[typ], bbuff)
  118.         elif typ == 6:
  119.             for i in range(3):
  120.                 o += pack('int', val[i])
  121.         else:
  122.             return None
  123.     o += pack('byte', 127)
  124.     return o
  125.  
  126. endian = '>'
  127.  
  128. def unpack(data_type, bbuff):
  129.     if data_type in mcdata.data_types:
  130.         format = mcdata.data_types[data_type]
  131.         return struct.unpack(endian+format[0], bbuff.recv(format[1]))[0]
  132.     if data_type == "string":
  133.         return bbuff.recv(unpack(bbuff, 'short')).decode('utf-8')
  134.     if data_type == 'varint':
  135.         return unpack_varint(bbuff)
  136.     if data_type == 'metadata':
  137.         return unpack_metadata(bbuff)
  138.     else:
  139.         return None
  140.  
  141. def pack(data_type, data):
  142.     if data_type in mcdata.data_types:
  143.         format = mcdata.data_types[data_type]
  144.         return struct.pack(endian+format[0], data)
  145.     elif data_type == "string":
  146.         return pack("short", len(data)) + data.encode('utf-8')
  147.     elif data_type == "slot":
  148.         return pack_slot(data)             
  149.     elif data_type == "metadata":
  150.         return pack_metadata(data)
  151.     else:
  152.         return None
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement