void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) { uint32 opcode = recv_data.GetOpcode(); sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode); recv_data.hexlike(); Unit *mover = _player->m_mover; Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL; // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if(plMover && plMover->IsBeingTeleported()) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } /* extract packet */ ObjectGuid guid; MovementInfo movementInfo; recv_data >> guid.ReadAsPacked(); recv_data >> movementInfo; /*----------------*/ if (!MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o)) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } /* 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()->x > 50 || movementInfo.GetTransportPos()->y > 50 || movementInfo.GetTransportPos()->z > 100 ) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } if( !MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x + movementInfo.GetTransportPos()->x, movementInfo.GetPos()->y + movementInfo.GetTransportPos()->y, movementInfo.GetPos()->z + movementInfo.GetTransportPos()->z, movementInfo.GetPos()->o + movementInfo.GetTransportPos()->o) ) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam 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::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.GetTransportGuid()) { 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.SetTransportData(0, 0.0f, 0.0f, 0.0f, 0.0f, 0, -1); } // 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->HandleFall(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()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z) ); } /*----------------------*/ /* process position-change */ movementInfo.UpdateTime(getMSTime()); WorldPacket data(opcode, recv_data.size()); data.appendPackGUID(mover->GetGUID()); // write guid movementInfo.Write(data); // write data GetPlayer()->SendMessageToSet(&data, false); if(plMover) // nothing is charmed, or player charmed { plMover->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); plMover->m_movementInfo = movementInfo; plMover->UpdateFallInformationIfNeed(movementInfo, opcode); // after move info set if ((opcode == MSG_MOVE_SET_WALK_MODE || opcode == MSG_MOVE_SET_RUN_MODE)) plMover->UpdateWalkMode(plMover, false); if(plMover->isMovingOrTurning()) plMover->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); if(movementInfo.GetPos()->z < -500.0f) { if(plMover->InBattleGround() && plMover->GetBattleGround() && plMover->GetBattleGround()->HandlePlayerUnderMap(_player)) { // do nothing, the handle already did if returned true } else { // 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(plMover->isAlive()) { plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // pl can be alive if GM/etc if(!plMover->isAlive()) { // change the death state to CORPSE to prevent the death timer from // starting in the next player update plMover->KillPlayer(); plMover->BuildPlayerRepop(); } } // cancel the death timer here if started plMover->RepopAtGraveyard(); } } } else // creature charmed { if(mover->IsInWorld()) mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); } }
void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) { uint32 opcode = recv_data.GetOpcode(); sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode); recv_data.hexlike(); Unit *mover = _player->m_mover; Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL; // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if(plMover && plMover->IsBeingTeleported()) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } /* extract packet */ ObjectGuid guid; MovementInfo movementInfo; recv_data >> guid.ReadAsPacked(); recv_data >> movementInfo; /*----------------*/ if (!MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o)) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } /* handle special cases */ if (movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT) && !mover->GetVehicleGUID()) { // 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()->x > 50 || movementInfo.GetTransportPos()->y > 50 || movementInfo.GetTransportPos()->z > 100 ) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } if( !MaNGOS::IsValidMapCoord(movementInfo.GetPos()->x + movementInfo.GetTransportPos()->x, movementInfo.GetPos()->y + movementInfo.GetTransportPos()->y, movementInfo.GetPos()->z + movementInfo.GetTransportPos()->z, movementInfo.GetPos()->o + movementInfo.GetTransportPos()->o) ) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plMover && !plMover->m_transport) { float trans_rad = movementInfo.GetTransportPos()->x*movementInfo.GetTransportPos()->x + movementInfo.GetTransportPos()->y*movementInfo.GetTransportPos()->y + movementInfo.GetTransportPos()->z*movementInfo.GetTransportPos()->z; if (trans_rad > 3600.0f) // transport radius = 60 yards //cheater with on_transport_flag { return; } // elevators also cause the client to send MOVEFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr.m_Transports.begin(); iter != sMapMgr.m_Transports.end(); ++iter) { if ((*iter)->GetObjectGuid() == movementInfo.GetTransportGuid()) { 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.SetTransportData(0, 0.0f, 0.0f, 0.0f, 0.0f, 0, -1); } // 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->HandleFall(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()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z) ); if(plMover->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z-7.0f)) { plMover->m_anti_BeginFallZ=INVALID_HEIGHT; } } if (movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING)) { if(mover->GetTypeId() == TYPEID_UNIT) { if(((Creature*)mover)->isVehicle() && !((Creature*)mover)->canSwim()) { // NOTE : we should enter evade mode here, but... ((Vehicle*)mover)->SetSpawnDuration(1); } } } // ---- anti-cheat features -->>> uint32 Anti_TeleTimeDiff=plMover ? time(NULL) - plMover->Anti__GetLastTeleTime() : time(NULL); static const uint32 Anti_TeleTimeIgnoreDiff=sWorld.GetMvAnticheatIgnoreAfterTeleport(); if (plMover && (plMover->m_transport == 0) && sWorld.GetMvAnticheatEnable() && GetPlayer()->GetSession()->GetSecurity() <= sWorld.GetMvAnticheatGmLevel() && GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()!=FLIGHT_MOTION_TYPE && Anti_TeleTimeDiff>Anti_TeleTimeIgnoreDiff) { const uint32 CurTime=getMSTime(); if(getMSTimeDiff(GetPlayer()->m_anti_lastalarmtime,CurTime) > sWorld.GetMvAnticheatAlarmPeriod()) { GetPlayer()->m_anti_alarmcount = 0; } /* I really don't care about movement-type yet (todo) UnitMoveType move_type; if (movementInfo.flags & MOVEMENTFLAG_FLYING) move_type = MOVE_FLY; else if (movementInfo.flags & MOVEMENTFLAG_SWIMMING) move_type = MOVE_SWIM; else if (movementInfo.flags & MOVEMENTFLAG_WALK_MODE) move_type = MOVE_WALK; else move_type = MOVE_RUN;*/ float delta_x = GetPlayer()->GetPositionX() - movementInfo.GetPos()->x; float delta_y = GetPlayer()->GetPositionY() - movementInfo.GetPos()->y; float delta_z = GetPlayer()->GetPositionZ() - movementInfo.GetPos()->z; float delta = sqrt(delta_x * delta_x + delta_y * delta_y); // Len of movement-vector via Pythagoras (a^2+b^2=Len^2) float tg_z = 0.0f; //tangens float delta_t = getMSTimeDiff(GetPlayer()->m_anti_lastmovetime,CurTime); GetPlayer()->m_anti_lastmovetime = CurTime; GetPlayer()->m_anti_MovedLen += delta; if(delta_t > 15000.0f) { delta_t = 15000.0f; } // Tangens of walking angel if (!(movementInfo.GetMovementFlags() & (MOVEFLAG_FLYING | MOVEFLAG_SWIMMING))) { //Mount hack detection currently disabled tg_z = ((delta !=0.0f) && (delta_z > 0.0f)) ? (atan((delta_z*delta_z) / delta) * 180.0f / M_PI) : 0.0f; } //antiOFF fall-damage, MOVEMENTFLAG_UNK4 seted by client if player try movement when falling and unset in this case the MOVEMENTFLAG_FALLING flag. if((GetPlayer()->m_anti_BeginFallZ == INVALID_HEIGHT) && (movementInfo.GetMovementFlags() & (MOVEFLAG_FALLING | MOVEFLAG_FALLINGFAR)) != 0) { GetPlayer()->m_anti_BeginFallZ=(float)(movementInfo.GetPos()->z); } if(GetPlayer()->m_anti_NextLenCheck <= CurTime) { // Check every 500ms is a lot more advisable then 1000ms, because normal movment packet arrives every 500ms uint32 OldNextLenCheck=GetPlayer()->m_anti_NextLenCheck; float delta_xyt=GetPlayer()->m_anti_MovedLen/(float)(getMSTimeDiff(OldNextLenCheck-500,CurTime)); GetPlayer()->m_anti_NextLenCheck = CurTime+500; GetPlayer()->m_anti_MovedLen = 0.0f; static const float MaxDeltaXYT = sWorld.GetMvAnticheatMaxXYT(); #ifdef __ANTI_DEBUG__ SendAreaTriggerMessage("XYT: %f ; Flags: %s",delta_xyt,FlagsToStr(movementInfo.GetMovementFlags()).c_str()); #endif //__ANTI_DEBUG__ if(delta_xyt > MaxDeltaXYT && delta<=100.0f && GetPlayer()->GetZoneId() != 2257) { Anti__CheatOccurred(CurTime,"Speed hack",delta_xyt,LookupOpcodeName(opcode), (float)(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()), (float)(getMSTimeDiff(OldNextLenCheck-500,CurTime))/*,&movementInfo*/); } } if(delta > 100.0f && GetPlayer()->GetZoneId() != 2257) { Anti__ReportCheat("Tele hack",delta,LookupOpcodeName(opcode)); } // Check for waterwalking . Fix new way of checking for waterwalking by Darky88 if (movementInfo.HasMovementFlag(MOVEFLAG_WATERWALKING) && !(GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) || GetPlayer()->HasAuraType(SPELL_AURA_GHOST))) { Anti__CheatOccurred(CurTime,"Water walking",0.0f,NULL,0.0f,(uint32)(movementInfo.GetMovementFlags())); } // Check for walking upwards a mountain while not beeing able to do that if ((tg_z > 85.0f)) { Anti__CheatOccurred(CurTime,"Mount hack",tg_z,NULL,delta,delta_z); } static const float DIFF_OVERGROUND = 10.0f; float Anti__GroundZ = GetPlayer()->GetMap()->GetHeight(GetPlayer()->GetPositionX(),GetPlayer()->GetPositionY(),MAX_HEIGHT); float Anti__FloorZ = GetPlayer()->GetMap()->GetHeight(GetPlayer()->GetPositionX(),GetPlayer()->GetPositionY(),GetPlayer()->GetPositionZ()); float Anti__MapZ = ((Anti__FloorZ <= (INVALID_HEIGHT+5.0f)) ? Anti__GroundZ : Anti__FloorZ) + DIFF_OVERGROUND; if(!GetPlayer()->CanFly() && /*!plMover->isGameMaster() &&*/ !GetPlayer()->GetBaseMap()->IsUnderWater(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z-7.0f) && Anti__MapZ < GetPlayer()->GetPositionZ() && Anti__MapZ > (INVALID_HEIGHT+DIFF_OVERGROUND + 5.0f)) { static const float DIFF_AIRJUMP=25.0f; // 25 is realy high, but to many false positives... // Air-Jump-Detection definitively needs a better way to be detected... if((movementInfo.GetMovementFlags() & (MOVEFLAG_CAN_FLY | MOVEFLAG_FLYING | MOVEFLAG_ROOT)) != 0) // Fly Hack { Anti__CheatOccurred(CurTime,"Fly hack", ((uint8)(GetPlayer()->HasAuraType(SPELL_AURA_FLY))) + ((uint8)(GetPlayer()->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED))*2), NULL,GetPlayer()->GetPositionZ()-Anti__MapZ); } /* Need a better way to do that - currently a lot of fake alarms else if((Anti__MapZ+DIFF_AIRJUMP < GetPlayer()->GetPositionZ() && (movementInfo.flags & (MOVEMENTFLAG_FALLING | MOVEMENTFLAG_UNK4))==0) || (Anti__MapZ < GetPlayer()->GetPositionZ() && opcode==MSG_MOVE_JUMP)) { Anti__CheatOccurred(CurTime,"Possible Air Jump Hack", 0.0f,LookupOpcodeName(opcode),0.0f,movementInfo.flags,&movementInfo); }*/ } /*if(Anti__FloorZ < -199900.0f && Anti__GroundZ >= -199900.0f && GetPlayer()->GetPositionZ()+5.0f < Anti__GroundZ) { Anti__CheatOccurred(CurTime,"Teleport2Plane hack", GetPlayer()->GetPositionZ(),NULL,Anti__GroundZ); }*/ } // <<---- anti-cheat features /* process position-change */ movementInfo.UpdateTime(getMSTime()); WorldPacket data(opcode, recv_data.size()); data.appendPackGUID(mover->GetGUID()); // write guid movementInfo.Write(data); // write data GetPlayer()->SendMessageToSet(&data, false); if(plMover) // nothing is charmed, or player charmed { plMover->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); plMover->m_movementInfo = movementInfo; plMover->UpdateFallInformationIfNeed(movementInfo, opcode); // after move info set if ((opcode == MSG_MOVE_SET_WALK_MODE || opcode == MSG_MOVE_SET_RUN_MODE)) plMover->UpdateWalkMode(plMover, false); if(plMover->isMovingOrTurning()) plMover->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); if(movementInfo.GetPos()->z < -500.0f) { if(plMover->InBattleGround() && plMover->GetBattleGround() && plMover->GetBattleGround()->HandlePlayerUnderMap(_player)) { // do nothing, the handle already did if returned true } else { // 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(plMover->isAlive()) { plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // pl can be alive if GM/etc if(!plMover->isAlive()) { // change the death state to CORPSE to prevent the death timer from // starting in the next player update plMover->KillPlayer(); plMover->BuildPlayerRepop(); } } // cancel the death timer here if started plMover->RepopAtGraveyard(); } } } else // creature charmed { if(mover->IsInWorld()) mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); } }