int dumpNBT_list(NBT_list *input, uint8 *buffer) { int curpos=0; buffer[curpos]=TAG_LIST; curpos++; curpos+=dumpNBT_string(&buffer[curpos],input->name); buffer[curpos]=input->tagId; curpos++; putSint32(&buffer[curpos],input->length); curpos+=4; for(int i=0; i<input->length; i++) { switch(input->tagId) { case TAG_BYTE: buffer[curpos]=*(char *)input->items[i]; curpos++; break; case TAG_SHORT: putSint16(&buffer[curpos], *(int *)input->items[i]); curpos+=2; break; case TAG_INT: putSint32(&buffer[curpos], *(int *)input->items[i]); curpos+=4; break; case TAG_LONG: putSint64(&buffer[curpos], *(long long *)input->items[i]); curpos+=8; break; case TAG_FLOAT: putFloat(&buffer[curpos], *(float *)input->items[i]); curpos+=4; break; case TAG_DOUBLE: putDouble(&buffer[curpos], *(double *)input->items[i]); curpos+=8; break; case TAG_STRING: curpos+=dumpNBT_string(&buffer[curpos],*(std::string *)input->items[i]); break; case TAG_BYTE_ARRAY: curpos+=dumpNBT_byte_array((NBT_byte_array *)input->items[i], &buffer[curpos],true); break; case TAG_COMPOUND: curpos+=dumpNBT_struct((NBT_struct *)input->items[i], &buffer[curpos],true); break; } } return curpos; }
int dumpNBT_struct(NBT_struct *input, uint8 *buffer, bool list) { int curpos=0; if(!list) { buffer[curpos]=TAG_COMPOUND; curpos++; curpos+=dumpNBT_string(&buffer[curpos],input->name); } //Dump all values for(unsigned int i=0; i<input->values.size(); i++) { curpos+=dumpNBT_value(&input->values[i],&buffer[curpos]); } //Dump byte arrays for(unsigned int i=0; i<input->byte_arrays.size(); i++) { curpos+=dumpNBT_byte_array(&input->byte_arrays[i],&buffer[curpos]); } //Dump lists for(unsigned int i=0; i<input->lists.size(); i++) { curpos+=dumpNBT_list(&input->lists[i],&buffer[curpos]); } //Dump compounds for(unsigned int i=0; i<input->compounds.size(); i++) { curpos+=dumpNBT_struct(&input->compounds[i],&buffer[curpos]); } buffer[curpos]=0x00; //TAG_END curpos++; //Total size 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; }