int TAG_Compound(uint8* input, NBT_struct *output, bool start) { char tagType=1; std::string name; int curpos=0; while(tagType!=0) { curpos+=TAG_Byte(&input[curpos], &tagType); if(tagType==0) { //std::cout << "TAG_end" << std::endl; return curpos; } curpos+=TAG_String(&input[curpos], &name); //std::cout << "Name: " << name << std::endl; NBT_value value; value.type=tagType; value.name=name; NBT_byte_array bytearray; bytearray.name=name; NBT_list list; list.name=name; NBT_struct compound; compound.name=name; switch(tagType) { case TAG_BYTE: value.value=(void *)new char; curpos+=TAG_Byte(&input[curpos], (char *)value.value); output->values.push_back(value); break; case TAG_SHORT: value.value=(void *)new int; curpos+=TAG_Short(&input[curpos], (int *)value.value); output->values.push_back(value); break; case TAG_INT: value.value=(void *)new int; curpos+=TAG_Int(&input[curpos], (int *)value.value); output->values.push_back(value); break; case TAG_LONG: value.value=(void *)new long long; curpos+=TAG_Long(&input[curpos], (long long *)value.value); output->values.push_back(value); break; case TAG_FLOAT: value.value=(void *)new float; curpos+=TAG_Float(&input[curpos], (float *)value.value); output->values.push_back(value); break; case TAG_DOUBLE: value.value=(void *)new double; curpos+=TAG_Double(&input[curpos], (double *)value.value); output->values.push_back(value); break; case TAG_BYTE_ARRAY: curpos+=TAG_Byte_Array(&input[curpos], &bytearray); //Special handling with lightmaps if(bytearray.length==0) { //If zero sized lightmap, generate a new empty lightmap if(bytearray.name=="BlockLight" || bytearray.name=="SkyLight") { bytearray.length=16*16*128/2; bytearray.data=new uint8[bytearray.length]; memset(bytearray.data, 0, bytearray.length); } } output->byte_arrays.push_back(bytearray); break; case TAG_STRING: value.value=(void *)new std::string; curpos+=TAG_String(&input[curpos], (std::string *)value.value); output->values.push_back(value); break; case TAG_LIST: curpos+=TAG_List(&input[curpos], &list); output->lists.push_back(list); break; case TAG_COMPOUND: if(start) { curpos+=TAG_Compound(&input[curpos], output); } else { curpos+=TAG_Compound(&input[curpos], (NBT_struct *)&compound); output->compounds.push_back(compound); } break; } if(start) break; } return curpos; }
int TAG_List(uint8* input, NBT_list *output) { int curpos=0; curpos+=TAG_Byte(&input[curpos],&output->tagId); curpos+=TAG_Int(&input[curpos],&output->length); //If zero length list if(!output->length) { return curpos; } switch(output->tagId) { case TAG_BYTE: output->items=(void **)new char *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new char; curpos+=TAG_Byte(&input[curpos], (char *)output->items[i]); } break; case TAG_SHORT: output->items=(void **)new int *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new int; curpos+=TAG_Short(&input[curpos], (int *)output->items[i]); } break; case TAG_INT: output->items=(void **)new int *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new int; curpos+=TAG_Int(&input[curpos], (int *)output->items[i]); } break; case TAG_LONG: output->items=(void **)new long long *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new long long; curpos+=TAG_Long(&input[curpos], (long long *)output->items[i]); } break; case TAG_FLOAT: output->items=(void **)new float *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new float; curpos+=TAG_Float(&input[curpos], (float *)output->items[i]); } break; case TAG_DOUBLE: output->items=(void **)new double *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new double; curpos+=TAG_Double(&input[curpos], (double *)output->items[i]); } break; case TAG_BYTE_ARRAY: output->items=(void **)new NBT_byte_array *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new NBT_byte_array; curpos+=TAG_Byte_Array(&input[curpos], (NBT_byte_array *)output->items[i]); } break; case TAG_STRING: output->items=(void **)new std::string *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new std::string; curpos+=TAG_String(&input[curpos], (std::string *)output->items[i]); } break; case TAG_LIST: output->items=(void **)new NBT_list *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new NBT_list; curpos+=TAG_List(&input[curpos], (NBT_list *)output->items[i]); } break; case TAG_COMPOUND: output->items=(void **)new NBT_struct *[output->length]; for(int i=0; i<output->length; i++) { output->items[i]=(void *)new NBT_struct; curpos+=TAG_Compound(&input[curpos], (NBT_struct *)output->items[i]); } break; } return curpos; }
int PacketHandler::complex_entities(User *user) { if(user->buffer.size() < 12) return PACKET_NEED_MORE_DATA; int curpos = 0; unsigned int i; uint8 intArray[4]; uint8 shortArray[2]; std::copy(user->buffer.begin()+curpos, user->buffer.begin()+curpos+4, intArray); int x = getSint32(&intArray[0]); curpos += 4; std::copy(user->buffer.begin()+curpos, user->buffer.begin()+curpos+2, shortArray); int y = getSint16(&shortArray[0]); curpos += 2; std::copy(user->buffer.begin()+curpos, user->buffer.begin()+curpos+4, intArray); int z = getSint32(&intArray[0]); curpos += 4; std::copy(user->buffer.begin()+curpos, user->buffer.begin()+curpos+2, shortArray); int len = getSint16(&shortArray[0]); curpos += 2; // Wait for whole message if(user->buffer.size() < (unsigned int)curpos+len) return PACKET_NEED_MORE_DATA; //ToDo: check len uint8 *buffer = new uint8[len]; for(i = 0; i < (uint32)len; i++) { buffer[i] = user->buffer[curpos+i]; } curpos += len; user->buffer.erase(user->buffer.begin(), user->buffer.begin()+curpos); uint8 block, meta; Map::get().getBlock(x, y, z, &block, &meta); //We only handle chest for now if(block != BLOCK_CHEST) { delete[] buffer; return curpos; } //Calculate uncompressed size and allocate memory uLongf uncompressedSize = ALLOCATE_NBTFILE; //buffer[len-3] + (buffer[len-2]<<8) + (buffer[len-1]<<16) + (buffer[len]<<24); uint8 *uncompressedBuffer = new uint8[uncompressedSize]; //Initialize zstream to handle gzip format z_stream zstream; zstream.zalloc = (alloc_func)0; zstream.zfree = (free_func)0; zstream.opaque = (voidpf)0; zstream.next_in = buffer; zstream.next_out = uncompressedBuffer; zstream.avail_in = len; zstream.avail_out = uncompressedSize; zstream.total_in = 0; zstream.total_out = 0; zstream.data_type = Z_BINARY; inflateInit2(&zstream, 1+MAX_WBITS); //Uncompress if(/*int state=*/inflate(&zstream, Z_FULL_FLUSH)!=Z_OK) { inflateEnd(&zstream); } //Get size uncompressedSize = zstream.total_out; //Push data to NBT struct NBT_struct newObject; TAG_Compound(uncompressedBuffer, &newObject, true); //These are not needed anymore delete[] buffer; delete[] uncompressedBuffer; //Get chunk position int block_x = blockToChunk(x); int block_z = blockToChunk(z); uint32 chunkID; NBT_struct *theEntity = 0; //Load map if(Map::get().loadMap(block_x, block_z)) { Map::get().posToId(block_x, block_z, &chunkID); NBT_struct mapData = Map::get().maps[chunkID]; //Try to find entitylist from the chunk NBT_list *entitylist = get_NBT_list(&mapData, "TileEntities"); //If list exists if(entitylist) { //Verify list type if(entitylist->tagId != TAG_COMPOUND) { //If wrong type, recreate freeNBT_list(entitylist); for(i = 0; i < mapData.lists.size(); i++) { if(mapData.lists[i].name == "TileEntities") { //Destroy old list freeNBT_list(&mapData.lists[i]); mapData.lists.erase(mapData.lists.begin()+i); break; } } //New list NBT_list newlisting; newlisting.name = "TileEntities"; newlisting.tagId = TAG_COMPOUND; newlisting.length = 0; mapData.lists.push_back(newlisting); entitylist = get_NBT_list(&mapData, "TileEntities"); } NBT_struct **entities = (NBT_struct **)entitylist->items; bool entityExists = false; int existingID = -1; //Search for mathing entity in the list for(int i = 0; i < entitylist->length; i++) { NBT_struct *entity = entities[i]; std::string id; //Get ID if(get_NBT_value(entity, "id", &id)) { int entity_x, entity_y, entity_z; if(!get_NBT_value(entity, "x", &entity_x) || !get_NBT_value(entity, "y", &entity_y) || !get_NBT_value(entity, "z", &entity_z)) { continue; } //Check for mathing blocktype and ID if(block == BLOCK_CHEST && id == "Chest") { if(x == entity_x && y == entity_y && z == entity_z) { entityExists = true; theEntity = entity; existingID = i; break; } } } } //End For entitylist //Generate struct theEntity = new NBT_struct; NBT_value value; //Push ID value.type = TAG_STRING; value.name = "id"; std::string *name = new std::string; value.value = (void *)name; *(std::string *)value.value = "Chest"; theEntity->values.push_back(value); //Position value.type = TAG_INT; value.name = "x"; value.value = (void *)new int; *(int *)value.value = x; theEntity->values.push_back(value); value.name = "y"; value.value = (void *)new int; *(int *)value.value = y; theEntity->values.push_back(value); value.name = "z"; value.value = (void *)new int; *(int *)value.value = z; theEntity->values.push_back(value); //Put special chest items if(block == BLOCK_CHEST) { NBT_list *newlist = get_NBT_list(&newObject, "Items"); if(!newlist) { //std::cout << "Items not found!" << std::endl; return curpos; } NBT_list itemlist; itemlist.name = "Items"; itemlist.tagId = TAG_COMPOUND; itemlist.length = newlist->length; itemlist.items = (void **)new NBT_struct *[itemlist.length]; NBT_struct **structlist = (NBT_struct **)itemlist.items; for(int i = 0; i < itemlist.length; i++) { structlist[i] = new NBT_struct; char type_char; sint16 type_sint16; //Generate struct value.type = TAG_BYTE; value.name = "Count"; get_NBT_value((NBT_struct *)((NBT_struct **)newlist->items)[i], "Count", &type_char); value.value = (void *)new char; *(char *)value.value = type_char; structlist[i]->values.push_back(value); value.type = TAG_BYTE; value.name = "Slot"; get_NBT_value((NBT_struct *)((NBT_struct **)newlist->items)[i], "Slot", &type_char); value.value = (void *)new char; *(char *)value.value = type_char; structlist[i]->values.push_back(value); value.type = TAG_SHORT; value.name = "Damage"; get_NBT_value((NBT_struct *)((NBT_struct **)newlist->items)[i], "Damage", &type_sint16); value.value = (void *)new sint16; *(sint16 *)value.value = type_sint16; structlist[i]->values.push_back(value); value.type = TAG_SHORT; value.name = "id"; get_NBT_value((NBT_struct *)((NBT_struct **)newlist->items)[i], "id", &type_sint16); value.value = (void *)new sint16; *(sint16 *)value.value = type_sint16; structlist[i]->values.push_back(value); } theEntity->lists.push_back(itemlist); } //If entity doesn't exist in the list, resize the list to fit it in if(!entityExists) { //ToDo: try this! NBT_struct **newlist = new NBT_struct *[entitylist->length+1]; NBT_struct **oldlist = (NBT_struct **)entitylist->items; uint8 *structbuffer = new uint8[ALLOCATE_NBTFILE]; for(int i = 0; i < entitylist->length; i++) { newlist[i] = new NBT_struct; dumpNBT_struct(oldlist[i],structbuffer); TAG_Compound(structbuffer, newlist[i],true); freeNBT_struct(oldlist[i]); oldlist[i] = NULL; } delete [] structbuffer; entitylist->length++; entitylist->items = (void **)newlist; delete [] (NBT_struct **)oldlist; newlist[entitylist->length-1] = theEntity; } //If item exists, replace the old with the new else { //Destroy old entitylist NBT_struct **oldlist = (NBT_struct **)entitylist->items; freeNBT_struct(oldlist[existingID]); //Replace with the new oldlist[existingID] = theEntity; } //Mark chunk as changed Map::get().mapChanged[chunkID] = true; } //If entity exists } //If loaded map //Send complex entity packet to others if(theEntity) { uint8 *structdump = new uint8[ALLOCATE_NBTFILE]; uint8 *packetData = new uint8[ALLOCATE_NBTFILE]; int dumped = dumpNBT_struct(theEntity, structdump); uLongf written = ALLOCATE_NBTFILE; z_stream zstream2; zstream2.zalloc = Z_NULL; zstream2.zfree = Z_NULL; zstream2.opaque = Z_NULL; zstream2.next_out = &packetData[13]; zstream2.next_in = structdump; zstream2.avail_in = dumped; zstream2.avail_out = written; zstream2.total_out = 0; zstream2.total_in = 0; deflateInit2(&zstream2, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15+MAX_WBITS, 8, Z_DEFAULT_STRATEGY); if(int state=deflate(&zstream2,Z_FULL_FLUSH)!=Z_OK) { std::cout << "Error in deflate: " << state << std::endl; deflateEnd(&zstream2); } else { written = zstream2.total_out; packetData[0] = 0x3b; //Complex Entities putSint32(&packetData[1],x); putSint16(&packetData[5],y); putSint32(&packetData[7],z); putSint16(&packetData[11], (sint16)written); user->sendAll((uint8 *)&packetData[0], 13+written); } delete [] packetData; delete [] structdump; } return curpos; }