From c3a63acc75fdb21fe47b1967ef34edfd2ec35834 Mon Sep 17 00:00:00 2001 From: killerwife Date: Tue, 15 Aug 2023 09:43:21 +0200 Subject: [PATCH] Aura/Spell: Rework aura protocol more closely based on discovered new info --- src/game/Entities/Player.cpp | 31 +++++++++--- src/game/Entities/Unit.cpp | 2 +- src/game/Spells/Spell.cpp | 27 ++++++----- src/game/Spells/SpellAuras.cpp | 89 +++++++++++++++++++++++++--------- src/game/Spells/SpellAuras.h | 6 ++- 5 files changed, 109 insertions(+), 46 deletions(-) diff --git a/src/game/Entities/Player.cpp b/src/game/Entities/Player.cpp index 510718e22f..3d7141094f 100644 --- a/src/game/Entities/Player.cpp +++ b/src/game/Entities/Player.cpp @@ -19782,18 +19782,13 @@ void Player::SendAuraDurationsForTarget(Unit* target) if (holder->GetAuraSlot() >= MAX_AURAS || holder->IsPassive() || holder->GetCasterGuid() != GetObjectGuid()) continue; - holder->SendAuraDurationForCaster(this); + holder->SendAuraDurationToCaster(this); } } void Player::SendAuraDurationsOnLogin(bool visible) { - if (!visible) - { - WorldPacket data(SMSG_SET_EXTRA_AURA_INFO, 8); - data << GetPackGUID(); - SendDirectMessage(data); - } + std::vector holders; uint32 counter = MAX_AURAS; SpellAuraHolderMap const& auraHolders = GetSpellAuraHolderMap(); @@ -19813,9 +19808,29 @@ void Player::SendAuraDurationsOnLogin(bool visible) continue; } - holder->SendAuraDurationForTarget(!visible ? counter : MAX_AURAS); + holders.push_back(holder); ++counter; } + + if (!visible) // slots >= 56 are sent as singles + { + std::sort(holders.begin(), holders.end(), [](SpellAuraHolder* left, SpellAuraHolder* right) { return left->GetAuraSlot() > right->GetAuraSlot(); }); + for (SpellAuraHolder* holder : holders) + holder->SendAuraDurationToCaster(this); + } + else // visible slots (<= 56) are sent in a single packet + { + WorldPacket data(SMSG_INIT_EXTRA_AURA_INFO, (8 + (1 + 4 + 4 + 4) * holders.size())); + data << GetPackGUID(); + for (SpellAuraHolder* holder : holders) + { + data << uint8(holder->GetAuraSlot()); + data << uint32(holder->GetId()); + data << uint32(holder->GetAuraMaxDuration()); + data << uint32(holder->GetAuraDuration()); + } + SendDirectMessage(data); + } } ItemSetEffect* Player::GetItemSetEffect(uint32 setId) diff --git a/src/game/Entities/Unit.cpp b/src/game/Entities/Unit.cpp index ac78eab070..2a56c82588 100644 --- a/src/game/Entities/Unit.cpp +++ b/src/game/Entities/Unit.cpp @@ -5771,7 +5771,7 @@ void Unit::DelaySpellAuraHolder(uint32 spellId, int32 delaytime, ObjectGuid cast else holder->SetAuraDuration(holder->GetAuraDuration() - delaytime); - holder->UpdateAuraDuration(); + holder->ForceUpdateAuraDuration(); DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u partially interrupted on %s, new duration: %u ms", spellId, GetGuidStr().c_str(), holder->GetAuraDuration()); } diff --git a/src/game/Spells/Spell.cpp b/src/game/Spells/Spell.cpp index 13fa0c5079..6b7507fefa 100644 --- a/src/game/Spells/Spell.cpp +++ b/src/game/Spells/Spell.cpp @@ -7168,23 +7168,26 @@ void Spell::DelayedChannel() DEBUG_FILTER_LOG(LOG_FILTER_SPELL_CAST, "Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer); - for (TargetList::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + SendChannelUpdate(m_timer); + + if (m_timer != 0) // must be after channel update { - if ((*ihit).missCondition == SPELL_MISS_NONE) + for (auto& target : m_UniqueTargetInfo) { - if (Unit* unit = m_caster->GetObjectGuid() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID)) - unit->DelaySpellAuraHolder(m_spellInfo->Id, delaytime, m_caster->GetObjectGuid()); + if (target.missCondition == SPELL_MISS_NONE) + { + if (Unit* unit = m_caster->GetObjectGuid() == target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID)) + unit->DelaySpellAuraHolder(m_spellInfo->Id, delaytime, m_caster->GetObjectGuid()); + } } - } - for (int j = 0; j < MAX_EFFECT_INDEX; ++j) - { - // partially interrupt persistent area auras - if (DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id, SpellEffectIndex(j))) - dynObj->Delay(delaytime); + for (int j = 0; j < MAX_EFFECT_INDEX; ++j) + { + // partially interrupt persistent area auras + if (DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id, SpellEffectIndex(j))) + dynObj->Delay(delaytime); + } } - - SendChannelUpdate(m_timer); } void Spell::UpdateOriginalCasterPointer() diff --git a/src/game/Spells/SpellAuras.cpp b/src/game/Spells/SpellAuras.cpp index 310fdc9cb1..9eff5b9470 100755 --- a/src/game/Spells/SpellAuras.cpp +++ b/src/game/Spells/SpellAuras.cpp @@ -7913,10 +7913,34 @@ void SpellAuraHolder::_AddSpellAuraHolder() } } + if (slot >= MAX_AURAS) + { + uint32 theoreticalSlot = MAX_AURAS; + std::set freeSlot; + for (auto& data : m_target->GetSpellAuraHolderMap()) + { + SpellAuraHolder* holder = data.second; + if (holder->GetAuraSlot() >= MAX_AURAS) + freeSlot.insert(holder->GetAuraSlot()); + } + for (uint32 slot : freeSlot) + { + // set is sorted so this should yield first empty + if (theoreticalSlot == slot) + theoreticalSlot++; + else + break; + } + // slot is uint8 - avoid shenanigans - 56 max visible auras - 255 max all auras + if (theoreticalSlot > NULL_AURA_SLOT) + theoreticalSlot = NULL_AURA_SLOT; + slot = theoreticalSlot; + } + SetAuraSlot(slot); // Not update fields for not first spell's aura, all data already in fields - if (slot < MAX_AURAS) // slot found + if (slot < MAX_AURAS) // slot found { SetAura(slot, false); SetAuraFlag(slot, true); @@ -7928,6 +7952,8 @@ void SpellAuraHolder::_AddSpellAuraHolder() UpdateAuraDuration(); } + else if (m_target == caster && caster->IsPlayer()) // if slot >= MAX_AURAS only this is sent to target + SendAuraDurationToCaster(static_cast(caster)); //***************************************************** // Update target aura state flag (at 1 aura apply) @@ -8736,33 +8762,53 @@ void SpellAuraHolder::ClearExtraAuraInfo(Unit* caster) void SpellAuraHolder::UpdateAuraDuration() { - if (GetAuraSlot() >= MAX_AURAS || m_isPassive) + if (GetAuraSlot() >= MAX_AURAS) return; - if (m_target->IsPlayer()) + // not send in case player loading - on load is sent differently + if (m_target->IsPlayer() && static_cast(m_target)->GetSession()->PlayerLoading()) + return; + + Unit* caster = GetCaster(); + + if (!GetSpellProto()->HasAttribute(SPELL_ATTR_EX5_DO_NOT_DISPLAY_DURATION)) { - if (!GetSpellProto()->HasAttribute(SPELL_ATTR_EX5_DO_NOT_DISPLAY_DURATION)) - { - WorldPacket data(SMSG_UPDATE_AURA_DURATION, 5); - data << uint8(GetAuraSlot()); - data << uint32(GetAuraDuration()); - static_cast(m_target)->SendDirectMessage(data); + // if caster == target and player, meant to get both + SendAuraDuration(); - SendAuraDurationForTarget(); - } + if (caster && caster->IsPlayer()) + SendAuraDurationToCaster(static_cast(caster)); } +} - // not send in case player loading (will not work anyway until player not added to map), sent in visibility change code - if (m_target->GetTypeId() == TYPEID_PLAYER && static_cast(m_target)->GetSession()->PlayerLoading()) +void SpellAuraHolder::ForceUpdateAuraDuration() +{ + if (GetAuraSlot() >= MAX_AURAS) return; Unit* caster = GetCaster(); + if (!GetSpellProto()->HasAttribute(SPELL_ATTR_EX5_DO_NOT_DISPLAY_DURATION)) + { + // if caster == target and player, meant to get both + SendAuraDuration(); + + if (caster && caster->IsPlayer()) + SendAuraDurationToCasterNeedUpdate(static_cast(caster)); + } +} + +void SpellAuraHolder::SendAuraDuration() +{ + if (!m_target->IsPlayer()) + return; - if (caster && caster->GetTypeId() == TYPEID_PLAYER && caster != m_target) - SendAuraDurationForCaster(static_cast(caster)); + WorldPacket data(SMSG_UPDATE_AURA_DURATION, 5); + data << uint8(GetAuraSlot()); + data << uint32(GetAuraDuration()); + static_cast(m_target)->SendDirectMessage(data); } -void SpellAuraHolder::SendAuraDurationForTarget(uint32 slot) +void SpellAuraHolder::SendAuraDurationToCaster(Player* caster, uint32 slot) { WorldPacket data(SMSG_SET_EXTRA_AURA_INFO, (8 + 1 + 4 + 4 + 4)); data << m_target->GetPackGUID(); @@ -8771,20 +8817,17 @@ void SpellAuraHolder::SendAuraDurationForTarget(uint32 slot) data << uint32(GetAuraMaxDuration()); data << uint32(GetAuraDuration()); - static_cast(m_target)->SendDirectMessage(data); + caster->SendDirectMessage(data); } -void SpellAuraHolder::SendAuraDurationForCaster(Player* caster) +void SpellAuraHolder::SendAuraDurationToCasterNeedUpdate(Player* caster) { WorldPacket data(SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE, (8 + 1 + 4 + 4 + 4)); data << m_target->GetPackGUID(); data << uint8(GetAuraSlot()); data << uint32(GetId()); - if (!GetSpellProto()->HasAttribute(SPELL_ATTR_EX5_DO_NOT_DISPLAY_DURATION)) - { - data << uint32(GetAuraMaxDuration()); // full - data << uint32(GetAuraDuration()); // remain - } + data << uint32(GetAuraMaxDuration()); // full + data << uint32(GetAuraDuration()); // remain caster->GetSession()->SendPacket(data); } diff --git a/src/game/Spells/SpellAuras.h b/src/game/Spells/SpellAuras.h index 0e57615d1a..d22397ac8e 100644 --- a/src/game/Spells/SpellAuras.h +++ b/src/game/Spells/SpellAuras.h @@ -190,8 +190,10 @@ class SpellAuraHolder bool IsDispellableByMask(uint32 dispelMask, Unit const* caster, SpellEntry const* spellInfo) const; void UpdateAuraDuration(); - void SendAuraDurationForTarget(uint32 slot = MAX_AURAS); - void SendAuraDurationForCaster(Player* caster); + void ForceUpdateAuraDuration(); + void SendAuraDuration(); + void SendAuraDurationToCaster(Player* caster, uint32 slot = MAX_AURAS); + void SendAuraDurationToCasterNeedUpdate(Player* caster); void SetAura(uint32 slot, bool remove) { m_target->SetUInt32Value(UNIT_FIELD_AURA + slot, remove ? 0 : GetId()); } void SetAuraFlag(uint32 slot, bool add);