Reduce update-object and inventory update overhead

This commit is contained in:
Kelsi 2026-02-22 08:37:02 -08:00
parent 37888c666d
commit 0631b9f5dc
2 changed files with 71 additions and 48 deletions

View file

@ -4750,48 +4750,46 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
// Process out-of-range objects first // Process out-of-range objects first
for (uint64_t guid : data.outOfRangeGuids) { for (uint64_t guid : data.outOfRangeGuids) {
if (entityManager.hasEntity(guid)) { auto entity = entityManager.getEntity(guid);
const bool isKnownTransport = transportGuids_.count(guid) > 0; if (!entity) continue;
if (isKnownTransport) {
// Keep transports alive across out-of-range flapping.
// Boats/zeppelins are global movers and removing them here can make
// them disappear until a later movement snapshot happens to recreate them.
const bool playerAboardNow = (playerTransportGuid_ == guid);
const bool stickyAboard = (playerTransportStickyGuid_ == guid && playerTransportStickyTimer_ > 0.0f);
const bool movementSaysAboard = (movementInfo.transportGuid == guid);
LOG_INFO("Preserving transport on out-of-range: 0x",
std::hex, guid, std::dec,
" now=", playerAboardNow,
" sticky=", stickyAboard,
" movement=", movementSaysAboard);
continue;
}
LOG_DEBUG("Entity went out of range: 0x", std::hex, guid, std::dec); const bool isKnownTransport = transportGuids_.count(guid) > 0;
// Trigger despawn callbacks before removing entity if (isKnownTransport) {
auto entity = entityManager.getEntity(guid); // Keep transports alive across out-of-range flapping.
if (entity) { // Boats/zeppelins are global movers and removing them here can make
if (entity->getType() == ObjectType::UNIT && creatureDespawnCallback_) { // them disappear until a later movement snapshot happens to recreate them.
creatureDespawnCallback_(guid); const bool playerAboardNow = (playerTransportGuid_ == guid);
} else if (entity->getType() == ObjectType::PLAYER && playerDespawnCallback_) { const bool stickyAboard = (playerTransportStickyGuid_ == guid && playerTransportStickyTimer_ > 0.0f);
playerDespawnCallback_(guid); const bool movementSaysAboard = (movementInfo.transportGuid == guid);
otherPlayerVisibleItemEntries_.erase(guid); LOG_INFO("Preserving transport on out-of-range: 0x",
otherPlayerVisibleDirty_.erase(guid); std::hex, guid, std::dec,
otherPlayerMoveTimeMs_.erase(guid); " now=", playerAboardNow,
inspectedPlayerItemEntries_.erase(guid); " sticky=", stickyAboard,
pendingAutoInspect_.erase(guid); " movement=", movementSaysAboard);
} else if (entity->getType() == ObjectType::GAMEOBJECT && gameObjectDespawnCallback_) { continue;
gameObjectDespawnCallback_(guid);
}
}
transportGuids_.erase(guid);
serverUpdatedTransportGuids_.erase(guid);
clearTransportAttachment(guid);
if (playerTransportGuid_ == guid) {
clearPlayerTransport();
}
entityManager.removeEntity(guid);
} }
LOG_DEBUG("Entity went out of range: 0x", std::hex, guid, std::dec);
// Trigger despawn callbacks before removing entity
if (entity->getType() == ObjectType::UNIT && creatureDespawnCallback_) {
creatureDespawnCallback_(guid);
} else if (entity->getType() == ObjectType::PLAYER && playerDespawnCallback_) {
playerDespawnCallback_(guid);
otherPlayerVisibleItemEntries_.erase(guid);
otherPlayerVisibleDirty_.erase(guid);
otherPlayerMoveTimeMs_.erase(guid);
inspectedPlayerItemEntries_.erase(guid);
pendingAutoInspect_.erase(guid);
} else if (entity->getType() == ObjectType::GAMEOBJECT && gameObjectDespawnCallback_) {
gameObjectDespawnCallback_(guid);
}
transportGuids_.erase(guid);
serverUpdatedTransportGuids_.erase(guid);
clearTransportAttachment(guid);
if (playerTransportGuid_ == guid) {
clearPlayerTransport();
}
entityManager.removeEntity(guid);
} }
// Process update blocks // Process update blocks
@ -5466,7 +5464,12 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
} }
// Update XP / inventory slot / skill fields for player entity // Update XP / inventory slot / skill fields for player entity
if (block.guid == playerGuid) { if (block.guid == playerGuid) {
std::map<uint16_t, uint32_t> oldFieldsSnapshot = lastPlayerFields_; const bool needCoinageDetectSnapshot =
(pendingMoneyDelta_ != 0 && pendingMoneyDeltaTimer_ > 0.0f);
std::map<uint16_t, uint32_t> oldFieldsSnapshot;
if (needCoinageDetectSnapshot) {
oldFieldsSnapshot = lastPlayerFields_;
}
if (block.hasMovement && block.runSpeed > 0.1f && block.runSpeed < 100.0f) { if (block.hasMovement && block.runSpeed > 0.1f && block.runSpeed < 100.0f) {
serverRunSpeed_ = block.runSpeed; serverRunSpeed_ = block.runSpeed;
// Some server dismount paths update run speed without updating mount display field. // Some server dismount paths update run speed without updating mount display field.
@ -5480,10 +5483,13 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
} }
} }
} }
auto mergeHint = lastPlayerFields_.end();
for (const auto& [key, val] : block.fields) { for (const auto& [key, val] : block.fields) {
lastPlayerFields_[key] = val; mergeHint = lastPlayerFields_.insert_or_assign(mergeHint, key, val);
}
if (needCoinageDetectSnapshot) {
maybeDetectCoinageIndex(oldFieldsSnapshot, lastPlayerFields_);
} }
maybeDetectCoinageIndex(oldFieldsSnapshot, lastPlayerFields_);
maybeDetectVisibleItemLayout(); maybeDetectVisibleItemLayout();
detectInventorySlotBases(block.fields); detectInventorySlotBases(block.fields);
bool slotsChanged = false; bool slotsChanged = false;
@ -5545,17 +5551,33 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
// Update item stack count for online items // Update item stack count for online items
if (entity->getType() == ObjectType::ITEM || entity->getType() == ObjectType::CONTAINER) { if (entity->getType() == ObjectType::ITEM || entity->getType() == ObjectType::CONTAINER) {
bool inventoryChanged = false;
const uint16_t itemStackField = fieldIndex(UF::ITEM_FIELD_STACK_COUNT);
const uint16_t containerNumSlotsField = fieldIndex(UF::CONTAINER_FIELD_NUM_SLOTS);
const uint16_t containerSlot1Field = fieldIndex(UF::CONTAINER_FIELD_SLOT_1);
for (const auto& [key, val] : block.fields) { for (const auto& [key, val] : block.fields) {
if (key == fieldIndex(UF::ITEM_FIELD_STACK_COUNT)) { if (key == itemStackField) {
auto it = onlineItems_.find(block.guid); auto it = onlineItems_.find(block.guid);
if (it != onlineItems_.end()) it->second.stackCount = val; if (it != onlineItems_.end() && it->second.stackCount != val) {
it->second.stackCount = val;
inventoryChanged = true;
}
} }
} }
// Update container slot GUIDs on bag content changes // Update container slot GUIDs on bag content changes
if (entity->getType() == ObjectType::CONTAINER) { if (entity->getType() == ObjectType::CONTAINER) {
for (const auto& [key, _] : block.fields) {
if ((containerNumSlotsField != 0xFFFF && key == containerNumSlotsField) ||
(containerSlot1Field != 0xFFFF && key >= containerSlot1Field && key < containerSlot1Field + 72)) {
inventoryChanged = true;
break;
}
}
extractContainerFields(block.guid, block.fields); extractContainerFields(block.guid, block.fields);
} }
rebuildOnlineInventory(); if (inventoryChanged) {
rebuildOnlineInventory();
}
} }
if (block.hasMovement && entity->getType() == ObjectType::GAMEOBJECT) { if (block.hasMovement && entity->getType() == ObjectType::GAMEOBJECT) {
if (transportGuids_.count(block.guid) && transportMoveCallback_) { if (transportGuids_.count(block.guid) && transportMoveCallback_) {

View file

@ -1117,7 +1117,8 @@ bool UpdateObjectParser::parseUpdateFields(network::Packet& packet, UpdateBlock&
highestSetBit = fieldIndex; highestSetBit = fieldIndex;
} }
uint32_t value = packet.readUInt32(); uint32_t value = packet.readUInt32();
block.fields[fieldIndex] = value; // fieldIndex is monotonically increasing here, so end() is a good insertion hint.
block.fields.emplace_hint(block.fields.end(), fieldIndex, value);
valuesReadCount++; valuesReadCount++;
LOG_DEBUG(" Field[", fieldIndex, "] = 0x", std::hex, value, std::dec); LOG_DEBUG(" Field[", fieldIndex, "] = 0x", std::hex, value, std::dec);
@ -1256,7 +1257,7 @@ bool UpdateObjectParser::parse(network::Packet& packet, UpdateObjectData& data)
return false; return false;
} }
data.blocks.push_back(block); data.blocks.emplace_back(std::move(block));
} }