From 0a34290484f3e90a2a642986de682a8b0c7e3815 Mon Sep 17 00:00:00 2001 From: Shauren Date: Sun, 9 Jul 2023 14:45:49 +0200 Subject: [PATCH] Core/Spells: Corrected client control checks in spell packets Closes #26510 Closes #27863 --- src/server/game/Entities/Player/Player.cpp | 3 ++ src/server/game/Handlers/PetHandler.cpp | 1 + src/server/game/Handlers/SpellHandler.cpp | 54 ++++++---------------- src/server/game/Spells/Spell.cpp | 29 +++++------- src/server/game/Spells/Spell.h | 1 + 5 files changed, 31 insertions(+), 57 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c08aeb7ba7ff3..7cf2edefaabf9 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8081,6 +8081,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 } Spell* spell = new Spell(this, spellInfo, TRIGGERED_NONE); + spell->m_fromClient = true; spell->m_CastItem = item; spell->m_cast_count = cast_count; //set count of casts spell->SetSpellValue(SPELLVALUE_BASE_POINT0, learning_spell_id); @@ -8109,6 +8110,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 } Spell* spell = new Spell(this, spellInfo, TRIGGERED_NONE); + spell->m_fromClient = true; spell->m_CastItem = item; spell->m_cast_count = cast_count; // set count of casts spell->m_glyphIndex = glyphIndex; // glyph index @@ -8136,6 +8138,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 } Spell* spell = new Spell(this, spellInfo, TRIGGERED_NONE); + spell->m_fromClient = true; spell->m_CastItem = item; spell->m_cast_count = cast_count; // set count of casts spell->m_glyphIndex = glyphIndex; // glyph index diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index a068faff3764c..68d3c77be6baf 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -799,6 +799,7 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) } Spell* spell = new Spell(caster, spellInfo, triggerCastFlags); + spell->m_fromClient = true; spell->m_cast_count = castCount; // probably pending spell cast spell->m_targets = targets; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 3991676dca9ce..62921828e5a41 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -67,10 +67,6 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) /// @todo add targets.read() check Player* pUser = _player; - // ignore for remote control state - if (pUser->IsCharming()) - return; - uint8 bagIndex, slot, castFlags; uint8 castCount; // next cast if exists (single or not) ObjectGuid itemGUID; @@ -179,7 +175,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) Player* player = GetPlayer(); // ignore for remote control state - if (player->IsCharming()) + if (player->IsCharmed()) return; // additional check, client outputs message on its own @@ -299,7 +295,7 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData) if (GameObject* obj = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) { // ignore for remote control state - if (GetPlayer()->IsCharming()) + if (GetPlayer()->IsCharmed()) if (!(GetPlayer()->IsOnVehicle(GetPlayer()->GetCharmed()) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) return; @@ -315,7 +311,7 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [%s]", guid.ToString().c_str()); // ignore for remote control state - if (_player->IsCharming()) + if (_player->IsCharmed()) return; if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) @@ -336,14 +332,6 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) TC_LOG_DEBUG("network", "WORLD: got cast spell packet, castCount: %u, spellId: %u, castFlags: %u, data length = %u", castCount, spellId, castFlags, (uint32)recvPacket.size()); - // ignore for remote control state (for player case) - Unit* mover = GetGameClient()->GetActivelyMovedUnit(); - if (!mover || (mover != _player && mover->GetTypeId() == TYPEID_PLAYER)) - { - recvPacket.rfinish(); // prevent spam at ignore packet - return; - } - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) { @@ -358,37 +346,23 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) return; } - Unit* caster = mover; - if (caster->GetTypeId() == TYPEID_UNIT && !caster->ToCreature()->HasSpell(spellId)) - { - // If the vehicle creature does not have the spell but it allows the passenger to cast own spells - // change caster to player and let him cast - if (!_player->IsOnVehicle(caster) || spellInfo->CheckVehicle(_player) != SPELL_CAST_OK) - { - recvPacket.rfinish(); // prevent spam at ignore packet - return; - } - - caster = _player; - } - // client provided targets SpellCastTargets targets; - targets.Read(recvPacket, caster); + targets.Read(recvPacket, _player); HandleClientCastFlags(recvPacket, castFlags, targets); // not have spell in spellbook - if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellId)) + if (_player->GetTypeId() == TYPEID_PLAYER && !_player->ToPlayer()->HasActiveSpell(spellId)) { bool allow = false; // allow casting of unknown spells for special lock cases if (GameObject *go = targets.GetGOTarget()) - if (go->GetSpellForLock(caster->ToPlayer()) == spellInfo) + if (go->GetSpellForLock(_player->ToPlayer()) == spellInfo) allow = true; // allow casting of spells triggered by clientside periodic trigger auras - if (caster->HasAuraTypeWithTriggerSpell(SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT, spellId)) + if (_player->HasAuraTypeWithTriggerSpell(SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT, spellId)) { allow = true; triggerFlag = TRIGGERED_FULL_MASK; @@ -398,16 +372,12 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) return; } - // can't use our own spells when we're in possession of another unit, - if (_player->isPossessing()) - return; - // Client is resending autoshot cast opcode when other spell is cast during shoot rotation // Skip it to prevent "interrupt" message // Also check targets! target may have changed and we need to interrupt current spell if (spellInfo->IsAutoRepeatRangedSpell()) { - if (Spell* spell = caster->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)) + if (Spell* spell = _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)) { if (spell->m_spellInfo == spellInfo && spell->m_targets.GetUnitTargetGUID() == targets.GetUnitTargetGUID()) { @@ -429,13 +399,17 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) spellInfo = actualSpellInfo; } - Spell* spell = new Spell(caster, spellInfo, triggerFlag); + Spell* spell = new Spell(_player, spellInfo, triggerFlag); + spell->m_fromClient = true; spell->m_cast_count = castCount; // set count of casts spell->prepare(targets); } void WorldSession::HandleCancelCastOpcode(WorldPackets::Spells::CancelCast& cancelCast) { + if (_player->IsCharmed()) + return; + if (_player->IsNonMeleeSpellCast(false)) _player->InterruptNonMeleeSpells(false, cancelCast.SpellID, false); } @@ -571,7 +545,7 @@ void WorldSession::HandleCancelChanneling(WorldPackets::Spells::CancelChannellin void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& totemDestroyed) { // ignore for remote control state - if (_player->IsCharming()) + if (_player->IsCharmed()) return; uint8 slotId = totemDestroyed.Slot; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index dfdc492b0ebf3..ecc4855eacdc4 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -504,6 +504,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO , m_spellValue(new SpellValue(m_spellInfo)), _spellEvent(nullptr) { m_customError = SPELL_CUSTOM_ERROR_NONE; + m_fromClient = false; m_selfContainer = nullptr; m_referencedFromCurrentSpell = false; m_executedCurrently = false; @@ -4170,7 +4171,7 @@ void Spell::SendSpellStart() if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; - if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count) + if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_fromClient) castFlags |= CAST_FLAG_PENDING; if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) @@ -4227,7 +4228,7 @@ void Spell::SendSpellGo() uint32 castFlags = CAST_FLAG_UNKNOWN_9; // triggered spells with spell visual != 0 - if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count) + if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_fromClient) castFlags |= CAST_FLAG_PENDING; if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) @@ -4318,10 +4319,9 @@ void Spell::SendSpellGo() } // should be sent to self only - if (castFlags & CAST_FLAG_POWER_LEFT_SELF) + if (castFlags & CAST_FLAG_POWER_LEFT_SELF && m_caster->IsPlayer()) { - if (Player* player = m_caster->GetAffectingPlayer()) - player->SendDirectMessage(packet.Write()); + m_caster->ToPlayer()->SendDirectMessage(packet.Write()); packet.Clear(); @@ -5221,6 +5221,9 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_MOVING; } + if (unitCaster->IsCharmed() && m_spellInfo->HasAttribute(SPELL_ATTR5_NOT_USABLE_WHILE_CHARMED)) + return SPELL_FAILED_CHARMED; + // Check vehicle flags if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE)) { @@ -5931,7 +5934,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_NO_PET; if (pet->GetCharmerGUID()) - return SPELL_FAILED_CHARMED; + return SPELL_FAILED_ALREADY_HAVE_CHARM; break; } case SPELL_AURA_MOD_POSSESS: @@ -5964,7 +5967,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint return SPELL_FAILED_CANT_BE_CHARMED; if (target->GetCharmerGUID()) - return SPELL_FAILED_CHARMED; + return SPELL_FAILED_CANT_BE_CHARMED; if (target->GetOwner() && target->GetOwner()->GetTypeId() == TYPEID_PLAYER) return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED; @@ -6170,16 +6173,8 @@ SpellCastResult Spell::CheckCasterAuras(uint32* param1) const // Get unit state uint32 const unitflag = unitCaster->GetUnitFlags(); - // this check should only be done when player does cast directly - // (ie not when it's called from a script) Breaks for example PlayerAI when charmed - /* - if (unitCaster->GetCharmerGUID()) - { - if (Unit* charmer = unitCaster->GetCharmer()) - if (charmer->GetCharmed() != unitCaster && !CheckSpellCancelsCharm(param1)) - result = SPELL_FAILED_CHARMED; - } - */ + if (m_fromClient && unitCaster->IsCharmed() && unitCaster->IsPlayer() && !CheckSpellCancelsCharm(param1)) + result = SPELL_FAILED_CHARMED; // spell has attribute usable while having a cc state, check if caster has allowed mechanic auras, another mechanic types must prevent cast spell auto mechanicCheck = [&](AuraType type) -> SpellCastResult diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index f71c8829164a8..3359bf6d730ba 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -394,6 +394,7 @@ class TC_GAME_API Spell ObjectGuid m_castItemGUID; uint32 m_castItemEntry; uint8 m_cast_count; + bool m_fromClient; uint32 m_glyphIndex; SpellCastTargets m_targets;