Index: src/game/MovementHandler.cpp =================================================================== --- src/game/MovementHandler.cpp (revision 5515) +++ src/game/MovementHandler.cpp (working copy) @@ -135,6 +135,9 @@ if(GetPlayer()->GetDontMove()) return; + //get opcode and preview flags + uint32 m_flags = GetPlayer()->GetMovementFlags(); + uint16 opcode = recv_data.GetOpcode(); /* extract packet */ MovementInfo movementInfo; @@ -205,6 +208,8 @@ /* handle special cases */ if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) { + if ((GetPlayer()->m_anti_transportGUID == 0) && (movementInfo.t_guid !=0)) + { // if we boarded a transport, add us to it if (!GetPlayer()->m_transport) { @@ -221,23 +226,34 @@ } } } + GetPlayer()->m_anti_transportGUID = movementInfo.t_guid; + } } - else if (GetPlayer()->m_transport) // if we were on a transport, leave - { - GetPlayer()->m_transport->RemovePassenger(GetPlayer()); - GetPlayer()->m_transport = NULL; + else if (GetPlayer()->m_anti_transportGUID != 0) { + if (GetPlayer()->m_transport) // if we were on a transport, leave + { + GetPlayer()->m_transport->RemovePassenger(GetPlayer()); + GetPlayer()->m_transport = NULL; + } movementInfo.t_x = 0.0f; movementInfo.t_y = 0.0f; movementInfo.t_z = 0.0f; movementInfo.t_o = 0.0f; movementInfo.t_time = 0; + GetPlayer()->m_anti_transportGUID = 0; } // fall damage generation (ignore in flight case that can be triggred also at lags in moment teleportation to another map). if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND && !GetPlayer()->isInFlight()) { Player *target = GetPlayer(); - + //alternate falltime calculation in cheat case + if (GetPlayer()->m_anti_beginfalltime != 0) + { + movementInfo.fallTime = movementInfo.time - GetPlayer()->m_anti_beginfalltime + 500; //falltime, +500 microsecond for first packet when begin falling + GetPlayer()->m_anti_beginfalltime = 0; + //sLog.outBasic("%d",movementInfo.fallTime ); + } //Players with Feather Fall or low fall time, or physical immunity are ignored if (movementInfo.fallTime > 1100 && !target->isDead() && !target->isGameMaster() && !target->HasAuraType(SPELL_AURA_HOVER) && !target->HasAuraType(SPELL_AURA_FEATHER_FALL) && @@ -289,28 +305,152 @@ GetPlayer()->SetInWater( !GetPlayer()->IsInWater() || GetPlayer()->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); } - /*----------------------*/ + // ---- anti-cheat features -->>> + bool kicked = true; + if ((GetPlayer()->m_anti_transportGUID == 0) && sWorld.GetEnableMvAnticheat()) //&& !GetPlayer()->isInFlight() + { + UnitMoveType move_type; + + if (movementInfo.flags & MOVEMENTFLAG_FLYING) move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_FLYBACK : MOVE_FLY; + else if (movementInfo.flags & MOVEMENTFLAG_SWIMMING) move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_SWIMBACK : MOVE_SWIM; + else if (movementInfo.flags & MOVEMENTFLAG_WALK) move_type = MOVE_WALK; + else move_type = movementInfo.flags & MOVEMENTFLAG_BACKWARD ? MOVE_SWIMBACK : MOVE_RUN; + //hmm... in first time after login player has MOVE_SWIMBACK instead MOVE_WALKBACK + if (GetPlayer()->IsMounted() && (move_type == MOVE_RUN)) move_type = MOVE_MOUNTED; + + float c_delta= 0; + float cur_speed = GetPlayer()->GetSpeed(move_type); + float delta_x = GetPlayer()->GetPositionX() - movementInfo.x; + float delta_y = GetPlayer()->GetPositionY() - movementInfo.y; // + 0.002f Some vibration for HyperJump detect =) + float delta_z = GetPlayer()->GetPositionZ() - movementInfo.z; + float delta = delta_x * delta_x + delta_y * delta_y; + float tg_z = -99999; //tangens + float delta_t = movementInfo.time - GetPlayer()->m_anti_lastmovetime; + if (delta_t > 0) { + GetPlayer()->m_anti_lastmovetime = movementInfo.time; + } else {delta_t = 0;} + delta_t = (delta_t < 1500) ? delta_t/1000 : 1.5; + if (!(movementInfo.flags & (MOVEMENTFLAG_FLYING | MOVEMENTFLAG_SWIMMING))) + tg_z = (delta !=0) ? delta_z*delta_z / delta : -99999; + + //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_beginfalltime == 0) && (movementInfo.flags & GetPlayer()->m_movementInfo.flags & (MOVEMENTFLAG_FALLING | MOVEMENTFLAG_UNK4 | MOVEMENTFLAG_JUMPING))) + { + if ((movementInfo.fallTime - GetPlayer()->m_movementInfo.fallTime) < delta_t) + GetPlayer()->m_anti_beginfalltime = GetPlayer()->m_movementInfo.time; + //sLog.outBasic("%d",GetPlayer()->m_beginfalltime); + }; + if (cur_speed < GetPlayer()->m_anti_last_hspeed) + { + c_delta = GetPlayer()->m_anti_last_hspeed; + if (GetPlayer()->m_anti_lastspeed_changetime == 0 ) + GetPlayer()->m_anti_lastspeed_changetime = movementInfo.time + (uint32)floor(((GetPlayer()->m_anti_last_hspeed / cur_speed) * 1000)); + } else c_delta = cur_speed; + + c_delta = c_delta * delta_t; + c_delta = c_delta * c_delta + 2; + + //static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "Walkback", "Swim", "Swimback", "Turn", "Fly", "Flyback" }; + //sLog.outBasic("%s newcoord: tm:%d ftm:%d | %f,%f,%fo(%f) [%X][%s]$%s",GetPlayer()->GetName(),movementInfo.time,movementInfo.fallTime,movementInfo.x,movementInfo.y,movementInfo.z,movementInfo.o,movementInfo.flags, LookupName(opcode,g_worldOpcodeNames),move_type_name[move_type]); + //sLog.outBasic("%f",tg_z); + + if ((delta > c_delta) && (delta_z < 1)) + { + sLog.outError("Movement anticheat: %s is speed cheater. {m_delta=%f c_delta=%f | cspd=%f lspd=%f tm=%f}(%f %f %f %d)[%s]",GetPlayer()->GetName(),delta, c_delta, cur_speed, GetPlayer()->m_anti_last_vspeed,delta_t,GetPlayer()->GetPositionX(),GetPlayer()->GetPositionY(),GetPlayer()->GetPositionZ(), GetPlayer()->GetMapId(),LookupOpcodeName(opcode)); + kicked = false; + } + if ((delta>4900.0f) && !(delta < c_delta)) + { + sLog.outError("Movement anticheat: %s is teleport cheater. {m_delta=%f c_delta=%f | cspd=%f lspd=%f tm=%f}(%f %f %f %d)",GetPlayer()->GetName(),delta, c_delta, cur_speed, GetPlayer()->m_anti_last_vspeed,delta_t,GetPlayer()->GetPositionX(),GetPlayer()->GetPositionY(),GetPlayer()->GetPositionZ(), GetPlayer()->GetMapId()); + kicked = false; + } + + if (movementInfo.time>GetPlayer()->m_anti_lastspeed_changetime) + { + GetPlayer()->m_anti_last_hspeed = cur_speed; // store current speed + GetPlayer()->m_anti_last_vspeed = -3.2f; + if (GetPlayer()->m_anti_lastspeed_changetime != 0) GetPlayer()->m_anti_lastspeed_changetime = 0; + } + + if ((tg_z > 1.56f) && (delta_z < GetPlayer()->m_anti_last_vspeed)) + { + sLog.outError("Movement anticheat: %s is mountain cheater. {tg_z=%f} (%f %f %f %d)",GetPlayer()->GetName(),tg_z, GetPlayer()->GetPositionX(),GetPlayer()->GetPositionY(),GetPlayer()->GetPositionZ(), GetPlayer()->GetMapId()); + kicked = false; + } + if (((movementInfo.flags & (MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_FLYING)) != 0) && !GetPlayer()->isGameMaster() && !(GetPlayer()->HasAuraType(SPELL_AURA_MOD_SPEED_MOUNTED_FLIGHT) | GetPlayer()->HasAuraType(SPELL_AURA_FLY))) + { + sLog.outError("Movement anticheat: %s is fly cheater. {SPELL_AURA_FLY=[%X]|SPELL_AURA_MOD_SPEED_MOUNTED_FLIGHT=[%X]}", GetPlayer()->GetName(), GetPlayer()->HasAuraType(SPELL_AURA_FLY), GetPlayer()->HasAuraType(SPELL_AURA_MOD_SPEED_MOUNTED_FLIGHT)); + kicked = false; + } + if (((movementInfo.flags & MOVEMENTFLAG_WATERWALKING) != 0) && !GetPlayer()->isGameMaster() && !(GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) | GetPlayer()->HasAuraType(SPELL_AURA_GHOST))) + { + sLog.outError("Movement anticheat: %s is water-walk cheater. {SPELL_AURA_WATER_WALK=[%X]}", GetPlayer()->GetName(), GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK)); + kicked = false; + } + } else if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT) + { + float trans_rad = movementInfo.t_x*movementInfo.t_x + movementInfo.t_y*movementInfo.t_y + movementInfo.t_z*movementInfo.t_z; + if (trans_rad > 3600.0f) // transport radius = 60 yards //cheater with on_transport_flag + { + if (GetPlayer()->m_transport) + { + GetPlayer()->m_transport->RemovePassenger(GetPlayer()); + GetPlayer()->m_transport = NULL; + } + movementInfo.t_x = 0.0f; + movementInfo.t_y = 0.0f; + movementInfo.t_z = 0.0f; + movementInfo.t_o = 0.0f; + movementInfo.t_time = 0; + GetPlayer()->m_anti_transportGUID = 0; + } + } + // <<---- anti-cheat features /* process position-change */ - recv_data.put(5, getMSTime()); // offset flags(4) + unk(1) - WorldPacket data(recv_data.GetOpcode(), (GetPlayer()->GetPackGUID().size()+recv_data.size())); - data.append(GetPlayer()->GetPackGUID()); - data.append(recv_data.contents(), recv_data.size()); - GetPlayer()->SendMessageToSet(&data, false); + if (kicked) + { + recv_data.put(5, getMSTime()); // offset flags(4) + unk(1) + WorldPacket data(recv_data.GetOpcode(), (GetPlayer()->GetPackGUID().size()+recv_data.size())); + data.append(GetPlayer()->GetPackGUID()); + data.append(recv_data.contents(), recv_data.size()); + GetPlayer()->SendMessageToSet(&data, false); - GetPlayer()->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); - GetPlayer()->m_movementInfo = movementInfo; + GetPlayer()->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + GetPlayer()->m_movementInfo = movementInfo; - if(GetPlayer()->isMovingOrTurning()) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + if(GetPlayer()->isMovingOrTurning()) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - if(movementInfo.z < -500.0f) - { - if(GetPlayer()->isAlive()) - GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); - GetPlayer()->BuildPlayerRepop(); - GetPlayer()->RepopAtGraveyard(); - } + if(movementInfo.z < -500.0f) + { + if(GetPlayer()->isAlive()) + GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); + GetPlayer()->BuildPlayerRepop(); + GetPlayer()->RepopAtGraveyard(); + } + } else { + if (sWorld.GetAlarmKickMvAnticheat()) // if kick cheater enabled + { + uint32 alarm_delta = movementInfo.time - GetPlayer()->m_anti_lastalarmtime; + GetPlayer()->m_anti_lastalarmtime = movementInfo.time; + if (alarm_delta < sWorld.GetAlarmTimeMvAnticheat()) + { + GetPlayer()->m_anti_alarmcount = GetPlayer()->m_anti_alarmcount + 1; + if (GetPlayer()->m_anti_alarmcount > sWorld.GetAlarmCountMvAnticheat()) + { + GetPlayer()->GetSession()->KickPlayer(); + sLog.outError("Movement anticheat: %s kicked as cheater!!!",GetPlayer()->GetName()); + return; + } + } + } + WorldPacket data; + GetPlayer()->BuildTeleportAckMsg(&data, GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY(), GetPlayer()->GetPositionZ(), GetPlayer()->GetOrientation()); + GetPlayer()->GetSession()->SendPacket(&data); + GetPlayer()->BuildHeartBeatMsg(&data); + GetPlayer()->SendMessageToSet(&data, true); + } } void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) @@ -463,34 +603,40 @@ GetPlayer()->SendMessageToSet(&data, false); } -void WorldSession::HandleMoveKnockBackAck( WorldPacket & /*recv_data*/ ) +void WorldSession::HandleMoveKnockBackAck( WorldPacket & recv_data ) { // CHECK_PACKET_SIZE(recv_data,?); sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK"); // Currently not used but maybe use later for recheck final player position // (must be at call same as into "recv_data >> x >> y >> z >> orientation;" - /* - uint32 flags, time; - float x, y, z, orientation; - uint64 guid; - uint32 sequence; - uint32 ukn1; - float xdirection,ydirection,hspeed,vspeed; + CHECK_PACKET_SIZE(recv_data, 8+4+4+1+4+4+4+4+4); + MovementInfo movementInfo; + uint64 guid; + uint32 sequence; + uint32 ukn1; + uint8 unkB; + float xdirection,ydirection,hspeed,vspeed; - recv_data >> guid; - recv_data >> sequence; - recv_data >> flags >> time; - recv_data >> x >> y >> z >> orientation; - recv_data >> ukn1; //unknown - recv_data >> vspeed >> xdirection >> ydirection >> hspeed; + recv_data >> guid; + recv_data >> sequence; + recv_data >> movementInfo.flags >> unkB >> movementInfo.time; + recv_data >> movementInfo.x >> movementInfo.y >> movementInfo.z >> movementInfo.o; + recv_data >> ukn1; //unknown + recv_data >> vspeed >> xdirection >> ydirection >> hspeed; + //sLog.outBasic("%s CMSG_MOVE_KNOCK_BACK_ACK: tm:%d ftm:%d | %f,%f,%fo(%f) [%X]",GetPlayer()->GetName(),movementInfo.time,movementInfo.fallTime,movementInfo.x,movementInfo.y,movementInfo.z,movementInfo.o,movementInfo.flags); + //sLog.outBasic("%s CMSG_MOVE_KNOCK_BACK_ACK additional: vspeed:%f, hspeed:%f, xdir:%f ydir:%f",GetPlayer()->GetName(), vspeed, hspeed, xdirection, ydirection); + // skip not personal message; if(GetPlayer()->GetGUID()!=guid) return; - // check code - */ + GetPlayer()->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + GetPlayer()->m_movementInfo = movementInfo; + GetPlayer()->m_anti_last_hspeed = hspeed; + GetPlayer()->m_anti_last_vspeed = vspeed < 3.2f ? vspeed - 1.0f : 3.2f; + GetPlayer()->m_anti_lastspeed_changetime = movementInfo.time + 1750; } void WorldSession::HandleMoveHoverAck( WorldPacket& /*recv_data*/ ) @@ -512,3 +658,4 @@ } + Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 5515) +++ src/game/Player.cpp (working copy) @@ -208,7 +208,17 @@ m_rest_bonus=0; rest_type=REST_TYPE_NO; ////////////////////Rest System///////////////////// + //movement anticheat + m_anti_lastmovetime = 0; //last movement time + m_anti_transportGUID = 0; //current transport GUID + m_anti_last_hspeed = 7.0f; //horizontal speed, default RUN speed + m_anti_lastspeed_changetime = 0; //last speed change time + m_anti_last_vspeed = -3.2f;//vertical speed, default max jump height + m_anti_beginfalltime = 0; //alternative falling begin time + uint32 m_anti_lastalarmtime = 0; //last time when alarm generated + uint32 m_anti_alarmcount = 0; //alarm counter + ///////////////////////////////// m_mailsLoaded = false; m_mailsUpdated = false; unReadMails = 0; Index: src/game/Player.h =================================================================== --- src/game/Player.h (revision 5515) +++ src/game/Player.h (working copy) @@ -2093,7 +2093,16 @@ float m_rest_bonus; RestType rest_type; ////////////////////Rest System///////////////////// - + //movement anticheat + uint32 m_anti_lastmovetime; //last movement time + uint64 m_anti_transportGUID; //current transport GUID + float m_anti_last_hspeed; //horizontal speed, default RUN speed + uint32 m_anti_lastspeed_changetime; //last speed change time + float m_anti_last_vspeed; //vertical speed, default max jump height + uint32 m_anti_beginfalltime; //alternative falling begin time + + uint32 m_anti_lastalarmtime; //last time when alarm generated + uint32 m_anti_alarmcount; //alarm counter // Transports Transport * m_transport; Index: src/game/TaxiHandler.cpp =================================================================== --- src/game/TaxiHandler.cpp (revision 5515) +++ src/game/TaxiHandler.cpp (working copy) @@ -153,6 +153,7 @@ _player->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); FlightPathMovementGenerator *flight = new FlightPathMovementGenerator(path,pathNode); + GetPlayer()->GetMotionMaster()->Clear(true); GetPlayer()->GetMotionMaster()->Mutate(flight); SendPath(flight->GetPath(),flight->GetCurrentNode(),flight->GetPathAtMapEnd()); @@ -221,9 +222,21 @@ GetPlayer()->ActivateTaxiPathTo(nodes); } -void WorldSession::HandleTaxiNextDestinationOpcode(WorldPacket& /*recv_data*/) +void WorldSession::HandleTaxiNextDestinationOpcode(WorldPacket& recvPacket) { - sLog.outDebug( "WORLD: Received CMSG_MOVE_SPLINE_DONE" ); + //sLog.outBasic( "WORLD: Received CMSG_MOVE_SPLINE_DONE" ); + MovementInfo movementInfo; + recvPacket >> movementInfo.flags; + recvPacket >> movementInfo.unk1; + recvPacket >> movementInfo.time; + recvPacket >> movementInfo.x; + recvPacket >> movementInfo.y; + recvPacket >> movementInfo.z; + recvPacket >> movementInfo.o; + GetPlayer()->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + GetPlayer()->m_movementInfo = movementInfo; + GetPlayer()->m_anti_lastmovetime = movementInfo.time; + //sLog.outBasic("%s CMSG_MOVE_SPLINE_DONE: tm:%d ftm:%d | %f,%f,%fo(%f) [%X]",GetPlayer()->GetName(),movementInfo.time,movementInfo.fallTime,movementInfo.x,movementInfo.y,movementInfo.z,movementInfo.o,movementInfo.flags); // in taxi flight packet received in 2 case: // 1) end taxi path in far (multi-node) flight Index: src/game/World.cpp =================================================================== --- src/game/World.cpp (revision 5515) +++ src/game/World.cpp (working copy) @@ -382,7 +382,11 @@ sLog.outError("Rate.Talent (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_TALENT]); rate_values[RATE_TALENT] = 1; } - + // movement anticheat + m_EnableMvAnticheat = sConfig.GetBoolDefault("Anticheat.Movement.Enable",true); + m_AlarmKickMvAnticheat = sConfig.GetBoolDefault("Anticheat.Movement.Kick",false); + m_AlarmCountMvAnticheat = sConfig.GetIntDefault("Anticheat.Movement.AlarmCount", 5); + m_AlarmTimeMvAnticheat = sConfig.GetIntDefault("Anticheat.Movement.AlarmTime", 5000); ///- Read other configuration items from the config file m_configs[CONFIG_COMPRESSION] = sConfig.GetIntDefault("Compression", 1); Index: src/game/World.h =================================================================== --- src/game/World.h (revision 5515) +++ src/game/World.h (working copy) @@ -366,7 +366,12 @@ static float GetMaxVisibleDistanceInFlight() { return m_MaxVisibleDistanceInFlight; } static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; } static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; } - + //movement anticheat enable flag + bool GetEnableMvAnticheat() {return m_EnableMvAnticheat;} + bool GetAlarmKickMvAnticheat() {return m_AlarmKickMvAnticheat;} + uint32 GetAlarmCountMvAnticheat() {return m_AlarmCountMvAnticheat;} + uint32 GetAlarmTimeMvAnticheat() {return m_AlarmTimeMvAnticheat;} + void ProcessCliCommands(); void QueueCliCommand(CliCommandHolder* command) { cliCmdQueue.add(command); } @@ -418,7 +423,11 @@ static float m_MaxVisibleDistanceInFlight; static float m_VisibleUnitGreyDistance; static float m_VisibleObjectGreyDistance; - + //movement anticheat enable flag + bool m_EnableMvAnticheat; + bool m_AlarmKickMvAnticheat; + uint32 m_AlarmCountMvAnticheat; + uint32 m_AlarmTimeMvAnticheat; // CLI command holder to be thread safe ZThread::LockedQueue cliCmdQueue; SqlResultQueue *m_resultQueue; Index: src/mangosd/mangosd.conf.in =================================================================== --- src/mangosd/mangosd.conf.in (revision 5515) +++ src/mangosd/mangosd.conf.in (working copy) @@ -767,3 +767,29 @@ Ra.Port = 3443 Ra.MinLevel = 3 Ra.Secure = 1 + +################################################################################################################### +# MOVEMENT ANTICHEAT +# +# Anticheat.Movement.Enable +# Enable Movement Anticheat +# Default: 1 - on +# 0 - off +# +# Anticheat.Movement.Kick +# Enable Kick cheater +# Default: 1 - on +# 0 - off +# +# Anticheat.Movement.AlarmCount +# Count alarms after that player kicked (default 5) +# +# Anticheat.Movement.AlarmTime +# maximum time between alarms for count it (default 5000) +# +################################################################################################################### + +Anticheat.Movement.Enable = 1 +Anticheat.Movement.Kick = 1 +Anticheat.Movement.AlarmCount = 5 +Anticheat.Movement.AlarmTime = 5000