diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 3af9c195a4a..97746c09fcb 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -7614,6 +7614,13 @@ void Player::sendTakeScreenshot(Screenshot_t screenshotType) const { void Player::onThink(uint32_t interval) { Creature::onThink(interval); + currentTime = std::chrono::steady_clock::now(); + currentTimeMillis = std::chrono::duration_cast(currentTime.time_since_epoch()).count(); + if (updateInventory && !updatedItems.empty()) { + sendInventoryIds(); + updatedItems.clear(); + updateInventory = false; + } sendPing(); MessageBufferTicks += interval; @@ -7661,6 +7668,8 @@ void Player::postAddNotification(const std::shared_ptr &thing, const std: g_moveEvents().onPlayerEquip(getPlayer(), thing->getItem(), static_cast(index), false); } + currentTime = std::chrono::steady_clock::now(); + currentTimeMillis = std::chrono::duration_cast(currentTime.time_since_epoch()).count(); bool requireListUpdate = true; if (link == LINK_OWNER || link == LINK_TOPPARENT) { const auto &item = oldParent ? oldParent->getItem() : nullptr; @@ -7673,7 +7682,13 @@ void Player::postAddNotification(const std::shared_ptr &thing, const std: updateInventoryWeight(); updateItemsLight(); - sendInventoryIds(); + if (std::shared_ptr item = thing->getItem()) { + updatedItems.push_back(item); + } + if (updatedItems.size() >= maxUpdatesPerBatch || (currentTimeMillis - lastUpdateTime) > updateCooldown) { + updateInventory = true; + lastUpdateTime = currentTimeMillis; + } sendStats(); } @@ -7721,8 +7736,10 @@ void Player::postRemoveNotification(const std::shared_ptr &thing, const s g_moveEvents().onPlayerDeEquip(getPlayer(), item, static_cast(index)); } } - bool requireListUpdate = true; + currentTime = std::chrono::steady_clock::now(); + currentTimeMillis = std::chrono::duration_cast(currentTime.time_since_epoch()).count(); + bool requireListUpdate = true; if (link == LINK_OWNER || link == LINK_TOPPARENT) { const auto &item = copyNewParent ? copyNewParent->getItem() : nullptr; const auto &container = item ? item->getContainer() : nullptr; @@ -7734,7 +7751,14 @@ void Player::postRemoveNotification(const std::shared_ptr &thing, const s updateInventoryWeight(); updateItemsLight(); - sendInventoryIds(); + if (std::shared_ptr item = thing->getItem()) { + updatedItems.push_back(item); + } + + if (updatedItems.size() >= maxUpdatesPerBatch || (currentTimeMillis - lastUpdateTime) > updateCooldown) { + updateInventory = true; + lastUpdateTime = currentTimeMillis; + } sendStats(); } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 5962a337c0d..0c4f0b105e0 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -945,7 +945,13 @@ class Player final : public Creature, public Cylinder, public Bankable { void sendTakeScreenshot(Screenshot_t screenshotType) const; void onThink(uint32_t interval) override; - + bool updateInventory = false; + std::vector> updatedItems; + const size_t maxUpdatesPerBatch = 100; + unsigned int lastUpdateTime = 0; + unsigned int updateCooldown = 500; + std::chrono::steady_clock::time_point currentTime; + long long currentTimeMillis = 0; void postAddNotification(const std::shared_ptr &thing, const std::shared_ptr &oldParent, int32_t index, CylinderLink_t link = LINK_OWNER) override; void postRemoveNotification(const std::shared_ptr &thing, const std::shared_ptr &newParent, int32_t index, CylinderLink_t link = LINK_OWNER) override; diff --git a/src/game/game.cpp b/src/game/game.cpp index 910c3896ecc..be73bcf2543 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -2930,15 +2930,18 @@ void Game::playerQuickLootCorpse(const std::shared_ptr &player, const st return; } - std::vector> itemList; bool ignoreListItems = (player->quickLootFilter == QUICKLOOTFILTER_SKIPPEDLOOT); - bool missedAnyGold = false; bool missedAnyItem = false; + bool shouldNotifyCapacity = false; + ObjectCategory_t shouldNotifyNotEnoughRoom = OBJECTCATEGORY_NONE; + uint32_t totalLootedGold = 0; + uint32_t totalLootedItems = 0; for (ContainerIterator it = corpse->iterator(); it.hasNext(); it.advance()) { const auto &item = *it; bool listed = player->isQuickLootListedItem(item); + if ((listed && ignoreListItems) || (!listed && !ignoreListItems)) { if (item->getWorth() != 0) { missedAnyGold = true; @@ -2948,15 +2951,6 @@ void Game::playerQuickLootCorpse(const std::shared_ptr &player, const st continue; } - itemList.push_back(item); - } - - bool shouldNotifyCapacity = false; - ObjectCategory_t shouldNotifyNotEnoughRoom = OBJECTCATEGORY_NONE; - - uint32_t totalLootedGold = 0; - uint32_t totalLootedItems = 0; - for (const std::shared_ptr &item : itemList) { uint32_t worth = item->getWorth(); uint16_t baseCount = item->getItemCount(); ObjectCategory_t category = getObjectCategory(item); @@ -2975,7 +2969,6 @@ void Game::playerQuickLootCorpse(const std::shared_ptr &player, const st player->sendLootStats(item, baseCount); totalLootedGold += worth; } else { - // item is not completely moved totalLootedGold += worth - item->getWorth(); } } else { @@ -2987,78 +2980,79 @@ void Game::playerQuickLootCorpse(const std::shared_ptr &player, const st } } - std::stringstream ss; - if (totalLootedGold != 0 || missedAnyGold || totalLootedItems != 0 || missedAnyItem) { - bool lootedAllGold = totalLootedGold != 0 && !missedAnyGold; - bool lootedAllItems = totalLootedItems != 0 && !missedAnyItem; - if (lootedAllGold) { - if (totalLootedItems != 0 || missedAnyItem) { - ss << "You looted the complete " << totalLootedGold << " gold"; - - if (lootedAllItems) { - ss << " and all dropped items"; - } else if (totalLootedItems != 0) { - ss << ", but you only looted some of the items"; - } else if (missedAnyItem) { - ss << " but none of the dropped items"; - } - } else { - ss << "You looted " << totalLootedGold << " gold"; - } - } else if (lootedAllItems) { - if (totalLootedItems == 1) { - ss << "You looted 1 item"; - } else if (totalLootedGold != 0 || missedAnyGold) { - ss << "You looted all of the dropped items"; - } else { - ss << "You looted all items"; - } + if (!(totalLootedGold != 0 || missedAnyGold || totalLootedItems != 0 || missedAnyItem)) { + player->sendTextMessage(MESSAGE_STATUS, "No loot."); + return; + } - if (totalLootedGold != 0) { - ss << ", but you only looted " << totalLootedGold << " of the dropped gold"; - } else if (missedAnyGold) { - ss << " but none of the dropped gold"; - } - } else if (totalLootedGold != 0) { - ss << "You only looted " << totalLootedGold << " of the dropped gold"; - if (totalLootedItems != 0) { - ss << " and some of the dropped items"; + std::string message; + message.reserve(128); + + bool lootedAllGold = totalLootedGold != 0 && !missedAnyGold; + bool lootedAllItems = totalLootedItems != 0 && !missedAnyItem; + + if (lootedAllGold) { + message = "You looted the complete " + std::to_string(totalLootedGold) + " gold"; + + if (totalLootedItems != 0 || missedAnyItem) { + if (lootedAllItems) { + message += " and all dropped items"; + } else if (totalLootedItems != 0) { + message += ", but you only looted some of the items"; } else if (missedAnyItem) { - ss << " but none of the dropped items"; - } - } else if (totalLootedItems != 0) { - ss << "You looted some of the dropped items"; - if (missedAnyGold) { - ss << " but none of the dropped gold"; + message += " but none of the dropped items"; } + } + } else if (lootedAllItems) { + if (totalLootedItems == 1) { + message = "You looted 1 item"; + } else if (totalLootedGold != 0 || missedAnyGold) { + message = "You looted all of the dropped items"; + } else { + message = "You looted all items"; + } + + if (totalLootedGold != 0) { + message += ", but you only looted " + std::to_string(totalLootedGold) + " of the dropped gold"; } else if (missedAnyGold) { - ss << "You looted none of the dropped gold"; - if (missedAnyItem) { - ss << " and none of the items"; - } + message += " but none of the dropped gold"; + } + } else if (totalLootedGold != 0) { + message = "You only looted " + std::to_string(totalLootedGold) + " of the dropped gold"; + if (totalLootedItems != 0) { + message += " and some of the dropped items"; } else if (missedAnyItem) { - ss << "You looted none of the dropped items"; + message += " but none of the dropped items"; } - } else { - ss << "No loot"; + } else if (totalLootedItems != 0) { + message = "You looted some of the dropped items"; + if (missedAnyGold) { + message += " but none of the dropped gold"; + } + } else if (missedAnyGold) { + message = "You looted none of the dropped gold"; + if (missedAnyItem) { + message += " and none of the items"; + } + } else if (missedAnyItem) { + message = "You looted none of the dropped items"; } - ss << "."; - player->sendTextMessage(MESSAGE_STATUS, ss.str()); + + message += "."; + player->sendTextMessage(MESSAGE_STATUS, message); if (shouldNotifyCapacity) { - ss.str(std::string()); - ss << "Attention! The loot you are trying to pick up is too heavy for you to carry."; + message = "Attention! The loot you are trying to pick up is too heavy for you to carry."; } else if (shouldNotifyNotEnoughRoom != OBJECTCATEGORY_NONE) { - ss.str(std::string()); - ss << "Attention! The container assigned to category " << getObjectCategoryName(shouldNotifyNotEnoughRoom) << " is full."; + message = "Attention! The container assigned to category " + std::string(getObjectCategoryName(shouldNotifyNotEnoughRoom)) + " is full."; } else { return; } if (player->lastQuickLootNotification + 15000 < OTSYS_TIME()) { - player->sendTextMessage(MESSAGE_GAME_HIGHLIGHT, ss.str()); + player->sendTextMessage(MESSAGE_GAME_HIGHLIGHT, message); } else { - player->sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str()); + player->sendTextMessage(MESSAGE_EVENT_ADVANCE, message); } player->lastQuickLootNotification = OTSYS_TIME(); @@ -5696,52 +5690,52 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item } } -void Game::playerLootAllCorpses(const std::shared_ptr &player, const Position &pos, bool lootAllCorpses) { - if (lootAllCorpses) { - std::shared_ptr tile = g_game().map.getTile(pos.x, pos.y, pos.z); - if (!tile) { - player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); - return; - } +void Game::playerLootAllCorpses(std::shared_ptr player, const Position &pos, bool lootAllCorpses) { + if (!lootAllCorpses) { + browseField = false; + return; + } - const TileItemVector* itemVector = tile->getItemList(); - uint16_t corpses = 0; - for (auto &tileItem : *itemVector) { - if (!tileItem) { - continue; - } + std::shared_ptr tile = g_game().map.getTile(pos.x, pos.y, pos.z); + if (!tile) { + return; + } - std::shared_ptr tileCorpse = tileItem->getContainer(); - if (!tileCorpse || !tileCorpse->isCorpse() || tileCorpse->hasAttribute(ItemAttribute_t::UNIQUEID) || tileCorpse->hasAttribute(ItemAttribute_t::ACTIONID)) { - continue; - } + const TileItemVector* itemVector = tile->getItemList(); + uint16_t corpses = 0; + constexpr uint16_t maxCorpses = 30; - if (!tileCorpse->isRewardCorpse() - && tileCorpse->getCorpseOwner() != 0 - && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) { - player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); - g_logger().debug("Player {} cannot loot corpse from id {} in position {}", player->getName(), tileItem->getID(), tileItem->getPosition().toString()); - continue; - } + std::string message; + message.reserve(128); - corpses++; - playerQuickLootCorpse(player, tileCorpse, tileCorpse->getPosition()); - if (corpses >= 30) { - break; - } + for (auto &tileItem : *itemVector) { + if (!tileItem) { + continue; } - if (corpses > 0) { - if (corpses > 1) { - std::stringstream string; - string << "You looted " << corpses << " corpses."; - player->sendTextMessage(MESSAGE_LOOT, string.str()); - } + std::shared_ptr tileCorpse = tileItem->getContainer(); + if (!tileCorpse || !tileCorpse->isCorpse() || tileCorpse->hasAttribute(ItemAttribute_t::UNIQUEID) || tileCorpse->hasAttribute(ItemAttribute_t::ACTIONID)) { + continue; + } - return; + if (!tileCorpse->isRewardCorpse() && tileCorpse->getCorpseOwner() != 0 && !player->canOpenCorpse(tileCorpse->getCorpseOwner())) { + g_logger().debug("Player {} cannot loot corpse from id {} in position {}", player->getName(), tileItem->getID(), tileItem->getPosition().toString()); + continue; + } + + playerQuickLootCorpse(player, tileCorpse, tileCorpse->getPosition()); + + if (++corpses >= maxCorpses) { + break; } } + if (corpses > 0) { + std::stringstream string; + string << "You looted " << corpses << (corpses > 1 ? " corpses." : " corpse."); + player->sendTextMessage(MESSAGE_LOOT, string.str()); + } + browseField = false; }