void WorldSession::HandlePetAction(WorldPacket& recv_data) { ObjectGuid petGuid; uint32 data; ObjectGuid targetGuid; float x, y, z; recv_data >> petGuid; recv_data >> data; recv_data >> targetGuid; recv_data >> x >> y >> z; uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data); uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); // delete = 0x07 CastSpell = C1 DETAIL_LOG("HandlePetAction: %s flag is %u, spellid is %u, target %s.", petGuid.GetString().c_str(), uint32(flag), spellid, targetGuid.GetString().c_str()); // used also for charmed creature/player Unit* pet = _player->GetMap()->GetUnit(petGuid); if (!pet) { sLog.outError("HandlePetAction: %s not exist.", petGuid.GetString().c_str()); return; } if (_player->GetObjectGuid() != pet->GetCharmerOrOwnerGuid()) { sLog.outError("HandlePetAction: %s isn't controlled by %s.", petGuid.GetString().c_str(), _player->GetGuidStr().c_str()); return; } if (!pet->isAlive()) return; if (pet->GetTypeId() == TYPEID_PLAYER && pet->GetCharmer()->GetTypeId() == TYPEID_PLAYER) { // controller player cannot use controlled player's spells if (flag != (ACT_COMMAND || ACT_REACTION)) return; } else if (((Creature*)pet)->IsPet()) { // pet can have action bar disabled if (((Pet*)pet)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS) return; } CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId()); return; } switch (flag) { case ACT_COMMAND: // 0x07 switch (spellid) { case COMMAND_STAY: // flat=1792 // STAY { pet->StopMoving(); pet->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(true); ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_STAY); break; } case COMMAND_FOLLOW: // spellid=1792 // FOLLOW { pet->StopMoving(); pet->AttackStop(true, true); pet->GetMotionMaster()->Clear(); ((Pet*)pet)->SetStayPosition(); ((Pet*)pet)->SetIsRetreating(true); ((Pet*)pet)->SetSpellOpener(); charmInfo->SetCommandState(COMMAND_FOLLOW); break; } case COMMAND_ATTACK: // spellid=1792 // ATTACK { ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); Unit* targetUnit = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; if (targetUnit && targetUnit != pet && targetUnit->isTargetableForAttack() && targetUnit->isInAccessablePlaceFor((Creature*)pet)) { _player->SetInCombatState(true, targetUnit); // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != targetUnit) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(targetUnit); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(targetUnit, true); } } break; } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) { Creature* petC = (Creature*)pet; if (petC->IsPet()) { Pet* p = (Pet*)petC; if (p->getPetType() == HUNTER_PET) p->Unsummon(PET_SAVE_AS_DELETED, _player); else // dismissing a summoned pet is like killing them (this prevents returning a soulshard...) p->SetDeathState(CORPSE); } else // charmed _player->Uncharm(); if (petC->IsTemporarySummon()) // special case when pet was temporary summon through DoSummonPossesed { petC->ForcedDespawn(); return; } ((Pet*)pet)->SetStayPosition(); break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } break; case ACT_REACTION: // 0x6 switch (spellid) { case REACT_PASSIVE: // passive { pet->AttackStop(true, true); ((Pet*)pet)->SetSpellOpener(); } case REACT_DEFENSIVE: // recovery case REACT_AGGRESSIVE: // activete { charmInfo->SetReactState(ReactStates(spellid)); break; } } break; case ACT_DISABLED: // 0x81 spell (disabled), ignore case ACT_PASSIVE: // 0x01 case ACT_ENABLED: // 0xC1 spell { ((Pet*)pet)->SetIsRetreating(); ((Pet*)pet)->SetSpellOpener(); Unit* unit_target = targetGuid ? _player->GetMap()->GetUnit(targetGuid) : nullptr; // do not cast unknown spells SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid); if (!spellInfo) { sLog.outError("WORLD: unknown PET spell id %i", spellid); return; } if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) return; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) { SpellEffectEntry const* spellEffect = spellInfo->GetSpellEffect(SpellEffectIndex(i)); if (!spellEffect) continue; if (spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellEffect->EffectImplicitTargetA == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) return; } // do not cast not learned spells if (!pet->HasSpell(spellid) || IsPassiveSpell(spellInfo)) return; pet->clearUnitState(UNIT_STAT_MOVING); Spell* spell = new Spell(pet, spellInfo, false); SpellCastResult result = spell->CheckPetCast(unit_target); const SpellRangeEntry* sRange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); if (unit_target && !(pet->IsWithinDistInMap(unit_target, sRange->maxRange) && pet->IsWithinLOSInMap(unit_target)) && !(GetPlayer()->IsFriendlyTo(unit_target) || pet->HasAuraType(SPELL_AURA_MOD_POSSESS))) { ((Pet*)pet)->SetSpellOpener(spellid, sRange->minRange, sRange->maxRange); spell->finish(false); delete spell; pet->AttackStop(); pet->GetMotionMaster()->Clear(); ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); return; } // auto turn to target unless possessed if (result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { if (unit_target) { pet->SetInFront(unit_target); if (unit_target->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target); } else if (Unit* unit_target2 = spell->m_targets.getUnitTarget()) { pet->SetInFront(unit_target2); if (unit_target2->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)unit_target2); } if (Unit* powner = pet->GetCharmerOrOwner()) if (powner->GetTypeId() == TYPEID_PLAYER) pet->SendCreateUpdateToPlayer((Player*)powner); result = SPELL_CAST_OK; } if (result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); unit_target = spell->m_targets.getUnitTarget(); if (unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != unit_target) { pet->AttackStop(); pet->GetMotionMaster()->Clear(); _player->SetInCombatState(true, unit_target); if (((Creature*)pet)->AI()) { ((Creature*)pet)->AI()->AttackStart(unit_target); // 10% chance to play special warlock pet attack talk, else growl if (((Creature*)pet)->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != unit_target && roll_chance_i(10)) pet->SendPetTalk((uint32)PET_TALK_ATTACK); pet->SendPetAIReaction(); } else pet->Attack(unit_target, true); } } ((Pet*)pet)->SetSpellOpener(); spell->SpellStart(&(spell->m_targets)); } else { if (pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else { Unit* owner = pet->GetCharmerOrOwner(); if (owner && owner->GetTypeId() == TYPEID_PLAYER) Spell::SendCastResult((Player*)owner, spellInfo, 0, result, true); } if (!((Creature*)pet)->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); ((Pet*)pet)->SetSpellOpener(); spell->finish(false); delete spell; } break; } default: sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid); } }
void WorldSession::HandleMovementOpcodes(WorldPacket& recv_data) { Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plMover = mover->GetTypeId() == TYPEID_PLAYER ? mover->ToPlayer() : NULL; // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plMover && plMover->IsBeingTeleported()) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } //get opcode uint16 opcode = recv_data.GetOpcode(); /* extract packet */ MovementInfo movementInfo; recv_data >> movementInfo; /*----------------*/ /*if (recv_data.size() != recv_data.rpos()) { sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is %u bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), opcode, recv_data.size() - recv_data.rpos()); KickPlayer(); return; }*/ if (!BlizzLike::IsValidMapCoord(movementInfo.GetPos()->GetPositionX(), movementInfo.GetPos()->GetPositionY(), movementInfo.GetPos()->GetPositionZ(), movementInfo.GetPos()->GetOrientation())) return; //Save movement flags mover->SetUnitMovementFlags(movementInfo.GetMovementFlags()); /* handle special cases */ if (movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT)) { // transports size limited // (also received at zeppelin/lift leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.GetTransportPos()->GetPositionX() > 50 || movementInfo.GetTransportPos()->GetPositionY() > 50 || movementInfo.GetTransportPos()->GetPositionZ() > 50) return; if (!BlizzLike::IsValidMapCoord(movementInfo.GetPos()->GetPositionX() + movementInfo.GetTransportPos()->GetPositionX(), movementInfo.GetPos()->GetPositionY() + movementInfo.GetTransportPos()->GetPositionY(), movementInfo.GetPos()->GetPositionZ() + movementInfo.GetTransportPos()->GetPositionZ(), movementInfo.GetPos()->GetOrientation() + movementInfo.GetTransportPos()->GetOrientation())) return; // if we boarded a transport, add us to it if (plMover && !plMover->m_transport) { // elevators also cause the client to send MOVEFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { // unmount before boarding plMover->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); plMover->m_transport = (*iter); (*iter)->AddPassenger(plMover); break; } } } } else if (plMover && plMover->m_transport) // if we were on a transport, leave { plMover->m_transport->RemovePassenger(plMover); plMover->m_transport = NULL; movementInfo.ClearTransportData(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight()) plMover->HandleFallDamage(movementInfo); if (plMover && (movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING) != plMover->IsInWater())) { // now client not include swimming flag in case jumping under water plMover->SetInWater(!plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->GetPositionX(), movementInfo.GetPos()->GetPositionY(), movementInfo.GetPos()->GetPositionZ())); } /* process position-change */ recv_data.put<uint32>(5, getMSTime()); // offset flags(4) + unk(1) WorldPacket data(opcode, mover->GetPackGUID().size() + recv_data.size()); data << mover->GetPackGUID(); data.append(recv_data.contents(), recv_data.size()); if (mover->isCharmed() && mover->GetCharmer()) mover->GetCharmer()->SendMessageToSet(&data, false); else mover->SendMessageToSet(&data, false); mover->m_movementInfo = movementInfo; mover->SetPosition(movementInfo.GetPos()->GetPositionX(), movementInfo.GetPos()->GetPositionY(), movementInfo.GetPos()->GetPositionZ(), movementInfo.GetPos()->GetOrientation()); if (plMover) // nothing is charmed, or player charmed { if (opcode == MSG_MOVE_FALL_LAND || plMover->m_lastFallTime > movementInfo.GetFallTime() || plMover->m_lastFallZ < movementInfo.GetPos()->GetPositionZ()) plMover->SetFallInformation(movementInfo.GetFallTime(), movementInfo.GetPos()->GetPositionZ()); // we should add the check only for class hunter if (plMover->isMovingOrTurning()) plMover->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); if (movementInfo.GetPos()->GetPositionZ() < -500.0f) plMover->HandleFallUnderMap(); } }
void WorldSession::HandleMovementOpcodes(WorldPacket & recvData) { uint16 opcode = recvData.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL; Vehicle* vehMover = mover->GetVehicleKit(); if (vehMover) if (mover->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)) if (Unit* charmer = mover->GetCharmer()) if (charmer->GetTypeId() == TYPEID_PLAYER) plrMover = (Player*)charmer; // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvData.rfinish(); // prevent warnings spam return; } /* extract packet */ ObjectGuid guid; recvData >> guid.ReadAsPacked(); MovementInfo movementInfo; movementInfo.guid = guid; ReadMovementInfo(recvData, &movementInfo); recvData.rfinish(); // prevent warnings spam // prevent tampered movement data if (guid != mover->GetGUID()) return; if (!movementInfo.pos.IsPositionValid()) { recvData.rfinish(); // prevent warnings spam return; } /* handle special cases */ if (movementInfo.HasMovementFlag(MOVEMENTFLAG_ONTRANSPORT)) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.transport.pos.GetPositionX() > 50 || movementInfo.transport.pos.GetPositionY() > 50 || movementInfo.transport.pos.GetPositionZ() > 50) { recvData.rfinish(); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation())) { recvData.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover && !plrMover->m_transport && !plrMover->m_temp_transport) { if (!plrMover->GetTransport()) { if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { plrMover->GetTransport()->RemovePassenger(plrMover); if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) transport->AddPassenger(plrMover); else movementInfo.transport.Reset(); } } if (!plrMover->m_transport) if (Map *tempMap = mover->GetMap()) if (GameObject *tempTransport = tempMap->GetGameObject(movementInfo.transport.guid)) if (tempTransport->IsTransport()) plrMover->m_temp_transport = tempTransport; if ((!plrMover && !mover->GetTransport() && !mover->GetVehicle()) || (plrMover && !plrMover->m_vehicle && !plrMover->m_transport && !plrMover->m_temp_transport)) // Not sure if the first part is needed. Just added it for verbosity. { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ONTRANSPORT); } } else if (plrMover && (plrMover->m_transport || plrMover->m_temp_transport)) // if we were on a transport, leave { if (plrMover->m_transport) { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; } plrMover->m_temp_transport = NULL; movementInfo.transport.Reset(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) { // movement anticheat plrMover->m_anti_JumpCount = 0; plrMover->m_anti_JumpBaseZ = 0; if (!vehMover) plrMover->HandleFall(movementInfo); } if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } if (plrMover) sAnticheatMgr->StartHackDetection(plrMover, movementInfo, opcode); uint32 mstime = getMSTime(); /*----------------------*/ if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; // begin anti cheat bool check_passed = true; #ifdef ANTICHEAT_DEBUG TC_LOG_WARN("cheat", "AC2-%s > time: %d fall-time: %d | xyzo: %f, %f, %fo(%f) flags[%X] opcode[%s] | transport (xyzo): %f, %f, %fo(%f)", plrMover->GetName(), movementInfo.time, movementInfo.fallTime, movementInfo.pos.m_positionX, movementInfo.pos.m_positionY, movementInfo.pos.m_positionZ, movementInfo.pos.m_orientation, movementInfo.flags, LookupOpcodeName(opcode), movementInfo.transport.pos.m_positionX, movementInfo.transport.pos.m_positionY, movementInfo.transport.pos.m_positionZ, movementInfo.transport.pos.m_orientation); TC_LOG_WARN("cheat", "AC2-%s Transport > GUID: (low)%d - (high)%d", plrMover->GetName(), GUID_LOPART(movementInfo.transport.guid), GUID_HIPART(movementInfo.transport.guid)); #endif if (plrMover) { if (World::GetEnableMvAnticheat() && !plrMover->IsGameMaster() && !plrMover->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster() && !plrMover->GetCharmerOrOwnerPlayerOrPlayerItself()->GetVehicle()) { // calc time deltas int32 cClientTimeDelta = 1500; if (plrMover->m_anti_LastClientTime != 0) { cClientTimeDelta = movementInfo.time - plrMover->m_anti_LastClientTime; plrMover->m_anti_DeltaClientTime += cClientTimeDelta; plrMover->m_anti_LastClientTime = movementInfo.time; } else plrMover->m_anti_LastClientTime = movementInfo.time; const uint64 cServerTime = getMSTime(); uint32 cServerTimeDelta = 1500; if (plrMover->m_anti_LastServerTime != 0) { cServerTimeDelta = cServerTime - plrMover->m_anti_LastServerTime; plrMover->m_anti_DeltaServerTime += cServerTimeDelta; plrMover->m_anti_LastServerTime = cServerTime; } else plrMover->m_anti_LastServerTime = cServerTime; // resync times on client login (first 15 sec for heavy areas) if (plrMover->m_anti_DeltaServerTime < 15000 && plrMover->m_anti_DeltaClientTime < 15000) plrMover->m_anti_DeltaClientTime = plrMover->m_anti_DeltaServerTime; const int32 sync_time = plrMover->m_anti_DeltaClientTime - plrMover->m_anti_DeltaServerTime; #ifdef ANTICHEAT_DEBUG TC_LOG_WARN("cheat", "AC2-%s Time > cClientTimeDelta: %d, cServerTime: %d | deltaC: %d - deltaS: %d | SyncTime: %d", plrMover->GetName(), cClientTimeDelta, cServerTime, plrMover->m_anti_DeltaClientTime, plrMover->m_anti_DeltaServerTime, sync_time); #endif // mistiming checks const int32 GetMistimingDelta = abs(int32(World::GetMistimingDelta())); if (sync_time > GetMistimingDelta) { cClientTimeDelta = cServerTimeDelta; ++(plrMover->m_anti_MistimingCount); const bool bMistimingModulo = plrMover->m_anti_MistimingCount % 50 == 0; if (bMistimingModulo) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, mistiming exception #%d, mistiming: %dms Action: %s", plrMover->GetName().c_str(), plrMover->m_anti_MistimingCount, sync_time, &Warden::Penalty); #endif check_passed = false; } //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); /* Disabled, not passive at all, and apparently causing crashes: if (plrMover->m_anti_MistimingCount > World::GetMistimingAlarms()) { sWorld.SendWorldText(3, strcat("Kicking cheater: ", plrMover->GetName())); KickPlayer(); return; } */ } // end mistiming checks const uint32 curDest = plrMover->m_taxi.GetTaxiDestination(); // check taxi flight if (!curDest) { UnitMoveType move_type; // calculating section // current speed if (movementInfo.flags & MOVEMENTFLAG_FLYING) move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_FLIGHT_BACK : MOVE_FLIGHT; else if (movementInfo.flags & MOVEMENTFLAG_SWIMMING) move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_SWIM_BACK : MOVE_SWIM; else if (movementInfo.flags & MOVEMENTFLAG_WALKING) move_type = MOVE_WALK; // hmm... in first time after login player has MOVE_SWIMBACK instead MOVE_WALKBACK else move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_SWIM_BACK : MOVE_RUN; const float current_speed = mover->GetSpeed(move_type); // end current speed // movement distance const float delta_x = plrMover->m_transport || plrMover->m_temp_transport ? 0 : plrMover->GetPositionX() - movementInfo.pos.GetPositionX(); const float delta_y = plrMover->m_transport || plrMover->m_temp_transport ? 0 : plrMover->GetPositionY() - movementInfo.pos.GetPositionY(); const float delta_z = plrMover->m_transport || plrMover->m_temp_transport ? 0 : plrMover->GetPositionZ() - movementInfo.pos.GetPositionZ(); const float real_delta = plrMover->m_transport || plrMover->m_temp_transport ? 0 : pow(delta_x, 2) + pow(delta_y, 2); // end movement distance const bool no_fly_auras = !(plrMover->HasAuraType(SPELL_AURA_FLY) || plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED) || plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || plrMover->HasAuraType(SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS) || plrMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK)); const bool no_fly_flags = (movementInfo.flags & (MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_FLYING)) == 0; const bool no_swim_flags = (movementInfo.flags & MOVEMENTFLAG_SWIMMING) == 0; const bool no_swim_in_water = !mover->IsInWater(); const bool no_swim_above_water = movementInfo.pos.GetPositionZ() - 7.0f >= mover->GetBaseMap()->GetWaterLevel(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY()); const bool no_swim_water = no_swim_in_water && no_swim_above_water; const bool no_waterwalk_flags = (movementInfo.flags & MOVEMENTFLAG_WATERWALKING) == 0; const bool no_waterwalk_auras = !(plrMover->HasAuraType(SPELL_AURA_WATER_WALK) || plrMover->HasAuraType(SPELL_AURA_GHOST)); if (cClientTimeDelta < 0) cClientTimeDelta = 0; const float time_delta = cClientTimeDelta < 1500 ? float(cClientTimeDelta) / 1000.0f : 1.5f; // normalize time - 1.5 second allowed for heavy loaded server const float tg_z = (real_delta != 0 && no_fly_auras && no_swim_flags) ? (pow(delta_z, 2) / real_delta) : -99999; // movement distance tangents if (current_speed < plrMover->m_anti_Last_HSpeed && plrMover->m_anti_LastSpeedChangeTime == 0) plrMover->m_anti_LastSpeedChangeTime = movementInfo.time + uint32(floor(((plrMover->m_anti_Last_HSpeed / current_speed) * 1500)) + 100); // 100ms above for random fluctuation const float allowed_delta = plrMover->m_transport || plrMover->m_temp_transport ? 2 : // movement distance allowed delta pow(std::max(current_speed, plrMover->m_anti_Last_HSpeed) * time_delta, 2) +2 // minimum allowed delta +(tg_z > 2.2 ? pow(delta_z, 2) / 2.37f : 0); // mountain fall allowed delta if (movementInfo.time > plrMover->m_anti_LastSpeedChangeTime) { plrMover->m_anti_Last_HSpeed = current_speed; // store current speed plrMover->m_anti_Last_VSpeed = -2.3f; plrMover->m_anti_LastSpeedChangeTime = 0; } // end calculating section // AntiGravity (thanks to Meekro) const float JumpHeight = plrMover->m_anti_JumpBaseZ - movementInfo.pos.GetPositionZ(); if (no_fly_auras && no_swim_in_water && plrMover->m_anti_JumpBaseZ != 0 && JumpHeight < plrMover->m_anti_Last_VSpeed) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, AntiGravity exception. JumpHeight = %f, Allowed Vertical Speed = %f", plrMover->GetName().c_str(), JumpHeight, plrMover->m_anti_Last_VSpeed); #endif check_passed = false; //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); } // multi jump checks if (opcode == MSG_MOVE_JUMP) { if (no_fly_auras && no_swim_water) { if (plrMover->m_anti_JumpCount >= 1) { // don't process new jump packet check_passed = false; #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, Multijump exception.", plrMover->GetName().c_str(), JumpHeight, plrMover->m_anti_Last_VSpeed); #endif //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); plrMover->m_anti_JumpCount = 0; } else { plrMover->m_anti_JumpCount += 1; plrMover->m_anti_JumpBaseZ = movementInfo.pos.GetPositionZ(); } } else plrMover->m_anti_JumpCount = 0; } // speed and teleport hack checks if (real_delta > allowed_delta) { #ifdef ANTICHEAT_EXCEPTION_INFO if (real_delta < 4900.0f) { TC_LOG_WARN("cheat", "AC2-%s, speed exception | cDelta=%f aDelta=%f | cSpeed=%f lSpeed=%f deltaTime=%f", plrMover->GetName().c_str(), real_delta, allowed_delta, current_speed, plrMover->m_anti_Last_HSpeed, time_delta); } else { TC_LOG_WARN("cheat", "AC2-%s, teleport exception | cDelta=%f aDelta=%f | cSpeed=%f lSpeed=%f deltaTime=%f", plrMover->GetName().c_str(), real_delta, allowed_delta, current_speed, plrMover->m_anti_Last_HSpeed, time_delta); } #endif check_passed = false; //if (vehMover) // vehMover->Die(); //plrMover->FallGround(2); } // mountain hack checks // 1.56f (delta_z < GetPlayer()->m_anti_Last_VSpeed)) if (delta_z < plrMover->m_anti_Last_VSpeed && plrMover->m_anti_JumpCount == 0 && tg_z > 2.37f) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, mountain exception | tg_z=%f", plrMover->GetName().c_str(), tg_z); #endif check_passed = false; // if (vehMover) // vehMover->Die(); } // Fly hack checks if (no_fly_auras && !no_fly_flags) { #ifdef ANTICHEAT_EXCEPTION_INFO // Aura numbers: 201, 206, 207, 208, 209, 211 //TC_LOG_WARN("cheat", "AC2-%s, flight exception. {SPELL_AURA_FLY=[%X]} {SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED=[%X]} {SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED=[%X]} {SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS=[%X]} {SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK=[%X]} {plrMover->GetVehicle()=[%X]}", plrMover->GetName().c_str(), //Last Updated By ShopWoW.ir plrMover->HasAuraType(SPELL_AURA_FLY), plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED), plrMover->HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED), plrMover->HasAuraType(SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS), plrMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK), plrMover->GetVehicle(); #endif check_passed = false; //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); } // Waterwalk checks if (no_waterwalk_auras && !no_waterwalk_flags) { #ifdef ANTICHEAT_EXCEPTION_INFO TC_LOG_WARN("cheat", "AC2-%s, waterwalk exception. [%X]{SPELL_AURA_WATER_WALK=[%X]}", plrMover->GetName().c_str(), movementInfo.flags, plrMover->HasAuraType(SPELL_AURA_WATER_WALK)); #endif check_passed = false; //if (vehMover) // vehMover->Die(); // Tell the player "Sure, you can fly!" { WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } // Then tell the player "Wait, no, you can't." { WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); data << plrMover->GetPackGUID(); data << uint32(0); SendPacket(&data); } //plrMover->FallGround(2); } // Teleport To Plane checks if (no_swim_in_water && movementInfo.pos.GetPositionZ() < 0.0001f && movementInfo.pos.GetPositionZ() > -0.0001f) { if (const Map *map = plrMover->GetMap()) { float plane_z = map->GetHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), MAX_HEIGHT) - movementInfo.pos.GetPositionZ(); plane_z = (plane_z < -500.0f) ? 0.0f : plane_z; // check holes in height map if (plane_z > 0.1f || plane_z < -0.1f) { #ifdef ANTICHEAT_DEBUG TC_LOG_WARN("cheat", "AC2-%s, teleport to plane exception. plane_z: %f", plrMover->GetName(), plane_z); #endif #ifdef ANTICHEAT_EXCEPTION_INFO if (plrMover->m_anti_TeleToPlane_Count > World::GetTeleportToPlaneAlarms()) { TC_LOG_WARN("cheat", "AC2-%s, teleport to plane exception. Exception count: %d", plrMover->GetName().c_str(), plrMover->m_anti_TeleToPlane_Count); /* Disabled, not passive at all, and apparently causing crashes: sWorld.SendWorldText(3, strcat("Kicking cheater: ", plrMover->GetName())); KickPlayer(); return; */ } #endif ++(plrMover->m_anti_TeleToPlane_Count); check_passed = false; //if (vehMover) // vehMover->Die(); } } } else plrMover->m_anti_TeleToPlane_Count = 0; } } } /* process position-change */ if (check_passed) { WorldPacket data(opcode, recvData.size()); movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); WriteMovementInfo(&data, &movementInfo); mover->SendMessageToSet(&data, _player); mover->m_movementInfo = movementInfo; // Some vehicles allow the passenger to turn by himself if (Vehicle* vehicle = mover->GetVehicle()) { if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(mover)) { if (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING) { if (movementInfo.pos.GetOrientation() != mover->GetOrientation()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); } } } return; } mover->UpdatePosition(movementInfo.pos); if (plrMover && !vehMover) // nothing is charmed, or player charmed { if (plrMover->IsSitState() && (movementInfo.flags & (MOVEMENTFLAG_MASK_MOVING | MOVEMENTFLAG_MASK_TURNING))) plrMover->SetStandState(UNIT_STAND_STATE_STAND); plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); float underMapValueZ; switch (plrMover->GetMapId()) { case 617: underMapValueZ = 3.0f; break; // Dalaran Sewers case 618: underMapValueZ = 28.0f; break; // Ring of Valor default: underMapValueZ = -500.0f; break; } if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY()) || movementInfo.pos.GetPositionZ() < underMapValueZ) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { plrMover->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->IsAlive()) plrMover->KillPlayer(); } } } // movement anticheat if (plrMover->m_anti_AlarmCount > 0) { TC_LOG_WARN("cheat", "AC2-%s produce %d anticheat alarms.", plrMover->GetName().c_str(), plrMover->m_anti_AlarmCount); plrMover->m_anti_AlarmCount = 0; } // end movement anticheat } } else if (plrMover) { if (plrMover->m_transport) { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; } plrMover->m_temp_transport = NULL; ++(plrMover->m_anti_AlarmCount); WorldPacket data; plrMover->SetUnitMovementFlags(0); plrMover->SendTeleportAckPacket(); plrMover->BuildHeartBeatMsg(&data); plrMover->SendMessageToSet(&data, true); } }