Skip to content

Commit

Permalink
Core/Spells: Corrected client control checks in spell packets
Browse files Browse the repository at this point in the history
  • Loading branch information
Shauren committed Jul 9, 2023
1 parent 2eb778a commit 0a34290
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 57 deletions.
3 changes: 3 additions & 0 deletions src/server/game/Entities/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/server/game/Handlers/PetHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
54 changes: 14 additions & 40 deletions src/server/game/Handlers/SpellHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand All @@ -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))
Expand All @@ -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)
{
Expand All @@ -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;
Expand All @@ -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())
{
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
29 changes: 12 additions & 17 deletions src/server/game/Spells/Spell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/server/game/Spells/Spell.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit 0a34290

Please sign in to comment.