feat(Core/Optimization): Optimize build of units update object by leveraging cache (#18637)

* feat(Core/Optimization): Optimize build of units update object by leveraging cache.

* Remove whitespaces.

* Add alternative hooks to handle transmog and other similar things.

* Fix build on some compilers.

* Fix codestyle

* Fix build again.

* Take into account updateType.
This commit is contained in:
Anton Popovichenko
2024-04-05 08:03:11 +02:00
committed by GitHub
parent 94df67b1c2
commit 1f640c9872
15 changed files with 302 additions and 156 deletions

View File

@@ -534,6 +534,8 @@ void Unit::Update(uint32 p_time)
UpdateSplineMovement(p_time);
GetMotionMaster()->UpdateMotion(p_time);
InvalidateValuesUpdateCache();
}
bool Unit::haveOffhandWeapon() const
@@ -21034,16 +21036,11 @@ void Unit::SendMovementHover(Player* sendTo)
sendTo->SendDirectMessage(&data);
}
void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const
void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
{
if (!target)
return;
ByteBuffer fieldBuffer;
UpdateMask updateMask;
updateMask.SetCount(m_valuesCount);
uint32* flags = UnitUpdateFieldFlags;
uint32 visibleFlag = UF_FLAG_PUBLIC;
@@ -21061,7 +21058,30 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
if (plr && plr->IsInSameRaidWith(target))
visibleFlag |= UF_FLAG_PARTY_MEMBER;
Creature const* creature = ToCreature();
uint64 cacheKey = static_cast<uint64>(visibleFlag) << 8 | updateType;
auto cacheIt = _valuesUpdateCache.find(cacheKey);
if (cacheIt != _valuesUpdateCache.end())
{
int32 cachePos = static_cast<int32>(data->wpos());
data->append(cacheIt->second.buffer);
BuildValuesCachePosPointers dataAdjustedPos = cacheIt->second.posPointers;
if (cachePos)
dataAdjustedPos.ApplyOffset(cachePos);
PatchValuesUpdate(*data, dataAdjustedPos, target);
return;
}
BuildValuesCachedBuffer cacheValue(500);
ByteBuffer fieldBuffer(400);
UpdateMask updateMask;
updateMask.SetCount(m_valuesCount);
for (uint16 index = 0; index < m_valuesCount; ++index)
{
if (_fieldNotifyFlags & flags[index] ||
@@ -21073,37 +21093,13 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
if (index == UNIT_NPC_FLAGS)
{
uint32 appendValue = m_uint32Values[UNIT_NPC_FLAGS];
if (creature)
{
if (sWorld->getIntConfig(CONFIG_INSTANT_TAXI) == 2 && appendValue & UNIT_NPC_FLAG_FLIGHTMASTER)
{
appendValue |= UNIT_NPC_FLAG_GOSSIP; // flight masters need NPC gossip flag to show instant flight toggle option
}
if (!target->CanSeeSpellClickOn(creature))
{
appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK;
}
if (!target->CanSeeVendor(creature))
{
appendValue &= ~UNIT_NPC_FLAG_VENDOR_MASK;
}
if (!creature->IsValidTrainerForPlayer(target, &appendValue))
{
appendValue &= ~UNIT_NPC_FLAG_TRAINER;
}
}
fieldBuffer << uint32(appendValue);
cacheValue.posPointers.UnitNPCFlagsPos = int32(fieldBuffer.wpos());
fieldBuffer << m_uint32Values[UNIT_NPC_FLAGS];
}
else if (index == UNIT_FIELD_AURASTATE)
{
// Check per caster aura states to not enable using a spell in client if specified aura is not by target
fieldBuffer << BuildAuraStateUpdateForTarget(target);
cacheValue.posPointers.UnitFieldAuraStatePos = int32(fieldBuffer.wpos());
fieldBuffer << uint32(0); // Fill in later.
}
// FIXME: Some values at server stored in float format but must be sent to client in uint32 format
else if (index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME)
@@ -21122,115 +21118,35 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
// Gamemasters should be always able to select units - remove not selectable flag
else if (index == UNIT_FIELD_FLAGS)
{
uint32 appendValue = m_uint32Values[UNIT_FIELD_FLAGS];
if (target->IsGameMaster() && target->GetSession()->IsGMAccount())
appendValue &= ~UNIT_FLAG_NOT_SELECTABLE;
fieldBuffer << uint32(appendValue);
cacheValue.posPointers.UnitFieldFlagsPos = int32(fieldBuffer.wpos());
fieldBuffer << m_uint32Values[UNIT_FIELD_FLAGS];
}
// use modelid_a if not gm, _h if gm for CREATURE_FLAG_EXTRA_TRIGGER creatures
else if (index == UNIT_FIELD_DISPLAYID)
{
uint32 displayId = m_uint32Values[UNIT_FIELD_DISPLAYID];
if (creature)
{
CreatureTemplate const* cinfo = creature->GetCreatureTemplate();
// this also applies for transform auras
if (SpellInfo const* transform = sSpellMgr->GetSpellInfo(getTransForm()))
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (transform->Effects[i].IsAura(SPELL_AURA_TRANSFORM))
if (CreatureTemplate const* transformInfo = sObjectMgr->GetCreatureTemplate(transform->Effects[i].MiscValue))
{
cinfo = transformInfo;
break;
}
if (cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER)
{
if (target->IsGameMaster() && target->GetSession()->IsGMAccount())
{
if (cinfo->Modelid1)
displayId = cinfo->Modelid1; // Modelid1 is a visible model for gms
else
displayId = 17519; // world visible trigger's model
}
else
{
if (cinfo->Modelid2)
displayId = cinfo->Modelid2; // Modelid2 is an invisible model for players
else
displayId = 11686; // world invisible trigger's model
}
}
}
fieldBuffer << uint32(displayId);
cacheValue.posPointers.UnitFieldDisplayPos = int32(fieldBuffer.wpos());
fieldBuffer << m_uint32Values[UNIT_FIELD_DISPLAYID];
}
// hide lootable animation for unallowed players
else if (index == UNIT_DYNAMIC_FLAGS)
{
cacheValue.posPointers.UnitDynamicFlagsPos = int32(fieldBuffer.wpos());
uint32 dynamicFlags = m_uint32Values[UNIT_DYNAMIC_FLAGS] & ~(UNIT_DYNFLAG_TAPPED | UNIT_DYNFLAG_TAPPED_BY_PLAYER);
if (creature)
{
if (creature->hasLootRecipient())
{
dynamicFlags |= UNIT_DYNFLAG_TAPPED;
if (creature->isTappedBy(target))
dynamicFlags |= UNIT_DYNFLAG_TAPPED_BY_PLAYER;
}
if (!target->isAllowedToLoot(creature))
dynamicFlags &= ~UNIT_DYNFLAG_LOOTABLE;
}
// unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras
if (dynamicFlags & UNIT_DYNFLAG_TRACK_UNIT)
if (!HasAuraTypeWithCaster(SPELL_AURA_MOD_STALKED, target->GetGUID()))
dynamicFlags &= ~UNIT_DYNFLAG_TRACK_UNIT;
fieldBuffer << dynamicFlags;
}
// FG: pretend that OTHER players in own group are friendly ("blue")
else if (index == UNIT_FIELD_BYTES_2 || index == UNIT_FIELD_FACTIONTEMPLATE)
else if (index == UNIT_FIELD_BYTES_2)
{
if (IsControlledByPlayer() && target != this && sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && IsInRaidWith(target))
{
FactionTemplateEntry const* ft1 = GetFactionTemplateEntry();
FactionTemplateEntry const* ft2 = target->GetFactionTemplateEntry();
if (ft1 && ft2 && !ft1->IsFriendlyTo(*ft2))
{
if (index == UNIT_FIELD_BYTES_2)
// Allow targetting opposite faction in party when enabled in config
fieldBuffer << (m_uint32Values[UNIT_FIELD_BYTES_2] & ((UNIT_BYTE2_FLAG_SANCTUARY /*| UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5*/) << 8)); // this flag is at uint8 offset 1 !!
else
// pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work)
fieldBuffer << uint32(target->GetFaction());
}
else
fieldBuffer << m_uint32Values[index];
}// pussywizard / Callmephil
else if (target->IsSpectator() && target->FindMap() && target->FindMap()->IsBattleArena() &&
(this->GetTypeId() == TYPEID_PLAYER || this->GetTypeId() == TYPEID_UNIT || this->GetTypeId() == TYPEID_DYNAMICOBJECT))
{
if (index == UNIT_FIELD_BYTES_2)
fieldBuffer << (m_uint32Values[index] & 0xFFFFF2FF); // clear UNIT_BYTE2_FLAG_PVP, UNIT_BYTE2_FLAG_FFA_PVP, UNIT_BYTE2_FLAG_SANCTUARY
else
fieldBuffer << (uint32)target->GetFaction();
}
else
if (!sScriptMgr->IsCustomBuildValuesUpdate(this, updateType, fieldBuffer, target, index))
{
fieldBuffer << m_uint32Values[index];
}
cacheValue.posPointers.UnitFieldBytes2Pos = int32(fieldBuffer.wpos());
fieldBuffer << m_uint32Values[index];
}
else if (index == UNIT_FIELD_FACTIONTEMPLATE)
{
cacheValue.posPointers.UnitFieldFactionTemplatePos = int32(fieldBuffer.wpos());
fieldBuffer << m_uint32Values[index];
}
else
{
if (sScriptMgr->OnBuildValuesUpdate(this, updateType, fieldBuffer, target, index))
{
continue;
}
if (sScriptMgr->ShouldTrackValuesUpdatePosByIndex(this, updateType, index))
cacheValue.posPointers.other[index] = static_cast<uint32>(fieldBuffer.wpos());
// send in current format (float as float, uint32 as uint32)
fieldBuffer << m_uint32Values[index];
@@ -21238,9 +21154,167 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
}
}
*data << uint8(updateMask.GetBlockCount());
updateMask.AppendToPacket(data);
data->append(fieldBuffer);
cacheValue.buffer << uint8(updateMask.GetBlockCount());
updateMask.AppendToPacket(&cacheValue.buffer);
int32 fieldBufferPos = static_cast<int32>(cacheValue.buffer.wpos());
cacheValue.buffer.append(fieldBuffer);
cacheValue.posPointers.ApplyOffset(fieldBufferPos);
int32 cachePos = static_cast<int32>(data->wpos());
data->append(cacheValue.buffer);
BuildValuesCachePosPointers dataAdjustedPos = cacheValue.posPointers;
if (cachePos)
dataAdjustedPos.ApplyOffset(cachePos);
PatchValuesUpdate(*data, dataAdjustedPos, target);
_valuesUpdateCache.insert(std::pair<uint64, BuildValuesCachedBuffer>(cacheKey, std::move(cacheValue)));
}
void Unit::PatchValuesUpdate(ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPointers& posPointers, Player* target)
{
Creature const* creature = ToCreature();
// UNIT_NPC_FLAGS
if (creature && posPointers.UnitNPCFlagsPos >= 0)
{
uint32 appendValue = m_uint32Values[UNIT_NPC_FLAGS];
if (sWorld->getIntConfig(CONFIG_INSTANT_TAXI) == 2 && appendValue & UNIT_NPC_FLAG_FLIGHTMASTER)
appendValue |= UNIT_NPC_FLAG_GOSSIP; // flight masters need NPC gossip flag to show instant flight toggle option
if (!target->CanSeeSpellClickOn(creature))
appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK;
if (!target->CanSeeVendor(creature))
appendValue &= ~UNIT_NPC_FLAG_VENDOR_MASK;
if (!creature->IsValidTrainerForPlayer(target, &appendValue))
appendValue &= ~UNIT_NPC_FLAG_TRAINER;
valuesUpdateBuf.put(posPointers.UnitNPCFlagsPos, appendValue);
}
// UNIT_FIELD_AURASTATE
if (posPointers.UnitFieldAuraStatePos >= 0)
valuesUpdateBuf.put(posPointers.UnitFieldAuraStatePos, uint32(BuildAuraStateUpdateForTarget(target)));
// UNIT_FIELD_FLAGS
if (posPointers.UnitFieldFlagsPos >= 0)
{
uint32 appendValue = m_uint32Values[UNIT_FIELD_FLAGS];
if (target->IsGameMaster() && target->GetSession()->IsGMAccount())
appendValue &= ~UNIT_FLAG_NOT_SELECTABLE;
valuesUpdateBuf.put(posPointers.UnitFieldFlagsPos, appendValue);
}
// UNIT_FIELD_DISPLAYID
// Use modelid_a if not gm, _h if gm for CREATURE_FLAG_EXTRA_TRIGGER creatures.
if (posPointers.UnitFieldDisplayPos >= 0)
{
uint32 displayId = m_uint32Values[UNIT_FIELD_DISPLAYID];
if (creature)
{
CreatureTemplate const* cinfo = creature->GetCreatureTemplate();
// this also applies for transform auras
if (SpellInfo const* transform = sSpellMgr->GetSpellInfo(getTransForm()))
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (transform->Effects[i].IsAura(SPELL_AURA_TRANSFORM))
if (CreatureTemplate const* transformInfo = sObjectMgr->GetCreatureTemplate(transform->Effects[i].MiscValue))
{
cinfo = transformInfo;
break;
}
if (cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER)
{
if (target->IsGameMaster() && target->GetSession()->IsGMAccount())
{
if (cinfo->Modelid1)
displayId = cinfo->Modelid1; // Modelid1 is a visible model for gms
else
displayId = 17519; // world visible trigger's model
}
else
{
if (cinfo->Modelid2)
displayId = cinfo->Modelid2; // Modelid2 is an invisible model for players
else
displayId = 11686; // world invisible trigger's model
}
}
}
valuesUpdateBuf.put(posPointers.UnitFieldDisplayPos, uint32(displayId));
}
// UNIT_DYNAMIC_FLAGS
// Hide lootable animation for unallowed players.
if (posPointers.UnitDynamicFlagsPos >= 0)
{
uint32 dynamicFlags = m_uint32Values[UNIT_DYNAMIC_FLAGS] & ~(UNIT_DYNFLAG_TAPPED | UNIT_DYNFLAG_TAPPED_BY_PLAYER);
if (creature)
{
if (creature->hasLootRecipient())
{
dynamicFlags |= UNIT_DYNFLAG_TAPPED;
if (creature->isTappedBy(target))
dynamicFlags |= UNIT_DYNFLAG_TAPPED_BY_PLAYER;
}
if (!target->isAllowedToLoot(creature))
dynamicFlags &= ~UNIT_DYNFLAG_LOOTABLE;
}
// unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras
if (dynamicFlags & UNIT_DYNFLAG_TRACK_UNIT)
if (!HasAuraTypeWithCaster(SPELL_AURA_MOD_STALKED, target->GetGUID()))
dynamicFlags &= ~UNIT_DYNFLAG_TRACK_UNIT;
valuesUpdateBuf.put(posPointers.UnitDynamicFlagsPos, dynamicFlags);
}
// UNIT_FIELD_BYTES_2
if (posPointers.UnitFieldBytes2Pos >= 0)
{
if (IsControlledByPlayer() && target != this && sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && IsInRaidWith(target))
{
FactionTemplateEntry const* ft1 = GetFactionTemplateEntry();
FactionTemplateEntry const* ft2 = target->GetFactionTemplateEntry();
if (ft1 && ft2 && !ft1->IsFriendlyTo(*ft2))
// Allow targetting opposite faction in party when enabled in config
valuesUpdateBuf.put(posPointers.UnitFieldBytes2Pos, (m_uint32Values[UNIT_FIELD_BYTES_2] & ((UNIT_BYTE2_FLAG_SANCTUARY /*| UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5*/) << 8))); // this flag is at uint8 offset 1 !!
}// pussywizard / Callmephil
else if (target->IsSpectator() && target->FindMap() && target->FindMap()->IsBattleArena() &&
(this->GetTypeId() == TYPEID_PLAYER || this->GetTypeId() == TYPEID_UNIT || this->GetTypeId() == TYPEID_DYNAMICOBJECT))
{
valuesUpdateBuf.put(posPointers.UnitFieldBytes2Pos, (m_uint32Values[UNIT_FIELD_BYTES_2] & 0xFFFFF2FF)); // clear UNIT_BYTE2_FLAG_PVP, UNIT_BYTE2_FLAG_FFA_PVP, UNIT_BYTE2_FLAG_SANCTUARY
}
}
// UNIT_FIELD_FACTIONTEMPLATE
if (posPointers.UnitFieldFactionTemplatePos >= 0)
{
if (IsControlledByPlayer() && target != this && sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && IsInRaidWith(target))
{
FactionTemplateEntry const* ft1 = GetFactionTemplateEntry();
FactionTemplateEntry const* ft2 = target->GetFactionTemplateEntry();
if (ft1 && ft2 && !ft1->IsFriendlyTo(*ft2))
// pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work)
valuesUpdateBuf.put(posPointers.UnitFieldFactionTemplatePos, uint32(target->GetFaction()));
}// pussywizard / Callmephil
else if (target->IsSpectator() && target->FindMap() && target->FindMap()->IsBattleArena() &&
(this->GetTypeId() == TYPEID_PLAYER || this->GetTypeId() == TYPEID_UNIT || this->GetTypeId() == TYPEID_DYNAMICOBJECT))
{
valuesUpdateBuf.put(posPointers.UnitFieldFactionTemplatePos, uint32(target->GetFaction()));
}
}
sScriptMgr->OnPatchValuesUpdate(this, valuesUpdateBuf, posPointers, target);
}
void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown)