From 1f640c98729ddfd5e3aba885fd0fcb6bc8740000 Mon Sep 17 00:00:00 2001 From: Anton Popovichenko Date: Fri, 5 Apr 2024 08:03:11 +0200 Subject: [PATCH] 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. --- src/server/game/Entities/Corpse/Corpse.cpp | 2 +- src/server/game/Entities/Corpse/Corpse.h | 2 +- .../game/Entities/GameObject/GameObject.cpp | 2 +- .../game/Entities/GameObject/GameObject.h | 2 +- .../game/Entities/Item/Container/Bag.cpp | 2 +- src/server/game/Entities/Item/Container/Bag.h | 2 +- src/server/game/Entities/Object/Object.cpp | 8 +- src/server/game/Entities/Object/Object.h | 8 +- src/server/game/Entities/Player/Player.cpp | 2 +- src/server/game/Entities/Player/Player.h | 2 +- src/server/game/Entities/Unit/Unit.cpp | 340 +++++++++++------- src/server/game/Entities/Unit/Unit.h | 64 +++- .../Scripting/ScriptDefines/UnitScript.cpp | 14 +- .../game/Scripting/ScriptDefines/UnitScript.h | 5 +- src/server/game/Scripting/ScriptMgr.h | 3 +- 15 files changed, 302 insertions(+), 156 deletions(-) diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index b2304cb18..ab0b84dca 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -198,7 +198,7 @@ void Corpse::ResetGhostTime() m_time = GameTime::GetGameTime().count(); } -void Corpse::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const +void Corpse::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) { if (!target) return; diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index 6d1dedcf0..e74f2c943 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -54,7 +54,7 @@ public: void AddToWorld() override; void RemoveFromWorld() override; - void BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const override; + void BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) override; bool Create(ObjectGuid::LowType guidlow); bool Create(ObjectGuid::LowType guidlow, Player* owner); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 7f9886aa4..83f21ac4c 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -2738,7 +2738,7 @@ GameObject* GameObject::GetLinkedTrap() return ObjectAccessor::GetGameObject(*this, m_linkedTrap); } -void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const +void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) { if (!target) return; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index a2e352ea8..ed47aa084 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -124,7 +124,7 @@ public: explicit GameObject(); ~GameObject() override; - void BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, Player* target) const override; + void BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) override; void AddToWorld() override; void RemoveFromWorld() override; diff --git a/src/server/game/Entities/Item/Container/Bag.cpp b/src/server/game/Entities/Item/Container/Bag.cpp index ad7164c56..100df4604 100644 --- a/src/server/game/Entities/Item/Container/Bag.cpp +++ b/src/server/game/Entities/Item/Container/Bag.cpp @@ -166,7 +166,7 @@ void Bag::StoreItem(uint8 slot, Item* pItem, bool /*update*/) } } -void Bag::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const +void Bag::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) { Item::BuildCreateUpdateBlockForPlayer(data, target); diff --git a/src/server/game/Entities/Item/Container/Bag.h b/src/server/game/Entities/Item/Container/Bag.h index dfe7654f8..071f4d902 100644 --- a/src/server/game/Entities/Item/Container/Bag.h +++ b/src/server/game/Entities/Item/Container/Bag.h @@ -55,7 +55,7 @@ public: // overwrite virtual Item::DeleteFromDB void DeleteFromDB(CharacterDatabaseTransaction trans) override; - void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override; + void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) override; std::string GetDebugInfo() const override; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a831a0804..5f505655d 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -186,7 +186,7 @@ void Object::BuildMovementUpdateBlock(UpdateData* data, uint32 flags) const data->AddUpdateBlock(buf); } -void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const +void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) { if (!target) return; @@ -254,7 +254,7 @@ void Object::SendUpdateToPlayer(Player* player) player->GetSession()->SendPacket(&packet); } -void Object::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player* target) const +void Object::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player* target) { ByteBuffer buf(500); @@ -494,7 +494,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const } } -void Object::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const +void Object::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) { if (!target) return; @@ -542,7 +542,7 @@ void Object::ClearUpdateMask(bool remove) } } -void Object::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const +void Object::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) { UpdateDataMapType::iterator iter = data_map.find(player); diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 3547b36ce..f9a9ac69c 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -121,10 +121,10 @@ public: [[nodiscard]] TypeID GetTypeId() const { return m_objectTypeId; } [[nodiscard]] bool isType(uint16 mask) const { return (mask & m_objectType); } - virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const; + virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target); void SendUpdateToPlayer(Player* player); - void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player* target) const; + void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player* target); void BuildOutOfRangeUpdateBlock(UpdateData* data) const; void BuildMovementUpdateBlock(UpdateData* data, uint32 flags = 0) const; @@ -183,7 +183,7 @@ public: [[nodiscard]] virtual bool hasQuest(uint32 /* quest_id */) const { return false; } [[nodiscard]] virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; } virtual void BuildUpdate(UpdateDataMapType&, UpdatePlayerSet&) {} - void BuildFieldsUpdate(Player*, UpdateDataMapType&) const; + void BuildFieldsUpdate(Player*, UpdateDataMapType&); void SetFieldNotifyFlag(uint16 flag) { _fieldNotifyFlags |= flag; } void RemoveFieldNotifyFlag(uint16 flag) { _fieldNotifyFlags &= ~flag; } @@ -223,7 +223,7 @@ protected: uint32 GetUpdateFieldData(Player const* target, uint32*& flags) const; void BuildMovementUpdate(ByteBuffer* data, uint16 flags) const; - virtual void BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, Player* target) const; + virtual void BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target); uint16 m_objectType; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index fd173a16b..bde2d0a92 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3791,7 +3791,7 @@ Mail* Player::GetMail(uint32 id) return nullptr; } -void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const +void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) { if (target == this) { diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 70bd05ba9..f10be00ed 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1974,7 +1974,7 @@ public: [[nodiscard]] WorldSession* GetSession() const { return m_session; } void SetSession(WorldSession* sess) { m_session = sess; } - void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override; + void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) override; void DestroyForPlayer(Player* target, bool onDeath = false) const override; void SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 BonusXP, bool recruitAFriend = false, float group_rate = 1.0f); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f89793acd..9cdb7343c 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -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(visibleFlag) << 8 | updateType; + + auto cacheIt = _valuesUpdateCache.find(cacheKey); + if (cacheIt != _valuesUpdateCache.end()) + { + int32 cachePos = static_cast(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(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(cacheValue.buffer.wpos()); + cacheValue.buffer.append(fieldBuffer); + cacheValue.posPointers.ApplyOffset(fieldBufferPos); + + int32 cachePos = static_cast(data->wpos()); + data->append(cacheValue.buffer); + + BuildValuesCachePosPointers dataAdjustedPos = cacheValue.posPointers; + if (cachePos) + dataAdjustedPos.ApplyOffset(cachePos); + + PatchValuesUpdate(*data, dataAdjustedPos, target); + + _valuesUpdateCache.insert(std::pair(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) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index b4e5cc699..3fb0e55ab 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1321,6 +1321,62 @@ private: Unit* defaultValue; }; +// BuildValuesCachePosPointers is marks of the position of some data inside of BuildValue cache. +struct BuildValuesCachePosPointers +{ + BuildValuesCachePosPointers() : + UnitNPCFlagsPos(-1), UnitFieldAuraStatePos(-1), UnitFieldFlagsPos(-1), UnitFieldDisplayPos(-1), + UnitDynamicFlagsPos(-1), UnitFieldBytes2Pos(-1), UnitFieldFactionTemplatePos(-1) {} + + void ApplyOffset(uint32 offset) + { + if (UnitNPCFlagsPos >= 0) + UnitNPCFlagsPos += offset; + + if (UnitFieldAuraStatePos >= 0) + UnitFieldAuraStatePos += offset; + + if (UnitFieldFlagsPos >= 0) + UnitFieldFlagsPos += offset; + + if (UnitFieldDisplayPos >= 0) + UnitFieldDisplayPos += offset; + + if (UnitDynamicFlagsPos >= 0) + UnitDynamicFlagsPos += offset; + + if (UnitFieldBytes2Pos >= 0) + UnitFieldBytes2Pos += offset; + + if (UnitFieldFactionTemplatePos >= 0) + UnitFieldFactionTemplatePos += offset; + + for (auto it = other.begin(); it != other.end(); ++it) + it->second += offset; + } + + int32 UnitNPCFlagsPos; + int32 UnitFieldAuraStatePos; + int32 UnitFieldFlagsPos; + int32 UnitFieldDisplayPos; + int32 UnitDynamicFlagsPos; + int32 UnitFieldBytes2Pos; + int32 UnitFieldFactionTemplatePos; + + std::unordered_map other; +}; + +// BuildValuesCachedBuffer cache for calculated BuildValue. +struct BuildValuesCachedBuffer +{ + BuildValuesCachedBuffer(uint32 bufferSize) : + buffer(bufferSize), posPointers() {} + + ByteBuffer buffer; + + BuildValuesCachePosPointers posPointers; +}; + class Unit : public WorldObject { public: @@ -2505,7 +2561,7 @@ public: protected: explicit Unit (bool isWorldObject); - void BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, Player* target) const override; + void BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) override; UnitAI* i_AI, *i_disabledAI; @@ -2598,6 +2654,9 @@ private: [[nodiscard]] float GetCombatRatingReduction(CombatRating cr) const; [[nodiscard]] uint32 GetCombatRatingDamageReduction(CombatRating cr, float rate, float cap, uint32 damage) const; + void PatchValuesUpdate(ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPointers& posPointers, Player* target); + void InvalidateValuesUpdateCache() { _valuesUpdateCache.clear(); } + protected: void SetFeared(bool apply, Unit* fearedBy = nullptr, bool isFear = false); void SetConfused(bool apply); @@ -2635,6 +2694,9 @@ private: uint32 _lastExtraAttackSpell; std::unordered_map extraAttacksTargets; ObjectGuid _lastDamagedTargetGuid; + + typedef std::unordered_map ValuesUpdateCache; + ValuesUpdateCache _valuesUpdateCache; }; namespace Acore diff --git a/src/server/game/Scripting/ScriptDefines/UnitScript.cpp b/src/server/game/Scripting/ScriptDefines/UnitScript.cpp index eb070a6cf..f493b5166 100644 --- a/src/server/game/Scripting/ScriptDefines/UnitScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/UnitScript.cpp @@ -192,18 +192,24 @@ bool ScriptMgr::IsCustomBuildValuesUpdate(Unit const* unit, uint8 updateType, By return false; } -bool ScriptMgr::OnBuildValuesUpdate(Unit const* unit, uint8 updateType, ByteBuffer& fieldBuffer, Player* target, uint16 index) +bool ScriptMgr::ShouldTrackValuesUpdatePosByIndex(Unit const* unit, uint8 updateType, uint16 index) { - auto ret = IsValidBoolScript([&](UnitScript* script) { return script->OnBuildValuesUpdate(unit, updateType, fieldBuffer, target, index); }); + auto ret = IsValidBoolScript([&](UnitScript* script) { return script->ShouldTrackValuesUpdatePosByIndex(unit, updateType, index); }); if (ret && *ret) - { return true; - } return false; } +void ScriptMgr::OnPatchValuesUpdate(Unit const* unit, ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPointers& posPointers, Player* target) +{ + ExecuteScript([&](UnitScript* script) + { + script->OnPatchValuesUpdate(unit, valuesUpdateBuf, posPointers, target); + }); +} + void ScriptMgr::OnUnitUpdate(Unit* unit, uint32 diff) { ExecuteScript([&](UnitScript* script) diff --git a/src/server/game/Scripting/ScriptDefines/UnitScript.h b/src/server/game/Scripting/ScriptDefines/UnitScript.h index 3b44ca03d..f0341909c 100644 --- a/src/server/game/Scripting/ScriptDefines/UnitScript.h +++ b/src/server/game/Scripting/ScriptDefines/UnitScript.h @@ -22,6 +22,7 @@ enum ReputationRank : uint8; class ByteBuffer; +struct BuildValuesCachePosPointers; class UnitScript : public ScriptObject { @@ -69,7 +70,9 @@ public: [[nodiscard]] virtual bool IsCustomBuildValuesUpdate(Unit const* /*unit*/, uint8 /*updateType*/, ByteBuffer& /*fieldBuffer*/, Player const* /*target*/, uint16 /*index*/) { return false; } - [[nodiscard]] virtual bool OnBuildValuesUpdate(Unit const* /*unit*/, uint8 /*updateType*/, ByteBuffer& /*fieldBuffer*/, Player* /*target*/, uint16 /*index*/) { return false; } + [[nodiscard]] virtual bool ShouldTrackValuesUpdatePosByIndex(Unit const* /*unit*/, uint8 /*updateType*/, uint16 /*index*/) { return false; } + + virtual void OnPatchValuesUpdate(Unit const* /*unit*/, ByteBuffer& /*valuesUpdateBuf*/, BuildValuesCachePosPointers& /*posPointers*/, Player* /*target*/) { } /** * @brief This hook runs in Unit::Update diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 0bb977fb9..a2f1cf5f5 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -548,7 +548,8 @@ public: /* UnitScript */ bool IsNeedModHealPercent(Unit const* unit, AuraEffect* auraEff, float& doneTotalMod, SpellInfo const* spellProto); bool CanSetPhaseMask(Unit const* unit, uint32 newPhaseMask, bool update); bool IsCustomBuildValuesUpdate(Unit const* unit, uint8 updateType, ByteBuffer& fieldBuffer, Player const* target, uint16 index); - bool OnBuildValuesUpdate(Unit const* unit, uint8 updateType, ByteBuffer& fieldBuffer, Player* target, uint16 index); + bool ShouldTrackValuesUpdatePosByIndex(Unit const* unit, uint8 updateType, uint16 index); + void OnPatchValuesUpdate(Unit const* unit, ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPointers& posPointers, Player* target); void OnUnitUpdate(Unit* unit, uint32 diff); void OnDisplayIdChange(Unit* unit, uint32 displayId); void OnUnitEnterEvadeMode(Unit* unit, uint8 why);