void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) { uint32 spellId; uint8 cast_count, cast_flags; recvPacket >> cast_count; recvPacket >> spellId; recvPacket >> cast_flags; // flags (if 0x02 - some additional data are received) // ignore for remote control state (for player case) Unit* mover = _player->GetMover(); if (mover != _player && mover->GetTypeId() == TYPEID_PLAYER) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } DEBUG_LOG("WORLD: CMSG_CAST_SPELL, spellId - %u, cast_count: %u, unk_flags %u, data length = " SIZEFMTD, spellId, cast_count, cast_flags, recvPacket.size()); SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellId); if (!spellInfo) { sLog.outError("WORLD: unknown spell id %u", spellId); recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } Aura* triggeredByAura = mover->GetTriggeredByClientAura(spellId); if (mover->GetTypeId() == TYPEID_PLAYER) { // not have spell in spellbook or spell passive and not casted by client if ((!((Player*)mover)->HasActiveSpell(spellId) && !triggeredByAura) || IsPassiveSpell(spellInfo)) { sLog.outError("World: %s casts spell %u which he shouldn't have", mover->GetGuidStr().c_str(), spellId); // cheater? kick? ban? recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } else { // not have spell in spellbook or spell passive and not casted by client if (!((Creature*)mover)->HasSpell(spellId) || IsPassiveSpell(spellInfo)) { // cheater? kick? ban? recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet return; } } // client provided targets SpellCastTargets targets; #ifdef BUILD_PLAYERBOT recvPacket >> targets.ReadForCaster(mover); #else recvPacket >> targets.ReadForCaster(_player); #endif // some spell cast packet including more data (for projectiles) targets.ReadAdditionalSpellData(recvPacket, cast_flags); // auto-selection buff level base at target level (in spellInfo) if (Unit* target = targets.getUnitTarget()) { // if rank not found then function return nullptr but in explicit cast case original spell can be casted and later failed with appropriate error message if (SpellEntry const* actualSpellInfo = sSpellMgr.SelectAuraRankForLevel(spellInfo, target->getLevel())) spellInfo = actualSpellInfo; } Spell* spell = new Spell(mover, spellInfo, triggeredByAura ? TRIGGERED_OLD_TRIGGERED : TRIGGERED_NONE, mover->GetObjectGuid(), triggeredByAura ? triggeredByAura->GetSpellProto() : nullptr); spell->m_cast_count = cast_count; // set count of casts spell->SpellStart(&targets, triggeredByAura); }
void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) { uint8 bagIndex, slot; uint8 cast_flags; // flags (if 0x02 - some additional data are received) uint8 cast_count; // next cast if exists (single or not) ObjectGuid itemGuid; uint32 glyphIndex; // something to do with glyphs? uint32 spellid; // casted spell id recvPacket >> bagIndex >> slot >> cast_count >> spellid >> itemGuid >> glyphIndex >> cast_flags; // TODO: add targets.read() check Player* pUser = _player; // ignore for remote control state if (!pUser->IsSelfMover()) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail return; } // reject fake data if (glyphIndex >= MAX_GLYPH_SLOT_INDEX) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } Item* pItem = pUser->GetItemByPos(bagIndex, slot); if (!pItem) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } if (pItem->GetObjectGuid() != itemGuid) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } DETAIL_LOG("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, cast_count: %u, spellid: %u, Item: %u, glyphIndex: %u, unk_flags: %u, data length = %u", bagIndex, slot, cast_count, spellid, pItem->GetEntry(), glyphIndex, cast_flags, (uint32)recvPacket.size()); ItemPrototype const* proto = pItem->GetProto(); if (!proto) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, nullptr); return; } // some item classes can be used only in equipped state if (proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped()) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, nullptr); return; } InventoryResult msg = pUser->CanUseItem(pItem); if (msg != EQUIP_ERR_OK) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(msg, pItem, nullptr); return; } // not allow use item from trade (cheat way only) if (pItem->IsInTrade()) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, nullptr); return; } // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB) if (proto->Class == ITEM_CLASS_CONSUMABLE && !(proto->Flags & ITEM_FLAG_IGNORE_DEFAULT_ARENA_RESTRICTIONS) && pUser->InArena()) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH, pItem, nullptr); return; } if (pUser->isInCombat()) { for (const auto& Spell : proto->Spells) { if (SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(Spell.SpellId)) { if (IsNonCombatSpell(spellInfo)) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT, pItem, nullptr); return; } } } // Prevent potion drink if another potion in processing (client have potions disabled in like case) if (pItem->IsPotion() && pUser->GetLastPotionId()) { recvPacket.rpos(recvPacket.wpos()); // prevent spam at not read packet tail pUser->SendEquipError(EQUIP_ERR_OBJECT_IS_BUSY, pItem, nullptr); return; } } // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory) if (pItem->GetProto()->Bonding == BIND_WHEN_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM) { if (!pItem->IsSoulBound()) { pItem->SetState(ITEM_CHANGED, pUser); pItem->SetBinding(true); } } SpellCastTargets targets; recvPacket >> targets.ReadForCaster(pUser); // some spell cast packet including more data (for projectiles) targets.ReadAdditionalSpellData(recvPacket, cast_flags); targets.Update(pUser); if (!pItem->IsTargetValidForItemUse(targets.getUnitTarget())) { // free gray item after use fail pUser->SendEquipError(EQUIP_ERR_NONE, pItem, nullptr); // send spell error if (SpellEntry const* spellInfo = sSpellTemplate.LookupEntry<SpellEntry>(spellid)) { // for implicit area/coord target spells if (IsPointEffectTarget(Targets(spellInfo->EffectImplicitTargetA[EFFECT_INDEX_0])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[EFFECT_INDEX_0]))) Spell::SendCastResult(_player, spellInfo, cast_count, SPELL_FAILED_NO_VALID_TARGETS); // for explicit target spells else Spell::SendCastResult(_player, spellInfo, cast_count, SPELL_FAILED_BAD_TARGETS); } return; } // Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state. if (!sScriptDevAIMgr.OnItemUse(pUser, pItem, targets)) { // no script or script not process request by self pUser->CastItemUseSpell(pItem, targets, cast_count, glyphIndex); } }