diff --git a/dCommon/dEnums/eFunnessTypes.h b/dCommon/dEnums/eFunnessTypes.h new file mode 100644 index 00000000..e3df34fa --- /dev/null +++ b/dCommon/dEnums/eFunnessTypes.h @@ -0,0 +1,26 @@ +#ifndef EFUNNESSTYPES_H +#define EFUNNESSTYPES_H + +#include + +// Most of these are guesses from the client, some are unused (maybe server side only?) and +// some are really hard to pin a definition down for because they are buried in havok stuff +enum class eFunnessTypes : int32_t { + DebuggerActive = 1, + CharacterPosLength = 2, + CharacterVelLength = 5, + CharacterRunMultiplier = 6, + CharacterGravityScale = 7, + Unknown_9 = 9, + Unknown_10 = 10, + RacingBoostTimeTooLong = 12, + SomeRacingManipCheat = 13, + FdbFailedChecksum = 14, +}; + +struct CaughtFunness { + eFunnessTypes cheatType{}; + float cheatInfo{}; +}; + +#endif //!EFUNNESSTYPES_H diff --git a/dGame/User.cpp b/dGame/User.cpp index 1f8a41fa..42e0196c 100644 --- a/dGame/User.cpp +++ b/dGame/User.cpp @@ -9,6 +9,7 @@ #include "eGameMasterLevel.h" #include "BitStreamUtils.h" #include "MessageType/Chat.h" +#include "StringifiedEnum.h" #include #include @@ -127,11 +128,16 @@ void User::SetMuteExpire(time_t value) { m_MuteExpire = value; } -void User::UserOutOfSync() { +void User::UserOutOfSync(const CaughtFunness& funness) { + m_CaughtFunness.push_back(funness); m_AmountOfTimesOutOfSync++; if (m_AmountOfTimesOutOfSync > m_MaxDesyncAllowed) { //YEET - LOG("User %s was out of sync %i times out of %i, disconnecting for suspected speedhacking.", m_Username.c_str(), m_AmountOfTimesOutOfSync, m_MaxDesyncAllowed); + LOG("User %s was out of sync %i times out of %i, disconnecting for the following reasons: {", m_Username.c_str(), m_AmountOfTimesOutOfSync, m_MaxDesyncAllowed); + for (const auto [cheatType, cheatInfo] : m_CaughtFunness) { + LOG(" Reason %s value %f", StringifiedEnum::ToString(cheatType).data(), cheatInfo); + } + LOG("}"); Game::server->Disconnect(this->m_SystemAddress, eServerDisconnectIdentifiers::PLAY_SCHEDULE_TIME_DONE); } } diff --git a/dGame/User.h b/dGame/User.h index 2c9f374c..d9d49d84 100644 --- a/dGame/User.h +++ b/dGame/User.h @@ -6,6 +6,7 @@ #include #include "RakNetTypes.h" #include "dCommonVars.h" +#include "eFunnessTypes.h" #include @@ -55,7 +56,7 @@ public: // Added for GameMessageHandler std::unordered_map uiBehaviorHandles; - void UserOutOfSync(); + void UserOutOfSync(const CaughtFunness& funness); private: uint32_t m_AccountID; @@ -75,6 +76,7 @@ private: const int m_MaxDesyncAllowed = 12; uint64_t m_MuteExpire; std::chrono::steady_clock::time_point m_LastMuteCheck{}; + std::vector m_CaughtFunness{}; }; #endif // USER_H diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index e4d324f1..15b9cd6f 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -82,6 +82,7 @@ #include "SlashCommandHandler.h" #include "InventoryComponent.h" #include "Item.h" +#include "eFunnessTypes.h" namespace Game { Logger* logger = nullptr; @@ -1427,16 +1428,61 @@ void HandlePacket(Packet* packet) { //Could be insane lag, but I'mma just YEET them as it's usually speedhacking. //This is updated to now count the amount of times we've been caught "speedhacking" to kick with a delay //This is hopefully going to fix the random disconnects people face sometimes. - if (Game::config->GetValue("disable_anti_speedhack") == "1") { + + CINSTREAM_SKIP_HEADER; + CaughtFunness funness; + inStream.Read(funness.cheatInfo); + inStream.Read(funness.cheatType); + const auto [cheatType, cheatInfo] = funness; + LOG_DEBUG("Received cheat type %s with info %f", StringifiedEnum::ToString(cheatType).data(), cheatInfo); + const auto disabledCheats = Game::config->GetValue("disable_anti_speedhack") == "1"; + + User* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) { + Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::KICK); return; } - User* user = UserManager::Instance()->GetUser(packet->systemAddress); - if (user) { - user->UserOutOfSync(); - } else { - Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::KICK); + const auto* const character = user->GetLastUsedChar(); + if (character) { + const auto dcUser = [](const User& user, const SystemAddress& sysAddr) { + if (user.GetMaxGMLevel() < eGameMasterLevel::DEVELOPER) { + Game::server->Disconnect(sysAddr, eServerDisconnectIdentifiers::KICK); + } + }; + if (cheatType == eFunnessTypes::DebuggerActive) { + LOG("Player %s was detected to be using a debugger (funness thread was %f seconds longer than expected deviation).", character->GetName().c_str(), cheatInfo); + // Immediately kick unless they are a gm + if (!disabledCheats) dcUser(*user, packet->systemAddress); + } else if (cheatType == eFunnessTypes::FdbFailedChecksum) { + LOG("Player %s has failed the fdb checksum after passing it to sign into this server, highly likely cheating.", character->GetName().c_str()); + // Immedately kick unless they are a gm + if (!disabledCheats) dcUser(*user, packet->systemAddress); + } else if (cheatType == eFunnessTypes::RacingBoostTimeTooLong) { + if (cheatInfo == 0.0f) LOG("Player %s has enabled a speedboost for far too long (> 3.501 seconds).", character->GetName().c_str()); + else if (cheatInfo == 1.0f) LOG("Unknown racing boost/speed variable was tampered with."); + else if (cheatInfo == 2.0f) LOG("Player vehicle top speed was tampered with."); + if (!disabledCheats) dcUser(*user, packet->systemAddress); + } else if (cheatType == eFunnessTypes::SomeRacingManipCheat) { + if (cheatInfo >= 0.0f && cheatInfo <= 6.0f) LOG("Cheat RNG value A does not match what it should be %f.", cheatInfo); + else if (cheatInfo >= 7.0f && cheatInfo <= 10.0f) LOG("Cheat RNG value B does not match what it should be %f.", cheatInfo); + } else if (cheatType == eFunnessTypes::Unknown_9) { + LOG("Racing cheat 9 detected with value %f.", cheatInfo); + } else if (cheatType == eFunnessTypes::Unknown_10) { + LOG("Racing cheat 10 detected with value %f.", cheatInfo); + } else if (cheatType == eFunnessTypes::CharacterPosLength) { + LOG("Detected pos length of %f which is greater than the expected value 1.0f, not normal!", cheatInfo); + } else if (cheatType == eFunnessTypes::CharacterVelLength) { + LOG("Detected vel length of %f which is greater than the expected value 1.0f, not normal!", cheatInfo); + } else if (cheatType == eFunnessTypes::CharacterGravityScale) { + LOG("Detected gravity scale difference of %f which is greater than the expected value of 0.0f, not normal!", cheatInfo); + } else if (cheatType == eFunnessTypes::CharacterRunMultiplier) { + LOG("Detected run multiplier difference of %f which is greater than the expected value 0.0f, not normal!", cheatInfo); + } + + if (!disabledCheats && user->GetMaxGMLevel() < eGameMasterLevel::DEVELOPER) user->UserOutOfSync(funness); } + break; }