bool BlockChest::onInteract(User* user, int32_t x, int16_t y, int32_t z, int map) { //ToDo: check for large chest! sChunk* chunk = ServerInstance->map(map)->getChunk(blockToChunk(x), blockToChunk(z)); if(chunk == NULL) { return false; } chestDataPtr _chestData; for(uint32_t i = 0; i < chunk->chests.size(); i++) { if((chunk->chests[i]->y() == y) && (chunk->chests[i]->x() == x) && (chunk->chests[i]->z() == z)) { _chestData = chunk->chests[i]; break; } } if(_chestData != NULL) { ServerInstance->inventory()->windowOpen(user, (_chestData->large() ? WINDOW_LARGE_CHEST : WINDOW_CHEST), x, y, z); return true; } else { return false; } }
//Falling physics loop bool Physics::updateFall() { uint32_t listSize = fallSimList.size(); for (int32_t simIt = listSize-1; simIt >= 0; simIt--) { bool hitGround = false; double timeInSec = (microTime()-fallSimList[simIt].startTime)/1000000.0; fallSimList[simIt].ticks++; const double gravity = 9.81; double offset = 0.5*gravity*timeInSec*timeInSec; int blockOffset = fallSimList[simIt].pos.y() - fallSimList[simIt].lastY; if(blockOffset != (int)offset) { for(int ypos = fallSimList[simIt].lastY-1; ypos >= fallSimList[simIt].lastY-((int)offset-blockOffset);ypos--) { uint8_t block, meta; ServerInstance->map(map)->getBlock(fallSimList[simIt].pos.x(),ypos,fallSimList[simIt].pos.z(), &block, &meta); fallSimList[simIt].lastY--; switch (block) { case BLOCK_AIR: case BLOCK_WATER: case BLOCK_STATIONARY_WATER: case BLOCK_LAVA: case BLOCK_STATIONARY_LAVA: case BLOCK_SNOW: break; //If we hit ground default: { ServerInstance->map(map)->setBlock(fallSimList[simIt].pos.x(),ypos+1,fallSimList[simIt].pos.z(), fallSimList[simIt].block, 0); ServerInstance->map(map)->sendBlockChange(fallSimList[simIt].pos.x(),ypos+1,fallSimList[simIt].pos.z(), fallSimList[simIt].block, 0); //Despawn entity Packet pkt = Protocol::destroyEntity(fallSimList[simIt].EID); const int chunk_x = blockToChunk(fallSimList[simIt].pos.x()); const int chunk_z = blockToChunk(fallSimList[simIt].pos.z()); const ChunkMap::const_iterator it = ServerInstance->map(map)->chunks.find(Coords(chunk_x, chunk_z)); if (it != ServerInstance->map(map)->chunks.end()) { it->second->sendPacket(pkt); } //Erase from the simulation list fallSimList.erase(fallSimList.begin()+simIt); hitGround = true; break; } } if(hitGround) { break; } } } } return true; }
//Falling physics loop bool Physics::updateFall() { uint32_t listSize = fallSimList.size(); for (int32_t simIt = listSize-1; simIt >= 0; simIt--) { Falling& f = fallSimList[simIt]; double timeInSec = (microTime()-f.startTime)/1000000.0; f.ticks++; const double gravity = 9.81; double offset = 0.5*gravity*timeInSec*timeInSec; int blockOffset = f.pos.y() - f.lastY; if(blockOffset != (int)offset) { /// not necessary, doesn't optimize much int yStart = f.pos.y(); int x = f.pos.x(); int z = f.pos.z(); int ypos = f.lastY; f.lastY = yStart-(int)offset; for(; ypos >= f.lastY; ypos--) { uint8_t block, meta; map->getBlock(x,ypos,z, &block, &meta); switch (block) { case BLOCK_AIR: case BLOCK_WATER: case BLOCK_STATIONARY_WATER: case BLOCK_LAVA: case BLOCK_STATIONARY_LAVA: case BLOCK_SNOW: break; //If we hit ground default: { map->setBlock(x, ++ypos, z, f.block, 0); map->sendBlockChange(x, ypos, z, f.block, 0); //Despawn entity Packet pkt = Protocol::destroyEntity(f.EID); const int chunk_x = blockToChunk(x); const int chunk_z = blockToChunk(z); const ChunkMap::const_iterator it = map->chunks.find(Coords(chunk_x, chunk_z)); if (it != map->chunks.end()) { it->second->sendPacket(pkt); } //Erase from the simulation list fallSimList.erase(fallSimList.begin()+simIt); goto breakout; } } } } breakout: continue; } return true; }
void BlockFalling::applyPhysics(User* user, int32_t x, int16_t y, int32_t z, int map) { uint8_t fallblock, block; uint8_t fallmeta, meta; if (!ServerInstance->map(map)->getBlock(x, y, z, &fallblock, &fallmeta)) { return; } if (ServerInstance->map(map)->getBlock(x, y - 1, z, &block, &meta)) { switch (block) { case BLOCK_AIR: case BLOCK_WATER: case BLOCK_STATIONARY_WATER: case BLOCK_LAVA: case BLOCK_STATIONARY_LAVA: case BLOCK_SNOW: break; default: return; break; } // Destroy original block ServerInstance->map(map)->sendBlockChange(x, y, z, BLOCK_AIR, 0); ServerInstance->map(map)->setBlock(x, y, z, BLOCK_AIR, 0); y--; //Spawn an entity for the falling block const int chunk_x = blockToChunk(x); const int chunk_z = blockToChunk(z); const ChunkMap::const_iterator it = ServerInstance->map(map)->chunks.find(Coords(chunk_x, chunk_z)); if (it == ServerInstance->map(map)->chunks.end()) return; uint32_t EID = Mineserver::generateEID(); uint8_t object = 70; //type == Falling object Packet pkt = Protocol::addObject(EID,object, (x<<5)+16, ((y+1)<<5)+16, (z<<5)+16, fallblock|(fallmeta<<0x10)); it->second->sendPacket(pkt); //Add to physics loop ServerInstance->physics(map)->addFallSimulation(fallblock,vec(x, y, z), EID); this->notifyNeighbours(x, y + 1, z, map, "onNeighbourMove", user, fallblock, BLOCK_BOTTOM); } }
bool BlockChest::getChestByCoordinates(int32_t x, int16_t y, int32_t z, int map, chestDataPtr& chest) { sChunk* chunk = ServerInstance->map(map)->getChunk(blockToChunk(x), blockToChunk(z)); for(size_t i = 0; i < chunk->chests.size(); i++) { if((chunk->chests[i]->x() == x) && (chunk->chests[i]->y() == y) && (chunk->chests[i]->z() == z)) { chest = chunk->chests[i]; return true; } } return false; }
void ItemFood::onRightClick(User* user, Item* item) { int healammount = 0; switch (item->getType()) { case ITEM_GOLDEN_APPLE: healammount = 20; break; case ITEM_MUSHROOM_SOUP: healammount = 10; break; case ITEM_GRILLED_PORK: healammount = 8; break; case ITEM_PORK: healammount = 3; break; case ITEM_BREAD: healammount = 5; break; case ITEM_COOKED_FISH: healammount = 5; break; case ITEM_RAW_FISH: healammount = 2; break; case ITEM_APPLE: healammount = 4; break; } int newhealth = user->health + healammount; if (newhealth > 20) { newhealth = 20; } user->sethealth(newhealth); //Accept eating user->buffer << Protocol::entityStatus(user->UID, 9); //Eating animation MetaData meta; meta.set(MetaDataElem(0,0x10)); Packet pkt = Protocol::animation(user->UID,5); //pkt << Protocol::entityMetadata(user->UID, meta); //ToDo: add timer stop animation sChunk* chunk = ServerInstance->map(user->pos.map)->getChunk(blockToChunk((int32_t)user->pos.x), blockToChunk((int32_t)user->pos.z)); if (chunk != nullptr) { chunk->sendPacket(pkt); } if(item->getCount() > 1) { item->setCount(item->getCount()-1); } else { item->setType(-1); item->setCount(0); } }
bool Map::getBlock(int x, int y, int z, uint8 *type, uint8 *meta, bool generate) { #ifdef MSDBG printf("getBlock(x=%d, y=%d, z=%d)\n", x, y, z); #endif if((y < 0) || (y > 127)) { printf("(%i, %i, %i) ", x, y, z); LOG("Invalid y value (getBlock)"); return false; } int chunk_x = blockToChunk(x); int chunk_z = blockToChunk(z); uint32 mapId; Map::posToId(chunk_x, chunk_z, &mapId); sChunk *chunk = getMapData(chunk_x, chunk_z, generate); if(!chunk) { if(generate) LOG("Loading chunk failed (getBlock)"); return false; } int chunk_block_x = blockToChunkBlock(x); int chunk_block_z = blockToChunkBlock(z); uint8 *blocks = chunk->blocks; uint8 *metapointer = chunk->data; int index = y + (chunk_block_z * 128) + (chunk_block_x * 128 * 16); *type = blocks[index]; uint8 metadata = metapointer[(index)>>1]; if(y%2) { metadata &= 0xf0; metadata >>= 4; }
bool BlockFurnace::onBroken(User* user, int8_t status, int32_t x, int8_t y, int32_t z, int map, int8_t direction) { uint8_t block; uint8_t meta; if (!Mineserver::get()->map(map)->getBlock(x, y, z, &block, &meta)) { return true; } bool destroy = false; int chunk_x = blockToChunk(x); int chunk_z = blockToChunk(z); sChunk* chunk = Mineserver::get()->map(map)->loadMap(chunk_x, chunk_z); if (chunk == NULL) { return true; } for (uint32_t i = 0; i < chunk->furnaces.size(); i++) { if (chunk->furnaces[i]->x == x && chunk->furnaces[i]->y == y && chunk->furnaces[i]->z == z) { chunk->furnaces.erase(chunk->furnaces.begin() + i); break; } } Mineserver::get()->map(map)->sendBlockChange(x, y, z, BLOCK_AIR, 0); Mineserver::get()->map(map)->setBlock(x, y, z, BLOCK_AIR, 0); this->spawnBlockItem(x, y, z, map, block); return false; }
int PacketHandler::entity_crouch(User* user) { int32_t EID; int8_t action; MetaData meta; MetaDataElemByte *element; user->buffer >> EID >> action; Packet pkt; bool packetData = false; //ToDo: handle other actions switch(action) { //Crouch case 1: element = new MetaDataElemByte(0,0x02); meta.set(element); pkt << Protocol::animation(user->UID, 104) << Protocol::entityMetadata(user->UID,meta); packetData = true; break; //Uncrouch case 2: element = new MetaDataElemByte(0,0x00); meta.set(element); pkt << Protocol::animation(user->UID, 105) << Protocol::entityMetadata(user->UID,meta); packetData = true; break; default: break; } if(packetData) { sChunk* chunk = ServerInstance->map(user->pos.map)->getChunk(blockToChunk((int32_t)user->pos.x), blockToChunk((int32_t)user->pos.z)); if (chunk != NULL) { chunk->sendPacket(pkt); } } user->buffer.removePacket(); return PACKET_OK; }
void BlockChest::onStartedDigging(User* user, int8_t status, int32_t x, int16_t y, int32_t z, int map, int8_t direction) { // Locksystem if (user->inv[36 + user->currentItemSlot()].getType() == ITEM_WOODEN_AXE) { int chunk_x = blockToChunk(x); int chunk_z = blockToChunk(z); sChunk* chunk = ServerInstance->map(map)->loadMap(chunk_x, chunk_z); if (chunk == NULL) { return; } NBT_Value* entityList = (*(*(chunk->nbt))["Level"])["TileEntities"]; if (!entityList) { entityList = new NBT_Value(NBT_Value::TAG_LIST, NBT_Value::TAG_COMPOUND); chunk->nbt->Insert("TileEntities", entityList); } if (entityList->GetType() == NBT_Value::TAG_LIST) { if (entityList->GetListType() != NBT_Value::TAG_COMPOUND) { entityList->SetType(NBT_Value::TAG_LIST, NBT_Value::TAG_COMPOUND); } std::vector<NBT_Value*> *entities = entityList->GetList(); std::vector<NBT_Value*>::iterator iter = entities->begin(), end = entities->end(); //bool done = false; // Unused variable for (; iter != end; iter++) { if ((**iter)["x"] == NULL || (**iter)["y"] == NULL || (**iter)["z"] == NULL || (**iter)["x"]->GetType() != NBT_Value::TAG_INT || (**iter)["y"]->GetType() != NBT_Value::TAG_INT || (**iter)["z"]->GetType() != NBT_Value::TAG_INT) { continue; } if ((int32_t)(*(**iter)["x"]) == x && (int32_t)(*(**iter)["y"]) == y && (int32_t)(*(**iter)["z"]) == z) { int8_t locked; NBT_Value* nbtLockdata = (**iter)["Lockdata"]; if (nbtLockdata != NULL) { std::string player = *(*nbtLockdata)["player"]->GetString(); // Toggle lock if player is the owner of block if (player == user->nick) { locked = *(*nbtLockdata)["locked"]; locked = (locked == 1) ? 0 : 1; *(*nbtLockdata)["locked"] = locked; if (locked == 1) { ServerInstance->chat()->sendMsg(user, MC_COLOR_RED + "Chest locked", Chat::USER); } else { ServerInstance->chat()->sendMsg(user, MC_COLOR_RED + "Chest opened", Chat::USER); } } } else { // If lockdata is missing (old chest) NBT_Value* nbtLock = new NBT_Value(NBT_Value::TAG_COMPOUND); nbtLock->Insert("player", new NBT_Value(user->nick)); nbtLock->Insert("locked", new NBT_Value((int8_t)1)); (*iter)->Insert("Lockdata", nbtLock); } break; } } } } }
bool InventoryWorkbench::onwindowClick(User* user, int8_t windowID, int16_t slot, int8_t button, int16_t actionNumber, int16_t itemID, int8_t itemCount, int16_t itemUses, int8_t mode) { // Safeguard against overflow if (slot > MAX_SLOT_CRAFTING_TABLE) return false; if (slot != -999 && slot < 0) return false; Inventory* inventory = ServerInstance->inventory(); //Ack if(actionNumber) { // ToDo: actually check the action before ack user->writePacket(Protocol::confirmTransaction(windowID, actionNumber, 1)); } // Handle drag mode in a base class helper function if (mode == INVENTORY_MODE_DRAG) { return this->handleDrag(user, windowID, slot, button, actionNumber, itemID, itemCount, itemUses, mode); } else if (mode == INVENTORY_MODE_NUMBER) { return this->handleNumber(user, windowID, slot, button, actionNumber, itemID, itemCount, itemUses, mode); } user->openInv.recordAction = false; //Click outside the window if (slot == -999) { //Dropping outside of the window if(button == 0 && mode == 0 && user->inventoryHolding.getType() != -1) { ServerInstance->map(user->pos.map)->createPickupSpawn((int)user->pos.x, (int)user->pos.y, (int)user->pos.z, user->inventoryHolding.getType(), user->inventoryHolding.getCount(), user->inventoryHolding.getHealth(), user); user->inventoryHolding.setType(-1); return true; } return true; } //on click-and-drag mode, recording the slots used else if(user->openInv.recordAction) { if(mode == 5) { user->openInv.slotActions.push_back(slot); } else { user->openInv.recordAction = false; } return true; } if (!user->isOpenInv && windowID != 0) { return false; } sChunk* chunk = NULL; chunk = ServerInstance->map(user->pos.map)->getChunk(blockToChunk(user->openInv.x), blockToChunk(user->openInv.z)); if (chunk == NULL) { return false; } chunk->changed = true; std::vector<User*>* otherUsers = NULL; OpenInvPtr currentInventory; std::vector<OpenInvPtr>& inv = inventory->openWorkbenches; for (size_t i = 0; i < inv.size(); i++) { if (inv[i]->x == user->openInv.x && inv[i]->y == user->openInv.y && inv[i]->z == user->openInv.z) { otherUsers = &inv[i]->users; currentInventory = inv[i]; break; } } if (otherUsers == NULL || currentInventory == NULL) { return false; } Item* slotItem = NULL; if (slot > 9) { slotItem = &user->inv[slot - 1]; } else { slotItem = ¤tInventory->workbench[slot]; } bool workbenchCrafting = false; //Empty slot and holding something if ((slotItem->getType() == -1 || (slotItem->getType() == user->inventoryHolding.getType() && slotItem->getHealth() == user->inventoryHolding.getHealth() && slotItem->getCount() < 64)) && user->inventoryHolding.getType() != -1) { //If accessing crafting output slot if (slotItem->getType() != -1 && slot == 0) { if (user->inventoryHolding.getType() == slotItem->getType() && 64 - user->inventoryHolding.getCount() >= slotItem->getCount()) { user->inventoryHolding.decCount(-slotItem->getCount()); for (uint8_t workbenchSlot = 1; workbenchSlot < 10; workbenchSlot++) { if (currentInventory->workbench[workbenchSlot].getType() != -1) { currentInventory->workbench[workbenchSlot].decCount(); inventory->setSlot(user, windowID, workbenchSlot, ¤tInventory->workbench[workbenchSlot]); } } workbenchCrafting = true; } } else { //ToDo: Make sure we have room for the items! //Make sure not putting anything to the crafting space if (slot != 0) { int16_t addCount = (64 - slotItem->getCount() >= user->inventoryHolding.getCount()) ? user->inventoryHolding.getCount() : 64 - slotItem->getCount(); slotItem->decCount(0 - ((button) ? 1 : addCount)); slotItem->setHealth(user->inventoryHolding.getHealth()); slotItem->setType(user->inventoryHolding.getType()); user->inventoryHolding.decCount((button) ? 1 : addCount); } } } //We are not holding anything, get the item we clicked else if (user->inventoryHolding.getType() == -1) { //If accessing crafting output slot, remove from input! if (slotItem->getType() != -1 && slot == 0) { user->inventoryHolding.setType(slotItem->getType()); user->inventoryHolding.setCount(slotItem->getCount()); user->inventoryHolding.setHealth(slotItem->getHealth()); for (uint8_t workbenchSlot = 1; workbenchSlot < 10; workbenchSlot++) { if (currentInventory->workbench[workbenchSlot].getType() != -1) { currentInventory->workbench[workbenchSlot].decCount(); inventory->setSlot(user, windowID, workbenchSlot, ¤tInventory->workbench[workbenchSlot]); } } workbenchCrafting = true; } else { //Shift+click -> items to player inv //ToDo: from player inventory to chest if(!button && mode &&inventory-> isSpace(user, slotItem->getType(), slotItem->getCount())) { inventory->addItems(user, slotItem->getType(), slotItem->getCount(), slotItem->getHealth()); slotItem->setCount(0); } else { user->inventoryHolding.setType(slotItem->getType()); user->inventoryHolding.setHealth(slotItem->getHealth()); user->inventoryHolding.setCount(slotItem->getCount()); if (button == 1) { user->inventoryHolding.decCount(slotItem->getCount() >> 1); } slotItem->decCount(user->inventoryHolding.getCount()); } if (slotItem->getCount() == 0) { slotItem->setHealth(0); slotItem->setType(-1); } } }
bool BlockChest::onPlace(User* user, int16_t newblock, int32_t x, int16_t y, int32_t z, int map, int8_t direction) { uint8_t oldblock; uint8_t oldmeta; if (!ServerInstance->map(map)->getBlock(x, y, z, &oldblock, &oldmeta)) { revertBlock(user, x, y, z, map); return true; } /* Check block below allows blocks placed on top */ if (!this->isBlockStackable(oldblock)) { revertBlock(user, x, y, z, map); return true; } /* move the x,y,z coords dependent upon placement direction */ if (!this->translateDirection(&x, &y, &z, map, direction)) { revertBlock(user, x, y, z, map); return true; } if (this->isUserOnBlock(x, y, z, map)) { revertBlock(user, x, y, z, map); return true; } if (!this->isBlockEmpty(x, y, z, map)) { revertBlock(user, x, y, z, map); return true; } // if there is a large chest around --> block { chestDataPtr _connectedChest; if(findConnectedChest(x, y, z, map, _connectedChest)) { if(_connectedChest->large()) { revertBlock(user, x, y, z, map); return true; } } } direction = user->relativeToBlock(x, y, z); //// Fix orientation switch (direction) { case BLOCK_EAST: //direction = BLOCK_SOUTH; break; case BLOCK_BOTTOM: direction = BLOCK_WEST; break; case BLOCK_NORTH: direction = BLOCK_TOP; break; case BLOCK_SOUTH: //direction = BLOCK_NORTH; break; } int32_t connectedChestX, connectedChestZ; if(findConnectedChest(x, y, z, map, &connectedChestX, &connectedChestZ)) { if(connectedChestX != x) { if(!(direction == 3 || direction == 2)) { direction = 3; } } if(connectedChestZ != z) { if(!(direction == 4 || direction == 5)) { direction = 4; } } ServerInstance->map(map)->setBlock(connectedChestX, y, connectedChestZ, (char)newblock, direction); ServerInstance->map(map)->sendBlockChange(connectedChestX, y, connectedChestZ, (char)newblock, direction); // create a new chest and connect it to another chest --> large chest chestDataPtr connectedChest; if(getChestByCoordinates(connectedChestX, y, connectedChestZ, map, connectedChest)) { chestDataPtr newchest; if(!getChestByCoordinates(x, y, z, map, newchest)) { sChunk* chunk = ServerInstance->map(map)->getChunk(blockToChunk(x), blockToChunk(z)); if(chunk != NULL) { newchest = chestDataPtr(new chestData); newchest->items(connectedChest->items()); newchest->x(x); newchest->y(y); newchest->z(z); chunk->chests.push_back(newchest); } } else { newchest->large(true); } connectedChest->large(true); } } else { // create a new (small) chest chestDataPtr newchest(new chestData); sChunk* chunk = ServerInstance->map(map)->getChunk(blockToChunk(x), blockToChunk(z)); if(chunk != NULL) { newchest->x(x); newchest->y(y); newchest->z(z); chunk->chests.push_back(newchest); } } ServerInstance->map(map)->setBlock(x, y, z, (char)newblock, direction); ServerInstance->map(map)->sendBlockChange(x, y, z, (char)newblock, direction); return false; }
bool BlockChest::onBroken(User* user, int8_t status, int32_t x, int16_t y, int32_t z, int map, int8_t direction) { uint8_t block; uint8_t meta; if (!ServerInstance->map(map)->getBlock(x, y, z, &block, &meta)) { return true; } bool destroy = false; int chunk_x = blockToChunk(x); int chunk_z = blockToChunk(z); sChunk* chunk = ServerInstance->map(map)->loadMap(chunk_x, chunk_z); if (chunk == NULL) { return true; } for (uint32_t i = 0; i < chunk->chests.size(); i++) { if (chunk->chests[i]->x() == x && chunk->chests[i]->y() == y && chunk->chests[i]->z() == z) { if(!chunk->chests[i]->large()) { // clean up a small chest int32_t item_i = 26; for(int32_t item_i = 26; 0 <= item_i; item_i--) { if ((*chunk->chests[i]->items())[(size_t)item_i]->getType() != -1) { ServerInstance->map(map)->createPickupSpawn(chunk->chests[i]->x(), chunk->chests[i]->y(), chunk->chests[i]->z(), (*chunk->chests[i]->items())[(size_t)item_i]->getType(), (*chunk->chests[i]->items())[(size_t)item_i]->getCount(), (*chunk->chests[i]->items())[(size_t)item_i]->getHealth(), NULL); } chunk->chests[i]->items()->pop_back(); } } else { // size a large chest down for(uint32_t item_i = 53; 27-3 <= item_i; item_i--) { if ((*chunk->chests[i]->items())[item_i]->getType() != -1) { ServerInstance->map(map)->createPickupSpawn(chunk->chests[i]->x(), chunk->chests[i]->y(), chunk->chests[i]->z(), (*chunk->chests[i]->items())[item_i]->getType(), (*chunk->chests[i]->items())[item_i]->getCount(), (*chunk->chests[i]->items())[item_i]->getHealth(), NULL); } chunk->chests[i]->items()->pop_back(); } } chunk->chests.erase(chunk->chests.begin() + i); break; } } ServerInstance->map(map)->sendBlockChange(x, y, z, BLOCK_AIR, 0); ServerInstance->map(map)->setBlock(x, y, z, BLOCK_AIR, 0); this->spawnBlockItem(x, y, z, map, block); return false; }
bool BlockFurnace::onPlace(User* user, int16_t newblock, int32_t x, int8_t y, int32_t z, int map, int8_t direction) { uint8_t oldblock; uint8_t oldmeta; if (!Mineserver::get()->map(map)->getBlock(x, y, z, &oldblock, &oldmeta)) { revertBlock(user, x, y, z, map); return true; } /* Check block below allows blocks placed on top */ if (!this->isBlockStackable(oldblock)) { revertBlock(user, x, y, z, map); return true; } /* move the x,y,z coords dependent upon placement direction */ if (!this->translateDirection(&x, &y, &z, map, direction)) { revertBlock(user, x, y, z, map); return true; } if (this->isUserOnBlock(x, y, z, map)) { revertBlock(user, x, y, z, map); return true; } if (!this->isBlockEmpty(x, y, z, map)) { revertBlock(user, x, y, z, map); return true; } direction = user->relativeToBlock(x, y, z); // Fix orientation //switch (direction) //{ //case BLOCK_EAST: // direction = BLOCK_SOUTH; // break; //case BLOCK_BOTTOM: // direction = BLOCK_EAST; // break; //case BLOCK_NORTH: // direction = BLOCK_NORTH; // break; //case BLOCK_SOUTH: // direction = BLOCK_BOTTOM; // break; //} Mineserver::get()->map(map)->setBlock(x, y, z, (char)newblock, direction); Mineserver::get()->map(map)->sendBlockChange(x, y, z, (char)newblock, direction); int chunk_x = blockToChunk(x); int chunk_z = blockToChunk(z); sChunk* chunk = Mineserver::get()->map(map)->loadMap(chunk_x, chunk_z); if (chunk == NULL) { revertBlock(user, x, y, z, map); } return true; for (uint32_t i = 0; i < chunk->furnaces.size(); i++) { if (chunk->furnaces[i]->x == x && chunk->furnaces[i]->y == y && chunk->furnaces[i]->z == z) { chunk->furnaces.erase(chunk->furnaces.begin() + i); break; } } return false; }
bool Inventory::windowClick(User *user,int8_t windowID, int16_t slot, int8_t rightClick, int16_t actionNumber, int16_t itemID, int8_t itemCount,int16_t itemUses) { //Ack user->buffer << (int8_t)PACKET_TRANSACTION << (int8_t)windowID << (int16_t)actionNumber << (int8_t)1; //Mineserver::get()->logger()->log(1,"window: " + dtos(windowID) + " slot: " + dtos(slot) + " (" + dtos(actionNumber) + ") itemID: " + dtos(itemID)); //Click outside the window if(slot == -999) { if(user->inventoryHolding.type != -1) { Mineserver::get()->map()->createPickupSpawn((int)user->pos.x, (int)user->pos.y, (int)user->pos.z, user->inventoryHolding.type, user->inventoryHolding.count, user->inventoryHolding.health,user); user->inventoryHolding.count = 0; user->inventoryHolding.type =-1; user->inventoryHolding.health= 0; } return true; } if(!user->isOpenInv && windowID != 0) { return false; } sChunk* chunk = NULL; if(windowID != 0) { chunk = Mineserver::get()->map()->chunks.getChunk(blockToChunk(user->openInv.x),blockToChunk(user->openInv.z)); if(chunk == NULL) { return false; } chunk->changed = true; } std::vector<User*>* otherUsers = NULL; OpenInventory* currentInventory = NULL; if(windowID != WINDOW_PLAYER) { std::vector<OpenInventory*>* inv = NULL; switch(user->openInv.type) { case WINDOW_CHEST: inv = &openChests; break; case WINDOW_FURNACE: inv = &openFurnaces; break; case WINDOW_WORKBENCH: inv = &openWorkbenches; break; } for(uint32_t i = 0; i < inv->size(); i++) { if((*inv)[i]->x == user->openInv.x && (*inv)[i]->y == user->openInv.y && (*inv)[i]->z == user->openInv.z) { otherUsers = &(*inv)[i]->users; currentInventory = (*inv)[i]; break; } } if(otherUsers == NULL || currentInventory == NULL) { return false; } } Item* slotItem = NULL; switch(windowID) { //Player inventory case WINDOW_PLAYER: slotItem=&user->inv[slot]; break; case WINDOW_CHEST: if(slot>26) { slotItem=&user->inv[slot-18]; } else { for(uint32_t i = 0; i < chunk->chests.size(); i ++) { if(chunk->chests[i]->x == user->openInv.x && chunk->chests[i]->y == user->openInv.y && chunk->chests[i]->z == user->openInv.z) { slotItem = &chunk->chests[i]->items[slot]; break; } } //Create chest data if it doesn't exist if(slotItem == NULL) { chestData *newChest = new chestData; newChest->x = user->openInv.x; newChest->y = user->openInv.y; newChest->z = user->openInv.z; chunk->chests.push_back(newChest); slotItem = &newChest->items[slot]; } } break; case WINDOW_LARGE_CHEST: if(slot>54) { slotItem=&user->inv[slot-47]; } else { //ToDo: Handle large chest } break; case WINDOW_FURNACE: if(slot>3) { slotItem=&user->inv[slot+6]; } else { for(uint32_t i = 0; i < chunk->furnaces.size(); i ++) { if(chunk->furnaces[i]->x == user->openInv.x && chunk->furnaces[i]->y == user->openInv.y && chunk->furnaces[i]->z == user->openInv.z) { slotItem = &chunk->furnaces[i]->items[slot]; } } //Create furnace data if it doesn't exist if(slotItem == NULL) { furnaceData *newFurnace = new furnaceData; newFurnace->x = user->openInv.x; newFurnace->y = user->openInv.y; newFurnace->z = user->openInv.z; newFurnace->burnTime = 0; newFurnace->cookTime = 0; chunk->furnaces.push_back(newFurnace); slotItem = &newFurnace->items[slot]; } } break; case WINDOW_WORKBENCH: if(slot > 9) { slotItem=&user->inv[slot-1]; } else { slotItem=¤tInventory->workbench[slot]; } break; } bool workbenchCrafting = false; bool playerCrafting = false; //Empty slot and holding something if((itemID == -1 || (slotItem->type == user->inventoryHolding.type && slotItem->count < 64) ) && user->inventoryHolding.type != -1) { //If accessing crafting output slot if(slotItem->type != -1 && (windowID == WINDOW_WORKBENCH || windowID == WINDOW_PLAYER) && slot == 0) { if(user->inventoryHolding.type == slotItem->type && 64-user->inventoryHolding.count >= slotItem->count) { user->inventoryHolding.count += slotItem->count; if(windowID == WINDOW_WORKBENCH) { for(uint8_t workbenchSlot = 1; workbenchSlot < 10; workbenchSlot++) { if(currentInventory->workbench[workbenchSlot].type != -1) { currentInventory->workbench[workbenchSlot].count --; if(currentInventory->workbench[workbenchSlot].count == 0) { currentInventory->workbench[workbenchSlot] = Item(); } setSlot(user, windowID, workbenchSlot, currentInventory->workbench[workbenchSlot].type, currentInventory->workbench[workbenchSlot].count, currentInventory->workbench[workbenchSlot].health); } } workbenchCrafting = true; } else { for(uint8_t playerSlot = 1; playerSlot < 5; playerSlot++) { if(user->inv[playerSlot].type != -1) { user->inv[playerSlot].count --; if(user->inv[playerSlot].count == 0) { user->inv[playerSlot] = Item(); } setSlot(user, windowID, playerSlot, user->inv[playerSlot].type, user->inv[playerSlot].count, user->inv[playerSlot].health); } } playerCrafting = true; } } } else { //ToDo: Make sure we have room for the items! //Make sure not putting anything to the crafting space if((windowID != WINDOW_WORKBENCH && windowID != WINDOW_PLAYER) || slot != 0) { int16_t addCount = (64-slotItem->count>=user->inventoryHolding.count)?user->inventoryHolding.count:64-slotItem->count; slotItem->count += ((rightClick)?1:addCount); slotItem->health = user->inventoryHolding.health; slotItem->type = user->inventoryHolding.type; user->inventoryHolding.count -= ((rightClick)?1:addCount); if(user->inventoryHolding.count == 0) { user->inventoryHolding.type = -1; user->inventoryHolding.health= 0; } } } } else if(user->inventoryHolding.type == -1) { //If accessing crafting output slot, remove from input! if(slotItem->type != -1 && (windowID == WINDOW_WORKBENCH || windowID == WINDOW_PLAYER) && slot == 0) { user->inventoryHolding.type = slotItem->type; user->inventoryHolding.count = slotItem->count; user->inventoryHolding.health = slotItem->health; if(windowID == WINDOW_WORKBENCH) { for(uint8_t workbenchSlot = 1; workbenchSlot < 10; workbenchSlot++) { if(currentInventory->workbench[workbenchSlot].type != -1) { currentInventory->workbench[workbenchSlot].count --; if(currentInventory->workbench[workbenchSlot].count == 0) { currentInventory->workbench[workbenchSlot] = Item(); } setSlot(user, windowID, workbenchSlot,currentInventory->workbench[workbenchSlot].type, currentInventory->workbench[workbenchSlot].count, currentInventory->workbench[workbenchSlot].health); } } workbenchCrafting = true; } else { for(uint8_t playerSlot = 1; playerSlot < 5; playerSlot++) { if(user->inv[playerSlot].type != -1) { user->inv[playerSlot].count --; if(user->inv[playerSlot].count == 0) { user->inv[playerSlot] = Item(); } setSlot(user, windowID, playerSlot, user->inv[playerSlot].type, user->inv[playerSlot].count, user->inv[playerSlot].health); } } playerCrafting = true; } } else { user->inventoryHolding.type = slotItem->type; user->inventoryHolding.health = slotItem->health; user->inventoryHolding.count = slotItem->count; if(rightClick == 1) { user->inventoryHolding.count -= slotItem->count>>1; } slotItem->count -= user->inventoryHolding.count; if(slotItem->count == 0) { slotItem->health = 0; slotItem->type =-1; } } }
bool Inventory::windowClick(User* user, int8_t windowID, int16_t slot, int8_t rightClick, int16_t actionNumber, int16_t itemID, int8_t itemCount, int16_t itemUses, int8_t shift) { //Ack user->buffer << Protocol::transaction( (int8_t)windowID, (int16_t)actionNumber, (int8_t)1 ); //Click outside the window if (slot == -999) { if (user->inventoryHolding.getType() != -1) { Mineserver::get()->map(user->pos.map)->createPickupSpawn((int)user->pos.x, (int)user->pos.y, (int)user->pos.z, user->inventoryHolding.getType(), user->inventoryHolding.getCount(), user->inventoryHolding.getHealth(), user, false); user->inventoryHolding.setType(-1); } return true; } if (!user->isOpenInv && windowID != 0) { return false; } sChunk* chunk = NULL; if (windowID != 0) { chunk = Mineserver::get()->map(user->pos.map)->getChunk(blockToChunk(user->openInv.x), blockToChunk(user->openInv.z)); if (chunk == NULL) { return false; } chunk->changed = true; } std::vector<User*>* otherUsers = NULL; OpenInvPtr currentInventory; if (windowID != WINDOW_PLAYER) { std::vector<OpenInvPtr>* pinv = NULL; switch (user->openInv.type) { case WINDOW_CHEST: case WINDOW_LARGE_CHEST: pinv = &openChests; break; case WINDOW_FURNACE: pinv = &openFurnaces; break; case WINDOW_WORKBENCH: pinv = &openWorkbenches; break; } std::vector<OpenInvPtr>& inv = *pinv; for (size_t i = 0; i < inv.size(); i++) { if (inv[i]->x == user->openInv.x && inv[i]->y == user->openInv.y && inv[i]->z == user->openInv.z) { otherUsers = &inv[i]->users; currentInventory = inv[i]; break; } } if (otherUsers == NULL || currentInventory == NULL) { return false; } } Item* slotItem; furnaceDataPtr tempFurnace; switch (windowID) { //Player inventory case WINDOW_PLAYER: slotItem = &user->inv[slot]; break; case WINDOW_CHEST: if (slot > 26) { slotItem = &user->inv[slot - 18]; } else { for (uint32_t i = 0; i < chunk->chests.size(); i ++) { if (chunk->chests[i]->x() == user->openInv.x && chunk->chests[i]->y() == user->openInv.y && chunk->chests[i]->z() == user->openInv.z) { slotItem = (*chunk->chests[i]->items())[slot].get(); break; } } //Create chest data if it doesn't exist if (slotItem == NULL) { chestDataPtr newChest(new chestData); newChest->x(user->openInv.x); newChest->y(user->openInv.y); newChest->z(user->openInv.z); chunk->chests.push_back(newChest); slotItem = (*newChest->items())[slot].get(); } } break; case WINDOW_LARGE_CHEST: if (slot > 54) { slotItem = &user->inv[slot - 45]; } else { //ToDo: Handle large chest for (uint32_t i = 0; i < chunk->chests.size(); i++) { //if(!chunk->chests[i]->large()) // continue; if(chunk->chests[i]->x() == user->openInv.x && chunk->chests[i]->y() == user->openInv.y && chunk->chests[i]->z() == user->openInv.z) { slotItem = (*chunk->chests[i]->items())[slot].get(); break; } } if(slotItem == NULL) { chestDataPtr newChest(new chestData); newChest->x(user->openInv.x); newChest->y(user->openInv.y); newChest->z(user->openInv.z); newChest->large(true); chunk->chests.push_back(newChest); slotItem = (*newChest->items())[slot].get(); } } break; case WINDOW_FURNACE: if (slot >= 3) { slotItem = &user->inv[slot + 6]; } else { for (uint32_t i = 0; i < chunk->furnaces.size(); i ++) { if (chunk->furnaces[i]->x == user->openInv.x && chunk->furnaces[i]->y == user->openInv.y && chunk->furnaces[i]->z == user->openInv.z) { slotItem = &chunk->furnaces[i]->items[slot]; tempFurnace = chunk->furnaces[i]; } } //Create furnace data if it doesn't exist if (slotItem == NULL) { furnaceDataPtr newFurnace(new furnaceData); newFurnace->x = user->openInv.x; newFurnace->y = user->openInv.y; newFurnace->z = user->openInv.z; newFurnace->burnTime = 0; newFurnace->cookTime = 0; chunk->furnaces.push_back(newFurnace); slotItem = &newFurnace->items[slot]; tempFurnace = newFurnace; } } break; case WINDOW_WORKBENCH: if (slot > 9) { slotItem = &user->inv[slot - 1]; } else { slotItem = ¤tInventory->workbench[slot]; } break; } bool workbenchCrafting = false; bool playerCrafting = false; if (windowID == WINDOW_PLAYER && slot >= 5 && slot <= 8) { // Armour slots are a strange case. Only a quantity of one should be allowed, so this must be checked for. if (slotItem->getType() == -1 && user->inventoryHolding.getType() > 0) { if (canBeArmour(slot, user->inventoryHolding.getType())) { slotItem->setType(user->inventoryHolding.getType()); slotItem->setHealth(user->inventoryHolding.getHealth()); slotItem->setCount(1); user->inventoryHolding.decCount(); } else { slotItem->decCount(0); user->inventoryHolding.decCount(0); // Refresh both } } else if (slotItem->getType() > 0 && user->inventoryHolding.getType() == -1) { user->inventoryHolding.setType(slotItem->getType()); user->inventoryHolding.setCount(slotItem->getCount()); user->inventoryHolding.setHealth(slotItem->getHealth()); slotItem->setType(-1); } else if (slotItem->getType() > 0 && user->inventoryHolding.getType() > 0 && user->inventoryHolding.getCount() == 1) { if (canBeArmour(slot, user->inventoryHolding.getType())) { uint16_t t_type = slotItem->getType(); uint8_t t_count = slotItem->getCount(); uint16_t t_health = slotItem->getHealth(); slotItem->setCount(1); slotItem->setHealth(user->inventoryHolding.getHealth()); slotItem->setType(user->inventoryHolding.getType()); user->inventoryHolding.setCount(t_count); user->inventoryHolding.setHealth(t_health); user->inventoryHolding.setType(t_type); } else { slotItem->decCount(0); user->inventoryHolding.decCount(0); // Refresh both } } setSlot(user, WINDOW_CURSOR, 0, user->inventoryHolding.getType(), user->inventoryHolding.getCount(), user->inventoryHolding.getHealth()); return false; } //Empty slot and holding something if ((slotItem->getType() == -1 || (slotItem->getType() == user->inventoryHolding.getType() && slotItem->getHealth() == user->inventoryHolding.getHealth() && slotItem->getCount() < 64)) && user->inventoryHolding.getType() != -1) { //If accessing crafting output slot if (slotItem->getType() != -1 && (windowID == WINDOW_WORKBENCH || windowID == WINDOW_PLAYER) && slot == 0) { if (user->inventoryHolding.getType() == slotItem->getType() && 64 - user->inventoryHolding.getCount() >= slotItem->getCount()) { user->inventoryHolding.decCount(-slotItem->getCount()); if (windowID == WINDOW_WORKBENCH) { for (uint8_t workbenchSlot = 1; workbenchSlot < 10; workbenchSlot++) { if (currentInventory->workbench[workbenchSlot].getType() != -1) { currentInventory->workbench[workbenchSlot].decCount(); setSlot(user, windowID, workbenchSlot, currentInventory->workbench[workbenchSlot].getType(), currentInventory->workbench[workbenchSlot].getCount(), currentInventory->workbench[workbenchSlot].getHealth()); } } workbenchCrafting = true; } else { for (uint8_t playerSlot = 1; playerSlot < 5; playerSlot++) { if (user->inv[playerSlot].getType() != -1) { user->inv[playerSlot].decCount(); setSlot(user, windowID, playerSlot, user->inv[playerSlot].getType(), user->inv[playerSlot].getCount(), user->inv[playerSlot].getHealth()); } } playerCrafting = true; } } } else { //ToDo: Make sure we have room for the items! //Make sure not putting anything to the crafting space if ((windowID != WINDOW_WORKBENCH && windowID != WINDOW_PLAYER) || slot != 0) { int16_t addCount = (64 - slotItem->getCount() >= user->inventoryHolding.getCount()) ? user->inventoryHolding.getCount() : 64 - slotItem->getCount(); slotItem->decCount(0 - ((rightClick) ? 1 : addCount)); slotItem->setHealth(user->inventoryHolding.getHealth()); slotItem->setType(user->inventoryHolding.getType()); user->inventoryHolding.decCount((rightClick) ? 1 : addCount); } } } else if (user->inventoryHolding.getType() == -1) { //If accessing crafting output slot, remove from input! if (slotItem->getType() != -1 && (windowID == WINDOW_WORKBENCH || windowID == WINDOW_PLAYER) && slot == 0) { user->inventoryHolding.setType(slotItem->getType()); user->inventoryHolding.setCount(slotItem->getCount()); user->inventoryHolding.setHealth(slotItem->getHealth()); if (windowID == WINDOW_WORKBENCH) { for (uint8_t workbenchSlot = 1; workbenchSlot < 10; workbenchSlot++) { if (currentInventory->workbench[workbenchSlot].getType() != -1) { currentInventory->workbench[workbenchSlot].decCount(); setSlot(user, windowID, workbenchSlot, currentInventory->workbench[workbenchSlot].getType(), currentInventory->workbench[workbenchSlot].getCount(), currentInventory->workbench[workbenchSlot].getHealth()); } } workbenchCrafting = true; } else { for (uint8_t playerSlot = 1; playerSlot < 5; playerSlot++) { if (user->inv[playerSlot].getType() != -1) { user->inv[playerSlot].decCount(); } } playerCrafting = true; } } else { user->inventoryHolding.setType(slotItem->getType()); user->inventoryHolding.setHealth(slotItem->getHealth()); user->inventoryHolding.setCount(slotItem->getCount()); if (rightClick == 1) { user->inventoryHolding.decCount(slotItem->getCount() >> 1); } slotItem->decCount(user->inventoryHolding.getCount()); if (slotItem->getCount() == 0) { slotItem->setHealth(0); slotItem->setType(-1); } } }
void MapGen::ExpandBeaches(int x, int z, int map) { sChunk *chunk = Mineserver::get()->map(map)->chunks.getChunk(blockToChunk(x),blockToChunk(z)); int beachExtentSqr = (beachExtent + 1) * (beachExtent + 1); int xBlockpos = x<<4; int zBlockpos = z<<4; int blockX, blockZ, h; uint8_t block = 0; uint8_t meta = 0; for(int bX = 0; bX < 16; bX++) { for(int bZ = 0; bZ < 16; bZ++) { blockX = xBlockpos+bX; blockZ = zBlockpos+bZ; h = heightmap[(bZ<<4)+bX]; if(h < 0) continue; bool found = false; for(int dx = -beachExtent; !found && dx <= beachExtent; dx++) { for(int dz = -beachExtent; !found && dz <= beachExtent; dz++) { for(int dh = -beachHeight; !found && dh <= 0; dh++) { if(dx * dx + dz * dz + dh * dh > beachExtentSqr) continue; int xx = bX + dx; int zz = bZ + dz; int hh = h + dh; if(xx < 0 || xx >= 15 || zz < 0 || zz >= 15 || hh < 0 || hh >= 127 ) continue; //ToDo: add getBlock!! if( block == BLOCK_WATER || block == BLOCK_STATIONARY_WATER ) { found = true; break; } } } } if(found) { Mineserver::get()->map(map)->sendBlockChange(blockX, h, blockZ, BLOCK_SAND, 0); Mineserver::get()->map(map)->setBlock(blockX, h, blockZ, BLOCK_SAND, 0); Mineserver::get()->map(map)->getBlock(blockX, h-1, blockZ, &block, &meta); if( h > 0 && block == BLOCK_DIRT ) { Mineserver::get()->map(map)->sendBlockChange(blockX, h-1, blockZ, BLOCK_SAND, 0); Mineserver::get()->map(map)->setBlock(blockX, h-1, blockZ, BLOCK_SAND, 0); } } } } }
int PacketHandler::change_sign(User* user) { if (!user->buffer.haveData(16)) { return PACKET_NEED_MORE_DATA; } int32_t x, z; int16_t y; std::string strings1, strings2, strings3, strings4; user->buffer >> x >> y >> z; if (!user->buffer.haveData(8)) { return PACKET_NEED_MORE_DATA; } user->buffer >> strings1; if (!user->buffer.haveData(6)) { return PACKET_NEED_MORE_DATA; } user->buffer >> strings2; if (!user->buffer.haveData(4)) { return PACKET_NEED_MORE_DATA; } user->buffer >> strings3; if (!user->buffer.haveData(2)) { return PACKET_NEED_MORE_DATA; } user->buffer >> strings4; //ToDo: Save signs! signDataPtr newSign(new signData); newSign->x = x; newSign->y = y; newSign->z = z; newSign->text1 = strings1; newSign->text2 = strings2; newSign->text3 = strings3; newSign->text4 = strings4; sChunk* chunk = ServerInstance->map(user->pos.map)->getChunk(blockToChunk(x), blockToChunk(z)); if (chunk != NULL) { //Check if this sign data already exists and remove chunk->signs.erase(std::remove_if(chunk->signs.begin(), chunk->signs.end(), DataFinder<signData>(x,y,z)), chunk->signs.end()); // Insert new sign chunk->signs.push_back(newSign); //Send sign packet to everyone Packet pkt; pkt << (int8_t)PACKET_SIGN << x << y << z; pkt << strings1 << strings2 << strings3 << strings4; user->sendAll(pkt); } LOG2(INFO, "Sign: " + strings1 + strings2 + strings3 + strings4); //No need to do anything user->buffer.removePacket(); return PACKET_OK; }
bool User::updatePos(double x, double y, double z, double stance) { if(nick.size() && logged) { //Do we send relative or absolute move values if(0) //abs(x-this->pos.x)<127 //&& abs(y-this->pos.y)<127 //&& abs(z-this->pos.z)<127) { uint8 movedata[8]; movedata[0] = 0x1f; //Relative move putSint32(&movedata[1], (sint32)this->UID); movedata[5] = (char)(x-this->pos.x); movedata[6] = (char)(y-this->pos.y); movedata[7] = (char)(z-this->pos.z); this->sendOthers(&movedata[0], 8); } else { this->pos.x = x; this->pos.y = y; this->pos.z = z; this->pos.stance = stance; uint8 teleportData[19]; teleportData[0] = 0x22; //Teleport putSint32(&teleportData[1], this->UID); putSint32(&teleportData[5], (int)(this->pos.x*32)); putSint32(&teleportData[9], (int)(this->pos.y*32)); putSint32(&teleportData[13], (int)(this->pos.z*32)); teleportData[17] = (char)this->pos.yaw; teleportData[18] = (char)this->pos.pitch; this->sendOthers(&teleportData[0], 19); } //Check if there are items in this chunk! sint32 chunk_x = blockToChunk((sint32)x); sint32 chunk_z = blockToChunk((sint32)z); uint32 chunkHash; Map::get().posToId(chunk_x, chunk_z, &chunkHash); if(Map::get().mapItems.count(chunkHash)) { //Loop through items and check if they are close enought to be picked up for(sint32 i = Map::get().mapItems[chunkHash].size()-1; i >= 0; i--) { //No more than 2 blocks away if(abs((sint32)x-Map::get().mapItems[chunkHash][i]->pos.x()/32) < 2 && abs((sint32)z-Map::get().mapItems[chunkHash][i]->pos.z()/32) < 2 && abs((sint32)y-Map::get().mapItems[chunkHash][i]->pos.y()/32) < 2) { //Dont pickup own spawns right away if(Map::get().mapItems[chunkHash][i]->spawnedBy != this->UID || Map::get().mapItems[chunkHash][i]->spawnedAt+2 < time(0)) { //Check player inventory for space! if(checkInventory(Map::get().mapItems[chunkHash][i]->item, Map::get().mapItems[chunkHash][i]->count)) { //Send player collect item packet uint8 *packet = new uint8[9]; packet[0] = PACKET_COLLECT_ITEM; putSint32(&packet[1], Map::get().mapItems[chunkHash][i]->EID); putSint32(&packet[5], this->UID); buffer.addToWrite(packet, 9); //Send everyone destroy_entity-packet packet[0] = PACKET_DESTROY_ENTITY; putSint32(&packet[1], Map::get().mapItems[chunkHash][i]->EID); //ToDo: Only send users in range this->sendAll(packet, 5); packet[0] = PACKET_ADD_TO_INVENTORY; putSint16(&packet[1], Map::get().mapItems[chunkHash][i]->item); packet[3] = Map::get().mapItems[chunkHash][i]->count; putSint16(&packet[4], Map::get().mapItems[chunkHash][i]->health); buffer.addToWrite(packet, 6); //We're done, release packet memory delete[] packet; Map::get().items.erase(Map::get().mapItems[chunkHash][i]->EID); delete Map::get().mapItems[chunkHash][i]; Map::get().mapItems[chunkHash].erase(Map::get().mapItems[chunkHash].begin()+i); } } } } } //Chunk position changed, check for map updates if((int)(x/16) != curChunk.x() || (int)(z/16) != curChunk.z()) { //This is not accurate chunk!! curChunk.x() = (int)(x/16); curChunk.z() = (int)(z/16); for(int mapx = -viewDistance+curChunk.x(); mapx <= viewDistance+curChunk.x(); mapx++) { for(int mapz = -viewDistance+curChunk.z(); mapz <= viewDistance+curChunk.z(); mapz++) { addQueue(mapx, mapz); } } for(unsigned int i = 0; i < mapKnown.size(); i++) { //If client has map data more than viesDistance+1 chunks away, remove it if(mapKnown[i].x() < curChunk.x()-viewDistance-1 || mapKnown[i].x() > curChunk.x()+viewDistance+1 || mapKnown[i].z() < curChunk.z()-viewDistance-1 || mapKnown[i].z() > curChunk.z()+viewDistance+1) addRemoveQueue(mapKnown[i].x(), mapKnown[i].z()); } } } this->pos.x = x; this->pos.y = y; this->pos.z = z; this->pos.stance = stance; return true; }
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; }
int PacketHandler::change_sign(User *user) { if(!user->buffer.haveData(16)) return PACKET_NEED_MORE_DATA; int32_t x,z; int16_t y; std::string strings1,strings2,strings3,strings4; user->buffer >> x >> y >> z; if(!user->buffer.haveData(8)) return PACKET_NEED_MORE_DATA; user->buffer >> strings1; if(!user->buffer.haveData(6)) return PACKET_NEED_MORE_DATA; user->buffer >> strings2; if(!user->buffer.haveData(4)) return PACKET_NEED_MORE_DATA; user->buffer >> strings3; if(!user->buffer.haveData(2)) return PACKET_NEED_MORE_DATA; user->buffer >> strings4; //ToDo: Save signs! signData *newSign = new signData; newSign->x = x; newSign->y = y; newSign->z = z; newSign->text1 = strings1; newSign->text2 = strings2; newSign->text3 = strings3; newSign->text4 = strings4; sChunk* chunk = Mineserver::get()->map(user->pos.map)->chunks.getChunk(blockToChunk(x),blockToChunk(z)); if(chunk != NULL) { //Check if this sign data already exists and remove for(uint32_t i = 0; i < chunk->signs.size(); i++) { if(chunk->signs[i]->x == x && chunk->signs[i]->y == y && chunk->signs[i]->z == z) { //Erase existing data delete chunk->signs[i]; chunk->signs.erase(chunk->signs.begin()+i); break; } } chunk->signs.push_back(newSign); //Send sign packet to everyone Packet pkt; pkt << (int8_t)PACKET_SIGN << x << y << z; pkt << strings1 << strings2 << strings3 << strings4; user->sendAll((uint8_t *)pkt.getWrite(), pkt.getWriteLen()); } Mineserver::get()->logger()->log(LogType::LOG_INFO, "Packets", "Sign: " + strings1 + strings2 + strings3 + strings4); //No need to do anything user->buffer.removePacket(); return PACKET_OK; }