2ce6c1d0b771e84aca8b4525bc93bcaabf37e234 src/game/MovementHandler.cpp | 519 +++++++++++++++++++++++++++++++++----- src/game/ObjectMgr.cpp | 27 ++- src/game/ObjectMgr.h | 2 +- src/game/Player.cpp | 36 +++ src/game/Player.h | 24 ++ src/game/TaxiHandler.cpp | 107 ++++++++- src/game/Unit.h | 2 +- src/game/World.cpp | 20 ++ src/game/World.h | 17 ++ src/mangosd/mangosd.conf.dist.in | 24 ++ 10 files changed, 706 insertions(+), 72 deletions(-) diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 9ecb7b0..7852cd3 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -31,6 +31,7 @@ #include "WaypointMovementGenerator.h" #include "InstanceSaveMgr.h" #include "ObjectMgr.h" +#include "World.h" void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ ) { @@ -61,6 +62,8 @@ void WorldSession::HandleMoveWorldportAckOpcode() GetPlayer()->TeleportToHomebind(); return; } + //movement anticheat + GetPlayer()->m_anti_JustTeleported = 1; // get the destination map entry, not the current one, this will fix homebind and reset greeting MapEntry const* mEntry = sMapStore.LookupEntry(loc.mapid); @@ -233,6 +236,8 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) if(plMover && plMover->IsBeingTeleported()) { recv_data.rpos(recv_data.wpos()); // prevent warnings spam + // movement anticheat + plMover->m_anti_JustTeleported = 1; return; } @@ -279,29 +284,47 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) 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)->GetObjectGuid() == movementInfo.GetTransportGuid()) - { - plMover->m_transport = (*iter); - (*iter)->AddPassenger(plMover); + // Movement anticheat + if (!plMover->IsInWorld()) return; - if (plMover->GetVehicleKit()) - plMover->GetVehicleKit()->RemoveAllPassengers(); - break; - } - } - } + if (plMover && plMover->m_anti_TransportGUID == 0 && (movementInfo.t_guid.GetRawValue() !=0)) + { + // 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)->GetObjectGuid() == movementInfo.GetTransportGuid()) + { + plMover->m_transport = (*iter); + (*iter)->AddPassenger(plMover); + + if (plMover->GetVehicleKit()) + plMover->GetVehicleKit()->RemoveAllPassengers(); + break; + } + } + } + //movement anticheat + //Correct finding GO guid in DB (thanks to GriffonHeart) + GameObject *obj = ObjectAccessor::GetGameObjectInWorld(movementInfo.t_guid.GetRawValue()); + if(obj) + plMover->m_anti_TransportGUID = obj->GetDBTableGUIDLow(); + else + plMover->m_anti_TransportGUID = GUID_LOPART(movementInfo.t_guid.GetRawValue()); + //end movement anticheat + } } - else if (plMover && plMover->m_transport) // if we were on a transport, leave + else if (plMover && plMover->m_anti_TransportGUID != 0) // Movement anticheat { - plMover->m_transport->RemovePassenger(plMover); - plMover->m_transport = NULL; + if (plMover && plMover->m_transport) // if we were on a transport, leave + { + plMover->m_transport->RemovePassenger(plMover); + plMover->m_transport = NULL; + } movementInfo.ClearTransportData(); + plMover->m_anti_TransportGUID = 0; // Movement anticheat } if (movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING)) { @@ -317,75 +340,417 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) // 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->IsTaxiFlying()) - plMover->HandleFall(movementInfo); + { + plMover->m_anti_JustJumped = 0; // Movement anticheat + plMover->m_anti_JumpBaseZ = 0; // Movement anticheat + 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) ); } /*----------------------*/ + /* anti-cheat features */ + bool check_passed = true; - /* process position-change */ - movementInfo.UpdateTime(getMSTime()); + #ifdef MOVEMENT_ANTICHEAT_DEBUG + if(plMover) + { + sLog.outBasic("MA-%s > client-time:%d fall-time:%d | xyzo: %f,%f,%fo(%f) flags[%X] opcode[%s]| transport (xyzo): %f,%f,%fo(%f)", + plMover->GetName(),movementInfo.time,movementInfo.fallTime,movementInfo.x,movementInfo.y,movementInfo.z,movementInfo.o, + movementInfo.flags, LookupOpcodeName(opcode),movementInfo.t_x,movementInfo.t_y,movementInfo.t_z,movementInfo.t_o); + sLog.outBasic("MA-%s Transport > server GUID: %d | client GUID: (lo)%d - (hi)%d", + plMover->GetName(),plMover->m_anti_TransportGUID, GUID_LOPART(movementInfo.t_guid.GetRawValue()), GUID_HIPART(movementInfo.t_guid.GetRawValue())); + } + else + { + sLog.outBasic("MA > client-time:%d fall-time:%d | xyzo: %f,%f,%fo(%f) flags[%X] opcode[%s]| transport (xyzo): %f,%f,%fo(%f)", + movementInfo.time,movementInfo.fallTime,movementInfo.x,movementInfo.y,movementInfo.z,movementInfo.o, + movementInfo.flags, LookupOpcodeName(opcode),movementInfo.t_x,movementInfo.t_y,movementInfo.t_z,movementInfo.t_o); + sLog.outBasic("MA Transport > server GUID: | client GUID: (lo)%d - (hi)%d", + GUID_LOPART(movementInfo.t_guid.GetRawValue()), GUID_HIPART(movementInfo.t_guid.GetRawValue())); + } + #endif + + if (plMover && World::GetEnableMvAnticheat()) + { + //calc time deltas + int32 cClientTimeDelta = 1500; + if (plMover->m_anti_LastClientTime !=0) + { + cClientTimeDelta = movementInfo.time - plMover->m_anti_LastClientTime; + plMover->m_anti_DeltaClientTime += cClientTimeDelta; + plMover->m_anti_LastClientTime = movementInfo.time; + } + else + plMover->m_anti_LastClientTime = movementInfo.time; + + uint32 cServerTime=getMSTime(); + uint32 cServerTimeDelta = 1500; + if (plMover->m_anti_LastServerTime != 0) + { + cServerTimeDelta = cServerTime - plMover->m_anti_LastServerTime; + plMover->m_anti_DeltaServerTime += cServerTimeDelta; + plMover->m_anti_LastServerTime = cServerTime; + } + else + plMover->m_anti_LastServerTime = cServerTime; - WorldPacket data(opcode, recv_data.size()); - data.appendPackGUID(mover->GetGUID()); // write guid - movementInfo.Write(data); // write data - mover->SendMessageToSetExcept(&data, _player); + //resync times on client login (first 15 sec for heavy areas) + if (plMover->m_anti_DeltaServerTime < 15000 && plMover->m_anti_DeltaClientTime < 15000) + plMover->m_anti_DeltaClientTime = plMover->m_anti_DeltaServerTime; - mover->m_movementInfo = movementInfo; - mover->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); + int32 sync_time = plMover->m_anti_DeltaClientTime - plMover->m_anti_DeltaServerTime; - 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); + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outBasic("MA-%s Time > cClientTimeDelta: %d, cServerTime: %d || deltaC: %d - deltaS: %d || SyncTime: %d", + plMover->GetName(),cClientTimeDelta, cServerTime, + plMover->m_anti_DeltaClientTime, plMover->m_anti_DeltaServerTime, sync_time); + #endif + + //mistiming checks + int32 gmd = World::GetMistimingDelta(); + if (sync_time > gmd || sync_time < -gmd) + { + cClientTimeDelta = cServerTimeDelta; + plMover->m_anti_MistimingCount++; + + sLog.outError("MA-%s, mistiming exception. #:%d, mistiming: %dms ", + plMover->GetName(), plMover->m_anti_MistimingCount, sync_time); + + if (plMover->m_anti_MistimingCount > World::GetMistimingAlarms()) + { + plMover->GetSession()->KickPlayer(); + return; + } + check_passed = false; + } + // end mistiming checks + + + uint32 curDest = plMover->m_taxi.GetTaxiDestination(); //check taxi flight + if ((plMover->m_anti_TransportGUID == 0) && !curDest) + { + UnitMoveType move_type; + + // calculating section --------------------- + //current speed + if (movementInfo.HasMovementFlag(MOVEFLAG_FLYING)) + move_type = movementInfo.HasMovementFlag(MOVEFLAG_BACKWARD) ? MOVE_FLIGHT_BACK : MOVE_FLIGHT; + else if (movementInfo.HasMovementFlag(MOVEFLAG_SWIMMING)) + move_type = movementInfo.HasMovementFlag(MOVEFLAG_BACKWARD) ? MOVE_SWIM_BACK : MOVE_SWIM; + else if (movementInfo.HasMovementFlag(MOVEFLAG_WALK_MODE)) + move_type = MOVE_WALK; + //hmm... in first time after login player has MOVE_SWIMBACK instead MOVE_WALKBACK + else + move_type = movementInfo.HasMovementFlag(MOVEFLAG_BACKWARD) ? MOVE_SWIM_BACK : MOVE_RUN; + + float current_speed = plMover->GetSpeed(move_type); + // end current speed + + // movement distance + float allowed_delta= 0; + + float delta_x = plMover->GetPositionX() - movementInfo.GetPos()->x; + float delta_y = plMover->GetPositionY() - movementInfo.GetPos()->y; + float delta_z = plMover->GetPositionZ() - movementInfo.GetPos()->z; + float real_delta = delta_x * delta_x + delta_y * delta_y; + float tg_z = -99999; //tangens + // end movement distance + + if (cClientTimeDelta < 0) + cClientTimeDelta = 0; + + //normalize time - 1.5 second allowed for heavy loaded server + float time_delta = (cClientTimeDelta < 1500) ? (float)cClientTimeDelta/1000 : 1.5f; + + if (!movementInfo.HasMovementFlag(MovementFlags(MOVEFLAG_FLYING | MOVEFLAG_SWIMMING))) + tg_z = (real_delta !=0) ? (delta_z*delta_z / real_delta) : -99999; + + if (current_speed < plMover->m_anti_Last_HSpeed) + { + allowed_delta = plMover->m_anti_Last_HSpeed; + if (plMover->m_anti_LastSpeedChangeTime == 0 ) + plMover->m_anti_LastSpeedChangeTime = movementInfo.time + (uint32)floor(((plMover->m_anti_Last_HSpeed / current_speed) * 1500)) + 100; //100ms above for random fluctuating =))) + } + else + allowed_delta = current_speed; + + allowed_delta = allowed_delta * time_delta; + allowed_delta = allowed_delta * allowed_delta + 2; + if (tg_z > 2.2) + allowed_delta = allowed_delta + (delta_z*delta_z)/2.37; // mountain fall allowed speed + + if (movementInfo.time>plMover->m_anti_LastSpeedChangeTime) + { + plMover->m_anti_Last_HSpeed = current_speed; // store current speed + plMover->m_anti_Last_VSpeed = -2.3f; + if (plMover->m_anti_LastSpeedChangeTime != 0) + plMover->m_anti_LastSpeedChangeTime = 0; + } + // end calculating section --------------------- + + //AntiGravitation (thanks to Meekro) + float JumpHeight = plMover->m_anti_JumpBaseZ - movementInfo.GetPos()->z; + if ((plMover->m_anti_JumpBaseZ != 0) + && !(movementInfo.HasMovementFlag(MovementFlags(MOVEFLAG_SWIMMING | MOVEFLAG_FLYING))) + && (JumpHeight < plMover->m_anti_Last_VSpeed)) + { + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, GraviJump exception. JumpHeight = %f, Allowed Veritcal Speed = %f", + plMover->GetName(), JumpHeight, plMover->m_anti_Last_VSpeed); + #endif + check_passed = false; + } + + //multi jump checks + if (opcode == MSG_MOVE_JUMP && !plMover->IsInWater()) + { + if (plMover->m_anti_JustJumped >= 1) + check_passed = false; //don't process new jump packet + else + { + plMover->m_anti_JustJumped += 1; + plMover->m_anti_JumpBaseZ = movementInfo.GetPos()->z; + } + } + else if (plMover->IsInWater()) + plMover->m_anti_JustJumped = 0; + + //speed hack checks + if ((real_delta > allowed_delta)) // && (delta_z < 0)) + { + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, speed exception | cDelta=%f aDelta=%f | cSpeed=%f lSpeed=%f deltaTime=%f", + plMover->GetName(), real_delta, allowed_delta, current_speed, plMover->m_anti_Last_HSpeed,time_delta); + #endif + check_passed = false; + } + //teleport hack checks + if ((real_delta>4900.0f) && !(real_delta < allowed_delta)) + { + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, is teleport exception | cDelta=%f aDelta=%f | cSpeed=%f lSpeed=%f deltaToime=%f", + plMover->GetName(),real_delta, allowed_delta, current_speed, plMover->m_anti_Last_HSpeed,time_delta); + #endif + check_passed = false; + } - // after move info set - if ((opcode == MSG_MOVE_SET_WALK_MODE || opcode == MSG_MOVE_SET_RUN_MODE)) - plMover->UpdateWalkMode(plMover, false); + //mountian hack checks // 1.56f (delta_z < GetPlayer()->m_anti_Last_VSpeed)) + if ((delta_z < plMover->m_anti_Last_VSpeed) && (plMover->m_anti_JustJumped == 0) && (tg_z > 2.37f)) + { + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, mountain exception | tg_z=%f", plMover->GetName(),tg_z); + #endif + check_passed = false; + } - if(plMover->isMovingOrTurning()) - plMover->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + //Fly hack checks + if ((movementInfo.HasMovementFlag(MovementFlags(MOVEFLAG_CAN_FLY | MOVEFLAG_FLYING))) + && !plMover->isGameMaster() + && !(plMover->HasAuraType(SPELL_AURA_FLY) || plMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED))) + { + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, flight exception. {SPELL_AURA_FLY=[%X]} {SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED=[%X]} {SPELL_AURA_MOD_SPEED_FLIGHT=[%X]} {SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS=[%X]} {SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK=[%X]}", + plMover->GetName(), + plMover->HasAuraType(SPELL_AURA_FLY), plMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_MOUNTED), + plMover->HasAuraType(SPELL_AURA_MOD_SPEED_FLIGHT), plMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS), + plMover->HasAuraType(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK)); + #endif + check_passed = false; + } - if(movementInfo.GetPos()->z < -500.0f) + //Water-Walk checks + if (movementInfo.HasMovementFlag(MOVEFLAG_WATERWALKING) + && !plMover->isGameMaster() + && !(plMover->HasAuraType(SPELL_AURA_WATER_WALK) | plMover->HasAuraType(SPELL_AURA_GHOST))) + { + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, water-walk exception. [%X]{SPELL_AURA_WATER_WALK=[%X]}", + plMover->GetName(), movementInfo.flags, plMover->HasAuraType(SPELL_AURA_WATER_WALK)); + #endif + check_passed = false; + } + + //Teleport To Plane checks + if (movementInfo.GetPos()->z < 0.0001f && movementInfo.GetPos()->z > -0.0001f + && (!movementInfo.HasMovementFlag(MovementFlags(MOVEFLAG_SWIMMING | MOVEFLAG_CAN_FLY | MOVEFLAG_FLYING))) + && !plMover->isGameMaster()) + { + // Prevent using TeleportToPlan. + Map *map = plMover->GetMap(); + if (map) + { + float plane_z = map->GetHeight(movementInfo.GetPos()->x, movementInfo.GetPos()->y, MAX_HEIGHT) - movementInfo.GetPos()->z; + plane_z = (plane_z < -500.0f) ? 0 : plane_z; //check holes in heigth map + if(plane_z > 0.1f || plane_z < -0.1f) + { + plMover->m_anti_TeleToPlane_Count++; + check_passed = false; + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outDebug("MA-%s, teleport to plan exception. plane_z: %f ", + plMover->GetName(), plane_z); + #endif + if (plMover->m_anti_TeleToPlane_Count > World::GetTeleportToPlaneAlarms()) + { + sLog.outError("MA-%s, teleport to plan exception. Exception count: %d ", + plMover->GetName(), plMover->m_anti_TeleToPlane_Count); + plMover->GetSession()->KickPlayer(); + return; + } + } + } + } + else + { + if (plMover->m_anti_TeleToPlane_Count != 0) + plMover->m_anti_TeleToPlane_Count = 0; + } + } + else if (movementInfo.HasMovementFlag(MOVEFLAG_ONTRANSPORT)) { - if(plMover->InBattleGround() - && plMover->GetBattleGround() - && plMover->GetBattleGround()->HandlePlayerUnderMap(_player)) + //antiwrap checks + if (plMover->m_transport) { - // do nothing, the handle already did if returned true + const Position* transportPos = movementInfo.GetTransportPos(); + float trans_rad = transportPos->x*transportPos->x + transportPos->y*transportPos->y + transportPos->z*transportPos->z; + if (trans_rad > 3600.0f) + { + check_passed = false; + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, leave transport.", plMover->GetName()); + #endif + } } 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()) + if (GameObjectData const* go_data = sObjectMgr.GetGOData(plMover->m_anti_TransportGUID)) { - plMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, plMover->GetMaxHealth()); - // pl can be alive if GM/etc - if(!plMover->isAlive()) + float delta_gox = go_data->posX - movementInfo.GetPos()->x; + float delta_goy = go_data->posY - movementInfo.GetPos()->y; + float delta_goz = go_data->posZ - movementInfo.GetPos()->z; + int mapid = go_data->mapid; + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outDebug("MA-%s, transport movement. GO xyzo: %f,%f,%f", + plMover->GetName(), go_data->posX,go_data->posY,go_data->posZ); + #endif + if (plMover->GetMapId() != mapid) + { + check_passed = false; + } + else if (mapid !=369) { - // change the death state to CORPSE to prevent the death timer from - // starting in the next player update - plMover->KillPlayer(); - plMover->BuildPlayerRepop(); + float delta_go = delta_gox*delta_gox + delta_goy*delta_goy; + if (delta_go > 3600.0f) + { + check_passed = false; + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outError("MA-%s, leave transport. GO xyzo: %f,%f,%f", + plMover->GetName(), go_data->posX,go_data->posY,go_data->posZ); + #endif + } } } - - // cancel the death timer here if started - plMover->RepopAtGraveyard(); + else + { + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outDebug("MA-%s, undefined transport.", plMover->GetName()); + #endif + check_passed = false; + } + } + if (!check_passed) + { + if (plMover->m_transport) + { + plMover->m_transport->RemovePassenger(plMover); + plMover->m_transport = NULL; + } + movementInfo.ClearTransportData(); + plMover->m_anti_TransportGUID = 0; } } } - else // creature charmed + + /* process position-change */ + if (check_passed) { - if(mover->IsInWorld()) + 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 { - mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); + 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(); + } + } + + //movement anticheat + if (plMover->m_anti_AlarmCount > 0) + { + sLog.outError("MA-%s produce %d anticheat alarms",plMover->GetName(),plMover->m_anti_AlarmCount); + plMover->m_anti_AlarmCount = 0; + } + //end movement anticheat + } + else // creature charmed + { + if(mover->IsInWorld()) + mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); + } + } + else if (plMover) + { + plMover->m_anti_AlarmCount++; + WorldPacket data; + plMover->BuildTeleportAckMsg(&data, plMover->GetPositionX(), plMover->GetPositionY(), plMover->GetPositionZ(), plMover->GetOrientation()); + plMover->GetSession()->SendPacket(&data); + plMover->BuildHeartBeatMsg(&data); + plMover->SendMessageToSet(&data, true); + { if(((Creature*)mover)->isVehicle()) ((Vehicle*)mover)->RellocatePassengers(mover->GetMap()); } @@ -482,13 +847,23 @@ void WorldSession::HandleMoveNotActiveMoverOpcode(WorldPacket &recv_data) DEBUG_LOG("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER"); recv_data.hexlike(); - ObjectGuid guid; + ObjectGuid old_mover_guid; MovementInfo mi; - recv_data >> guid.ReadAsPacked(); + recv_data >> old_mover_guid.ReadAsPacked(); recv_data >> mi; - GetPlayer()->m_movementInfo = mi; + if (_player->GetMover()->GetObjectGuid() == old_mover_guid.GetRawValue()) + { + sLog.outError("HandleMoveNotActiveMover: incorrect mover guid: mover is %s and should be %s instead of %s", + _player->GetMover()->GetObjectGuid().GetString().c_str(), + _player->GetObjectGuid().GetString().c_str(), + old_mover_guid.GetString().c_str()); + recv_data.rpos(recv_data.wpos()); // prevent warnings spam + return; + } + + _player->m_movementInfo = mi; } void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data) @@ -690,6 +1065,24 @@ void WorldSession::HandleMoveKnockBackAck( WorldPacket & recv_data ) recv_data >> guid.ReadAsPacked(); recv_data >> Unused(); // unk recv_data >> movementInfo; + + //movement anticheat + + //Save movement flags + //_player->SetUnitMovementFlags(movementInfo.flags); + + #ifdef MOVEMENT_ANTICHEAT_DEBUG + 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",GetPlayer()->GetName(), movementInfo.j_unk, movementInfo.j_xyspeed); + #endif + + _player->m_movementInfo = movementInfo; + _player->m_anti_Last_HSpeed = movementInfo.j_xyspeed; + _player->m_anti_Last_VSpeed = movementInfo.j_velocity < 3.2f ? movementInfo.j_velocity - 1.0f : 3.2f; + + uint32 dt = (_player->m_anti_Last_VSpeed < 0) ? (int)(ceil(_player->m_anti_Last_VSpeed/-25)*1000) : (int)(ceil(_player->m_anti_Last_VSpeed/25)*1000); + _player->m_anti_LastSpeedChangeTime = movementInfo.time + dt + 1000; + //end movement anticheat } void WorldSession::HandleMoveHoverAck( WorldPacket& recv_data ) diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index fc2c04c..04045e0 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -5691,7 +5691,8 @@ void ObjectMgr::LoadEventIdScripts() sLog.outString( ">> Loaded %u scripted event id", count ); } -uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team ) +//use searched_node for search some known node +uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team, uint32 searched_node ) // Movement anticheat { bool found = false; float dist; @@ -5700,9 +5701,24 @@ uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, u for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) { TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i); - if(!node || node->map_id != mapid || !node->MountCreatureID[team == ALLIANCE ? 1 : 0]) + + //movement anticheat + if (!node || node->map_id != mapid) continue; + float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z); + + if (searched_node != 0 && i == searched_node) + { + id = i; + dist = dist2; + break; + } + if(!node->MountCreatureID[team == ALLIANCE ? 1 : 0]) + continue; + + //end movement anticheat + uint8 field = (uint8)((i - 1) / 32); uint32 submask = 1<<((i-1)%32); @@ -5710,7 +5726,7 @@ uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, u if((sTaxiNodesMask[field] & submask)==0) continue; - float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z); + //float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z); if(found) { if(dist2 < dist) @@ -5726,7 +5742,10 @@ uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, u id = i; } } - + //movement anticheat fix + if (dist > 3600) + id = 0; + //movement anticheat fix return id; } diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index bdc5470..f026f0b 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -812,7 +812,7 @@ class ObjectMgr uint32 GetPlayerAccountIdByGUID(ObjectGuid guid) const; uint32 GetPlayerAccountIdByPlayerName(const std::string& name) const; - uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team ); + uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team, uint32 searched_node ); // Movement anticheat void GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost); uint32 GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_alt_team = false); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 825e552..ed90956 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -532,6 +532,30 @@ Player::Player (WorldSession *session): Unit(), m_mover(this), m_camera(this), m rest_type=REST_TYPE_NO; ////////////////////Rest System///////////////////// + ////////////////Movement Anticheat////////////////// + m_anti_LastClientTime = 0; //last movement client time + m_anti_LastServerTime = 0; //last movement server time + m_anti_DeltaClientTime = 0; //client side session time + m_anti_DeltaServerTime = 0; //server side session time + m_anti_MistimingCount = 0; //mistiming counts before kick + + m_anti_LastSpeedChangeTime = 0; //last speed change time + m_anti_BeginFallTime = 0; //alternative falling begin time (obsolete) + + m_anti_Last_HSpeed = 7.0f; //horizontal speed, default RUN speed + m_anti_Last_VSpeed = -2.3f; //vertical speed, default max jump height + + m_anti_TransportGUID = 0; //current transport GUID + + m_anti_JustTeleported = 0; //seted when player was teleported + m_anti_TeleToPlane_Count = 0; //Teleport To Plane alarm counter + + m_anti_AlarmCount = 0; //alarm counter + + m_anti_JustJumped = 0; //Jump already began, anti air jump check + m_anti_JumpBaseZ = 0; //Z coord before jump (AntiGrav) + ////////////////Movement Anticheat////////////////// + m_mailsUpdated = false; unReadMails = 0; m_nextMailDelivereTime = 0; @@ -1731,6 +1755,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati return false; } + //movement anticheat + m_anti_JustTeleported = 1; + // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later) Pet* pet = GetPet(); @@ -1791,6 +1818,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if ((GetMapId() == mapid) && (!m_transport)) { + //movement anticheat + m_anti_JumpBaseZ = 0; + //lets reset far teleport flag if it wasn't reset during chained teleports SetSemaphoreTeleportFar(false); //setup delayed teleport flag @@ -1945,6 +1975,10 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z, final_o); SetFallInformation(0, final_z); + + //movement anticheat + m_anti_JumpBaseZ = 0; + // if the player is saved before worldport ack (at logout for example) // this will be used instead of the current location in SaveToDB @@ -21427,6 +21461,8 @@ void Player::SendEnterVehicle(Vehicle *vehicle) data << uint64(GetGUID()); data << uint64(vehicle->GetVehicleId()); // not sure SendMessageToSet(&data, true);*/ + //movement anticheat fix + SetPosition(vehicle->GetPositionX(), vehicle->GetPositionY(), vehicle->GetPositionZ(),vehicle->GetOrientation()); } bool Player::isTotalImmune() diff --git a/src/game/Player.h b/src/game/Player.h index 4d3aeb8..1c700d3 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -2642,6 +2642,30 @@ class MANGOS_DLL_SPEC Player : public Unit RestType rest_type; ////////////////////Rest System///////////////////// + /////////////////Movement Anticheat///////////////// + uint32 m_anti_LastClientTime; //last movement client time + uint32 m_anti_LastServerTime; //last movement server time + uint32 m_anti_DeltaClientTime; //client side session time + uint32 m_anti_DeltaServerTime; //server side session time + uint32 m_anti_MistimingCount; //mistiming counts before kick + + uint32 m_anti_LastSpeedChangeTime; //last speed change time + uint32 m_anti_BeginFallTime; //alternative falling begin time (obsolete) + + float m_anti_Last_HSpeed; //horizontal speed, default RUN speed + float m_anti_Last_VSpeed; //vertical speed, default max jump height + + uint64 m_anti_TransportGUID; //current transport GUID + + uint32 m_anti_JustTeleported; //seted when player was teleported + uint32 m_anti_TeleToPlane_Count; //Teleport To Plane alarm counter + + uint64 m_anti_AlarmCount; //alarm counter + + uint32 m_anti_JustJumped; //Jump already began, anti air jump check + float m_anti_JumpBaseZ; //Z coord before jump + /////////////////Movement Anticheat///////////////// + // Transports Transport * m_transport; diff --git a/src/game/TaxiHandler.cpp b/src/game/TaxiHandler.cpp index 86ad5fd..c67bcb6 100644 --- a/src/game/TaxiHandler.cpp +++ b/src/game/TaxiHandler.cpp @@ -49,7 +49,7 @@ void WorldSession::SendTaxiStatus( uint64 guid ) return; } - uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam()); + uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam(), 0); //movement anticheat // not found nearest if(curloc == 0) @@ -94,7 +94,7 @@ void WorldSession::HandleTaxiQueryAvailableNodes( WorldPacket & recv_data ) void WorldSession::SendTaxiMenu( Creature* unit ) { // find current node - uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam()); + uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam(), 0); //movement anticheat if ( curloc == 0 ) return; @@ -129,7 +129,7 @@ void WorldSession::SendDoFlight( uint32 mountDisplayId, uint32 path, uint32 path bool WorldSession::SendLearnNewTaxiNode( Creature* unit ) { // find current node - uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam()); + uint32 curloc = sObjectMgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId(),GetPlayer( )->GetTeam(), 0); //movement anticheat if ( curloc == 0 ) return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result. @@ -198,11 +198,94 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data) // 1) end taxi path in far (multi-node) flight // 2) switch from one map to other in case multi-map taxi path // we need process only (1) + + //movement anticheat + Unit *mover = _player->m_mover; + Player *plMover = mover->GetTypeId()==TYPEID_PLAYER ? (Player*)mover : NULL; + if (!plMover) + return; + //end movement anticheat + uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination(); if(!curDest) + { + //movement anticheat + GetPlayer()->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); + GetPlayer()->m_movementInfo = movementInfo; + // GetPlayer()->SetUnitMovementFlags(movementInfo.flags); + + //calc time deltas + int32 cClientTimeDelta = 0; + if (GetPlayer()->m_anti_LastClientTime !=0) + { + cClientTimeDelta = movementInfo.time - GetPlayer()->m_anti_LastClientTime; + GetPlayer()->m_anti_DeltaClientTime += cClientTimeDelta; + GetPlayer()->m_anti_LastClientTime = movementInfo.time; + } + else + GetPlayer()->m_anti_LastClientTime = movementInfo.time; + + uint32 cServerTime=getMSTime(); + uint32 cServerTimeDelta = 0; + if (GetPlayer()->m_anti_LastServerTime != 0) + { + cServerTimeDelta = cServerTime - GetPlayer()->m_anti_LastServerTime; + GetPlayer()->m_anti_DeltaServerTime += cServerTimeDelta; + GetPlayer()->m_anti_LastServerTime = cServerTime; + } + else + GetPlayer()->m_anti_LastServerTime = cServerTime; + + GetPlayer()->m_anti_JustTeleported = 1; + //end movement anticheat + return; + } + + //movment anticheat + uint32 curloc = sObjectMgr.GetNearestTaxiNode(movementInfo.GetPos()->x,movementInfo.GetPos()->y,movementInfo.GetPos()->z,GetPlayer()->GetMapId(),GetPlayer( )->GetTeam(), curDest); + + #ifdef MOVEMENT_ANTICHEAT_DEBUG + sLog.outBasic("MA-%s > | xyzo: %f,%f,%fo(%f) flags[%X] | curloc: %d | destloc: %d ", + GetPlayer()->GetName(),movementInfo.x,movementInfo.y,movementInfo.z,movementInfo.o, + movementInfo.flags, curloc,curDest); + #endif + //end movement anticheat TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest); + //movement anticheat + if(curDestNode && curDestNode->map_id == GetPlayer()->GetMapId()) + { + while(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE) + GetPlayer()->GetMotionMaster()->MovementExpired(false); + } + + GetPlayer()->SetPosition(movementInfo.GetPos()->x, movementInfo.GetPos()->y, movementInfo.GetPos()->z, movementInfo.GetPos()->o); + GetPlayer()->m_movementInfo = movementInfo; + //GetPlayer()->SetUnitMovementFlags(movementInfo.flags); + + //calc time deltas + int32 cClientTimeDelta = 0; + if (GetPlayer()->m_anti_LastClientTime !=0) + { + cClientTimeDelta = movementInfo.time - GetPlayer()->m_anti_LastClientTime; + GetPlayer()->m_anti_DeltaClientTime += cClientTimeDelta; + GetPlayer()->m_anti_LastClientTime = movementInfo.time; + } + else + GetPlayer()->m_anti_LastClientTime = movementInfo.time; + + uint32 cServerTime=getMSTime(); + uint32 cServerTimeDelta = 0; + if (GetPlayer()->m_anti_LastServerTime != 0) + { + cServerTimeDelta = cServerTime - GetPlayer()->m_anti_LastServerTime; + GetPlayer()->m_anti_DeltaServerTime += cServerTimeDelta; + GetPlayer()->m_anti_LastServerTime = cServerTime; + } + else + GetPlayer()->m_anti_LastServerTime = cServerTime; + //end movement anticheat // far teleport case if(curDestNode && curDestNode->map_id != GetPlayer()->GetMapId()) @@ -223,6 +306,24 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& recv_data) return; } + //movement anticheat fix - disallow unmount from taxi + if(curloc != curDest) + { + // current source node for next destination + uint32 sourcenode = GetPlayer()->m_taxi.GetTaxiSource(); + uint16 MountId = sObjectMgr.GetTaxiMountDisplayId(sourcenode, GetPlayer()->GetTeam()); + + uint32 path, cost; + sObjectMgr.GetTaxiPath( sourcenode, curDest, path, cost); + + if(path && MountId) + SendDoFlight( MountId, path, 1 ); // skip start fly node + else + GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next + return; + } + //end movement anticheat + uint32 destinationnode = GetPlayer()->m_taxi.NextTaxiDestination(); if ( destinationnode > 0 ) // if more destinations to go { diff --git a/src/game/Unit.h b/src/game/Unit.h index 550da7c..9b82517 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -809,7 +809,7 @@ class MovementInfo void ChangePosition(float x, float y, float z, float o) { pos.x = x; pos.y = y; pos.z = z; pos.o = o; } void UpdateTime(uint32 _time) { time = _time; } - private: + public: // Changed for movement anticheat // common uint32 moveFlags; // see enum MovementFlags uint16 moveFlags2; // see enum MovementFlags2 diff --git a/src/game/World.cpp b/src/game/World.cpp index 4757c1b..bfaf86d 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -78,6 +78,12 @@ float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE; float World::m_VisibleUnitGreyDistance = 0; float World::m_VisibleObjectGreyDistance = 0; +//movement anticheat +bool World::m_EnableMvAnticheat = true; +uint32 World::m_TeleportToPlaneAlarms = 50; +uint32 World::m_MistimingAlarms = 20; +uint32 World::m_MistimingDelta = 10000; + /// World constructor World::World() { @@ -513,6 +519,20 @@ void World::LoadConfigSettings(bool reload) setConfigPos(CONFIG_FLOAT_CREATURE_FAMILY_ASSISTANCE_RADIUS, "CreatureFamilyAssistanceRadius", 10.0f); setConfigPos(CONFIG_FLOAT_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS, "CreatureFamilyFleeAssistanceRadius", 30.0f); + ///- Read movement anticheat from the config file + setConfigMinMax(CONFIG_UINT32_ANTICHEAT_TELEPORTTOPLANE_ALARMS, "Anticheat.Movement.TeleportToPlaneAlarms", 50, 20, 100); + setConfigMinMax(CONFIG_UINT32_ANTICHEAT_MISTIMING_DELTA, "Anticheat.Movement.MistimingDelta", 10000, 1000, 30000); + setConfigMinMax(CONFIG_UINT32_ANTICHEAT_MISTIMING_ALARMS, "Anticheat.Movement.MistimingAlarms", 20, 10, 50); + setConfig(CONFIG_BOOL_ANTICHEAT_ENABLE, "Anticheat.Movement.Enable", true); + + m_EnableMvAnticheat = sConfig.GetBoolDefault("Anticheat.Movement.Enable",true); + if (m_EnableMvAnticheat) { + m_TeleportToPlaneAlarms = sConfig.GetIntDefault("Anticheat.Movement.TeleportToPlaneAlarms",50); + m_MistimingDelta = sConfig.GetIntDefault("Anticheat.Movement.MistimingDelta",10000); + m_MistimingAlarms = sConfig.GetIntDefault("Anticheat.Movement.MistimingAlarms",20); + sLog.outDebug("Anticheat parameters %i, %i, %i",m_TeleportToPlaneAlarms,m_MistimingAlarms,m_MistimingDelta); + } else sLog.outError("Anticheat movement disabled"); + ///- Read other configuration items from the config file setConfigMinMax(CONFIG_UINT32_COMPRESSION, "Compression", 1, 1, 9); diff --git a/src/game/World.h b/src/game/World.h index 505358e..10d5540 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -187,6 +187,9 @@ enum eConfigUInt32Values CONFIG_UINT32_TIMERBAR_BREATH_MAX, CONFIG_UINT32_TIMERBAR_FIRE_GMLEVEL, CONFIG_UINT32_TIMERBAR_FIRE_MAX, + CONFIG_UINT32_ANTICHEAT_TELEPORTTOPLANE_ALARMS, + CONFIG_UINT32_ANTICHEAT_MISTIMING_DELTA, + CONFIG_UINT32_ANTICHEAT_MISTIMING_ALARMS, CONFIG_UINT32_MIN_LEVEL_STAT_SAVE, CONFIG_UINT32_CHARDELETE_KEEP_DAYS, CONFIG_UINT32_CHARDELETE_METHOD, @@ -323,6 +326,7 @@ enum eConfigBoolValues CONFIG_BOOL_BATTLEGROUND_CAST_DESERTER, CONFIG_BOOL_BATTLEGROUND_QUEUE_ANNOUNCER_START, CONFIG_BOOL_ARENA_AUTO_DISTRIBUTE_POINTS, + CONFIG_BOOL_ANTICHEAT_ENABLE, CONFIG_BOOL_ARENA_QUEUE_ANNOUNCER_JOIN, CONFIG_BOOL_ARENA_QUEUE_ANNOUNCER_EXIT, CONFIG_BOOL_KICK_PLAYER_ON_BAD_PACKET, @@ -570,6 +574,13 @@ class World static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; } static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; } + //movement anticheat + static bool GetEnableMvAnticheat() {return m_EnableMvAnticheat;} + static uint32 GetTeleportToPlaneAlarms() {return m_TeleportToPlaneAlarms;} + static uint32 GetMistimingDelta() {return m_MistimingDelta;} + static uint32 GetMistimingAlarms() {return m_MistimingAlarms;} + //end movement anticheat + void ProcessCliCommands(); void QueueCliCommand(CliCommandHolder* commandHolder) { cliCmdQueue.add(commandHolder); } @@ -663,6 +674,12 @@ class World static float m_VisibleUnitGreyDistance; static float m_VisibleObjectGreyDistance; + // for movement anticheat + static bool m_EnableMvAnticheat; + static uint32 m_TeleportToPlaneAlarms; + static uint32 m_MistimingDelta; + static uint32 m_MistimingAlarms; + // CLI command holder to be thread safe ACE_Based::LockedQueue cliCmdQueue; SqlResultQueue *m_resultQueue; diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 5632450..525b74e 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -1757,6 +1757,30 @@ SOAP.IP = 127.0.0.1 SOAP.Port = 7878 ################################################################################################################### +# MOVEMENT ANTICHEAT +# +# Anticheat.Movement.Enable +# Enable Movement Anticheat +# Default: 1 - on +# 0 - off +# +# Anticheat.Movement.TeleportToPlaneAlarms +# maximum alarms before player will be kicked (default 50, allowed 20 - 100) +# +# Anticheat.Movement.MistimingDelta +# mistiming intelval between client and serverside (default 10000 ms, allowed 1000 - 15000 ms) +# +# Anticheat.Movement.MistimingAlarms +# mistiming alarms before player will be kicked (default 20, allowed 10 - 50) +# +################################################################################################################### + +Anticheat.Movement.Enable = 1 +Anticheat.Movement.TeleportToPlaneAlarms = 50 +Anticheat.Movement.MistimingDelta = 10000 +Anticheat.Movement.MistimingAlarms = 20 + +################################################################################################################### # CharDelete.Method # Character deletion behavior # Default: 0 - Completely remove the character from the database