diff --git a/src/game/Entities/Player.cpp b/src/game/Entities/Player.cpp index 510718e22f..ac1325cbae 100644 --- a/src/game/Entities/Player.cpp +++ b/src/game/Entities/Player.cpp @@ -19532,17 +19532,18 @@ void Player::SendInitialPacketsAfterAddToMap() auraList.front()->ApplyModifier(true, true); } + SendAuraDurationsOnLogin(); + if (IsImmobilizedState()) // TODO: Figure out if this protocol is correct SendMoveRoot(true); - SendAuraDurationsOnLogin(true); - SendEnchantmentDurations(); // must be after add to map SendItemDurations(); // must be after add to map - CastSpell(this, 836, TRIGGERED_OLD_TRIGGERED); // LOGINEFFECT + CastSpell(this, 836, TRIGGERED_IGNORE_CURRENT_CASTED_SPELL); // LOGINEFFECT - SendAuraDurationsOnLogin(false); + SendExtraAuraDurationsOnLogin(true); + SendExtraAuraDurationsOnLogin(false); } void Player::SendUpdateToOutOfRangeGroupMembers() @@ -19782,18 +19783,25 @@ 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) +void Player::SendAuraDurationsOnLogin() { - if (!visible) + for (auto& data : GetSpellAuraHolderMap()) { - WorldPacket data(SMSG_SET_EXTRA_AURA_INFO, 8); - data << GetPackGUID(); - SendDirectMessage(data); + SpellAuraHolder* holder = data.second; + if (holder->GetAuraSlot() >= MAX_AURAS) + continue; + + holder->LoginAuraDuration(); } +} + +void Player::SendExtraAuraDurationsOnLogin(bool visible) +{ + std::vector holders; uint32 counter = MAX_AURAS; SpellAuraHolderMap const& auraHolders = GetSpellAuraHolderMap(); @@ -19813,9 +19821,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 << int32(holder->GetAuraMaxDuration()); + data << uint32(holder->GetAuraMaxDuration() == -1 ? 0 : holder->GetAuraDuration()); + } + SendDirectMessage(data); + } } ItemSetEffect* Player::GetItemSetEffect(uint32 setId) diff --git a/src/game/Entities/Player.h b/src/game/Entities/Player.h index a1621dcacc..52f036927a 100644 --- a/src/game/Entities/Player.h +++ b/src/game/Entities/Player.h @@ -1942,7 +1942,8 @@ class Player : public Unit void SendDirectMessage(WorldPacket const& data) const; void SendAuraDurationsForTarget(Unit* target); - void SendAuraDurationsOnLogin(bool visible); // uses different packets + void SendAuraDurationsOnLogin(); // sends own durations + void SendExtraAuraDurationsOnLogin(bool visible); // sends init and non visible slot auras PlayerMenu* GetPlayerMenu() const { return m_playerMenu.get(); } 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..cdae90153f 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); @@ -7925,10 +7949,10 @@ void SpellAuraHolder::_AddSpellAuraHolder() // update for out of range group members m_target->UpdateAuraForGroup(slot); - - UpdateAuraDuration(); } + UpdateAuraDuration(); + //***************************************************** // Update target aura state flag (at 1 aura apply) // TODO: Make it easer @@ -8736,55 +8760,78 @@ void SpellAuraHolder::ClearExtraAuraInfo(Unit* caster) void SpellAuraHolder::UpdateAuraDuration() { - if (GetAuraSlot() >= MAX_AURAS || m_isPassive) + // not send in case player loading - on load is sent differently + if (m_target->IsPlayer() && static_cast(m_target)->GetSession()->PlayerLoading()) return; - if (m_target->IsPlayer()) + 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 + if (GetAuraSlot() < MAX_AURAS) // only visible auras + 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::LoginAuraDuration() +{ + // target is currently logging in player and only send what he needs + SendAuraDuration(); + if (m_casterGuid == m_target->GetObjectGuid()) // no need to fetch caster - must only send if they are the same anyway + SendAuraDurationToCaster(static_cast(m_target)); +} + +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)); + } +} - if (caster && caster->GetTypeId() == TYPEID_PLAYER && caster != m_target) - SendAuraDurationForCaster(static_cast(caster)); +void SpellAuraHolder::SendAuraDuration() +{ + if (!m_target->IsPlayer()) + return; + + WorldPacket data(SMSG_UPDATE_AURA_DURATION, 5); + data << uint8(GetAuraSlot()); + data << uint32(GetAuraMaxDuration() == -1 ? 0 : 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(); data << uint8(slot == MAX_AURAS ? GetAuraSlot() : slot); data << uint32(GetId()); - data << uint32(GetAuraMaxDuration()); - data << uint32(GetAuraDuration()); + data << int32(GetAuraMaxDuration()); + data << uint32(GetAuraMaxDuration() == -1 ? 0 : 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 << int32(GetAuraMaxDuration()); // full + data << uint32(GetAuraMaxDuration() == -1 ? 0 : GetAuraDuration()); // remain caster->GetSession()->SendPacket(data); } diff --git a/src/game/Spells/SpellAuras.h b/src/game/Spells/SpellAuras.h index 0e57615d1a..297f3921aa 100644 --- a/src/game/Spells/SpellAuras.h +++ b/src/game/Spells/SpellAuras.h @@ -190,8 +190,11 @@ 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 LoginAuraDuration(); + 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);