├── cmake ├── Findglad2.cmake ├── init-preproject.cmake └── warning_level.cmake ├── tf2_bot_detector_winrt ├── empty.cpp ├── setup_winrt.bat ├── tf2_bot_detector_winrt.h └── CMakeLists.txt ├── msix └── package_staging │ ├── logo.png │ ├── logo_150.png │ ├── logo_44.png │ └── AppxManifest.xml ├── staging ├── fonts │ ├── ProggyClean.ttf │ └── ProggyTiny.ttf ├── images │ ├── heart_16.png │ ├── vac_icon_16.png │ └── game_ban_icon_16.png ├── tf2_addons │ └── aaaaaaaaaa_votefailed_eraser_v2 │ │ ├── info.vdf │ │ ├── sound │ │ └── ui │ │ │ └── vote_failure.wav │ │ ├── README │ │ ├── LICENSE │ │ └── resource │ │ └── ui │ │ └── votehud.res ├── cfg │ ├── sponsors.json │ └── untrusted_groups.official.json └── licenses │ ├── SDL2.txt │ ├── zlib.txt │ ├── proggyfonts.txt │ ├── cppcoro.txt │ ├── implot.txt │ ├── mh_stuff.txt │ ├── imgui_desktop.txt │ ├── tf2_bot_detector.txt │ ├── cpp-httplib.txt │ ├── imgui.txt │ ├── nlohmann_json.txt │ ├── ValveFileVDF.txt │ ├── cpprestsdk.txt │ ├── catch2.txt │ ├── libzippp.txt │ ├── libzip.txt │ ├── bzip2.txt │ └── glad2.txt ├── smartscreen ├── x64 │ └── tf2_bot_detector.exe ├── x86 │ └── tf2_bot_detector.exe └── sign_exes.bat ├── tf2_bot_detector ├── Art │ ├── TF2BotDetector.ico │ ├── game_ban_icon.svg │ └── heart.svg ├── Tests │ ├── Tests.h │ ├── HumanDurationTests.cpp │ ├── FormattingTests.cpp │ └── Catch2.cpp ├── Networking │ ├── NetworkHelpers.h │ ├── LogsTFAPI.h │ ├── LogsTFAPI.cpp │ ├── HTTPClient.h │ ├── GithubAPI.h │ ├── NetworkHelpers.cpp │ ├── HTTPHelpers.h │ └── GithubAPI.cpp ├── GlobalDispatcher.h ├── packages.config ├── TFConstants.h ├── GameData │ ├── TFParty.h │ ├── TFClassType.h │ └── MatchmakingQueue.h ├── Actions │ ├── RCONActionManager.h │ ├── ICommandSource.h │ ├── IActionManager.h │ ├── HijackActionManager.h │ ├── ActionGenerators.h │ ├── ActionGenerators.cpp │ ├── Actions.h │ └── Actions.cpp ├── DLLMain.h ├── Launcher │ ├── main.cpp │ └── Resources.rc ├── DiscordRichPresence.h ├── Clock.cpp ├── BaseTextures.h ├── Config │ ├── AccountAges.h │ ├── SponsorsList.h │ ├── DRPInfo.h │ ├── SponsorsList.cpp │ └── AccountAges.cpp ├── GenericErrors.h ├── Version.cpp ├── SetupFlow │ ├── BasicSettingsPage.h │ ├── AddonManagerPage.h │ ├── NetworkSettingsPage.h │ ├── SetupFlow.h │ ├── ChatWrappersVerifyPage.h │ ├── ChatWrappersGeneratorPage.h │ ├── CheckFaceitClosedPage.cpp │ ├── ISetupFlowPage.h │ ├── CheckSteamOpenPage.cpp │ ├── TF2CommandLinePage.h │ ├── ChatWrappersVerifyPage.cpp │ ├── BasicSettingsPage.cpp │ └── NetworkSettingsPage.cpp ├── PlayerStatus.h ├── UI │ └── SettingsWindow.h ├── Application.h ├── IPlayer.cpp ├── Resources.base.rc ├── TextureManager.h ├── Bitmap.h ├── Util │ ├── TextUtils.h │ ├── RegexUtils.h │ └── PathUtils.h ├── CompensatedTS.h ├── LobbyMember.h ├── Bitmap.cpp ├── WorldEventListener.cpp ├── ConsoleLog │ ├── ConsoleLineListener.cpp │ ├── ConsoleLogParser.h │ ├── ConsoleLineListener.h │ └── IConsoleLine.h ├── GenericErrors.cpp ├── Application.cpp ├── Platform │ ├── Windows │ │ ├── WindowsHelpers.h │ │ ├── CrashHandler.cpp │ │ ├── Steam.cpp │ │ └── PlatformInstall.cpp │ └── Platform.h ├── Version.base.h ├── CompensatedTS.cpp ├── BaseTextures.cpp ├── WorldEventListener.h ├── ModeratorLogic.h ├── Filesystem.h ├── BatchedAction.h ├── UpdateManager.h └── SteamID.cpp ├── tf2_bot_detector_updater ├── Update_MSIX.h ├── Update_Portable.h ├── vcpkg.json ├── Resources.base.rc ├── Common.h ├── Platform │ └── Platform.h ├── Update_Portable.cpp ├── CMakeLists.txt ├── Update_MSIX.cpp └── CMakeSettings.json ├── tf2_bot_detector_common ├── include │ ├── Platform │ │ └── PlatformCommon.h │ └── ReleaseChannel.h ├── CMakeLists.txt └── src │ └── Platform │ └── Windows │ └── PlatformCommon.cpp ├── .editorconfig ├── vcpkg.json ├── submodules └── glad2 │ └── CMakeLists.txt ├── .github ├── workflows │ ├── download_and_compile.yml │ └── validate.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── FUNDING.yml ├── .gitmodules ├── schemas └── v3 │ ├── whitelist.schema.json │ ├── account_ages.schema.json │ ├── sponsors.schema.json │ ├── discord_rich_presence.schema.json │ └── playerlist.schema.json ├── CMakeLists.txt ├── LICENSE ├── util └── download_and_compile.bat ├── CMakeSettings.json └── .gitattributes /cmake/Findglad2.cmake: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tf2_bot_detector_winrt/empty.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /msix/package_staging/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/msix/package_staging/logo.png -------------------------------------------------------------------------------- /staging/fonts/ProggyClean.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/staging/fonts/ProggyClean.ttf -------------------------------------------------------------------------------- /staging/fonts/ProggyTiny.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/staging/fonts/ProggyTiny.ttf -------------------------------------------------------------------------------- /staging/images/heart_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/staging/images/heart_16.png -------------------------------------------------------------------------------- /staging/images/vac_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/staging/images/vac_icon_16.png -------------------------------------------------------------------------------- /msix/package_staging/logo_150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/msix/package_staging/logo_150.png -------------------------------------------------------------------------------- /msix/package_staging/logo_44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/msix/package_staging/logo_44.png -------------------------------------------------------------------------------- /staging/tf2_addons/aaaaaaaaaa_votefailed_eraser_v2/info.vdf: -------------------------------------------------------------------------------- 1 | "aaaaaaaaa_votefailed_eraser_v2" 2 | { 3 | "ui_version" "3" 4 | } -------------------------------------------------------------------------------- /smartscreen/x64/tf2_bot_detector.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/smartscreen/x64/tf2_bot_detector.exe -------------------------------------------------------------------------------- /smartscreen/x86/tf2_bot_detector.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/smartscreen/x86/tf2_bot_detector.exe -------------------------------------------------------------------------------- /staging/images/game_ban_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/staging/images/game_ban_icon_16.png -------------------------------------------------------------------------------- /tf2_bot_detector/Art/TF2BotDetector.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/tf2_bot_detector/Art/TF2BotDetector.ico -------------------------------------------------------------------------------- /tf2_bot_detector_updater/Update_MSIX.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace tf2_bot_detector::Updater 4 | { 5 | [[nodiscard]] int Update_MSIX(); 6 | } 7 | -------------------------------------------------------------------------------- /tf2_bot_detector/Tests/Tests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef TF2BD_ENABLE_TESTS 4 | namespace tf2_bot_detector 5 | { 6 | int RunTests(); 7 | } 8 | #endif 9 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/Update_Portable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace tf2_bot_detector::Updater 4 | { 5 | [[nodiscard]] int Update_Portable(); 6 | } 7 | -------------------------------------------------------------------------------- /smartscreen/sign_exes.bat: -------------------------------------------------------------------------------- 1 | CALL vcvarsall.bat x86 2 | 3 | signtool sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 x64\tf2_bot_detector.exe x86\tf2_bot_detector.exe 4 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/NetworkHelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector::Networking 6 | { 7 | std::string GetLocalIP(); 8 | } 9 | -------------------------------------------------------------------------------- /tf2_bot_detector/GlobalDispatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | mh::dispatcher& GetDispatcher(); 8 | } 9 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tf2-bot-detector", 3 | "version-string": "", 4 | "dependencies": [ 5 | "fmt", 6 | "cpp-httplib", 7 | "openssl" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /staging/tf2_addons/aaaaaaaaaa_votefailed_eraser_v2/sound/ui/vote_failure.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/HEAD/staging/tf2_addons/aaaaaaaaaa_votefailed_eraser_v2/sound/ui/vote_failure.wav -------------------------------------------------------------------------------- /tf2_bot_detector/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tf2_bot_detector/TFConstants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | enum class TFTeam : uint8_t 8 | { 9 | Unknown, 10 | 11 | Spectator, 12 | Red, 13 | Blue, 14 | }; 15 | 16 | using UserID_t = uint16_t; 17 | } 18 | -------------------------------------------------------------------------------- /tf2_bot_detector/GameData/TFParty.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SteamID.h" 4 | 5 | namespace tf2_bot_detector 6 | { 7 | enum class TFPartyID : uint64_t; 8 | struct TFParty final 9 | { 10 | TFPartyID m_PartyID{}; 11 | SteamID m_LeaderID{}; 12 | uint8_t m_MemberCount{}; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /tf2_bot_detector_common/include/Platform/PlatformCommon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector::Platform 6 | { 7 | void* GetProcAddressHelper(const char* moduleName, const char* symbolName, bool isCritical = false, MH_SOURCE_LOCATION_AUTO(location)); 8 | } 9 | -------------------------------------------------------------------------------- /cmake/init-preproject.cmake: -------------------------------------------------------------------------------- 1 | cmake_policy(SET CMP0091 NEW) # Enable [CMAKE_]MSVC_RUNTIME_LIBRARY 2 | 3 | set(TF2BD_VERSION_MAJOR 1 CACHE STRING "TF2BD major version." FORCE) 4 | set(TF2BD_VERSION_MINOR 2 CACHE STRING "TF2BD minor version." FORCE) 5 | set(TF2BD_VERSION_PATCH 1 CACHE STRING "TF2BD patch version." FORCE) 6 | -------------------------------------------------------------------------------- /tf2_bot_detector/GameData/TFClassType.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace tf2_bot_detector 4 | { 5 | enum class TFClassType 6 | { 7 | Undefined = 0, 8 | Scout = 1, 9 | Sniper = 2, 10 | Soldier = 3, 11 | Demoman = 4, 12 | Medic = 5, 13 | Heavy = 6, 14 | Pyro = 7, 15 | Spy = 8, 16 | Engie = 9 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # To learn more about .editorconfig see https://aka.ms/editorconfigdocs 2 | 3 | # All files 4 | [*] 5 | indent_style = tab 6 | end_of_line = lf 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | charset = utf-8 10 | vc_generate_documentation_comments = xml 11 | 12 | [*.yml] 13 | indent_size = 2 14 | indent_style = space 15 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/RCONActionManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IActionManager.h" 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class Settings; 8 | class IWorldState; 9 | 10 | class IRCONActionManager : public IActionManager 11 | { 12 | public: 13 | static std::unique_ptr Create(const Settings& settings, IWorldState& world); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /tf2_bot_detector/DLLMain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tf2_bot_detector_export.h" 4 | 5 | #ifdef WIN32 6 | #include 7 | #endif 8 | 9 | namespace tf2_bot_detector 10 | { 11 | TF2_BOT_DETECTOR_EXPORT int RunProgram(int argc, const char** argv); 12 | 13 | #ifdef WIN32 14 | TF2_BOT_DETECTOR_EXPORT int RunProgram(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow); 15 | #endif 16 | } 17 | -------------------------------------------------------------------------------- /tf2_bot_detector/Launcher/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../DLLMain.h" 2 | 3 | #ifdef WIN32 4 | #include 5 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) 6 | { 7 | return tf2_bot_detector::RunProgram(hInstance, hPrevInstance, pCmdLine, nCmdShow); 8 | } 9 | #endif 10 | 11 | int main(int argc, const char** argv) 12 | { 13 | return tf2_bot_detector::RunProgram(argc, argv); 14 | } 15 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tf2-bot-detector", 3 | "version-string": "", 4 | "dependencies": [ 5 | "sdl2", 6 | "cryptopp", 7 | "libzippp", 8 | "fmt", 9 | { 10 | "name": "discord-game-sdk", 11 | "platform": "!static" 12 | }, 13 | "stb", 14 | "nlohmann-json", 15 | "catch2", 16 | "sqlitecpp", 17 | { 18 | "name": "cpprestsdk", 19 | "features": [ "compression" ] 20 | }, 21 | "zlib" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /tf2_bot_detector/DiscordRichPresence.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef TF2BD_ENABLE_DISCORD_INTEGRATION 3 | 4 | #include 5 | 6 | namespace tf2_bot_detector 7 | { 8 | class Settings; 9 | class IWorldState; 10 | 11 | class IDRPManager 12 | { 13 | public: 14 | virtual ~IDRPManager() = default; 15 | virtual void Update() = 0; 16 | 17 | static std::unique_ptr Create(const Settings& settings, IWorldState& world); 18 | }; 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /submodules/glad2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(glad2) 4 | 5 | add_library(glad2 6 | "include/glad/gl.h" 7 | "include/KHR/khrplatform.h" 8 | "src/gl.c" 9 | ) 10 | 11 | if (BUILD_SHARED_LIBS) 12 | target_compile_definitions(glad2 PUBLIC GLAD_API_CALL_EXPORT) 13 | target_compile_definitions(glad2 PRIVATE GLAD_API_CALL_EXPORT_BUILD) 14 | endif() 15 | 16 | target_include_directories(glad2 PUBLIC "include") 17 | 18 | add_library(glad2::glad2 ALIAS glad2) 19 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/LogsTFAPI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SteamID.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | class IHTTPClient; 12 | } 13 | 14 | namespace tf2_bot_detector::LogsTFAPI 15 | { 16 | struct PlayerLogsInfo 17 | { 18 | SteamID m_ID; 19 | uint32_t m_LogsCount; 20 | }; 21 | 22 | mh::task GetPlayerLogsInfoAsync(std::shared_ptr client, SteamID id); 23 | } 24 | -------------------------------------------------------------------------------- /tf2_bot_detector/Clock.cpp: -------------------------------------------------------------------------------- 1 | #include "Clock.h" 2 | 3 | #include 4 | 5 | using namespace tf2_bot_detector; 6 | 7 | tm tf2_bot_detector::ToTM(const time_point_t& ts) 8 | { 9 | return mh::chrono::to_tm(ts, mh::chrono::time_zone::local); 10 | } 11 | 12 | tm tf2_bot_detector::GetLocalTM() 13 | { 14 | return mh::chrono::current_tm(mh::chrono::time_zone::local); 15 | } 16 | 17 | time_point_t tf2_bot_detector::GetLocalTimePoint() 18 | { 19 | return mh::chrono::current_time_point(); 20 | } 21 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/ICommandSource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class ICommandWriter 8 | { 9 | public: 10 | virtual ~ICommandWriter() = default; 11 | 12 | virtual void Write(std::string cmd, std::string args) = 0; 13 | virtual void Write(std::string cmd) { return Write(cmd, {}); } 14 | }; 15 | 16 | class ICommandSource 17 | { 18 | public: 19 | virtual ~ICommandSource() = default; 20 | 21 | virtual void WriteCommands(ICommandWriter& writer) const = 0; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /tf2_bot_detector/BaseTextures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class ITextureManager; 8 | class ITexture; 9 | 10 | class IBaseTextures 11 | { 12 | public: 13 | virtual ~IBaseTextures() = default; 14 | 15 | static std::unique_ptr Create(ITextureManager& textureManager); 16 | 17 | virtual const ITexture* GetHeart_16() const = 0; 18 | virtual const ITexture* GetVACShield_16() const = 0; 19 | virtual const ITexture* GetGameBanIcon_16() const = 0; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /tf2_bot_detector/Config/AccountAges.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | #include "SteamID.h" 5 | 6 | #include 7 | 8 | namespace tf2_bot_detector 9 | { 10 | class SteamID; 11 | 12 | class IAccountAges 13 | { 14 | public: 15 | virtual ~IAccountAges() = default; 16 | 17 | static std::shared_ptr Create(); 18 | 19 | virtual void OnDataReady(const SteamID& id, time_point_t creationTime) = 0; 20 | 21 | virtual std::optional EstimateAccountCreationTime(const SteamID& id) const = 0; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/download_and_compile.yml: -------------------------------------------------------------------------------- 1 | name: download_and_compile 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | test: 8 | runs-on: windows-latest 9 | steps: 10 | - name: Download batch script from repo 11 | uses: suisei-cn/actions-download-file@v1 12 | with: 13 | url: "https://github.com/PazerOP/tf2_bot_detector/raw/${{ github.sha }}/util/download_and_compile.bat" 14 | 15 | - name: Run batch script 16 | shell: cmd 17 | run: download_and_compile.bat 18 | env: 19 | TF2BD_DL_AND_COMPILE_SKIP_OPEN: 1 20 | -------------------------------------------------------------------------------- /tf2_bot_detector/GenericErrors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | enum class ErrorCode 8 | { 9 | Success = 0, 10 | 11 | InternetConnectivityDisabled, 12 | LazyValueUninitialized, 13 | UnknownError, // I SWORE I WOULD NEVER TYPE THESE WORDS 14 | LogicError, 15 | }; 16 | 17 | std::error_condition make_error_condition(tf2_bot_detector::ErrorCode e); 18 | } 19 | 20 | namespace std 21 | { 22 | template<> struct is_error_condition_enum : std::bool_constant {}; 23 | } 24 | -------------------------------------------------------------------------------- /tf2_bot_detector/Launcher/Resources.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | IDI_ICON1 ICON "../Art/TF2BotDetector.ico" 5 | 6 | VS_VERSION_INFO VERSIONINFO 7 | BEGIN 8 | BLOCK "StringFileInfo" 9 | BEGIN 10 | BLOCK "040904b0" 11 | BEGIN 12 | VALUE "FileDescription", "TF2 Bot Detector" 13 | VALUE "Comments", "Automatically detects and votekicks cheaters/bots in TF2 casual." 14 | VALUE "ProductName", "TF2 Bot Detector" 15 | END 16 | END 17 | BLOCK "VarFileInfo" 18 | BEGIN 19 | VALUE "Translation", 0x0409,1200 20 | END 21 | END 22 | -------------------------------------------------------------------------------- /tf2_bot_detector_winrt/setup_winrt.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | SETLOCAL EnableDelayedExpansion 4 | 5 | nuget install "Microsoft.Windows.CppWinRT" -OutputDirectory nuget || EXIT /b 1 6 | 7 | CD nuget || EXIT /b 1 8 | 9 | PUSHD . 10 | CD "Microsoft.Windows.CppWinRT*" || EXIT /b 1 11 | 12 | CD bin || EXIT /b 1 13 | 14 | ECHO Generating winrt headers... 15 | cppwinrt.exe -in local -out include -verbose -overwrite || EXIT /b 1 16 | 17 | CD include || EXIT /b 1 18 | 19 | SET WINRT_INCLUDE_DIR=%CD% 20 | POPD 21 | 22 | ECHO %WINRT_INCLUDE_DIR% > winrt_include_dir.txt 23 | 24 | ENDLOCAL 25 | -------------------------------------------------------------------------------- /tf2_bot_detector_common/include/ReleaseChannel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | // DO NOT FORWARD DECLARE (implicit cast in fmt::format from enum class -> int) 8 | enum class ReleaseChannel 9 | { 10 | None = -1, // Don't auto update 11 | 12 | Public, 13 | Preview, 14 | Nightly, 15 | }; 16 | } 17 | 18 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::ReleaseChannel) 19 | MH_ENUM_REFLECT_VALUE(None) 20 | MH_ENUM_REFLECT_VALUE(Public) 21 | MH_ENUM_REFLECT_VALUE(Preview) 22 | MH_ENUM_REFLECT_VALUE(Nightly) 23 | MH_ENUM_REFLECT_END() 24 | -------------------------------------------------------------------------------- /cmake/warning_level.cmake: -------------------------------------------------------------------------------- 1 | 2 | function(set_warning_level level) 3 | 4 | get_property(TEMP_COMPILE_OPTIONS DIRECTORY PROPERTY COMPILE_OPTIONS) 5 | # message("COMPILE_OPTIONS pre-remove = ${TEMP_COMPILE_OPTIONS}") 6 | 7 | if (MSVC) 8 | list(FILTER TEMP_COMPILE_OPTIONS EXCLUDE REGEX "/W[0-9]") 9 | # message("COMPILE_OPTIONS post-remove = ${TEMP_COMPILE_OPTIONS}") 10 | list(APPEND TEMP_COMPILE_OPTIONS "/W${level}") 11 | endif() 12 | 13 | # message("COMPILE_OPTIONS post-add = ${TEMP_COMPILE_OPTIONS}") 14 | set_property(DIRECTORY PROPERTY COMPILE_OPTIONS "${TEMP_COMPILE_OPTIONS}") 15 | 16 | endfunction() 17 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/LogsTFAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "LogsTFAPI.h" 2 | #include "HTTPClient.h" 3 | #include "HTTPHelpers.h" 4 | 5 | #include 6 | 7 | using namespace tf2_bot_detector; 8 | 9 | mh::task LogsTFAPI::GetPlayerLogsInfoAsync(std::shared_ptr client, SteamID id) 10 | { 11 | const std::string string = co_await client->GetStringAsync(mh::format("https://logs.tf/api/v1/log?player={}&limit=0", id.ID64)); 12 | 13 | const nlohmann::json json = nlohmann::json::parse(string); 14 | 15 | PlayerLogsInfo info{}; 16 | info.m_ID = id; 17 | json.at("total").get_to(info.m_LogsCount); 18 | co_return info; 19 | } 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'Priority: Low, Type: Enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **A clear and concise description of what the problem is.** 11 | > Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | > A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | > A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | > Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [PazerOP] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /tf2_bot_detector/Version.cpp: -------------------------------------------------------------------------------- 1 | #include "Version.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace tf2_bot_detector; 7 | 8 | std::optional Version::Parse(const char* str) 9 | { 10 | Version v{}; 11 | const auto count = sscanf_s(str, "%hi.%hi.%hi.%hi", &v.m_Major, &v.m_Minor, &v.m_Patch, &v.m_Build); 12 | if (count < 2) 13 | return std::nullopt; 14 | 15 | return v; 16 | } 17 | 18 | void tf2_bot_detector::to_json(nlohmann::json& j, const Version& d) 19 | { 20 | j = mh::fmtstr<128>("{}", d).view(); 21 | } 22 | void tf2_bot_detector::from_json(const nlohmann::json& j, Version& d) 23 | { 24 | d = Version::Parse(j.get().c_str()).value(); 25 | } 26 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/BasicSettingsPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config/Settings.h" 4 | #include "ISetupFlowPage.h" 5 | 6 | #undef DrawState 7 | 8 | namespace tf2_bot_detector 9 | { 10 | class BasicSettingsPage final : public ISetupFlowPage 11 | { 12 | public: 13 | ValidateSettingsResult ValidateSettings(const Settings& settings) const override; 14 | OnDrawResult OnDraw(const DrawState& ds) override; 15 | void Init(const InitState& is) override; 16 | 17 | bool CanCommit() const override; 18 | void Commit(const CommitState& cs); 19 | 20 | SetupFlowPage GetPage() const override { return SetupFlowPage::BasicSettings; } 21 | 22 | private: 23 | AutoDetectedSettings m_Settings; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "implot"] 2 | path = submodules/implot 3 | url = https://github.com/PazerOP/implot.git 4 | [submodule "submodules/ValveFileVDF"] 5 | path = submodules/ValveFileVDF 6 | url=https://github.com/PazerOP/ValveFileVDF.git 7 | [submodule "submodules/SourceRCON"] 8 | path = submodules/SourceRCON 9 | url = https://github.com/PazerOP/SourceRCON.git 10 | [submodule "submodules/imgui_desktop"] 11 | path = submodules/imgui_desktop 12 | url = https://github.com/PazerOP/imgui_desktop.git 13 | [submodule "submodules/mh_stuff"] 14 | path = submodules/mh_stuff 15 | url = https://github.com/PazerOP/stuff.git 16 | [submodule "submodules/vcpkg"] 17 | path = submodules/vcpkg 18 | url = https://github.com/microsoft/vcpkg.git 19 | -------------------------------------------------------------------------------- /tf2_bot_detector/Tests/HumanDurationTests.cpp: -------------------------------------------------------------------------------- 1 | #include "Clock.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | using namespace tf2_bot_detector; 8 | using namespace std::chrono_literals; 9 | 10 | TEST_CASE("tf2bd_humanduration", "[tf2bd]") 11 | { 12 | { 13 | std::ostringstream ss; 14 | constexpr duration_t d = 5h + 16min; 15 | ss << HumanDuration(d); 16 | 17 | REQUIRE(ss.str() == "5 hours, 16 minutes"); 18 | } 19 | 20 | { 21 | //constexpr duration_t mins = minute_t(1834409518); 22 | //constexpr duration_t secs = second_t(813882256308); 23 | //constexpr duration_t totalDuration = mins + secs; 24 | //ss << HumanDuration(totalDuration); 25 | 26 | //REQUIRE(ss.str() == "1 minute"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/AddonManagerPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace tf2_bot_detector 8 | { 9 | class Settings; 10 | 11 | class IAddonManager 12 | { 13 | public: 14 | virtual ~IAddonManager() = default; 15 | 16 | static IAddonManager& Get(); 17 | 18 | virtual mh::generator GetAllAvailableAddons() const = 0; 19 | virtual mh::generator GetAllEnabledAddons(const Settings& settings) const = 0; 20 | virtual bool IsAddonEnabled(const Settings& settings, const std::filesystem::path& addon) const = 0; 21 | virtual void SetAddonEnabled(Settings& settings, const std::filesystem::path& addon, bool enabled) = 0; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /tf2_bot_detector/PlayerStatus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | #include "SteamID.h" 5 | #include "TFConstants.h" 6 | 7 | #include 8 | #include 9 | 10 | namespace tf2_bot_detector 11 | { 12 | enum class PlayerStatusState : uint8_t 13 | { 14 | Invalid, 15 | 16 | Challenging, 17 | Connecting, 18 | Spawning, 19 | Active, 20 | }; 21 | 22 | struct PlayerStatus 23 | { 24 | std::string m_Name; 25 | std::string m_Address; 26 | SteamID m_SteamID; 27 | 28 | time_point_t m_ConnectionTime{}; 29 | UserID_t m_UserID; 30 | uint16_t m_Ping; 31 | uint8_t m_Loss; 32 | PlayerStatusState m_State = PlayerStatusState::Invalid; 33 | }; 34 | 35 | struct PlayerStatusShort 36 | { 37 | std::string m_Name; 38 | uint8_t m_ClientIndex; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /tf2_bot_detector_winrt/tf2_bot_detector_winrt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mh 6 | { 7 | class exception_details_handler; 8 | } 9 | 10 | namespace tf2_bot_detector 11 | { 12 | class WinRT 13 | { 14 | public: 15 | virtual ~WinRT() = default; 16 | 17 | virtual std::filesystem::path GetPackageLocalAppDataDir() const = 0; 18 | virtual std::filesystem::path GetPackageRoamingAppDataDir() const = 0; 19 | virtual std::filesystem::path GetPackageTempDir() const = 0; 20 | 21 | virtual bool IsInPackage() const = 0; 22 | virtual std::wstring GetCurrentPackageFamilyName() const = 0; 23 | 24 | virtual const mh::exception_details_handler& GetWinRTExceptionDetailsHandler() const = 0; 25 | }; 26 | 27 | using CreateWinRTInterfaceFn = WinRT*(*)(); 28 | } 29 | -------------------------------------------------------------------------------- /tf2_bot_detector_common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | 3 | include(../cmake/init-preproject.cmake) 4 | project(tf2_bot_detector_common) 5 | include(../cmake/init-postproject.cmake) 6 | 7 | add_library(tf2_bot_detector_common STATIC) 8 | add_library(tf2_bot_detector::common ALIAS tf2_bot_detector_common) 9 | 10 | target_sources(tf2_bot_detector_common PRIVATE 11 | "include/ReleaseChannel.h" 12 | "include/Platform/PlatformCommon.h" 13 | ) 14 | 15 | if (WIN32) 16 | target_sources(tf2_bot_detector_common PRIVATE 17 | "src/Platform/Windows/PlatformCommon.cpp" 18 | ) 19 | endif() 20 | 21 | target_include_directories(tf2_bot_detector_common PUBLIC "include") 22 | 23 | find_package(fmt CONFIG REQUIRED) 24 | 25 | target_link_libraries(tf2_bot_detector_common 26 | PUBLIC 27 | mh::stuff 28 | fmt::fmt 29 | ) 30 | -------------------------------------------------------------------------------- /tf2_bot_detector/UI/SettingsWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class MainWindow; 8 | class Settings; 9 | 10 | class SettingsWindow final : public ImGuiDesktop::Window 11 | { 12 | public: 13 | SettingsWindow(ImGuiDesktop::Application& app, Settings& settings, MainWindow& mainWindow); 14 | 15 | private: 16 | void OnDraw() override; 17 | 18 | void OnDrawASOSettings(); 19 | void OnDrawCompatibilitySettings(); 20 | void OnDrawLoggingSettings(); 21 | void OnDrawModerationSettings(); 22 | void OnDrawModSettings(); 23 | void OnDrawPerformanceSettings(); 24 | void OnDrawServiceIntegrationSettings(); 25 | void OnDrawUISettings(); 26 | 27 | Settings& m_Settings; 28 | MainWindow& m_MainWindow; 29 | 30 | bool m_ModsChanged = false; 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /staging/cfg/sponsors.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/master/schemas/v3/sponsors.schema.json", 3 | "file_info": { 4 | "authors": [ 5 | "pazer" 6 | ], 7 | "title": "TF2 Bot Detector Sponsors", 8 | "update_url": "https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/master/staging/cfg/sponsors.json" 9 | }, 10 | "sponsors": [ 11 | { 12 | "message": "I like my Bots like I like my Potassium Bonnet: Burning in hell using pazer's tools!", 13 | "name": "Crazy Gunman" 14 | }, 15 | { 16 | "message": "die, cheaters", 17 | "name": "camp3r101" 18 | }, 19 | { 20 | "message": "", 21 | "name": "bgausden" 22 | }, 23 | { 24 | "message": "", 25 | "name": "YoukaiCat" 26 | }, 27 | { 28 | "message": "", 29 | "name": "TheWisehobgoblin" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /staging/tf2_addons/aaaaaaaaaa_votefailed_eraser_v2/README: -------------------------------------------------------------------------------- 1 | This disables the CallVoteFailed element and the vote_failed.wav noise. Made for Pazer 2 | as part of the TF2 Bot Detector project by ClusterConsultant and Moeb with <3. 3 | Credit for the idea goes to CrazyGunman#6724 on discord. This particular mod is 4 | licensed under the MIT license. See LICENSE for more details. 5 | 6 | This will reset all voting related UI elements to default. This is unavoidable as the 7 | way TF2 loads custom requires that the entire file be present so one could not just 8 | override the vote failed related parts. If you want your custom HUD to still apply 9 | to the voting ui simply replace 10 | `tf2_bot_detector/tf2_addons/aaaaaaaaa_votefailed_eraser_v2/resource/ui/botdetector_tf_hud_base/votehud.res` 11 | with your `custom/exampleHUD/resource/ui/votehud.res` file. 12 | -------------------------------------------------------------------------------- /tf2_bot_detector/Application.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace tf2_bot_detector 8 | { 9 | class MainWindow; 10 | 11 | namespace DB 12 | { 13 | class ITempDB; 14 | } 15 | 16 | class TF2BDApplication final : public ImGuiDesktop::Application 17 | { 18 | public: 19 | TF2BDApplication(); 20 | ~TF2BDApplication(); 21 | 22 | static TF2BDApplication& GetApplication(); 23 | 24 | MainWindow& GetMainWindow(); 25 | const MainWindow& GetMainWindow() const; 26 | 27 | DB::ITempDB& GetTempDB(); 28 | 29 | protected: 30 | void OnAddingManagedWindow(ImGuiDesktop::Window& window) override; 31 | void OnRemovingManagedWindow(ImGuiDesktop::Window& window) override; 32 | 33 | private: 34 | MainWindow* m_MainWindow = nullptr; 35 | 36 | std::unique_ptr m_TempDB; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/NetworkSettingsPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config/Settings.h" 4 | #include "ISetupFlowPage.h" 5 | #include "ReleaseChannel.h" 6 | 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | class NetworkSettingsPage final : public ISetupFlowPage 12 | { 13 | public: 14 | ValidateSettingsResult ValidateSettings(const Settings& settings) const override; 15 | OnDrawResult OnDraw(const DrawState& ds) override; 16 | void Init(const InitState& is) override; 17 | bool CanCommit() const override; 18 | void Commit(const CommitState& cs) override; 19 | SetupFlowPage GetPage() const override { return SetupFlowPage::NetworkSettings; } 20 | 21 | private: 22 | struct 23 | { 24 | std::optional m_AllowInternetUsage; 25 | std::optional m_ReleaseChannel; 26 | 27 | } m_Settings; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /tf2_bot_detector/Tests/FormattingTests.cpp: -------------------------------------------------------------------------------- 1 | #include "Config/PlayerListJSON.h" 2 | 3 | #include 4 | 5 | using namespace std::chrono_literals; 6 | using namespace tf2_bot_detector; 7 | 8 | TEST_CASE("mh::formatter", "[tf2bd][formatting]") 9 | { 10 | PlayerAttributesList list; 11 | 12 | list.SetAttribute(PlayerAttribute::Cheater); 13 | REQUIRE(mh::format("Attribs: {}", list) == "Attribs: PlayerAttribute::Cheater"); 14 | } 15 | 16 | TEST_CASE("mh::formatter", "[tf2bd][formatting]") 17 | { 18 | PlayerMarks marks; 19 | marks.m_Marks.push_back(PlayerMarks::Mark({ PlayerAttribute::Suspicious, PlayerAttribute::Racist }, "cfg/playerlist.json")); 20 | 21 | auto fmt = mh::format("marks: {}", marks); 22 | REQUIRE(fmt == "marks: \n\t - \"cfg/playerlist.json\" (PlayerAttribute::Suspicious, PlayerAttribute::Racist)"); 23 | } 24 | -------------------------------------------------------------------------------- /tf2_bot_detector/IPlayer.cpp: -------------------------------------------------------------------------------- 1 | #include "IPlayer.h" 2 | #include "WorldState.h" 3 | #include "Util/TextUtils.h" 4 | 5 | using namespace tf2_bot_detector; 6 | 7 | duration_t IPlayer::GetTimeSinceLastStatusUpdate() const 8 | { 9 | return GetWorld().GetCurrentTime() - GetLastStatusUpdateTime(); 10 | } 11 | 12 | std::optional IPlayer::GetEstimatedAccountAge() const 13 | { 14 | if (auto timePoint = GetEstimatedAccountCreationTime()) 15 | { 16 | auto age = clock_t::now() - *timePoint; 17 | 18 | if (age.count() < 0) 19 | { 20 | DebugLogWarning("{}: account creation time age = {}, timePoint = {}", *this, age.count(), 21 | timePoint->time_since_epoch().count()); 22 | return std::nullopt; 23 | } 24 | 25 | return age; 26 | } 27 | 28 | return std::nullopt; 29 | } 30 | 31 | std::string IPlayer::GetNameSafe() const 32 | { 33 | return CollapseNewlines(GetNameUnsafe()); 34 | } 35 | -------------------------------------------------------------------------------- /schemas/v3/whitelist.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "whitelist.schema.json", 4 | "title": "TF2 Bot Detector Player Whitelist Schema", 5 | "type": "object", 6 | "additionalProperties": false, 7 | "properties": { 8 | "$schema": { 9 | "description": "The JSON schema to validate this file against.", 10 | "type": "string" 11 | }, 12 | "players": { 13 | "description": "List of players who are trusted", 14 | "type": "array", 15 | "items": { 16 | "type": "object", 17 | "additionalProperties": false, 18 | "properties": { 19 | "nickname": { 20 | "type": "string", 21 | "description": "Nickname for this player." 22 | }, 23 | "steamid": { 24 | "description": "SteamID of the player.", 25 | "$ref": "./shared.schema.json#definitions/steamid" 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /staging/licenses/SDL2.txt: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/SetupFlow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ISetupFlowPage.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace tf2_bot_detector 9 | { 10 | class Settings; 11 | 12 | class SetupFlow final 13 | { 14 | public: 15 | SetupFlow(); 16 | 17 | // Returns true if the setup flow needs to draw. 18 | [[nodiscard]] bool OnUpdate(const Settings& settings); 19 | [[nodiscard]] bool OnDraw(Settings& settings, const ISetupFlowPage::DrawState& ds); 20 | 21 | bool ShouldDraw() const { return m_ShouldDraw; } 22 | 23 | SetupFlowPage GetCurrentPage() const; 24 | 25 | private: 26 | bool m_ShouldDraw = false; 27 | std::vector> m_Pages; 28 | 29 | void GetPageState(const Settings& settings, size_t& currentPage, bool& hasNextPage) const; 30 | 31 | static constexpr size_t INVALID_PAGE = size_t(-1); 32 | size_t m_ActivePage = INVALID_PAGE; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/Resources.base.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | VS_VERSION_INFO VERSIONINFO 5 | FILEVERSION ${CMAKE_PROJECT_VERSION_MAJOR},${CMAKE_PROJECT_VERSION_MINOR},${CMAKE_PROJECT_VERSION_PATCH},${CMAKE_PROJECT_VERSION_TWEAK} 6 | PRODUCTVERSION ${CMAKE_PROJECT_VERSION_MAJOR},${CMAKE_PROJECT_VERSION_MINOR},${CMAKE_PROJECT_VERSION_PATCH},${CMAKE_PROJECT_VERSION_TWEAK} 7 | FILEFLAGS ${TF2BD_RESOURCE_FILEFLAGS} 8 | BEGIN 9 | BLOCK "StringFileInfo" 10 | BEGIN 11 | BLOCK "040904b0" 12 | BEGIN 13 | VALUE "FileDescription", "TF2 Bot Detector Updater" 14 | VALUE "Comments", "Automatic updater for TF2 Bot Detector." 15 | VALUE "FileVersion", "${CMAKE_PROJECT_VERSION}" 16 | VALUE "ProductName", "TF2 Bot Detector Updater" 17 | VALUE "ProductVersion", "${CMAKE_PROJECT_VERSION}" 18 | END 19 | END 20 | BLOCK "VarFileInfo" 21 | BEGIN 22 | VALUE "Translation", 0x0409,1200 23 | END 24 | END 25 | -------------------------------------------------------------------------------- /tf2_bot_detector/Resources.base.rc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | VS_VERSION_INFO VERSIONINFO 5 | FILEVERSION ${CMAKE_PROJECT_VERSION_MAJOR},${CMAKE_PROJECT_VERSION_MINOR},${CMAKE_PROJECT_VERSION_PATCH},${CMAKE_PROJECT_VERSION_TWEAK} 6 | PRODUCTVERSION ${CMAKE_PROJECT_VERSION_MAJOR},${CMAKE_PROJECT_VERSION_MINOR},${CMAKE_PROJECT_VERSION_PATCH},${CMAKE_PROJECT_VERSION_TWEAK} 7 | FILEFLAGS ${TF2BD_RESOURCE_FILEFLAGS} 8 | BEGIN 9 | BLOCK "StringFileInfo" 10 | BEGIN 11 | BLOCK "040904b0" 12 | BEGIN 13 | VALUE "FileDescription", "TF2 Bot Detector" 14 | VALUE "Comments", "Automatically detects and votekicks cheaters/bots in TF2 casual." 15 | VALUE "FileVersion", "${CMAKE_PROJECT_VERSION}" 16 | VALUE "ProductName", "TF2 Bot Detector" 17 | VALUE "ProductVersion", "${CMAKE_PROJECT_VERSION}" 18 | END 19 | END 20 | BLOCK "VarFileInfo" 21 | BEGIN 22 | VALUE "Translation", 0x0409,1200 23 | END 24 | END 25 | -------------------------------------------------------------------------------- /schemas/v3/account_ages.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/master/schemas/v3/account_ages.schema.json", 4 | "title": "TF2 Bot Detector Account Ages Schema", 5 | "type": "object", 6 | "additionalProperties": false, 7 | "properties": { 8 | "$schema": { 9 | "description": "The JSON schema to validate this file against.", 10 | "type": "string" 11 | }, 12 | "accounts": { 13 | "description": "List of accounts", 14 | "type": "array", 15 | "items": { 16 | "type": "object", 17 | "additionalProperties": false, 18 | "properties": { 19 | "id": { 20 | "type": "integer", 21 | "description": "Account ID (*not* the full SteamID64)" 22 | }, 23 | "creation_time": { 24 | "type": "integer", 25 | "description": "Account creation time (unix epoch seconds)" 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tf2_bot_detector/TextureManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class Bitmap; 8 | 9 | struct TextureSettings 10 | { 11 | bool m_EnableMips = false; 12 | }; 13 | 14 | class ITexture 15 | { 16 | public: 17 | using handle_type = uint32_t; 18 | 19 | virtual ~ITexture() = default; 20 | 21 | virtual handle_type GetHandle() const = 0; 22 | virtual const TextureSettings& GetSettings() const = 0; 23 | 24 | virtual uint16_t GetWidth() const = 0; 25 | virtual uint16_t GetHeight() const = 0; 26 | }; 27 | 28 | class ITextureManager 29 | { 30 | public: 31 | virtual ~ITextureManager() = default; 32 | 33 | static std::shared_ptr Create(); 34 | 35 | virtual void EndFrame() = 0; 36 | 37 | virtual std::shared_ptr CreateTexture(const Bitmap& bitmap, 38 | const TextureSettings& settings = {}) = 0; 39 | 40 | virtual size_t GetActiveTextureCount() const = 0; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | 3 | option(TF2BD_ENABLE_DISCORD_INTEGRATION "Enable discord integration" on) 4 | option(TF2BD_ENABLE_TESTS "Enable test compilation" off) 5 | 6 | include(cmake/init-preproject.cmake) 7 | project(tf2_bot_detector) 8 | include(cmake/init-postproject.cmake) 9 | 10 | # apparent bug in VS 16.9.1 makes error messages unclickable because of wrong paths 11 | if (MSVC) 12 | add_compile_options(/FC) 13 | endif() 14 | 15 | set(MH_STUFF_BUILD_SHARED_LIBS ON) 16 | add_subdirectory(submodules/mh_stuff) 17 | 18 | set_warning_level(3) 19 | add_subdirectory(submodules/ValveFileVDF) 20 | add_subdirectory(submodules/SourceRCON) 21 | add_subdirectory(submodules/glad2) 22 | add_subdirectory(submodules/imgui_desktop) 23 | 24 | add_subdirectory(tf2_bot_detector_common) 25 | 26 | if (WIN32) 27 | add_subdirectory(tf2_bot_detector_winrt) 28 | endif() 29 | 30 | # add_subdirectory(tf2_bot_detector_updater) 31 | add_subdirectory(tf2_bot_detector) 32 | 33 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ReleaseChannel.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace tf2_bot_detector::Updater 14 | { 15 | enum class UpdateType 16 | { 17 | Unknown = 0, 18 | 19 | #ifdef _WIN32 20 | MSIX, 21 | // MSI -- maybe one day... 22 | #endif 23 | 24 | Portable, 25 | }; 26 | 27 | struct CmdLineArgs 28 | { 29 | bool m_PauseOnError = true; 30 | 31 | UpdateType m_UpdateType = UpdateType::Unknown; 32 | 33 | std::string m_SourcePath; 34 | std::filesystem::path m_DestPath; 35 | }; 36 | 37 | extern CmdLineArgs s_CmdLineArgs; 38 | } 39 | 40 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::Updater::UpdateType) 41 | MH_ENUM_REFLECT_VALUE(Unknown) 42 | MH_ENUM_REFLECT_VALUE(Portable) 43 | 44 | #ifdef _WIN32 45 | MH_ENUM_REFLECT_VALUE(MSIX) 46 | #endif 47 | 48 | MH_ENUM_REFLECT_END() 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to report an issue or bug 4 | title: "[BUG]" 5 | labels: 'Priority: Medium, Type: Bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | > A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | > Steps to reproduce the behavior: 15 | > 1. Go to '...' 16 | > 2. Click on '....' 17 | > 3. Scroll down to '....' 18 | > 4. See error 19 | 20 | **Expected behavior** 21 | > A clear and concise description of what you expected to happen. 22 | 23 | **Logs** 24 | > Add any relevant logs. Logs are found by going to File → Open Logs Folder 25 | 26 | **Screenshots** 27 | > If applicable, add screenshots to help explain your problem. 28 | 29 | **Desktop (please complete the following information):** 30 | > - OS: [e.g. Windows 10, 8, 7] 31 | > - Version of bot detector [e.g. 1.1.0.674] 32 | 33 | **Additional context** 34 | > Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /schemas/v3/sponsors.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/master/schemas/v3/sponsors.schema.json", 4 | "title": "TF2 Bot Detector Sponsors Schema", 5 | "type": "object", 6 | "additionalProperties": false, 7 | "properties": { 8 | "$schema": { 9 | "description": "The JSON schema to validate this file against.", 10 | "type": "string" 11 | }, 12 | "file_info": { 13 | "$ref": "./shared.schema.json#/definitions/file_info" 14 | }, 15 | "sponsors": { 16 | "description": "List of sponsors", 17 | "type": "array", 18 | "items": { 19 | "type": "object", 20 | "additionalProperties": false, 21 | "properties": { 22 | "name": { 23 | "type": "string", 24 | "description": "Sponsor name" 25 | }, 26 | "message": { 27 | "type": "string", 28 | "description": "Sponsor message" 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/IActionManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class IAction; 8 | class IPeriodicActionGenerator; 9 | 10 | class IActionManager 11 | { 12 | public: 13 | virtual ~IActionManager() = default; 14 | 15 | virtual void Update() = 0; 16 | 17 | // Returns false if the action was not queued 18 | virtual bool QueueAction(std::unique_ptr&& action) = 0; 19 | 20 | template 21 | bool QueueAction(TArgs&&... args) 22 | { 23 | return QueueAction(std::make_unique(std::forward(args)...)); 24 | } 25 | 26 | virtual void AddPeriodicActionGenerator(std::unique_ptr&& action) = 0; 27 | 28 | template 29 | void AddPeriodicActionGenerator(TArgs&&... args) 30 | { 31 | return AddPeriodicActionGenerator(std::make_unique(std::forward(args)...)); 32 | } 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate JSONs 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'staging/cfg/**.json' 7 | - 'schemas/**.json' 8 | - '.github/workflows/validate.yml' 9 | 10 | jobs: 11 | validate-json-files: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: fix go path 17 | run: | 18 | echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV 19 | echo "$(go env GOPATH)/bin" >> $GITHUB_PATH 20 | shell: bash 21 | 22 | - name: Install validator 23 | run: go get github.com/neilpa/yajsv 24 | 25 | - name: Validate playerlist 26 | run: yajsv -s schemas/v3/playerlist.schema.json staging/cfg/playerlist.official.json 27 | 28 | - name: Validate rules 29 | run: yajsv -s schemas/v3/rules.schema.json staging/cfg/rules.official.json 30 | 31 | - name: Validate sponsors 32 | run: yajsv -s schemas/v3/sponsors.schema.json staging/cfg/sponsors.json 33 | -------------------------------------------------------------------------------- /tf2_bot_detector/Bitmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace tf2_bot_detector 7 | { 8 | class Bitmap 9 | { 10 | public: 11 | Bitmap() = default; 12 | Bitmap(const std::filesystem::path& path); 13 | Bitmap(const std::filesystem::path& path, uint8_t desiredChannels); 14 | 15 | void LoadFile(const std::filesystem::path& path); 16 | void LoadFile(const std::filesystem::path& path, uint8_t desiredChannels); 17 | 18 | const void* GetData() const { return m_Image.get(); } 19 | uint32_t GetHeight() const { return m_Height; } 20 | uint32_t GetWidth() const { return m_Width; } 21 | uint8_t GetChannelCount() const { return m_Channels; } 22 | 23 | bool empty() const { return !m_Image; } 24 | 25 | private: 26 | struct Deleter final 27 | { 28 | void operator()(void* ptr) const; 29 | }; 30 | std::unique_ptr m_Image; 31 | uint32_t m_Width{}; 32 | uint32_t m_Height{}; 33 | uint8_t m_Channels{}; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/HTTPClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace tf2_bot_detector 9 | { 10 | class URL; 11 | 12 | // Only intended to be stored if you are doing something async 13 | class IHTTPClient : public std::enable_shared_from_this 14 | { 15 | public: 16 | virtual ~IHTTPClient() = default; 17 | 18 | static std::shared_ptr Create(); 19 | 20 | virtual std::string GetString(const URL& url) const = 0; 21 | virtual mh::task GetStringAsync(URL url) const = 0; 22 | 23 | struct RequestCounts 24 | { 25 | uint32_t m_Total; 26 | uint32_t m_Failed; 27 | uint32_t m_InProgress; // Waiting on the server 28 | uint32_t m_Throttled; // Locally throttled 29 | }; 30 | 31 | virtual RequestCounts GetRequestCounts() const = 0; 32 | }; 33 | 34 | using HTTPClient = IHTTPClient; // temp, but probably valve time temp if i'm being totally honest 35 | } 36 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/Platform/Platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace tf2_bot_detector 8 | { 9 | inline namespace Platform 10 | { 11 | void WaitForPIDToExit(int pid); 12 | 13 | enum class Arch 14 | { 15 | x86 = 86, 16 | x64 = 64, 17 | }; 18 | 19 | Arch GetArch(); 20 | 21 | namespace Processes 22 | { 23 | void Launch(const std::filesystem::path& executable, const std::string_view& arguments = {}); 24 | int LaunchAndWait(const std::filesystem::path& executable, const std::string_view& arguments = {}); 25 | } 26 | 27 | void RebootComputer(); 28 | 29 | enum class UpdateSystemDependenciesResult 30 | { 31 | Success, 32 | 33 | RebootRequired, 34 | }; 35 | UpdateSystemDependenciesResult UpdateSystemDependencies(); 36 | } 37 | } 38 | 39 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::Platform::Arch) 40 | MH_ENUM_REFLECT_VALUE(x86) 41 | MH_ENUM_REFLECT_VALUE(x64) 42 | MH_ENUM_REFLECT_END() 43 | -------------------------------------------------------------------------------- /tf2_bot_detector/Util/TextUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tf2_bot_detector 8 | { 9 | std::u16string ToU16(const std::u8string_view& input); 10 | std::u16string ToU16(const char* input, const char* input_end = nullptr); 11 | std::u16string ToU16(const std::string_view& input); 12 | std::u16string ToU16(const std::wstring_view& input); 13 | std::u8string ToU8(const std::string_view& input); 14 | std::u8string ToU8(const std::u16string_view& input); 15 | std::u8string ToU8(const std::wstring_view& input); 16 | std::string ToMB(const std::u8string_view& input); 17 | std::string ToMB(const std::u16string_view& input); 18 | std::string ToMB(const std::wstring_view& input); 19 | std::wstring ToWC(const std::string_view& input); 20 | 21 | std::u16string ReadWideFile(const std::filesystem::path& filename); 22 | void WriteWideFile(const std::filesystem::path& filename, const std::u16string_view& text); 23 | 24 | std::string CollapseNewlines(const std::string_view& input); 25 | } 26 | -------------------------------------------------------------------------------- /staging/licenses/zlib.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | Jean-loup Gailly Mark Adler 20 | jloup@gzip.org madler@alumni.caltech.edu 21 | -------------------------------------------------------------------------------- /staging/tf2_addons/aaaaaaaaaa_votefailed_eraser_v2/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 ClusterConsultant 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /tf2_bot_detector/CompensatedTS.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | 5 | #include 6 | 7 | namespace tf2_bot_detector 8 | { 9 | // A compensated timestamp. Allows time to appear to progress normally 10 | // (with all the sub-second precision offered by clock_t) despite the uneven 11 | // pacing of the output from the log file. 12 | struct CompensatedTS 13 | { 14 | public: 15 | void InvalidateRecorded() { m_Recorded.reset(); } 16 | bool IsRecordedValid() const { return m_Recorded.has_value(); } 17 | void SetRecorded(time_point_t recorded); 18 | 19 | void Snapshot(); 20 | bool IsSnapshotValid() const { return m_Snapshot.has_value(); } 21 | time_point_t GetSnapshot() const; 22 | 23 | private: 24 | std::optional m_Recorded; // real time at instant of recording 25 | time_point_t m_Parsed{}; // real time when the recorded value was read 26 | std::optional m_Snapshot; // compensated time when Snapshot() was called 27 | std::optional m_PreviousSnapshot; 28 | 29 | mutable bool m_SnapshotUsed = false; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /staging/licenses/proggyfonts.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2004, 2005 Tristan Grimmer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /staging/licenses/cppcoro.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 Lewis Baker 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Matt Haynie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/ChatWrappersVerifyPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | #include "ISetupFlowPage.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | class ChatWrappersVerifyPage final : public ISetupFlowPage 12 | { 13 | public: 14 | ValidateSettingsResult ValidateSettings(const Settings& settings) const override; 15 | OnDrawResult OnDraw(const DrawState& ds) override; 16 | void Init(const InitState& is) override; 17 | 18 | bool CanCommit() const override; 19 | void Commit(const CommitState& cs) override; 20 | 21 | bool WantsSetupText() const override { return false; } 22 | bool WantsContinueButton() const override { return false; } 23 | 24 | SetupFlowPage GetPage() const override { return SetupFlowPage::ChatWrappersVerify; } 25 | 26 | private: 27 | std::string m_ExpectedToken; 28 | std::shared_future m_Validation; 29 | std::string m_Message; 30 | std::array m_MessageColor{ 1, 1, 1, 1 }; 31 | time_point_t m_NextValidationTime{}; 32 | 33 | static constexpr duration_t VALIDATION_INTERVAL = std::chrono::seconds(1); 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /staging/licenses/implot.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Evan Pezent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /staging/licenses/mh_stuff.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Matt Haynie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /staging/licenses/imgui_desktop.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Matt Haynie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /staging/licenses/tf2_bot_detector.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Matt Haynie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /staging/licenses/cpp-httplib.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 yhirose 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /staging/licenses/imgui.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2020 Omar Cornut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /staging/licenses/nlohmann_json.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-2020 Niels Lohmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tf2_bot_detector/Util/RegexUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace tf2_bot_detector 12 | { 13 | using svmatch = std::match_results; 14 | 15 | template 16 | inline std::string_view to_string_view(const std::sub_match& match) 17 | { 18 | return std::string_view(&*match.first, match.length()); 19 | } 20 | 21 | template 22 | inline auto from_chars(const std::sub_match& match, T& out) 23 | { 24 | return mh::from_chars(to_string_view(match), out); 25 | } 26 | 27 | template 28 | inline void from_chars_throw(const std::sub_match& match, T& out, TArgs&&... args) 29 | { 30 | const auto sv = to_string_view(match); 31 | auto result = mh::from_chars(sv, out, std::forward(args)...); 32 | if (!result) 33 | { 34 | throw std::runtime_error(mh::format("Failed to parse {} as {}", std::quoted(sv), typeid(T).name())); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tf2_bot_detector/LobbyMember.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SteamID.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | enum class LobbyMemberTeam : uint8_t 12 | { 13 | Invaders, 14 | Defenders, 15 | }; 16 | 17 | inline constexpr LobbyMemberTeam OppositeTeam(LobbyMemberTeam team) 18 | { 19 | if (team == LobbyMemberTeam::Invaders) 20 | return LobbyMemberTeam::Defenders; 21 | else if (team == LobbyMemberTeam::Defenders) 22 | return LobbyMemberTeam::Invaders; 23 | else 24 | throw std::runtime_error(__FUNCTION__ ": Invalid LobbyMemberTeam"); 25 | } 26 | 27 | enum class LobbyMemberType : uint8_t 28 | { 29 | Player, 30 | InvalidPlayer, 31 | }; 32 | 33 | /// 34 | /// Represents a player managed by the GC, as reported by tf_lobby_debug. 35 | /// 36 | struct LobbyMember 37 | { 38 | auto operator<=>(const LobbyMember&) const = default; 39 | 40 | constexpr bool IsValid() const { return m_SteamID.IsValid(); } 41 | 42 | SteamID m_SteamID; 43 | unsigned m_Index; 44 | LobbyMemberTeam m_Team; 45 | LobbyMemberType m_Type; 46 | bool m_Pending; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /staging/licenses/ValveFileVDF.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Matthias Moeller 2016 m_moeller@live.de 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/GithubAPI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Version.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace tf2_bot_detector 11 | { 12 | class IHTTPClient; 13 | } 14 | 15 | namespace tf2_bot_detector::GithubAPI 16 | { 17 | struct NewVersionResult 18 | { 19 | bool IsReleaseAvailable() const { return m_Stable.has_value(); } 20 | bool IsPreviewAvailable() const { return m_Preview.has_value(); } 21 | bool IsError() const { return !IsReleaseAvailable() && !IsPreviewAvailable() && m_Error; } 22 | bool IsUpToDate() const { return !IsReleaseAvailable() && !IsPreviewAvailable(); } 23 | 24 | std::string GetURL() const 25 | { 26 | if (IsPreviewAvailable()) 27 | return m_Preview->m_URL; 28 | if (IsReleaseAvailable()) 29 | return m_Stable->m_URL; 30 | 31 | return {}; 32 | } 33 | 34 | struct Release 35 | { 36 | Version m_Version; 37 | std::string m_URL; 38 | }; 39 | 40 | bool m_Error = false; 41 | std::optional m_Stable; 42 | std::optional m_Preview; 43 | }; 44 | 45 | [[nodiscard]] mh::task CheckForNewVersion(const IHTTPClient& client); 46 | } 47 | -------------------------------------------------------------------------------- /staging/cfg/untrusted_groups.official.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [ 3 | { 4 | "id": 103582791437999430, 5 | "description": "coby_kingdom" 6 | }, 7 | { 8 | "id": 103582791462441233, 9 | "description": "DoesHotterAccounts" 10 | }, 11 | { 12 | "id": 103582791467166627, 13 | "description": "Suck my duck v2" 14 | }, 15 | { 16 | "id": 103582791458164360, 17 | "description": "cathook" 18 | }, 19 | { 20 | "id": 103582791462728102, 21 | "description": "chair owners" 22 | }, 23 | { 24 | "id": 103582791430087319, 25 | "description": "myg0t" 26 | }, 27 | { 28 | "id": 103582791461102125, 29 | "description": "just disable vac" 30 | }, 31 | { 32 | "id": 103582791465736092, 33 | "description": "chair owners" 34 | }, 35 | { 36 | "id": 103582791463313062, 37 | "description": "lmaobox" 38 | }, 39 | { 40 | "id": 103582791460581613, 41 | "description": "i cheat on my main" 42 | }, 43 | { 44 | "id": 103582791464137563, 45 | "description": "hvh masters" 46 | }, 47 | { 48 | "id": 103582791465130149, 49 | "description": "nullcore" 50 | }, 51 | { 52 | "id": 103582791469149369, 53 | "description": "hvh" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /tf2_bot_detector/Bitmap.cpp: -------------------------------------------------------------------------------- 1 | #include "Bitmap.h" 2 | 3 | #include 4 | 5 | #define STBI_FAILURE_USERMSG 1 6 | #define STB_IMAGE_IMPLEMENTATION 1 7 | #include 8 | 9 | using namespace tf2_bot_detector; 10 | using namespace std::string_literals; 11 | 12 | void Bitmap::Deleter::operator()(void* ptr) const 13 | { 14 | stbi_image_free(ptr); 15 | } 16 | 17 | Bitmap::Bitmap(const std::filesystem::path& path) 18 | { 19 | LoadFile(path); 20 | } 21 | 22 | Bitmap::Bitmap(const std::filesystem::path& path, uint8_t desiredChannels) 23 | { 24 | LoadFile(path, desiredChannels); 25 | } 26 | 27 | void Bitmap::LoadFile(const std::filesystem::path& path) 28 | { 29 | return LoadFile(path, 0); 30 | } 31 | 32 | void Bitmap::LoadFile(const std::filesystem::path& path, uint8_t desiredChannels) 33 | { 34 | int width, height, channels; 35 | m_Image.reset(reinterpret_cast( 36 | stbi_load(path.string().c_str(), &width, &height, &channels, desiredChannels))); 37 | 38 | m_Width = width; 39 | m_Height = height; 40 | m_Channels = channels; 41 | 42 | if (!m_Image) 43 | throw std::runtime_error("Failed to load image from "s << path << ": " << stbi_failure_reason()); 44 | } 45 | -------------------------------------------------------------------------------- /staging/licenses/cpprestsdk.txt: -------------------------------------------------------------------------------- 1 | C++ REST SDK 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) Microsoft Corporation 6 | 7 | All rights reserved. 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | this software and associated documentation files (the "Software"), to deal in 11 | the Software without restriction, including without limitation the rights to 12 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 13 | the Software, and to permit persons to whom the Software is furnished to do so, 14 | subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | -------------------------------------------------------------------------------- /util/download_and_compile.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Don't leak environment variables 4 | SETLOCAL 5 | 6 | IF EXIST tf2_bot_detector ( 7 | ECHO Updating TF2 Bot Detector source... 8 | CD tf2_bot_detector 9 | git pull --recurse-submodules 10 | IF %ERRORLEVEL% NEQ 0 EXIT /B 11 | ) ELSE ( 12 | ECHO Downloading TF2 Bot Detector source... 13 | git clone --depth=1 https://github.com/PazerOP/tf2_bot_detector.git tf2_bot_detector --recurse-submodules 14 | IF %ERRORLEVEL% NEQ 0 EXIT /B 15 | CD tf2_bot_detector 16 | ) 17 | 18 | ECHO Compiling TF2 Bot Detector... 19 | RMDIR /S /Q build 20 | MKDIR build 21 | CD build 22 | cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=..\submodules\vcpkg\scripts\buildsystems\vcpkg.cmake ../ 23 | IF %ERRORLEVEL% NEQ 0 EXIT /B 24 | cmake --build . --config Release 25 | IF %ERRORLEVEL% NEQ 0 EXIT /B 26 | 27 | PUSHD ..\staging 28 | SET STAGING_DIR=%CD% 29 | POPD 30 | 31 | ECHO Copying default configuration... 32 | XCOPY /E /Y tf2_bot_detector\Release\* "%STAGING_DIR%" 33 | IF %ERRORLEVEL% NEQ 0 EXIT /B 34 | 35 | IF NOT "%TF2BD_DL_AND_COMPILE_SKIP_OPEN%"=="1" ( 36 | ECHO Opening compiled output... 37 | explorer "%STAGING_DIR%" 38 | IF %ERRORLEVEL% NEQ 0 EXIT /B 39 | ) 40 | 41 | PAUSE 42 | ENDLOCAL 43 | -------------------------------------------------------------------------------- /tf2_bot_detector/WorldEventListener.cpp: -------------------------------------------------------------------------------- 1 | #include "WorldEventListener.h" 2 | #include "WorldState.h" 3 | 4 | using namespace tf2_bot_detector; 5 | 6 | AutoWorldEventListener::AutoWorldEventListener(IWorldState& world) : 7 | m_World(&world) 8 | { 9 | m_World->AddWorldEventListener(this); 10 | } 11 | 12 | AutoWorldEventListener::AutoWorldEventListener(const AutoWorldEventListener& other) : 13 | m_World(other.m_World) 14 | { 15 | m_World->AddWorldEventListener(this); 16 | } 17 | 18 | AutoWorldEventListener& AutoWorldEventListener::operator=(const AutoWorldEventListener& other) 19 | { 20 | m_World->RemoveWorldEventListener(this); 21 | m_World = other.m_World; 22 | m_World->AddWorldEventListener(this); 23 | return *this; 24 | } 25 | 26 | AutoWorldEventListener::AutoWorldEventListener(AutoWorldEventListener&& other) : 27 | m_World(other.m_World) 28 | { 29 | m_World->AddWorldEventListener(this); 30 | } 31 | 32 | AutoWorldEventListener& AutoWorldEventListener::operator=(AutoWorldEventListener&& other) 33 | { 34 | m_World->RemoveWorldEventListener(this); 35 | m_World = other.m_World; 36 | m_World->AddWorldEventListener(this); 37 | return *this; 38 | } 39 | 40 | AutoWorldEventListener::~AutoWorldEventListener() 41 | { 42 | m_World->RemoveWorldEventListener(this); 43 | } 44 | -------------------------------------------------------------------------------- /staging/tf2_addons/aaaaaaaaaa_votefailed_eraser_v2/resource/ui/votehud.res: -------------------------------------------------------------------------------- 1 | //Edited by The Immortal Nicholas Flamel with sanity checking by moeb for use with botdetector.tf 2 | #base "botdetector_tf_hud_base/votehud.res" 3 | "Resource/UI/VoteHud.res" 4 | { 5 | // This is sent to the vote caller when they're not able to start the vote 6 | "CallVoteFailed" 7 | { 8 | "ControlName" "EditablePanel" 9 | "fieldName" "CallVoteFailed" 10 | "visible" "0" 11 | "enabled" "0" 12 | "xpos" "9999" 13 | "ypos" "9999" 14 | "wide" "0" 15 | "tall" "0" 16 | "FailedIcon" 17 | { 18 | "ControlName" "ImagePanel" 19 | "fieldName" "FailedIcon" 20 | "visible" "0" 21 | "enabled" "0" 22 | "xpos" "9999" 23 | "ypos" "9999" 24 | "wide" "0" 25 | "tall" "0" 26 | } 27 | 28 | "FailedTitle" 29 | { 30 | "ControlName" "Label" 31 | "fieldName" "FailedTitle" 32 | "visible" "0" 33 | "enabled" "0" 34 | "xpos" "9999" 35 | "ypos" "9999" 36 | "wide" "0" 37 | "tall" "0" 38 | } 39 | 40 | "FailedReason" 41 | { 42 | "ControlName" "Label" 43 | "fieldName" "FailedReason" 44 | "visible" "0" 45 | "enabled" "0" 46 | "xpos" "9999" 47 | "ypos" "9999" 48 | "wide" "0" 49 | "tall" "0" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tf2_bot_detector/ConsoleLog/ConsoleLineListener.cpp: -------------------------------------------------------------------------------- 1 | #include "ConsoleLineListener.h" 2 | #include "WorldState.h" 3 | 4 | using namespace tf2_bot_detector; 5 | 6 | AutoConsoleLineListener::AutoConsoleLineListener(IWorldState& world) : 7 | m_World(&world) 8 | { 9 | m_World->AddConsoleLineListener(this); 10 | } 11 | 12 | AutoConsoleLineListener::AutoConsoleLineListener(const AutoConsoleLineListener& other) : 13 | m_World(other.m_World) 14 | { 15 | m_World->AddConsoleLineListener(this); 16 | } 17 | 18 | AutoConsoleLineListener& AutoConsoleLineListener::operator=(const AutoConsoleLineListener& other) 19 | { 20 | m_World->RemoveConsoleLineListener(this); 21 | m_World = other.m_World; 22 | m_World->AddConsoleLineListener(this); 23 | return *this; 24 | } 25 | 26 | AutoConsoleLineListener::AutoConsoleLineListener(AutoConsoleLineListener&& other) : 27 | m_World(other.m_World) 28 | { 29 | m_World->AddConsoleLineListener(this); 30 | } 31 | 32 | AutoConsoleLineListener& AutoConsoleLineListener::operator=(AutoConsoleLineListener&& other) 33 | { 34 | m_World->RemoveConsoleLineListener(this); 35 | m_World = other.m_World; 36 | m_World->AddConsoleLineListener(this); 37 | return *this; 38 | } 39 | 40 | AutoConsoleLineListener::~AutoConsoleLineListener() 41 | { 42 | m_World->RemoveConsoleLineListener(this); 43 | } 44 | -------------------------------------------------------------------------------- /tf2_bot_detector/Config/SponsorsList.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ConfigHelpers.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace tf2_bot_detector 11 | { 12 | class SponsorsList final 13 | { 14 | public: 15 | SponsorsList(const Settings& settings); 16 | 17 | void LoadFile(); 18 | 19 | bool IsLoading() const { return !m_Sponsors.is_ready(); } 20 | 21 | struct Sponsor 22 | { 23 | std::string m_Name; 24 | std::string m_Message; 25 | }; 26 | 27 | std::vector GetSponsors() const; 28 | 29 | private: 30 | const Settings* m_Settings = nullptr; 31 | 32 | struct SponsorsListFile final : public SharedConfigFileBase 33 | { 34 | using BaseClass = SharedConfigFileBase; 35 | 36 | void Deserialize(const nlohmann::json& json) override; 37 | void Serialize(nlohmann::json& json) const override; 38 | void ValidateSchema(const ConfigSchemaInfo& schema) const override; 39 | 40 | static constexpr int SPONSORS_SCHEMA_VERSION = 3; 41 | 42 | std::vector m_Sponsors; 43 | }; 44 | 45 | mh::task m_Sponsors; 46 | }; 47 | 48 | void to_json(nlohmann::json& j, const SponsorsList::Sponsor& d); 49 | void from_json(const nlohmann::json& j, SponsorsList::Sponsor& d); 50 | } 51 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/HijackActionManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef _WIN32 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tf2_bot_detector 9 | { 10 | class IAction; 11 | class Settings; 12 | 13 | class HijackActionManager final 14 | { 15 | public: 16 | HijackActionManager(const Settings& settings); 17 | ~HijackActionManager(); 18 | 19 | // Returns false if the action was not queued 20 | bool RunCommand(std::string cmd, std::string args = {}); 21 | 22 | void Update(); 23 | 24 | private: 25 | const Settings* m_Settings = nullptr; 26 | 27 | struct Writer; 28 | 29 | auto absolute_root() const; 30 | auto absolute_cfg() const; 31 | auto absolute_cfg_temp() const; 32 | 33 | // Map of temp cfg file content hashes to cfg file name so we don't 34 | // create a ton of duplicate files. 35 | std::unordered_multimap m_TempCfgFiles; 36 | uint32_t m_LastUpdateIndex = 0; 37 | bool ProcessSimpleCommands(const Writer& writer); 38 | bool ProcessComplexCommands(const Writer& writer); 39 | 40 | bool SendHijackCommands(const Writer& writer); 41 | bool SendHijackCommand(std::string cmd); 42 | 43 | struct RunningCommand; 44 | std::list m_RunningCommands; 45 | void ProcessRunningCommands(); 46 | }; 47 | } 48 | #endif 49 | -------------------------------------------------------------------------------- /tf2_bot_detector/GenericErrors.cpp: -------------------------------------------------------------------------------- 1 | #include "GenericErrors.h" 2 | 3 | using namespace tf2_bot_detector; 4 | 5 | namespace 6 | { 7 | class ErrorCategoryType final : public std::error_category 8 | { 9 | public: 10 | const char* name() const noexcept override { return "TF2 Bot Detector (Generic)"; } 11 | std::string message(int condition) const override 12 | { 13 | switch (ErrorCode(condition)) 14 | { 15 | case ErrorCode::Success: 16 | return "Success"; 17 | case ErrorCode::InternetConnectivityDisabled: 18 | return "Internet connectivity has been disabled by the user in Settings."; 19 | case ErrorCode::LazyValueUninitialized: 20 | return "A lazily-loaded value was uninitialized."; 21 | case ErrorCode::UnknownError: 22 | return "Unknown error."; 23 | case ErrorCode::LogicError: 24 | return "They were right all along! I *am* a bad programmer (logic error)."; 25 | } 26 | 27 | return mh::format("Unknown error condition {}", condition); 28 | } 29 | }; 30 | 31 | const ErrorCategoryType& ErrorCategory() 32 | { 33 | static const ErrorCategoryType s_Value; 34 | return s_Value; 35 | } 36 | } 37 | 38 | std::error_condition tf2_bot_detector::make_error_condition(tf2_bot_detector::ErrorCode e) 39 | { 40 | return std::error_condition(static_cast(e), ErrorCategory()); 41 | } 42 | -------------------------------------------------------------------------------- /tf2_bot_detector/Util/PathUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace tf2_bot_detector 11 | { 12 | struct [[nodiscard]] DirectoryValidatorResult 13 | { 14 | DirectoryValidatorResult(std::filesystem::path path) : m_Path(std::move(path)) {} 15 | 16 | enum class Result 17 | { 18 | Valid, 19 | 20 | Empty, // Path is empty 21 | DoesNotExist, // Directory doesn't exist 22 | NotADirectory, // Not a directory 23 | InvalidContents, // Contents don't match what we expect 24 | FilesystemError, // Some sort of std::filesystem::filesystem_error exception 25 | } m_Result = Result::Valid; 26 | 27 | operator bool() const { return m_Result == Result::Valid; } 28 | 29 | std::filesystem::path m_Path; 30 | std::string m_Message; 31 | }; 32 | 33 | DirectoryValidatorResult ValidateTFDir(std::filesystem::path path); 34 | DirectoryValidatorResult ValidateSteamDir(std::filesystem::path path); 35 | 36 | mh::generator GetSteamLibraryFolders(const std::filesystem::path& steamDir); 37 | std::filesystem::path FindTFDir(const std::filesystem::path& steamDir); 38 | 39 | void DeleteOldFiles(const std::filesystem::path& path, duration_t maxAge); 40 | } 41 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/ChatWrappersGeneratorPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config/ChatWrappers.h" 4 | #include "ISetupFlowPage.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #undef DrawState 12 | 13 | namespace tf2_bot_detector 14 | { 15 | class ChatWrappersGeneratorPage final : public ISetupFlowPage 16 | { 17 | public: 18 | ValidateSettingsResult ValidateSettings(const Settings& settings) const override; 19 | OnDrawResult OnDraw(const DrawState& ds) override; 20 | void Init(const InitState& is) override; 21 | 22 | bool CanCommit() const override; 23 | void Commit(const CommitState& cs) override; 24 | 25 | bool WantsSetupText() const override { return false; } 26 | bool WantsContinueButton() const override { return false; } 27 | 28 | SetupFlowPage GetPage() const override { return SetupFlowPage::ChatWrappersGenerate; } 29 | 30 | static std::string GetChatWrapperStringToken(uint32_t token); 31 | static constexpr char VERIFY_CFG_FILE_NAME[] = "__tf2bd_chat_wrappers_verify.cfg"; 32 | 33 | private: 34 | mh::status_reader m_Progress; 35 | 36 | mh::future m_ChatWrappersGenerated; 37 | bool m_WasInitiallyClosed = true; 38 | std::optional m_ChatWrappersLoaded; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /tf2_bot_detector/Config/DRPInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ConfigHelpers.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace tf2_bot_detector 13 | { 14 | class DRPInfo final 15 | { 16 | public: 17 | DRPInfo(const Settings& settings); 18 | 19 | void LoadFile(); 20 | 21 | struct Map 22 | { 23 | std::string GetLargeImageKey() const; 24 | std::string GetFriendlyName() const; 25 | bool Matches(const std::string_view& mapName) const; 26 | 27 | std::vector m_MapNames; 28 | std::string m_FriendlyNameOverride; 29 | std::string m_LargeImageKeyOverride; 30 | }; 31 | 32 | const Map* FindMap(const std::string_view& name) const; 33 | 34 | private: 35 | const Settings* m_Settings = nullptr; 36 | 37 | struct DRPFile final : public SharedConfigFileBase 38 | { 39 | using BaseClass = SharedConfigFileBase; 40 | 41 | void Deserialize(const nlohmann::json& json) override; 42 | void Serialize(nlohmann::json& json) const override; 43 | void ValidateSchema(const ConfigSchemaInfo& schema) const override; 44 | 45 | static constexpr int DRP_SCHEMA_VERSION = 3; 46 | 47 | std::vector m_Maps; 48 | }; 49 | 50 | mh::task m_DRPInfo; 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/NetworkHelpers.cpp: -------------------------------------------------------------------------------- 1 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 2 | 3 | #include "NetworkHelpers.h" 4 | #include "Log.h" 5 | 6 | #include 7 | 8 | #include 9 | 10 | #ifdef _WIN32 11 | #include 12 | #else 13 | #include 14 | #endif 15 | 16 | std::string tf2_bot_detector::Networking::GetLocalIP() 17 | { 18 | // Same basic logic that the source engine uses to figure out what ip to bind to 19 | // (attempting to connect to rcon at 127.0.0.1 doesn't work) 20 | char buf[512]; 21 | if (auto result = gethostname(buf, sizeof(buf)); result < 0) 22 | throw std::runtime_error(std::string(__FUNCTION__) << "(): gethostname() returned " << result); 23 | 24 | buf[sizeof(buf) - 1] = 0; 25 | 26 | const char* tempBuf = "localhost"; 27 | auto host = gethostbyname(tempBuf);// buf); 28 | if (!host) 29 | throw std::runtime_error(std::string(__FUNCTION__) << "(): gethostbyname returned nullptr"); 30 | 31 | in_addr addr{}; 32 | addr.s_addr = *(u_long*)host->h_addr_list[0]; 33 | 34 | std::string retVal; 35 | if (auto addrStr = inet_ntoa(addr); addrStr && addrStr[0]) 36 | retVal = addrStr; 37 | else 38 | throw std::runtime_error(std::string(__FUNCTION__) << "(): inet_ntoa returned null or empty string"); 39 | 40 | Log(std::string(__FUNCTION__) << "(): ip = " << retVal); 41 | return retVal; 42 | } 43 | -------------------------------------------------------------------------------- /tf2_bot_detector_common/src/Platform/Windows/PlatformCommon.cpp: -------------------------------------------------------------------------------- 1 | #include "Platform/PlatformCommon.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | void* tf2_bot_detector::Platform::GetProcAddressHelper(const char* moduleName, const char* symbolName, 10 | bool isCritical, const mh::source_location& location) 11 | { 12 | if (!moduleName) 13 | throw std::invalid_argument("moduleName was nullptr"); 14 | if (!moduleName[0]) 15 | throw std::invalid_argument("moduleName was empty"); 16 | if (!symbolName) 17 | throw std::invalid_argument("symbolName was nullptr"); 18 | if (!symbolName[0]) 19 | throw std::invalid_argument("symbolName was empty"); 20 | 21 | HMODULE moduleHandle = GetModuleHandleA(moduleName); 22 | if (!moduleHandle) 23 | { 24 | auto err = GetLastError(); 25 | throw std::system_error(err, std::system_category(), mh::format(MH_FMT_STRING("Failed to GetModuleHandle({})"), moduleName)); 26 | } 27 | 28 | auto address = GetProcAddress(moduleHandle, symbolName); 29 | if (!address) 30 | { 31 | auto err = GetLastError(); 32 | auto ec = std::error_code(err, std::system_category()); 33 | 34 | if (isCritical) 35 | { 36 | throw std::system_error(ec, mh::format(MH_FMT_STRING("{}: Failed to find function {} in {}"), location, symbolName, moduleName)); 37 | } 38 | } 39 | 40 | return address; 41 | } 42 | -------------------------------------------------------------------------------- /staging/licenses/catch2.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/Update_Portable.cpp: -------------------------------------------------------------------------------- 1 | #include "Update_Portable.h" 2 | #include "Platform/Platform.h" 3 | #include "Common.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | int tf2_bot_detector::Updater::Update_Portable() try 14 | { 15 | auto sysDepsResult = Platform::UpdateSystemDependencies(); 16 | 17 | std::cerr << "Attempting to install portable version from " 18 | << s_CmdLineArgs.m_SourcePath << " to " << s_CmdLineArgs.m_DestPath 19 | << "..." << std::endl; 20 | 21 | using copy_options = std::filesystem::copy_options; 22 | std::filesystem::copy(s_CmdLineArgs.m_SourcePath, s_CmdLineArgs.m_DestPath, 23 | copy_options::recursive | copy_options::overwrite_existing); 24 | 25 | std::cerr << "Finished copying files. Attempting to start tool..." << std::endl; 26 | 27 | // FIXME linux 28 | if (sysDepsResult != UpdateSystemDependenciesResult::RebootRequired) 29 | Platform::Processes::Launch(s_CmdLineArgs.m_DestPath / "tf2_bot_detector.exe"); 30 | 31 | if (sysDepsResult == UpdateSystemDependenciesResult::RebootRequired) 32 | Platform::RebootComputer(); 33 | 34 | return 0; 35 | } 36 | catch (const std::exception& e) 37 | { 38 | std::cerr << mh::format("Unhandled exception ({}) in {}: {}", 39 | typeid(e).name(), __FUNCTION__, e.what()) << std::endl; 40 | return 3; 41 | } 42 | -------------------------------------------------------------------------------- /tf2_bot_detector/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.h" 2 | #include "DB/TempDB.h" 3 | #include "UI/MainWindow.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace tf2_bot_detector; 10 | 11 | static TF2BDApplication* s_Application; 12 | 13 | TF2BDApplication::TF2BDApplication() 14 | { 15 | assert(!s_Application); 16 | s_Application = this; 17 | 18 | m_TempDB = DB::ITempDB::Create(); 19 | 20 | DebugLog("Initializing MainWindow..."); 21 | AddManagedWindow(std::make_unique(*this)); 22 | } 23 | 24 | TF2BDApplication::~TF2BDApplication() = default; 25 | 26 | TF2BDApplication& TF2BDApplication::GetApplication() 27 | { 28 | return *mh_ensure(s_Application); 29 | } 30 | 31 | MainWindow& TF2BDApplication::GetMainWindow() 32 | { 33 | return *mh_ensure(m_MainWindow); 34 | } 35 | 36 | const MainWindow& TF2BDApplication::GetMainWindow() const 37 | { 38 | return *mh_ensure(m_MainWindow); 39 | } 40 | 41 | DB::ITempDB& TF2BDApplication::GetTempDB() 42 | { 43 | return *mh_ensure(m_TempDB.get()); 44 | } 45 | 46 | void TF2BDApplication::OnAddingManagedWindow(ImGuiDesktop::Window& window) 47 | { 48 | if (auto mainWindow = dynamic_cast(&window)) 49 | { 50 | if (mh_ensure(!m_MainWindow)) 51 | m_MainWindow = mainWindow; 52 | } 53 | } 54 | 55 | void TF2BDApplication::OnRemovingManagedWindow(ImGuiDesktop::Window& window) 56 | { 57 | if (&window == m_MainWindow) 58 | m_MainWindow = nullptr; 59 | } 60 | -------------------------------------------------------------------------------- /staging/licenses/libzippp.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (C) 2013 Cédric Tabin 3 | 4 | The author can be contacted on http://www.astorm.ch/blog/index.php?contact 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | 16 | -------------------------------------------------------------------------------- /tf2_bot_detector/Platform/Windows/WindowsHelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace tf2_bot_detector::Windows 9 | { 10 | inline std::error_code GetLastErrorCode(DWORD lastError) { return std::error_code(lastError, std::system_category()); } 11 | inline std::error_code GetLastErrorCode() { return GetLastErrorCode(GetLastError()); } 12 | 13 | class GetLastErrorException final : public std::system_error 14 | { 15 | static std::string ConstructString(HRESULT hr, const std::string_view& context) 16 | { 17 | return std::string(context); 18 | } 19 | 20 | public: 21 | GetLastErrorException(HRESULT hr, DWORD lastError) : 22 | GetLastErrorException(hr, lastError, std::string_view{}) {} 23 | GetLastErrorException(HRESULT hr, DWORD lastError, const std::string_view& msg) : 24 | std::system_error(lastError, std::system_category(), ConstructString(m_Result = hr, msg)) {} 25 | 26 | HRESULT GetResult() const { return m_Result; } 27 | 28 | private: 29 | HRESULT m_Result; 30 | }; 31 | 32 | 33 | #define MH_STR(x) #x 34 | 35 | #define CHECK_HR(x) \ 36 | if (auto hr_temp_____ = (x); FAILED(hr_temp_____)) \ 37 | { \ 38 | auto lastError_____ = ::GetLastError(); \ 39 | throw ::tf2_bot_detector::Windows::GetLastErrorException(hr_temp_____, lastError_____, MH_STR(x)); \ 40 | } 41 | 42 | #define IUNKNOWN_QI_TYPE(type) \ 43 | if (riid == IID_ ## type) \ 44 | { \ 45 | *ppv = static_cast(this); \ 46 | AddRef(); \ 47 | return S_OK; \ 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /staging/licenses/libzip.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner 2 | 3 | The authors can be contacted at 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | 3. The names of the authors may not be used to endorse or promote 18 | products derived from this software without specific prior 19 | written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS 22 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 25 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 27 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 29 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 31 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /tf2_bot_detector/Version.base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace tf2_bot_detector 11 | { 12 | struct Version 13 | { 14 | using value_type = uint16_t; 15 | 16 | constexpr Version() = default; 17 | 18 | explicit constexpr Version(value_type major, value_type minor, value_type patch = 0, value_type build = 0) : 19 | m_Major(major), 20 | m_Minor(minor), 21 | m_Patch(patch), 22 | m_Build(build) 23 | { 24 | } 25 | 26 | static std::optional Parse(const char* str); 27 | 28 | constexpr auto operator<=>(const Version&) const = default; 29 | 30 | constexpr bool IsCustomBuild() const { return m_Build == 65535; } 31 | 32 | value_type m_Major{}; 33 | value_type m_Minor{}; 34 | value_type m_Patch{}; 35 | value_type m_Build{}; 36 | }; 37 | 38 | static constexpr Version VERSION( 39 | ${CMAKE_PROJECT_VERSION_MAJOR}, 40 | ${CMAKE_PROJECT_VERSION_MINOR}, 41 | ${CMAKE_PROJECT_VERSION_PATCH}, 42 | ${CMAKE_PROJECT_VERSION_TWEAK} 43 | ); 44 | 45 | void to_json(nlohmann::json& j, const Version& d); 46 | void from_json(const nlohmann::json& j, Version& d); 47 | 48 | template 49 | std::basic_ostream& operator<<(std::basic_ostream& os, const Version& v) 50 | { 51 | os << v.m_Major << '.' << v.m_Minor << '.' << v.m_Patch; 52 | 53 | if (v.IsCustomBuild()) 54 | os << " (Custom Build)"; 55 | else 56 | os << '.' << v.m_Build; 57 | 58 | return os; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /schemas/v3/discord_rich_presence.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/master/schemas/v3/discord_rich_presence.schema.json", 4 | "title": "TF2 Bot Detector Discord Rich Presence Schema", 5 | "type": "object", 6 | "additionalProperties": false, 7 | "properties": { 8 | "$schema": { 9 | "description": "The JSON schema to validate this file against.", 10 | "type": "string" 11 | }, 12 | "file_info": { 13 | "$ref": "./shared.schema.json#/definitions/file_info" 14 | }, 15 | "maps": { 16 | "description": "Map name <--> rich presence settings", 17 | "type": "object", 18 | "additionalProperties": true, 19 | "patternProperties": { 20 | ".*": { 21 | "type": "object", 22 | "additionalProperties": false, 23 | "description": "Entry for a specific map", 24 | "properties": { 25 | "map_name_aliases": { 26 | "type": "array", 27 | "description": "Additional names for this map", 28 | "items": { 29 | "type": "string", 30 | "format": "regex" 31 | } 32 | }, 33 | "friendly_name_override": { 34 | "type": "string", 35 | "description": "Friendly name of the map (pl_thundermountain -> Thunder Mountain)" 36 | }, 37 | "large_image_key_override": { 38 | "type": "string", 39 | "description": "Override for the name of the large image in Discord." 40 | } 41 | } 42 | } 43 | }, 44 | "item": {} 45 | } 46 | }, 47 | "required": [ 48 | "$schema", 49 | "maps" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /tf2_bot_detector/CompensatedTS.cpp: -------------------------------------------------------------------------------- 1 | #include "CompensatedTS.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace std::chrono_literals; 7 | using namespace tf2_bot_detector; 8 | 9 | void CompensatedTS::SetRecorded(time_point_t recorded) 10 | { 11 | assert(recorded.time_since_epoch() > 0s); 12 | m_Recorded = recorded; 13 | 14 | const auto now = clock_t::now(); 15 | if (m_Snapshot && (now - *m_Snapshot) >= 1s) 16 | m_Snapshot.reset(); 17 | 18 | if ((now - recorded) > 1250ms) 19 | m_Parsed = now; 20 | else 21 | m_Parsed = recorded; 22 | } 23 | 24 | void CompensatedTS::Snapshot() 25 | { 26 | const auto now = clock_t::now(); 27 | const auto extra = now - m_Parsed; 28 | [[maybe_unused]] const auto extraSeconds = to_seconds(extra); 29 | const time_point_t adjustedTS = m_Recorded.value() + extra; 30 | 31 | const auto error = adjustedTS - now; 32 | const auto errorSeconds = to_seconds(extra); 33 | assert(error <= 1s); 34 | 35 | auto newSnapshot = std::min(adjustedTS, now); 36 | 37 | #if 0 38 | if (m_SnapshotUsed && m_PreviousSnapshot && newSnapshot < *m_PreviousSnapshot) 39 | { 40 | // going backwards in time!!! 41 | const auto delta = to_seconds(newSnapshot - *m_PreviousSnapshot); 42 | assert(delta >= -0.01); 43 | newSnapshot = *m_PreviousSnapshot; 44 | } 45 | #endif 46 | 47 | m_Snapshot = newSnapshot; 48 | m_PreviousSnapshot = newSnapshot; 49 | m_SnapshotUsed = false; 50 | } 51 | 52 | time_point_t CompensatedTS::GetSnapshot() const 53 | { 54 | if (m_Snapshot.has_value()) 55 | { 56 | m_SnapshotUsed = true; 57 | return *m_Snapshot; 58 | } 59 | else 60 | { 61 | return {}; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/ActionGenerators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class IAction; 8 | class IActionManager; 9 | 10 | class IActionGenerator 11 | { 12 | public: 13 | virtual ~IActionGenerator() = default; 14 | 15 | virtual bool Execute(IActionManager& manager) = 0; 16 | }; 17 | 18 | class IPeriodicActionGenerator : public IActionGenerator 19 | { 20 | public: 21 | virtual ~IPeriodicActionGenerator() = default; 22 | 23 | virtual duration_t GetInterval() const = 0; 24 | virtual duration_t GetInitialDelay() const { return {}; } 25 | 26 | bool Execute(IActionManager& manager) override final; 27 | 28 | protected: 29 | [[nodiscard]] virtual bool ExecuteImpl(IActionManager& manager) = 0; 30 | 31 | private: 32 | time_point_t m_LastRunTime{}; 33 | }; 34 | 35 | class StatusUpdateActionGenerator final : public IPeriodicActionGenerator 36 | { 37 | public: 38 | duration_t GetInterval() const override; 39 | 40 | protected: 41 | bool ExecuteImpl(IActionManager& manager) override; 42 | 43 | private: 44 | bool m_NextShort = false; 45 | bool m_NextPing = false; 46 | }; 47 | 48 | class ConfigActionGenerator final : public IPeriodicActionGenerator 49 | { 50 | public: 51 | duration_t GetInterval() const override; 52 | 53 | protected: 54 | bool ExecuteImpl(IActionManager& manager) override; 55 | }; 56 | 57 | class LobbyDebugActionGenerator final : public IPeriodicActionGenerator 58 | { 59 | public: 60 | duration_t GetInterval() const override { return std::chrono::seconds(1); } 61 | 62 | protected: 63 | bool ExecuteImpl(IActionManager& manager) override; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /tf2_bot_detector/ConsoleLog/ConsoleLogParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CompensatedTS.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | class IConsoleLine; 12 | class IConsoleLineListener; 13 | class Settings; 14 | class IWorldState; 15 | 16 | class ConsoleLogParser final 17 | { 18 | public: 19 | ConsoleLogParser(IWorldState& world, const Settings& settings, std::filesystem::path conLogFile); 20 | 21 | void Update(); 22 | 23 | float GetParseProgress() const { return m_ParseProgress; } 24 | 25 | const CompensatedTS& GetCurrentTimestamp() const { return m_CurrentTimestamp; } 26 | 27 | private: 28 | const Settings* m_Settings = nullptr; 29 | IWorldState* m_WorldState = nullptr; 30 | 31 | void TrySnapshot(bool& snapshotUpdated); 32 | CompensatedTS m_CurrentTimestamp; 33 | 34 | enum class ParseLineResult 35 | { 36 | Unparsed, 37 | Defer, 38 | Success, 39 | Modified, 40 | }; 41 | 42 | using striter = std::string::const_iterator; 43 | void Parse(bool& linesProcessed, bool& snapshotUpdated, bool& consoleLinesUpdated); 44 | void ParseChunk(striter& parseEnd, bool& linesProcessed, bool& snapshotUpdated, bool& consoleLinesUpdated); 45 | bool ParseChatMessage(const std::string_view& lineStr, striter& parseEnd, std::shared_ptr& parsed); 46 | 47 | struct CustomDeleters 48 | { 49 | void operator()(FILE*) const; 50 | }; 51 | std::filesystem::path m_FileName; 52 | std::unique_ptr m_File; 53 | time_point_t m_LastFileLoadAttempt{}; 54 | std::string m_FileLineBuf; 55 | float m_ParseProgress = 0; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /tf2_bot_detector/Tests/Catch2.cpp: -------------------------------------------------------------------------------- 1 | #include "Log.h" 2 | #include "Tests.h" 3 | 4 | #define CATCH_CONFIG_RUNNER 5 | #define CATCH_CONFIG_NOSTDOUT 6 | 7 | #include 8 | #include 9 | 10 | namespace Catch 11 | { 12 | enum class LogBufType 13 | { 14 | Out, 15 | Log, 16 | Error, 17 | }; 18 | struct LogBuf final : std::stringbuf 19 | { 20 | LogBuf(LogBufType type) : m_Type(type) 21 | { 22 | } 23 | ~LogBuf() 24 | { 25 | pubsync(); 26 | } 27 | 28 | int sync() override 29 | { 30 | auto message = mh::format("[Catch2] {}", str()); 31 | switch (m_Type) 32 | { 33 | case LogBufType::Out: 34 | case LogBufType::Log: 35 | tf2_bot_detector::Log(std::move(message)); 36 | break; 37 | 38 | default: 39 | tf2_bot_detector::LogError(MH_SOURCE_LOCATION_CURRENT(), 40 | mh::format("[Catch2] Unknown LogBufType {}", int(m_Type))); 41 | [[fallthrough]]; 42 | case LogBufType::Error: 43 | tf2_bot_detector::LogError(std::move(message)); 44 | break; 45 | } 46 | 47 | str(""); // Clear out the buffer 48 | return 0; 49 | } 50 | 51 | LogBufType m_Type; 52 | }; 53 | 54 | std::ostream& cout() 55 | { 56 | static std::ostream s_Stream(new LogBuf(LogBufType::Out)); 57 | return s_Stream; 58 | } 59 | 60 | std::ostream& clog() 61 | { 62 | static std::ostream s_Stream(new LogBuf(LogBufType::Log)); 63 | return s_Stream; 64 | } 65 | 66 | std::ostream& cerr() 67 | { 68 | static std::ostream s_Stream(new LogBuf(LogBufType::Error)); 69 | return s_Stream; 70 | } 71 | } 72 | 73 | int tf2_bot_detector::RunTests() 74 | { 75 | DebugLog(MH_SOURCE_LOCATION_CURRENT()); 76 | return Catch::Session().run(); 77 | } 78 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/CheckFaceitClosedPage.cpp: -------------------------------------------------------------------------------- 1 | #include "UI/ImGui_TF2BotDetector.h" 2 | #include "Filesystem.h" 3 | #include "ISetupFlowPage.h" 4 | #include "Platform/Platform.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #undef DrawState 12 | 13 | using namespace tf2_bot_detector; 14 | 15 | namespace 16 | { 17 | class CheckFaceitClosedPage final : public ISetupFlowPage 18 | { 19 | public: 20 | ValidateSettingsResult ValidateSettings(const Settings& settings) const override 21 | { 22 | return Processes::IsProcessRunning("faceitservice.exe") ? ValidateSettingsResult::TriggerOpen : ValidateSettingsResult::Success; 23 | } 24 | 25 | OnDrawResult OnDraw(const DrawState& ds) override 26 | { 27 | if (ValidateSettings(*ds.m_Settings) == ValidateSettingsResult::TriggerOpen) 28 | { 29 | ImGui::TextFmt("TF2 Bot Detector is not compatible with FACEIT anti-cheat. You must close it before continuing.\n\n(Check your tray icons for an orange shield, right click it, and click Exit.)"); 30 | return OnDrawResult::ContinueDrawing; 31 | } 32 | else 33 | { 34 | return OnDrawResult::EndDrawing; 35 | } 36 | } 37 | 38 | void Init(const InitState&) override {} 39 | bool CanCommit() const override { return true; } 40 | void Commit(const CommitState& cs) override {} 41 | bool WantsSetupText() const { return false; } 42 | bool WantsContinueButton() const { return false; } 43 | SetupFlowPage GetPage() const override { return SetupFlowPage::CheckFaceitClosed; } 44 | }; 45 | } 46 | 47 | namespace tf2_bot_detector 48 | { 49 | std::unique_ptr CreateCheckFaceitClosedPage() 50 | { 51 | return std::make_unique(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tf2_bot_detector/ConsoleLog/ConsoleLineListener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | 5 | #include 6 | 7 | namespace tf2_bot_detector 8 | { 9 | class IConsoleLine; 10 | class IWorldState; 11 | 12 | class IConsoleLineListener 13 | { 14 | public: 15 | virtual ~IConsoleLineListener() = default; 16 | 17 | virtual void OnConsoleLineParsed(IWorldState& world, IConsoleLine& line) = 0; 18 | virtual void OnConsoleLineUnparsed(IWorldState& world, const std::string_view& text) = 0; 19 | 20 | /// 21 | /// Called when a chunk is read from console.log. 22 | /// 23 | /// The associated WorldState. 24 | /// True if console lines were parsed from this chunk. 25 | /// False if everything ended up going to OnConsoleLineUnparsed(). 26 | virtual void OnConsoleLogChunkParsed(IWorldState& world, bool consoleLinesParsed) = 0; 27 | }; 28 | 29 | class BaseConsoleLineListener : public IConsoleLineListener 30 | { 31 | public: 32 | void OnConsoleLineParsed(IWorldState& world, IConsoleLine& line) override {} 33 | void OnConsoleLineUnparsed(IWorldState& world, const std::string_view& text) override {} 34 | 35 | void OnConsoleLogChunkParsed(IWorldState& world, bool consoleLinesParsed) override {} 36 | }; 37 | 38 | class AutoConsoleLineListener : public BaseConsoleLineListener 39 | { 40 | public: 41 | AutoConsoleLineListener(IWorldState& world); 42 | AutoConsoleLineListener(const AutoConsoleLineListener& other); 43 | AutoConsoleLineListener& operator=(const AutoConsoleLineListener& other); 44 | AutoConsoleLineListener(AutoConsoleLineListener&& other); 45 | AutoConsoleLineListener& operator=(AutoConsoleLineListener&& other); 46 | ~AutoConsoleLineListener(); 47 | 48 | private: 49 | IWorldState* m_World = nullptr; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /tf2_bot_detector/BaseTextures.cpp: -------------------------------------------------------------------------------- 1 | #include "BaseTextures.h" 2 | #include "Bitmap.h" 3 | #include "Filesystem.h" 4 | #include "Log.h" 5 | #include "TextureManager.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | using namespace std::string_literals; 13 | using namespace tf2_bot_detector; 14 | 15 | namespace 16 | { 17 | class BaseTextures final : public IBaseTextures 18 | { 19 | public: 20 | BaseTextures(ITextureManager& textureManager); 21 | 22 | const ITexture* GetHeart_16() const override { return m_Heart_16.get(); } 23 | const ITexture* GetVACShield_16() const override { return m_VACShield_16.get(); } 24 | const ITexture* GetGameBanIcon_16() const override { return m_GameBanIcon_16.get(); } 25 | 26 | private: 27 | ITextureManager& m_TextureManager; 28 | 29 | std::shared_ptr m_Heart_16; 30 | std::shared_ptr m_VACShield_16; 31 | std::shared_ptr m_GameBanIcon_16; 32 | 33 | std::shared_ptr TryLoadTexture(std::filesystem::path file) const; 34 | }; 35 | } 36 | 37 | std::unique_ptr IBaseTextures::Create(ITextureManager& textureManager) 38 | { 39 | return std::make_unique(textureManager); 40 | } 41 | 42 | BaseTextures::BaseTextures(ITextureManager& textureManager) : 43 | m_TextureManager(textureManager), 44 | 45 | m_Heart_16(TryLoadTexture("images/heart_16.png")), 46 | m_VACShield_16(TryLoadTexture("images/vac_icon_16.png")), 47 | m_GameBanIcon_16(TryLoadTexture("images/game_ban_icon_16.png")) 48 | { 49 | } 50 | 51 | std::shared_ptr BaseTextures::TryLoadTexture(std::filesystem::path file) const 52 | { 53 | file = IFilesystem::Get().ResolvePath(file, PathUsage::Read); 54 | 55 | try 56 | { 57 | return m_TextureManager.CreateTexture(Bitmap(file)); 58 | } 59 | catch (const std::exception& e) 60 | { 61 | LogException(MH_SOURCE_LOCATION_CURRENT(), e, "Failed to load {}", file); 62 | } 63 | 64 | return nullptr; 65 | } 66 | -------------------------------------------------------------------------------- /tf2_bot_detector/WorldEventListener.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | 5 | #include 6 | 7 | namespace tf2_bot_detector 8 | { 9 | class IPlayer; 10 | class IWorldState; 11 | enum class TFClassType; 12 | 13 | class IWorldEventListener 14 | { 15 | public: 16 | virtual ~IWorldEventListener() = default; 17 | 18 | virtual void OnTimestampUpdate(IWorldState& world) = 0; 19 | virtual void OnPlayerStatusUpdate(IWorldState& world, const IPlayer& player) = 0; 20 | virtual void OnChatMsg(IWorldState& world, IPlayer& player, const std::string_view& msg) = 0; 21 | virtual void OnLocalPlayerInitialized(IWorldState& world, bool initialized) = 0; 22 | virtual void OnLocalPlayerSpawned(IWorldState& world, TFClassType classType) = 0; 23 | virtual void OnPlayerDroppedFromServer(IWorldState& world, IPlayer& player, const std::string_view& reason) = 0; 24 | }; 25 | 26 | class BaseWorldEventListener : public IWorldEventListener 27 | { 28 | public: 29 | void OnTimestampUpdate(IWorldState& world) override {} 30 | void OnPlayerStatusUpdate(IWorldState& world, const IPlayer& player) override {} 31 | void OnChatMsg(IWorldState& world, IPlayer& player, const std::string_view& msg) override {} 32 | void OnLocalPlayerInitialized(IWorldState& world, bool initialized) override {} 33 | void OnLocalPlayerSpawned(IWorldState& world, TFClassType classType) override {} 34 | void OnPlayerDroppedFromServer(IWorldState& world, IPlayer& player, const std::string_view& reason) override {} 35 | }; 36 | 37 | class AutoWorldEventListener : public BaseWorldEventListener 38 | { 39 | public: 40 | AutoWorldEventListener(IWorldState& world); 41 | AutoWorldEventListener(const AutoWorldEventListener& other); 42 | AutoWorldEventListener& operator=(const AutoWorldEventListener& other); 43 | AutoWorldEventListener(AutoWorldEventListener&& other); 44 | AutoWorldEventListener& operator=(AutoWorldEventListener&& other); 45 | ~AutoWorldEventListener(); 46 | 47 | private: 48 | IWorldState* m_World = nullptr; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /staging/licenses/bzip2.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------- 3 | 4 | This program, "bzip2", the associated library "libbzip2", and all 5 | documentation, are copyright (C) 1996-2010 Julian R Seward. All 6 | rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions 10 | are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | 2. The origin of this software must not be misrepresented; you must 16 | not claim that you wrote the original software. If you use this 17 | software in a product, an acknowledgment in the product 18 | documentation would be appreciated but is not required. 19 | 20 | 3. Altered source versions must be plainly marked as such, and must 21 | not be misrepresented as being the original software. 22 | 23 | 4. The name of the author may not be used to endorse or promote 24 | products derived from this software without specific prior written 25 | permission. 26 | 27 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 28 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 31 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 33 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 35 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 36 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 37 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | 39 | Julian Seward, jseward@bzip.org 40 | bzip2/libbzip2 version 1.0.6 of 6 September 2010 41 | 42 | -------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | 3 | set(VCPKG_CRT_LINKAGE static) 4 | set(VCPKG_LIBRARY_LINKAGE static) 5 | 6 | include(../cmake/init-preproject.cmake) 7 | project(tf2_bot_detector_updater) 8 | include(../cmake/init-postproject.cmake) 9 | 10 | add_executable(tf2_bot_detector_updater 11 | "Platform/Platform.h" 12 | "Common.h" 13 | "main.cpp" 14 | "Update_Portable.cpp" 15 | "Update_Portable.h" 16 | ) 17 | 18 | target_include_directories(tf2_bot_detector_updater PRIVATE .) 19 | 20 | if (WIN32) 21 | configure_file(Resources.base.rc Resources.rc) 22 | target_sources(tf2_bot_detector_updater PRIVATE 23 | "Platform/Windows/Platform_Windows.cpp" 24 | "Resources.rc" 25 | "Update_MSIX.cpp" 26 | "Update_MSIX.h" 27 | ) 28 | 29 | add_subdirectory(../tf2_bot_detector_winrt "${PROJECT_BINARY_DIR}/tf2_bot_detector_winrt") 30 | INCLUDE_TF2BD_WINRT(tf2_bot_detector_updater) 31 | endif() 32 | 33 | target_compile_features(tf2_bot_detector_updater PUBLIC cxx_std_20) 34 | 35 | set_property(GLOBAL PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 36 | 37 | message("CMAKE_VS_PLATFORM_NAME = ${CMAKE_VS_PLATFORM_NAME}") 38 | message("CMAKE_VS_PLATFORM_NAME_DEFAULT = ${CMAKE_VS_PLATFORM_NAME_DEFAULT}") 39 | message("CMAKE_SYSTEM_PROCESSOR = ${CMAKE_SYSTEM_PROCESSOR}") 40 | message("CMAKE_GENERATOR_PLATFORM = ${CMAKE_GENERATOR_PLATFORM}") 41 | message("VCPKG_TARGET_TRIPLET = ${VCPKG_TARGET_TRIPLET}") 42 | message("MSVC_RUNTIME_LIBRARY = ${MSVC_RUNTIME_LIBRARY}") 43 | 44 | find_package(fmt CONFIG REQUIRED) 45 | add_subdirectory(../submodules/mh_stuff "${PROJECT_BINARY_DIR}/submodules/mh_stuff") 46 | add_subdirectory(../tf2_bot_detector_common "${PROJECT_BINARY_DIR}/tf2_bot_detector_common") 47 | 48 | find_package(OpenSSL REQUIRED) 49 | 50 | target_link_libraries(tf2_bot_detector_updater PUBLIC 51 | tf2_bot_detector::common 52 | mh::stuff 53 | fmt::fmt 54 | OpenSSL::SSL # cpp-httplib requires openssl 55 | ) 56 | 57 | find_path(HTTPLIB_PATH NAMES httplib.h) 58 | target_include_directories(tf2_bot_detector_updater PRIVATE ${HTTPLIB_PATH}) 59 | -------------------------------------------------------------------------------- /schemas/v3/playerlist.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "$id": "https://raw.githubusercontent.com/PazerOP/tf2_bot_detector/master/schemas/v3/playerlist.schema.json", 4 | "title": "TF2 Bot Detector Player List Schema", 5 | "type": "object", 6 | "additionalProperties": false, 7 | "properties": { 8 | "$schema": { 9 | "description": "The JSON schema to validate this file against.", 10 | "type": "string" 11 | }, 12 | "file_info": { 13 | "$ref": "./shared.schema.json#/definitions/file_info" 14 | }, 15 | "players": { 16 | "description": "Players in this list", 17 | "type": "array", 18 | "items": { 19 | "$ref": "#/definitions/tfbd_playerlist_entry" 20 | } 21 | } 22 | }, 23 | "required": [ 24 | "$schema", 25 | "players" 26 | ], 27 | "definitions": { 28 | "tfbd_playerlist_entry": { 29 | "type": "object", 30 | "additionalProperties": false, 31 | "description": "A player entry.", 32 | "properties": { 33 | "steamid": { 34 | "description": "The SteamID of the player.", 35 | "$ref": "./shared.schema.json#definitions/steamid" 36 | }, 37 | "attributes": { 38 | "description": "Attributes applied to the player", 39 | "$ref": "./shared.schema.json#/definitions/tfbd_player_attributes_array" 40 | }, 41 | "proof": { 42 | "description": "Any associated evidence. Left untouched by the tool (for now).", 43 | "type": "array" 44 | }, 45 | "last_seen": { 46 | "type": "object", 47 | "additionalProperties": false, 48 | "description": "Information about the last time this player was seen.", 49 | "properties": { 50 | "player_name": { 51 | "description": "The name this player had the last time they were seen.", 52 | "type": "string" 53 | }, 54 | "time": { 55 | "description": "The time this player was last seen.", 56 | "type": "integer" 57 | } 58 | }, 59 | "required": [ 60 | "time" 61 | ] 62 | } 63 | }, 64 | "required": [ 65 | "steamid", 66 | "attributes" 67 | ] 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/HTTPHelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace tf2_bot_detector 13 | { 14 | class URL final 15 | { 16 | public: 17 | URL() = default; 18 | URL(std::nullptr_t) {} 19 | URL(const char* str) : URL(std::string_view(str)) {} 20 | URL(const std::string_view& url); 21 | URL(const std::string& str) : URL(std::string_view(str)) {} 22 | 23 | auto operator<=>(const URL&) const = default; 24 | 25 | std::string ToString() const; 26 | std::string GetSchemeHostPort() const; 27 | 28 | std::string m_Scheme; 29 | std::string m_Host; 30 | unsigned int m_Port = 80; 31 | std::string m_Path; 32 | }; 33 | 34 | enum class HTTPResponseCode 35 | { 36 | Continue = 100, 37 | SwitchingProtocol = 101, 38 | Processing = 102, 39 | EarlyHints = 103, 40 | 41 | OK = 200, 42 | Created = 201, 43 | Accepted = 202, 44 | 45 | MultipleChoice = 300, 46 | MovedPermanently = 301, 47 | MovedTemporarily = 302, 48 | SeeOther = 303, 49 | NotModified = 304, 50 | TemporaryRedirect = 307, 51 | PermanentRedirect = 308, 52 | 53 | BadRequest = 400, 54 | Unauthorized = 401, 55 | PaymentRequired = 402, 56 | Forbidden = 403, 57 | NotFound = 404, 58 | TooManyRequests = 429, 59 | 60 | InternalServerError = 500, 61 | NotImplemented = 501, 62 | BadGateway = 502, 63 | ServiceUnavailable = 503, 64 | GatewayTimeout = 504, 65 | }; 66 | 67 | std::error_condition make_error_condition(HTTPResponseCode e); 68 | 69 | class http_error : public mh::error_condition_exception 70 | { 71 | using super = mh::error_condition_exception; 72 | public: 73 | using super::super; 74 | }; 75 | 76 | template 77 | std::basic_ostream& operator<<(std::basic_ostream& os, const tf2_bot_detector::URL& url) 78 | { 79 | return os << url.m_Scheme << url.m_Host << ':' << url.m_Port << url.m_Path; 80 | } 81 | } 82 | 83 | namespace std 84 | { 85 | template<> struct is_error_condition_enum : true_type {}; 86 | } 87 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/ISetupFlowPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #undef DrawState 4 | 5 | namespace tf2_bot_detector 6 | { 7 | class IActionManager; 8 | class IUpdateManager; 9 | class Settings; 10 | 11 | enum class SetupFlowPage 12 | { 13 | Invalid = -1, 14 | 15 | PermissionsCheck = 0, 16 | NetworkSettings, 17 | UpdateCheck, 18 | CheckFaceitClosed, 19 | CheckSteamOpen, 20 | BasicSettings, 21 | AddonManager, 22 | ChatWrappersGenerate, 23 | TF2CommandLine, 24 | ChatWrappersVerify, 25 | }; 26 | 27 | class ISetupFlowPage 28 | { 29 | public: 30 | virtual ~ISetupFlowPage() = default; 31 | 32 | enum class ValidateSettingsResult 33 | { 34 | // Everything is A-OK. No reason to open this page for user interaction. 35 | Success, 36 | 37 | // Either something's wrong, or we need to run some blocking logic. 38 | // Open this page. 39 | TriggerOpen, 40 | }; 41 | 42 | [[nodiscard]] virtual ValidateSettingsResult ValidateSettings(const Settings& settings) const = 0; 43 | 44 | enum class OnDrawResult 45 | { 46 | // Draw again next frame unless the user clicks "Done"/"Next" 47 | ContinueDrawing, 48 | 49 | // Pretend the user pressed "Done"/"Next" 50 | EndDrawing, 51 | }; 52 | 53 | struct DrawState 54 | { 55 | IActionManager* m_ActionManager = nullptr; 56 | IUpdateManager* m_UpdateManager = nullptr; 57 | Settings* m_Settings = nullptr; 58 | }; 59 | [[nodiscard]] virtual OnDrawResult OnDraw(const DrawState& ds) = 0; 60 | 61 | struct InitState 62 | { 63 | explicit InitState(const Settings& settings) : m_Settings(settings) {} 64 | 65 | const Settings& m_Settings; 66 | IUpdateManager* m_UpdateManager = nullptr; 67 | }; 68 | virtual void Init(const InitState& is) = 0; 69 | virtual bool CanCommit() const = 0; 70 | 71 | struct CommitState 72 | { 73 | explicit CommitState(Settings& settings) : m_Settings(settings) {} 74 | 75 | Settings& m_Settings; 76 | IUpdateManager* m_UpdateManager = nullptr; 77 | }; 78 | virtual void Commit(const CommitState& cs) = 0; 79 | virtual bool WantsSetupText() const { return true; } 80 | virtual bool WantsContinueButton() const { return true; } 81 | 82 | virtual SetupFlowPage GetPage() const = 0; 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/CheckSteamOpenPage.cpp: -------------------------------------------------------------------------------- 1 | #include "ISetupFlowPage.h" 2 | #include "UI/ImGui_TF2BotDetector.h" 3 | #include "Platform/Platform.h" 4 | 5 | #undef DrawState 6 | 7 | using namespace tf2_bot_detector; 8 | 9 | namespace 10 | { 11 | class CheckSteamOpenPage final : public ISetupFlowPage 12 | { 13 | public: 14 | ValidateSettingsResult ValidateSettings(const Settings& settings) const override; 15 | 16 | OnDrawResult OnDraw(const DrawState& ds) override; 17 | 18 | void Init(const InitState& is) override; 19 | bool CanCommit() const override; 20 | void Commit(const CommitState& cs) override; 21 | 22 | bool WantsSetupText() const override { return false; } 23 | bool WantsContinueButton() const override { return m_CanContinue; } 24 | 25 | SetupFlowPage GetPage() const override { return SetupFlowPage::CheckSteamOpen; } 26 | 27 | private: 28 | bool m_CanContinue = false; 29 | }; 30 | 31 | auto CheckSteamOpenPage::ValidateSettings(const Settings& settings) const -> ValidateSettingsResult 32 | { 33 | if (!Platform::Processes::IsSteamRunning()) 34 | return ValidateSettingsResult::TriggerOpen; 35 | 36 | return ValidateSettingsResult::Success; 37 | } 38 | 39 | auto CheckSteamOpenPage::OnDraw(const DrawState& ds) -> OnDrawResult 40 | { 41 | ImGui::Text("Steam must be open to use TF2 Bot Detector."); 42 | 43 | const bool isSteamRunning = Platform::Processes::IsSteamRunning(); 44 | m_CanContinue = isSteamRunning; 45 | if (isSteamRunning) 46 | { 47 | m_CanContinue = true; 48 | 49 | if (Platform::GetCurrentActiveSteamID().IsValid()) 50 | { 51 | return OnDrawResult::EndDrawing; 52 | } 53 | else 54 | { 55 | ImGui::NewLine(); 56 | ImGui::Text("Steam is open, but it might not be logged into an account yet."); 57 | } 58 | } 59 | 60 | return OnDrawResult::ContinueDrawing; 61 | } 62 | void CheckSteamOpenPage::Init(const InitState& is) 63 | { 64 | } 65 | bool CheckSteamOpenPage::CanCommit() const 66 | { 67 | return m_CanContinue; 68 | } 69 | void CheckSteamOpenPage::Commit(const CommitState& cs) 70 | { 71 | } 72 | } 73 | 74 | namespace tf2_bot_detector 75 | { 76 | std::unique_ptr CreateCheckSteamOpenPage() 77 | { 78 | return std::make_unique(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tf2_bot_detector/ModeratorLogic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | enum class KickReason; 12 | enum class LobbyMemberTeam : uint8_t; 13 | enum class PlayerAttribute; 14 | enum class TeamShareResult; 15 | class IPlayer; 16 | struct ModerationRule; 17 | struct PlayerAttributesList; 18 | struct PlayerMarks; 19 | class IRCONActionManager; 20 | class Settings; 21 | class SteamID; 22 | class IWorldState; 23 | 24 | struct VoteCooldown 25 | { 26 | duration_t m_Elapsed; 27 | duration_t m_Total; 28 | 29 | duration_t GetRemainingDuration() const; 30 | float GetProgress() const; 31 | }; 32 | 33 | enum class AttributePersistence 34 | { 35 | Saved = (1 << 0), 36 | Transient = (1 << 1), 37 | 38 | Any = Saved | Transient, 39 | }; 40 | 41 | class IModeratorLogic 42 | { 43 | public: 44 | virtual ~IModeratorLogic() = default; 45 | 46 | static std::unique_ptr Create(IWorldState& world, const Settings& settings, IRCONActionManager& actionManager); 47 | 48 | virtual void Update() = 0; 49 | 50 | virtual bool InitiateVotekick(const IPlayer& player, KickReason reason, const PlayerMarks* marks = nullptr) = 0; 51 | 52 | virtual PlayerMarks GetPlayerAttributes(const SteamID& id) const = 0; 53 | virtual PlayerMarks HasPlayerAttributes(const SteamID& id, const PlayerAttributesList& attributes, 54 | AttributePersistence persistence = AttributePersistence::Any) const = 0; 55 | virtual bool SetPlayerAttribute(const IPlayer& player, PlayerAttribute markType, AttributePersistence persistence, bool set = true) = 0; 56 | 57 | virtual TeamShareResult GetTeamShareResult(const SteamID& id) const = 0; 58 | 59 | virtual const IPlayer* GetBotLeader() const = 0; 60 | 61 | virtual bool IsUserRunningTool(const SteamID& id) const = 0; 62 | virtual void SetUserRunningTool(const SteamID& id, bool isUserRunningTool = true) = 0; 63 | 64 | virtual size_t GetBlacklistedPlayerCount() const = 0; 65 | virtual size_t GetRuleCount() const = 0; 66 | 67 | virtual void ReloadConfigFiles() = 0; 68 | }; 69 | } 70 | 71 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::AttributePersistence) 72 | MH_ENUM_REFLECT_VALUE(Saved) 73 | MH_ENUM_REFLECT_VALUE(Transient) 74 | MH_ENUM_REFLECT_VALUE(Any) 75 | MH_ENUM_REFLECT_END() 76 | -------------------------------------------------------------------------------- /tf2_bot_detector/Config/SponsorsList.cpp: -------------------------------------------------------------------------------- 1 | #include "SponsorsList.h" 2 | #include "Util/JSONUtils.h" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std::string_literals; 8 | using namespace tf2_bot_detector; 9 | 10 | SponsorsList::SponsorsList(const Settings& settings) : 11 | m_Settings(&settings) 12 | { 13 | LoadFile(); 14 | } 15 | 16 | void SponsorsList::LoadFile() 17 | { 18 | m_Sponsors = LoadConfigFileAsync("cfg/sponsors.json", true, *m_Settings); 19 | } 20 | 21 | auto SponsorsList::GetSponsors() const -> std::vector 22 | { 23 | auto result = m_Sponsors.try_get(); 24 | return result ? result->m_Sponsors : std::vector{}; 25 | } 26 | 27 | void SponsorsList::SponsorsListFile::Deserialize(const nlohmann::json& json) 28 | { 29 | BaseClass::Deserialize(json); 30 | 31 | if (auto found = json.find("sponsors"); found != json.end()) 32 | m_Sponsors = found->get>(); 33 | } 34 | 35 | void SponsorsList::SponsorsListFile::Serialize(nlohmann::json& json) const 36 | { 37 | BaseClass::Serialize(json); 38 | 39 | if (!m_Schema || m_Schema->m_Version != SPONSORS_SCHEMA_VERSION) 40 | json["$schema"] = ConfigSchemaInfo("sponsors", SPONSORS_SCHEMA_VERSION); 41 | 42 | json["sponsors"] = m_Sponsors; 43 | } 44 | 45 | void SponsorsList::SponsorsListFile::ValidateSchema(const ConfigSchemaInfo& schema) const 46 | { 47 | BaseClass::ValidateSchema(schema); 48 | 49 | if (schema.m_Type != "sponsors") 50 | throw std::runtime_error("Schema "s << std::quoted(schema.m_Type) << " is not a sponsors list"); 51 | if (schema.m_Version != SPONSORS_SCHEMA_VERSION) 52 | throw std::runtime_error("Sponsors schema must be version "s << SPONSORS_SCHEMA_VERSION << ", but was " << schema.m_Version); 53 | } 54 | 55 | void tf2_bot_detector::to_json(nlohmann::json& j, const SponsorsList::Sponsor& d) 56 | { 57 | j = 58 | { 59 | { "name", d.m_Name }, 60 | { "message", d.m_Message }, 61 | }; 62 | } 63 | 64 | void tf2_bot_detector::from_json(const nlohmann::json& j, SponsorsList::Sponsor& d) 65 | { 66 | using lval_str = std::add_lvalue_reference_t; 67 | static_assert(std::is_same_v); 68 | static_assert(std::is_invocable_v), std::string&>); 69 | 70 | try_get_to_defaulted(j, d.m_Name, "name"); 71 | try_get_to_defaulted(j, d.m_Message, "message"); 72 | } 73 | -------------------------------------------------------------------------------- /msix/package_staging/AppxManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | TF2 Bot DetectorTF2BD_DISPLAYNAME_SUFFIX_REPLACE_ME 9 | pazer 10 | Automatically detects and votekicks cheaters/bots in TF2 casual. 11 | logo.png 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | logo.png 31 | TF2 Bot Detector URI Handler 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /tf2_bot_detector/Filesystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace tf2_bot_detector 14 | { 15 | enum class PathUsage 16 | { 17 | Read, 18 | 19 | WriteRoaming, 20 | WriteLocal, 21 | Write [[deprecated]] = WriteRoaming, 22 | }; 23 | 24 | class IFilesystem 25 | { 26 | public: 27 | virtual ~IFilesystem() = default; 28 | 29 | static IFilesystem& Get(); 30 | 31 | virtual void Init() = 0; 32 | 33 | virtual mh::generator GetSearchPaths() const = 0; 34 | 35 | virtual std::filesystem::path ResolvePath(const std::filesystem::path& path, PathUsage usage) const = 0; 36 | 37 | virtual std::filesystem::path GetLocalAppDataDir() const = 0; 38 | virtual std::filesystem::path GetRoamingAppDataDir() const = 0; 39 | virtual std::filesystem::path GetTempDir() const = 0; 40 | 41 | //virtual std::fstream OpenFile(const std::filesystem::path& path) = 0; 42 | virtual std::string ReadFile(std::filesystem::path path) const = 0; 43 | virtual void WriteFile(std::filesystem::path path, const void* begin, const void* end, PathUsage usage) const = 0; 44 | 45 | virtual mh::generator IterateDir(std::filesystem::path path, bool recursive, 46 | std::filesystem::directory_options options = std::filesystem::directory_options::none) const = 0; 47 | 48 | void WriteFile(const std::filesystem::path& path, const std::string_view& data, PathUsage usage) const 49 | { 50 | return WriteFile(path, data.data(), data.data() + data.size(), usage); 51 | } 52 | 53 | static std::filesystem::path GetLogsDir(const std::filesystem::path& baseDataDir) 54 | { 55 | return baseDataDir / "logs"; 56 | } 57 | std::filesystem::path GetLogsDir() const { return GetLogsDir(GetLocalAppDataDir()); } 58 | 59 | static std::filesystem::path GetConfigDir(const std::filesystem::path& baseDataDir) 60 | { 61 | return baseDataDir / "cfg"; 62 | } 63 | std::filesystem::path GetConfigDir() const { return GetConfigDir(GetRoamingAppDataDir()); } 64 | 65 | bool Exists(const std::filesystem::path& path) const 66 | { 67 | return !ResolvePath(path, PathUsage::Read).empty(); 68 | } 69 | }; 70 | } 71 | 72 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::PathUsage) 73 | MH_ENUM_REFLECT_VALUE(Read) 74 | MH_ENUM_REFLECT_VALUE(WriteRoaming) 75 | MH_ENUM_REFLECT_VALUE(WriteLocal) 76 | MH_ENUM_REFLECT_END() 77 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\out\\build_tf2_bot_detector\\${name}", 9 | "installRoot": "${projectDir}\\out\\install_tf2_bot_detector\\${name}", 10 | "cmakeCommandArgs": "", 11 | "buildCommandArgs": "", 12 | "ctestCommandArgs": "", 13 | "variables": [ 14 | { 15 | "name": "TF2BD_ENABLE_TESTS", 16 | "value": "True", 17 | "type": "BOOL" 18 | } 19 | ], 20 | "cmakeToolchain": "submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 21 | }, 22 | { 23 | "name": "x64-Release", 24 | "generator": "Ninja", 25 | "configurationType": "Release", 26 | "buildRoot": "${projectDir}\\out\\build_tf2_bot_detector\\${name}", 27 | "installRoot": "${projectDir}\\out\\install_tf2_bot_detector\\${name}", 28 | "cmakeCommandArgs": "", 29 | "buildCommandArgs": "", 30 | "ctestCommandArgs": "", 31 | "inheritEnvironments": [ "msvc_x64_x64" ], 32 | "variables": [ 33 | { 34 | "name": "TF2BD_ENABLE_TESTS", 35 | "value": "True", 36 | "type": "BOOL" 37 | } 38 | ], 39 | "cmakeToolchain": "submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 40 | }, 41 | { 42 | "name": "x86-Release", 43 | "generator": "Ninja", 44 | "configurationType": "Release", 45 | "buildRoot": "${projectDir}\\out\\build_tf2_bot_detector\\${name}", 46 | "installRoot": "${projectDir}\\out\\install_tf2_bot_detector\\${name}", 47 | "cmakeCommandArgs": "", 48 | "buildCommandArgs": "", 49 | "ctestCommandArgs": "", 50 | "inheritEnvironments": [ "msvc_x86_x64" ], 51 | "variables": [ 52 | { 53 | "name": "TF2BD_ENABLE_TESTS", 54 | "value": "True", 55 | "type": "BOOL" 56 | } 57 | ], 58 | "cmakeToolchain": "submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 59 | }, 60 | { 61 | "name": "x86-Debug", 62 | "generator": "Ninja", 63 | "configurationType": "Debug", 64 | "buildRoot": "${projectDir}\\out\\build_tf2_bot_detector\\${name}", 65 | "installRoot": "${projectDir}\\out\\install_tf2_bot_detector\\${name}", 66 | "cmakeCommandArgs": "", 67 | "buildCommandArgs": "", 68 | "ctestCommandArgs": "", 69 | "inheritEnvironments": [ "msvc_x86_x64" ], 70 | "variables": [ 71 | { 72 | "name": "TF2BD_ENABLE_TESTS", 73 | "value": "True", 74 | "type": "BOOL" 75 | } 76 | ], 77 | "cmakeToolchain": "submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 78 | } 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /tf2_bot_detector_winrt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.17) 2 | 3 | include(../cmake/init-preproject.cmake) 4 | project(tf2_bot_detector_winrt) 5 | include(../cmake/init-postproject.cmake) 6 | 7 | include(GenerateExportHeader) 8 | 9 | if (VCPKG_LIBRARY_LINKAGE MATCHES static) 10 | set(TF2BD_WINRT_LIBRARY_LINKAGE STATIC) 11 | else() 12 | set(TF2BD_WINRT_LIBRARY_LINKAGE MODULE) 13 | endif() 14 | 15 | message("TF2BD_WINRT_LIBRARY_LINKAGE = ${TF2BD_WINRT_LIBRARY_LINKAGE}") 16 | add_library(tf2_bot_detector_winrt ${TF2BD_WINRT_LIBRARY_LINKAGE} 17 | "tf2_bot_detector_winrt.cpp" 18 | "tf2_bot_detector_winrt.h" 19 | ) 20 | 21 | generate_export_header(tf2_bot_detector_winrt 22 | EXPORT_FILE_NAME "include/tf2_bot_detector_winrt_export.h" 23 | ) 24 | target_include_directories(tf2_bot_detector_winrt PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/include/") 25 | 26 | execute_process( 27 | COMMAND_ECHO STDOUT 28 | RESULT_VARIABLE EXECUTE_PROCESS_RESULT 29 | COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/setup_winrt.bat 30 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 31 | ) 32 | 33 | if (NOT EXECUTE_PROCESS_RESULT EQUAL 0) 34 | message(FATAL_ERROR "Failed to run setup_winrt.bat (exit code ${EXECUTE_PROCESS_RESULT})") 35 | endif() 36 | 37 | file(READ "${CMAKE_CURRENT_BINARY_DIR}/nuget/winrt_include_dir.txt" WINRT_INCLUDE_DIR) 38 | string(STRIP "${WINRT_INCLUDE_DIR}" WINRT_INCLUDE_DIR) 39 | target_include_directories(tf2_bot_detector_winrt SYSTEM BEFORE PUBLIC ${WINRT_INCLUDE_DIR}) 40 | 41 | target_include_directories(tf2_bot_detector_winrt PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 42 | 43 | find_package(fmt CONFIG REQUIRED) 44 | 45 | target_link_libraries(tf2_bot_detector_winrt PUBLIC 46 | tf2_bot_detector::common 47 | mh::stuff 48 | fmt::fmt 49 | ) 50 | 51 | function(Include_TF2BD_WinRT _target) 52 | 53 | # Add include directories 54 | get_target_property(TF2BD_WINRT_INCLUDE_DIRECTORIES tf2_bot_detector_winrt INCLUDE_DIRECTORIES) 55 | target_include_directories(${_target} PUBLIC ${TF2BD_WINRT_INCLUDE_DIRECTORIES}) 56 | 57 | # Copy DLL 58 | add_custom_command( 59 | TARGET ${_target} POST_BUILD 60 | COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ 61 | ) 62 | 63 | get_target_property(TF2BD_WINRT_TARGET_TYPE tf2_bot_detector_winrt TYPE) 64 | if (NOT TF2BD_WINRT_TARGET_TYPE STREQUAL "STATIC_LIBRARY") 65 | # Copy PDB 66 | add_custom_command( 67 | TARGET ${_target} POST_BUILD 68 | COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ 69 | ) 70 | endif() 71 | 72 | endfunction() 73 | -------------------------------------------------------------------------------- /tf2_bot_detector/GameData/MatchmakingQueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace tf2_bot_detector 6 | { 7 | enum class TFMatchGroup 8 | { 9 | Invalid = -1, 10 | 11 | MVMBootCamp = 0, 12 | MVMMannUp = 1, 13 | 14 | Competitive6s = 2, 15 | Competitive9s = 3, 16 | Competitive12s = 4, 17 | 18 | Casual6s = 5, 19 | Casual9s = 6, 20 | Casual12s = 7, 21 | 22 | COUNT = 8, 23 | }; 24 | 25 | enum class TFMatchGroupFlags : uint32_t; 26 | inline constexpr TFMatchGroupFlags AsFlag(TFMatchGroup group) 27 | { 28 | return TFMatchGroupFlags(1 << uint32_t(group)); 29 | } 30 | 31 | #undef TF_MATCH_GROUP_FLAG 32 | #define TF_MATCH_GROUP_FLAG(name) name = uint32_t(AsFlag(TFMatchGroup::name)) 33 | enum class TFMatchGroupFlags : uint32_t 34 | { 35 | None = 0, 36 | 37 | TF_MATCH_GROUP_FLAG(MVMBootCamp), 38 | TF_MATCH_GROUP_FLAG(MVMMannUp), 39 | MVM = MVMBootCamp | MVMMannUp, 40 | 41 | TF_MATCH_GROUP_FLAG(Competitive6s), 42 | TF_MATCH_GROUP_FLAG(Competitive9s), 43 | TF_MATCH_GROUP_FLAG(Competitive12s), 44 | Competitive = Competitive6s | Competitive9s | Competitive12s, 45 | 46 | TF_MATCH_GROUP_FLAG(Casual6s), 47 | TF_MATCH_GROUP_FLAG(Casual9s), 48 | TF_MATCH_GROUP_FLAG(Casual12s), 49 | Casual = Casual6s | Casual9s | Casual12s, 50 | }; 51 | #undef TF_MATCH_GROUP_FLAG 52 | 53 | enum class TFQueueStateChange 54 | { 55 | None = 0, 56 | 57 | Entered, 58 | RequestedEnter, 59 | Exited, 60 | RequestedExit, 61 | }; 62 | 63 | inline constexpr TFMatchGroupFlags operator|(TFMatchGroupFlags lhs, TFMatchGroupFlags rhs) 64 | { 65 | using ut = std::underlying_type_t; 66 | return TFMatchGroupFlags(ut(lhs) | ut(rhs)); 67 | } 68 | inline constexpr TFMatchGroupFlags operator&(TFMatchGroupFlags lhs, TFMatchGroupFlags rhs) 69 | { 70 | using ut = std::underlying_type_t; 71 | return TFMatchGroupFlags(ut(lhs) & ut(rhs)); 72 | } 73 | inline constexpr TFMatchGroupFlags operator&(TFMatchGroupFlags lhs, TFMatchGroup rhs) 74 | { 75 | return lhs & AsFlag(rhs); 76 | } 77 | inline constexpr TFMatchGroupFlags operator~(TFMatchGroupFlags x) 78 | { 79 | using ut = std::underlying_type_t; 80 | return TFMatchGroupFlags(~ut(x)); 81 | } 82 | inline constexpr bool operator!(TFMatchGroupFlags x) 83 | { 84 | return x == TFMatchGroupFlags::None; 85 | } 86 | 87 | inline constexpr TFMatchGroupFlags& operator|=(TFMatchGroupFlags& lhs, TFMatchGroupFlags rhs) 88 | { 89 | return lhs = lhs | rhs; 90 | } 91 | inline constexpr TFMatchGroupFlags& operator&=(TFMatchGroupFlags& lhs, TFMatchGroupFlags rhs) 92 | { 93 | return lhs = lhs & rhs; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tf2_bot_detector/BatchedAction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | #include "Log.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | template 12 | class BatchedAction 13 | { 14 | public: 15 | using state_type = TState; 16 | using queue_collection_type = std::unordered_set; 17 | using response_type = TResponse; 18 | using response_future_type = mh::task; 19 | 20 | BatchedAction() = default; 21 | BatchedAction(const TState& state) : m_State(state) {} 22 | BatchedAction(TState&& state) : m_State(std::move(state)) {} 23 | 24 | bool IsQueued(const TItem& item) const 25 | { 26 | std::lock_guard lock(m_Mutex); 27 | return m_Queued.contains(item); 28 | } 29 | 30 | void Queue(TItem&& item) 31 | { 32 | std::lock_guard lock(m_Mutex); 33 | m_Queued.insert(std::move(item)); 34 | } 35 | void Queue(const TItem& item) 36 | { 37 | std::lock_guard lock(m_Mutex); 38 | m_Queued.insert(item); 39 | } 40 | 41 | void Update() 42 | { 43 | std::invoke([&] 44 | { 45 | if (m_Queued.empty()) 46 | return; 47 | 48 | if (m_ResponseFuture.valid()) 49 | return; 50 | 51 | const auto curTime = clock_t::now(); 52 | if (curTime < (m_LastUpdate + MIN_INTERVAL)) 53 | return; 54 | 55 | m_LastUpdate = curTime; 56 | 57 | m_ResponseFuture = SendRequest(m_State, m_Queued); 58 | }); 59 | 60 | if (m_ResponseFuture.is_ready()) 61 | { 62 | std::lock_guard lock(m_Mutex); 63 | try 64 | { 65 | const auto& response = m_ResponseFuture.get(); 66 | 67 | try 68 | { 69 | OnDataReady(m_State, response, m_Queued); 70 | } 71 | catch (const std::exception& e) 72 | { 73 | LogException(MH_SOURCE_LOCATION_CURRENT(), e, "Failed to process batched action"); 74 | } 75 | } 76 | catch (const std::exception& e) 77 | { 78 | LogException(MH_SOURCE_LOCATION_CURRENT(), e, "Failed to get batched action future"); 79 | } 80 | 81 | m_ResponseFuture = {}; 82 | } 83 | } 84 | 85 | protected: 86 | virtual response_future_type SendRequest(state_type& state, queue_collection_type& collection) = 0; 87 | virtual void OnDataReady(state_type& state, const response_type& response, queue_collection_type& collection) = 0; 88 | 89 | private: 90 | static constexpr duration_t MIN_INTERVAL = std::chrono::seconds(5); 91 | state_type m_State{}; 92 | std::recursive_mutex m_Mutex; 93 | queue_collection_type m_Queued; 94 | response_future_type m_ResponseFuture; 95 | time_point_t m_LastUpdate{}; 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /tf2_bot_detector/Platform/Windows/CrashHandler.cpp: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #pragma comment(lib, "DbgHelp.lib") 10 | #include 11 | 12 | namespace 13 | { 14 | static LONG WINAPI ExceptionFilter(_EXCEPTION_POINTERS* exception); 15 | struct Init final 16 | { 17 | Init() 18 | { 19 | m_IsInitialized = SymInitialize(GetCurrentProcess(), nullptr, TRUE); 20 | m_OldFilter = SetUnhandledExceptionFilter(&ExceptionFilter); 21 | } 22 | ~Init() 23 | { 24 | SetUnhandledExceptionFilter(m_OldFilter); 25 | } 26 | 27 | bool IsInitialized() const { return m_IsInitialized; } 28 | auto& GetMutex() { return m_Mutex; } 29 | 30 | private: 31 | std::recursive_mutex m_Mutex; 32 | bool m_IsInitialized = false; 33 | LPTOP_LEVEL_EXCEPTION_FILTER m_OldFilter = nullptr; 34 | 35 | } static s_DbgHelp; 36 | 37 | static void ExceptionHandlerThreadFunc(DWORD threadID, _EXCEPTION_POINTERS* exception) 38 | { 39 | std::lock_guard lock(s_DbgHelp.GetMutex()); 40 | 41 | wchar_t filename[MAX_PATH] = L"tfbd_crash.mdmp"; 42 | { 43 | auto ts = std::time(nullptr); 44 | tm localTS{}; 45 | localtime_s(&localTS, &ts); 46 | wcsftime(filename, std::size(filename), L"tfbd_crash_%F-%H-%M-%S.mdmp", &localTS); 47 | } 48 | 49 | HANDLE file = CreateFileW( 50 | filename, 51 | GENERIC_WRITE, // write perms 52 | 0, // don't share 53 | nullptr, // security attributes 54 | CREATE_ALWAYS, // overwrite 55 | FILE_ATTRIBUTE_NORMAL, // normal attributes 56 | nullptr // no template file 57 | ); 58 | 59 | if (file != INVALID_HANDLE_VALUE) 60 | { 61 | MINIDUMP_EXCEPTION_INFORMATION info{}; 62 | info.ThreadId = threadID; 63 | info.ExceptionPointers = exception; 64 | 65 | MINIDUMP_USER_STREAM_INFORMATION userStreams{}; 66 | 67 | MiniDumpWriteDump( 68 | GetCurrentProcess(), 69 | GetProcessId(GetCurrentProcess()), 70 | file, 71 | MiniDumpNormal, 72 | &info, 73 | &userStreams, 74 | nullptr); 75 | 76 | CloseHandle(file); 77 | } 78 | } 79 | 80 | static std::thread s_ExceptionHandlerThread; 81 | static LONG WINAPI ExceptionFilter(_EXCEPTION_POINTERS* exception) 82 | { 83 | // Handle the minidump writing on another thread. This improves reliability 84 | // if we died because of a stack overflow or something. 85 | s_ExceptionHandlerThread = std::thread(&ExceptionHandlerThreadFunc, GetThreadId(GetCurrentThread()), exception); 86 | s_ExceptionHandlerThread.join(); 87 | return EXCEPTION_CONTINUE_SEARCH; 88 | } 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/Update_MSIX.cpp: -------------------------------------------------------------------------------- 1 | #include "Update_MSIX.h" 2 | #include "Common.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #undef max 12 | #undef min 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #pragma comment(lib, "windowsapp") 20 | 21 | using namespace tf2_bot_detector::Updater; 22 | using namespace winrt::Windows::ApplicationModel; 23 | using namespace winrt::Windows::Foundation; 24 | using namespace winrt::Windows::Foundation::Collections; 25 | using namespace winrt::Windows::Management::Deployment; 26 | 27 | int tf2_bot_detector::Updater::Update_MSIX() try 28 | { 29 | std::cerr << "Attempting to install via API..." << std::endl; 30 | 31 | const auto mainBundleURL = mh::change_encoding(s_CmdLineArgs.m_SourcePath); 32 | 33 | std::wcerr << "Main bundle URL: " << mainBundleURL << std::endl; 34 | const Uri uri = Uri(winrt::param::hstring(mainBundleURL)); 35 | 36 | IVector deps{ winrt::single_threaded_vector() }; 37 | 38 | unsigned bits = 86; 39 | { 40 | SYSTEM_INFO sysInfo; 41 | GetNativeSystemInfo(&sysInfo); 42 | if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) 43 | bits = 64; 44 | } 45 | 46 | const auto depURL = mh::format(L"https://tf2bd-util.pazer.us/AppInstaller/vcredist.x{}.msix", bits); 47 | std::wcerr << "Dependency URL: " << depURL << std::endl; 48 | deps.Append(Uri(depURL)); 49 | 50 | PackageManager mgr; 51 | 52 | std::cerr << "Starting async update..." << std::endl; 53 | auto task = mgr.AddPackageAsync(uri, deps, DeploymentOptions::ForceApplicationShutdown); 54 | 55 | std::cerr << "Waiting for results..." << std::endl; 56 | auto waitStatus = task.wait_for(TimeSpan::max()); 57 | if (waitStatus != AsyncStatus::Completed) 58 | { 59 | std::wcerr << "Error encountered during async update: " 60 | << task.ErrorCode() << ": " << task.get().ErrorText().c_str(); 61 | return 2; 62 | } 63 | else if (waitStatus == AsyncStatus::Completed) 64 | { 65 | std::cerr << "Update complete" << std::endl; 66 | } 67 | else 68 | { 69 | std::cerr << "Unknown update result" << std::endl; 70 | } 71 | 72 | std::cerr << "Reopening tool..." << std::endl; 73 | ShellExecuteA(nullptr, "open", "tf2bd:", nullptr, nullptr, SW_SHOWNORMAL); 74 | 75 | return 0; 76 | } 77 | catch (const std::exception& e) 78 | { 79 | std::cerr << mh::format("Unhandled exception ({}) in {}: {}", 80 | typeid(e).name(), __FUNCTION__, e.what()) << std::endl; 81 | return 2; 82 | } 83 | -------------------------------------------------------------------------------- /tf2_bot_detector/Platform/Windows/Steam.cpp: -------------------------------------------------------------------------------- 1 | #include "../Platform.h" 2 | #include "Util/TextUtils.h" 3 | #include "Log.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace std::string_literals; 10 | using namespace tf2_bot_detector; 11 | 12 | static constexpr char STEAM_ACTIVE_PROCESS_KEY[] = "Software\\Valve\\Steam\\ActiveProcess"; 13 | 14 | std::filesystem::path tf2_bot_detector::Platform::GetCurrentSteamDir() 15 | { 16 | char steamDLLstr[256]; 17 | DWORD dataSize = sizeof(steamDLLstr); 18 | DWORD type; 19 | auto result = std::error_code(RegGetValueA( 20 | HKEY_CURRENT_USER, 21 | STEAM_ACTIVE_PROCESS_KEY, "SteamClientDll", 22 | RRF_RT_REG_SZ, &type, steamDLLstr, &dataSize), std::system_category()); 23 | 24 | if (result) 25 | { 26 | LogError(MH_SOURCE_LOCATION_CURRENT(), "Failed to retrieve {}\\SteamClientDll: error {}", 27 | STEAM_ACTIVE_PROCESS_KEY, result); 28 | return {}; 29 | } 30 | 31 | std::filesystem::path steamDir(steamDLLstr); 32 | steamDir.remove_filename(); 33 | return steamDir; 34 | } 35 | 36 | SteamID tf2_bot_detector::Platform::GetCurrentActiveSteamID() 37 | { 38 | DWORD type; 39 | DWORD data; 40 | DWORD dataSize = sizeof(data); 41 | auto result = std::error_code(RegGetValueA( 42 | HKEY_CURRENT_USER, 43 | STEAM_ACTIVE_PROCESS_KEY, "ActiveUser", 44 | RRF_RT_DWORD, &type, &data, &dataSize), std::system_category()); 45 | 46 | if (result) 47 | { 48 | LogError(MH_SOURCE_LOCATION_CURRENT(), "Failed to retrieve {}\\ActiveUser: error {}", 49 | STEAM_ACTIVE_PROCESS_KEY, result); 50 | return {}; 51 | } 52 | 53 | char universeStr[256]; 54 | dataSize = sizeof(universeStr); 55 | result = std::error_code(RegGetValueA( 56 | HKEY_CURRENT_USER, 57 | STEAM_ACTIVE_PROCESS_KEY, "Universe", 58 | RRF_RT_REG_SZ, &type, universeStr, &dataSize), std::system_category()); 59 | 60 | if (result) 61 | { 62 | LogError(MH_SOURCE_LOCATION_CURRENT(), "Failed to retrieve {}\\Universe: error {}", 63 | STEAM_ACTIVE_PROCESS_KEY, result); 64 | return {}; 65 | } 66 | 67 | SteamAccountUniverse universe; 68 | if (_strnicmp(universeStr, "Public", dataSize) == 0) 69 | universe = SteamAccountUniverse::Public; 70 | else if (_strnicmp(universeStr, "Beta", dataSize) == 0) 71 | universe = SteamAccountUniverse::Beta; 72 | else if (_strnicmp(universeStr, "Internal", dataSize) == 0) 73 | universe = SteamAccountUniverse::Internal; 74 | else if (_strnicmp(universeStr, "Dev", dataSize) == 0) 75 | universe = SteamAccountUniverse::Dev; 76 | else 77 | { 78 | LogError(MH_SOURCE_LOCATION_CURRENT(), "Unknown steam account universe {}", std::quoted(universeStr)); 79 | return {}; 80 | } 81 | 82 | return SteamID(data, SteamAccountType::Individual, universe); 83 | } 84 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/TF2CommandLinePage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | #include "ISetupFlowPage.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace tf2_bot_detector 16 | { 17 | class TF2CommandLinePage final : public ISetupFlowPage 18 | { 19 | public: 20 | ValidateSettingsResult ValidateSettings(const Settings& settings) const override; 21 | OnDrawResult OnDraw(const DrawState& ds) override; 22 | 23 | void Init(const InitState& is) override; 24 | bool CanCommit() const override { return true; } 25 | void Commit(const CommitState& cs) override; 26 | 27 | bool WantsSetupText() const override { return false; } 28 | bool WantsContinueButton() const override { return false; } 29 | SetupFlowPage GetPage() const override { return SetupFlowPage::TF2CommandLine; } 30 | 31 | private: 32 | static constexpr duration_t CL_UPDATE_INTERVAL = std::chrono::seconds(1); 33 | 34 | struct TF2CommandLine 35 | { 36 | static TF2CommandLine Parse(const std::string_view& cmdLine); 37 | 38 | std::string m_FullCommandLine; 39 | 40 | bool m_UseRCON = false; 41 | std::string m_IP; 42 | std::string m_RCONPassword; 43 | std::optional m_RCONPort; 44 | 45 | bool IsPopulated() const; 46 | }; 47 | 48 | void DrawAutoLaunchTF2Checkbox(const DrawState& ds); 49 | void DrawLaunchTF2Button(const DrawState& ds); 50 | void DrawCommandLineArgsInvalid(const DrawState& ds, const TF2CommandLine& args); 51 | 52 | struct RCONClientData 53 | { 54 | RCONClientData(std::string pwd, uint16_t port); 55 | std::unique_ptr m_Client; 56 | std::shared_future m_Future; 57 | 58 | [[nodiscard]] bool Update(); 59 | 60 | private: 61 | bool m_Success = false; 62 | std::array m_MessageColor{ 1, 1, 1, 1 }; 63 | std::string m_Message; 64 | }; 65 | 66 | // We don't want the user to get stuck in an annoying loop of 67 | // tf2 auto-relaunching the moment they close the game. 68 | bool m_IsAutoLaunchAllowed = true; 69 | 70 | struct Data 71 | { 72 | time_point_t m_LastTF2LaunchTime{}; 73 | 74 | bool m_MultipleInstances = false; 75 | std::optional m_CommandLineArgs; 76 | mh::task> m_CommandLineArgsTask; 77 | bool m_AtLeastOneUpdateRun = false; 78 | 79 | time_point_t m_LastCLUpdate{}; 80 | 81 | std::string m_RandomRCONPassword; 82 | uint16_t m_RandomRCONPort; 83 | bool m_RCONSuccess = false; 84 | std::optional m_TestRCONClient; 85 | 86 | void TryUpdateCmdlineArgs(); 87 | 88 | } m_Data; 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/ActionGenerators.cpp: -------------------------------------------------------------------------------- 1 | #include "ActionGenerators.h" 2 | #include "Actions.h" 3 | #include "IActionManager.h" 4 | #include "Log.h" 5 | 6 | using namespace tf2_bot_detector; 7 | using namespace std::chrono_literals; 8 | 9 | duration_t StatusUpdateActionGenerator::GetInterval() const 10 | { 11 | return 3s; 12 | } 13 | 14 | bool StatusUpdateActionGenerator::ExecuteImpl(IActionManager& manager) 15 | { 16 | // 3 second interval, we want: 17 | // 1. status 18 | // 2. ping 19 | // 3. status short 20 | // 4. ping 21 | // repeat 22 | 23 | if (m_NextPing) 24 | { 25 | if (!manager.QueueAction("ping")) 26 | return false; 27 | } 28 | else 29 | { 30 | //if (!manager.QueueAction("status", m_NextShort ? "short" : "")) 31 | // return false; 32 | 33 | //m_NextShort = !m_NextShort; 34 | if (!manager.QueueAction("status")) 35 | return false; 36 | } 37 | 38 | m_NextPing = !m_NextPing; 39 | 40 | return true; 41 | } 42 | 43 | duration_t ConfigActionGenerator::GetInterval() const 44 | { 45 | return 10s; 46 | } 47 | 48 | bool ConfigActionGenerator::ExecuteImpl(IActionManager& manager) 49 | { 50 | if (!manager.QueueAction("con_logfile", "console.log")) 51 | return false; 52 | if (!manager.QueueAction("con_timestamp", "1")) 53 | return false; 54 | if (!manager.QueueAction("tf_mm_debug_level", "4")) 55 | return false; // This is defaulted to 4, but mastercom's stupid config turns this off 56 | 57 | if (!manager.QueueAction("net_showmsg", "svc_UserMessage")) 58 | return false; 59 | 60 | return true; 61 | } 62 | 63 | bool IPeriodicActionGenerator::Execute(IActionManager& manager) 64 | { 65 | const auto curTime = clock_t::now(); 66 | const auto interval = GetInterval(); 67 | if (m_LastRunTime == time_point_t{}) 68 | { 69 | const auto delay = GetInitialDelay(); 70 | m_LastRunTime = curTime - interval + delay; 71 | } 72 | 73 | if ((curTime - m_LastRunTime) >= interval) 74 | { 75 | if (ExecuteImpl(manager)) 76 | { 77 | m_LastRunTime = curTime; 78 | return true; 79 | } 80 | else 81 | { 82 | LogWarning("Couldn't execute IPeriodicActionGenerator!"); 83 | return false; 84 | } 85 | } 86 | 87 | return true; 88 | } 89 | 90 | bool LobbyDebugActionGenerator::ExecuteImpl(IActionManager& manager) 91 | { 92 | if (!manager.QueueAction("tf_lobby_debug")) 93 | return false; 94 | if (!manager.QueueAction("tf_party_debug")) 95 | return false; 96 | if (!manager.QueueAction("net_status")) 97 | return false; 98 | 99 | return true; 100 | } 101 | -------------------------------------------------------------------------------- /tf2_bot_detector/Config/AccountAges.cpp: -------------------------------------------------------------------------------- 1 | #include "AccountAges.h" 2 | #include "SteamID.h" 3 | #include "Util/JSONUtils.h" 4 | #include "Application.h" 5 | #include "DB/TempDB.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | using namespace tf2_bot_detector; 16 | 17 | namespace tf2_bot_detector::DB 18 | { 19 | class ITempDB; 20 | } 21 | 22 | namespace 23 | { 24 | class AccountAges final : public IAccountAges 25 | { 26 | public: 27 | void OnDataReady(const SteamID& id, time_point_t creationTime) override; 28 | 29 | std::optional EstimateAccountCreationTime(const SteamID& id) const override; 30 | 31 | private: 32 | [[nodiscard]] bool CheckSteamIDValid(const SteamID& id, MH_SOURCE_LOCATION_AUTO(location)) const; 33 | }; 34 | 35 | static const std::filesystem::path ACCOUNT_AGES_FILENAME = "cfg/account_ages.json"; 36 | } 37 | 38 | std::shared_ptr tf2_bot_detector::IAccountAges::Create() 39 | { 40 | return std::make_shared(); 41 | } 42 | 43 | bool AccountAges::CheckSteamIDValid(const SteamID& id, const mh::source_location& location) const 44 | { 45 | if (id.Type != SteamAccountType::Individual) 46 | { 47 | LogError(location, "Steam ID ({}) must be for an individual account", id); 48 | return false; 49 | } 50 | 51 | return true; 52 | } 53 | 54 | void AccountAges::OnDataReady(const SteamID& id, time_point_t creationTime) 55 | { 56 | if (!CheckSteamIDValid(id)) 57 | return; 58 | 59 | DB::ITempDB& tempDB = TF2BDApplication::GetApplication().GetTempDB(); 60 | DB::AccountAgeInfo info{}; 61 | info.m_SteamID = id; 62 | info.m_CreationTime = creationTime; 63 | tempDB.Store(info); 64 | } 65 | 66 | std::optional AccountAges::EstimateAccountCreationTime(const SteamID& id) const 67 | { 68 | if (!CheckSteamIDValid(id)) 69 | return std::nullopt; 70 | 71 | std::optional lower, upper; 72 | TF2BDApplication::GetApplication().GetTempDB().GetNearestAccountAgeInfos(id, lower, upper); 73 | 74 | if (!lower.has_value()) 75 | return std::nullopt; // super new, we don't have any data for this 76 | if (!upper.has_value()) 77 | return lower.value().m_CreationTime; // Nothing to interpolate to, pick the lower value 78 | 79 | if (lower->m_CreationTime == upper->m_CreationTime) 80 | return lower->m_CreationTime; // they're the same picture 81 | 82 | // Interpolate the time between the nearest lower and upper steam ID 83 | const auto interpValue = mh::remap(id.GetAccountID(), 84 | lower->m_SteamID.GetAccountID(), upper->m_SteamID.GetAccountID(), 85 | lower->m_CreationTime.time_since_epoch().count(), upper->m_CreationTime.time_since_epoch().count()); 86 | 87 | assert(interpValue >= 0); 88 | 89 | return time_point_t(time_point_t::duration(interpValue)); 90 | } 91 | -------------------------------------------------------------------------------- /tf2_bot_detector/Art/game_ban_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 23 | 24 | 26 | image/svg+xml 27 | 29 | 30 | 31 | 32 | 33 | 35 | 50 | 55 | 56 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | # STOP USING CRLF, PLEASE 7 | *.cpp text eol=lf 8 | *.h text eol=lf linguist-language=cpp 9 | 10 | # Ignore some dependencies in submodules (gl stuff that is not actually a submodule) 11 | submodules/* linguist-vendored 12 | 13 | ############################################################################### 14 | # Set default behavior for command prompt diff. 15 | # 16 | # This is need for earlier builds of msysgit that does not have it on by 17 | # default for csharp files. 18 | # Note: This is only used by command line 19 | ############################################################################### 20 | #*.cs diff=csharp 21 | 22 | ############################################################################### 23 | # Set the merge driver for project and solution files 24 | # 25 | # Merging from the command prompt will add diff markers to the files if there 26 | # are conflicts (Merging from VS is not affected by the settings below, in VS 27 | # the diff markers are never inserted). Diff markers may cause the following 28 | # file extensions to fail to load in VS. An alternative would be to treat 29 | # these files as binary and thus will always conflict and require user 30 | # intervention with every merge. To do so, just uncomment the entries below 31 | ############################################################################### 32 | #*.sln merge=binary 33 | #*.csproj merge=binary 34 | #*.vbproj merge=binary 35 | #*.vcxproj merge=binary 36 | #*.vcproj merge=binary 37 | #*.dbproj merge=binary 38 | #*.fsproj merge=binary 39 | #*.lsproj merge=binary 40 | #*.wixproj merge=binary 41 | #*.modelproj merge=binary 42 | #*.sqlproj merge=binary 43 | #*.wwaproj merge=binary 44 | 45 | ############################################################################### 46 | # behavior for image files 47 | # 48 | # image files are treated as binary by default. 49 | ############################################################################### 50 | #*.jpg binary 51 | #*.png binary 52 | #*.gif binary 53 | 54 | ############################################################################### 55 | # diff behavior for common document formats 56 | # 57 | # Convert binary document formats to text before diffing them. This feature 58 | # is only available from the command line. Turn it on by uncommenting the 59 | # entries below. 60 | ############################################################################### 61 | #*.doc diff=astextplain 62 | #*.DOC diff=astextplain 63 | #*.docx diff=astextplain 64 | #*.DOCX diff=astextplain 65 | #*.dot diff=astextplain 66 | #*.DOT diff=astextplain 67 | #*.pdf diff=astextplain 68 | #*.PDF diff=astextplain 69 | #*.rtf diff=astextplain 70 | #*.RTF diff=astextplain 71 | -------------------------------------------------------------------------------- /tf2_bot_detector/Platform/Windows/PlatformInstall.cpp: -------------------------------------------------------------------------------- 1 | #include "Platform/Platform.h" 2 | #include "Networking/HTTPClient.h" 3 | #include "Networking/HTTPHelpers.h" 4 | #include "Util/TextUtils.h" 5 | #include "Log.h" 6 | #include "UpdateManager.h" 7 | #include "Version.h" 8 | 9 | #include 10 | 11 | #include 12 | 13 | using namespace tf2_bot_detector; 14 | 15 | bool tf2_bot_detector::Platform::IsInstalled() 16 | { 17 | static const bool s_IsInstalled = []() -> bool 18 | { 19 | using func_type = LONG(WINAPI*)(UINT32* packageFullNameLength, PWSTR packageFullName); 20 | constexpr const char FUNC_NAME[] = "GetCurrentPackageFullName"; 21 | constexpr const char MODULE_NAME[] = "Kernel32.dll"; 22 | 23 | HMODULE kernel32 = GetModuleHandleA(MODULE_NAME); 24 | if (!kernel32) 25 | { 26 | LogError(MH_SOURCE_LOCATION_CURRENT(), "Failed to get module handle for {}", MODULE_NAME); 27 | return false; 28 | } 29 | 30 | const void* rawFnPtr = GetProcAddress(kernel32, FUNC_NAME); 31 | 32 | if (!rawFnPtr) 33 | { 34 | DebugLog(MH_SOURCE_LOCATION_CURRENT(), "Unable to find {} in {}", FUNC_NAME, MODULE_NAME); 35 | return false; 36 | } 37 | 38 | const auto fnPtr = reinterpret_cast(rawFnPtr); 39 | 40 | UINT32 length = 0; 41 | const LONG result = fnPtr(&length, nullptr); 42 | 43 | if (result == ERROR_INSUFFICIENT_BUFFER) 44 | return true; 45 | else if (result == APPMODEL_ERROR_NO_PACKAGE) 46 | return false; 47 | else 48 | { 49 | LogError(MH_SOURCE_LOCATION_CURRENT(), "Unknown error code returned by {}: {}", FUNC_NAME, result); 50 | return false; 51 | } 52 | }(); 53 | 54 | return s_IsInstalled; 55 | } 56 | 57 | bool tf2_bot_detector::Platform::CanInstallUpdate(const BuildInfo& bi) 58 | { 59 | if (!IsInstalled()) 60 | return false; 61 | 62 | if (bi.m_MSIXBundleURL.empty()) 63 | return false; 64 | 65 | return true; 66 | } 67 | 68 | mh::task tf2_bot_detector::Platform::BeginInstallUpdate( 69 | const BuildInfo& buildInfo, const HTTPClient& client) 70 | { 71 | if (!IsInstalled()) 72 | { 73 | constexpr const char ERROR_MSG[] = "Attempted to call " __FUNCTION__ "() when we aren't installed." 74 | " This should never happen."; 75 | LogError(MH_SOURCE_LOCATION_CURRENT(), ERROR_MSG); 76 | throw std::logic_error(ERROR_MSG); 77 | } 78 | 79 | if (buildInfo.m_MSIXBundleURL.empty()) 80 | { 81 | constexpr const char ERROR_MSG[] = "BuildInfo's msix bundle url is empty." 82 | " This should never happen; CanInstallUpdate() should have returned false"; 83 | LogError(MH_SOURCE_LOCATION_CURRENT(), ERROR_MSG); 84 | throw std::invalid_argument(ERROR_MSG); 85 | } 86 | 87 | const auto bundleUri = buildInfo.m_MSIXBundleURL; 88 | 89 | DebugLogWarning(MH_SOURCE_LOCATION_CURRENT(), 90 | "Microsoft has ensured that we can't have anything nice, requesting update tool"); 91 | 92 | co_return InstallUpdate::NeedsUpdateTool 93 | { 94 | .m_UpdateToolArgs = mh::format("--update-type MSIX --source-path {}", std::quoted(bundleUri)), 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /tf2_bot_detector/UpdateManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Platform/Platform.h" 4 | #include "ReleaseChannel.h" 5 | #include "Version.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace tf2_bot_detector 13 | { 14 | class Settings; 15 | 16 | struct BuildInfo 17 | { 18 | ReleaseChannel m_ReleaseChannel{}; 19 | Version m_Version{}; 20 | 21 | std::string m_GitHubURL; 22 | std::string m_MSIXBundleURL; 23 | 24 | struct BuildVariant 25 | { 26 | Platform::OS m_OS{}; 27 | Platform::Arch m_Arch{}; 28 | std::string m_DownloadURL; 29 | }; 30 | 31 | std::vector m_Updater; 32 | std::vector m_Portable; 33 | }; 34 | } 35 | 36 | namespace tf2_bot_detector 37 | { 38 | class IAvailableUpdate 39 | { 40 | public: 41 | IAvailableUpdate(BuildInfo&& bi) : m_BuildInfo(std::move(bi)) {} 42 | virtual ~IAvailableUpdate() = default; 43 | 44 | [[nodiscard]] virtual bool CanSelfUpdate() const = 0; 45 | virtual bool BeginSelfUpdate() const = 0; 46 | 47 | BuildInfo m_BuildInfo; 48 | }; 49 | 50 | enum class UpdateStatus 51 | { 52 | Unknown = 0, 53 | 54 | StateSwitchFailure, 55 | 56 | UpdateCheckDisabled, 57 | InternetAccessDisabled, 58 | 59 | CheckQueued, 60 | Checking, 61 | 62 | CheckFailed, 63 | UpToDate, 64 | UpdateAvailable, 65 | 66 | UpdateToolRequired, 67 | UpdateToolDownloading, 68 | UpdateToolDownloadFailed, 69 | UpdateToolDownloadSuccess, 70 | 71 | Downloading, 72 | DownloadFailed, 73 | DownloadSuccess, 74 | 75 | Updating, 76 | UpdateFailed, 77 | UpdateSuccess, 78 | }; 79 | 80 | class IUpdateManager 81 | { 82 | public: 83 | virtual ~IUpdateManager() = default; 84 | 85 | static std::unique_ptr Create(const Settings& settings); 86 | 87 | virtual void QueueUpdateCheck() = 0; 88 | 89 | virtual void Update() = 0; 90 | 91 | virtual mh::status_reader GetUpdateStatus() const = 0; 92 | virtual const IAvailableUpdate* GetAvailableUpdate() const = 0; 93 | }; 94 | } 95 | 96 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::UpdateStatus) 97 | MH_ENUM_REFLECT_VALUE(Unknown) 98 | 99 | MH_ENUM_REFLECT_VALUE(StateSwitchFailure) 100 | 101 | MH_ENUM_REFLECT_VALUE(UpdateCheckDisabled) 102 | MH_ENUM_REFLECT_VALUE(InternetAccessDisabled) 103 | 104 | MH_ENUM_REFLECT_VALUE(Checking) 105 | 106 | MH_ENUM_REFLECT_VALUE(CheckFailed) 107 | MH_ENUM_REFLECT_VALUE(UpToDate) 108 | MH_ENUM_REFLECT_VALUE(UpdateAvailable) 109 | 110 | MH_ENUM_REFLECT_VALUE(UpdateToolRequired) 111 | MH_ENUM_REFLECT_VALUE(UpdateToolDownloading) 112 | MH_ENUM_REFLECT_VALUE(UpdateToolDownloadFailed) 113 | MH_ENUM_REFLECT_VALUE(UpdateToolDownloadSuccess) 114 | 115 | MH_ENUM_REFLECT_VALUE(Downloading) 116 | MH_ENUM_REFLECT_VALUE(DownloadFailed) 117 | MH_ENUM_REFLECT_VALUE(DownloadSuccess) 118 | 119 | MH_ENUM_REFLECT_VALUE(Updating) 120 | MH_ENUM_REFLECT_VALUE(UpdateFailed) 121 | MH_ENUM_REFLECT_VALUE(UpdateSuccess) 122 | MH_ENUM_REFLECT_END() 123 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/ChatWrappersVerifyPage.cpp: -------------------------------------------------------------------------------- 1 | #include "ChatWrappersVerifyPage.h" 2 | #include "Config/ChatWrappers.h" 3 | #include "Config/Settings.h" 4 | #include "UI/ImGui_TF2BotDetector.h" 5 | #include "ChatWrappersGeneratorPage.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std::string_literals; 13 | using namespace tf2_bot_detector; 14 | 15 | auto ChatWrappersVerifyPage::ValidateSettings(const Settings& settings) const -> ValidateSettingsResult 16 | { 17 | if (!m_Validation.valid()) 18 | return ValidateSettingsResult::TriggerOpen; 19 | 20 | return ValidateSettingsResult::Success; 21 | } 22 | 23 | auto ChatWrappersVerifyPage::OnDraw(const DrawState& ds) -> OnDrawResult 24 | { 25 | if (!m_Validation.valid()) 26 | { 27 | if (m_Message.empty()) 28 | { 29 | m_Message = "Validating chat wrappers..."; 30 | m_MessageColor = { 1, 1, 1, 1 }; 31 | } 32 | 33 | const auto curTime = clock_t::now(); 34 | if (curTime >= m_NextValidationTime) 35 | { 36 | m_Validation = ds.m_Settings->m_Unsaved.m_RCONClient->send_command_async( 37 | "exec "s << ChatWrappersGeneratorPage::VERIFY_CFG_FILE_NAME, false); 38 | m_NextValidationTime = curTime + VALIDATION_INTERVAL; 39 | } 40 | } 41 | else if (mh::is_future_ready(m_Validation)) 42 | { 43 | try 44 | { 45 | auto val = m_Validation.get(); 46 | val = mh::trim(std::move(val)); 47 | 48 | const auto developerPrefix = "execing "s << ChatWrappersGeneratorPage::VERIFY_CFG_FILE_NAME; 49 | if (val.starts_with(developerPrefix)) 50 | { 51 | val.erase(0, developerPrefix.size()); 52 | val = mh::trim(std::move(val)); 53 | } 54 | 55 | if (val != m_ExpectedToken) 56 | { 57 | m_Message = ""s << 58 | "Failed to validate chat wrappers: \n" 59 | "\tExpected token : " << std::quoted(m_ExpectedToken) << "\n" 60 | "\tActual token " << std::quoted(val); 61 | 62 | m_MessageColor = { 1, 0.5, 0, 1 }; 63 | m_Validation = {}; 64 | } 65 | } 66 | catch (const std::exception& e) 67 | { 68 | m_Message = "Failed to validate chat wrappers: "s << typeid(e).name() << ": " << e.what(); 69 | m_MessageColor = { 1, 0.25, 0.25, 1 }; 70 | m_Validation = {}; 71 | } 72 | } 73 | 74 | ImGui::TextFmt(m_MessageColor, m_Message); 75 | 76 | ImGui::NewLine(); 77 | if (AutoLaunchTF2Checkbox(ds.m_Settings->m_AutoLaunchTF2)) 78 | ds.m_Settings->SaveFile(); 79 | 80 | if (mh::is_future_ready(m_Validation)) 81 | return OnDrawResult::EndDrawing; 82 | 83 | return OnDrawResult::ContinueDrawing; 84 | } 85 | 86 | void ChatWrappersVerifyPage::Init(const InitState& is) 87 | { 88 | m_Validation = {}; 89 | m_ExpectedToken = ChatWrappersGeneratorPage::GetChatWrapperStringToken(is.m_Settings.m_Unsaved.m_ChatMsgWrappersToken); 90 | } 91 | 92 | bool ChatWrappersVerifyPage::CanCommit() const 93 | { 94 | return true; 95 | } 96 | 97 | void ChatWrappersVerifyPage::Commit(const CommitState& cs) 98 | { 99 | } 100 | -------------------------------------------------------------------------------- /tf2_bot_detector/Art/heart.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 43 | 53 | 57 | 61 | 65 | 66 | 68 | 69 | 71 | image/svg+xml 72 | 74 | 75 | 76 | 77 | 78 | 82 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/BasicSettingsPage.cpp: -------------------------------------------------------------------------------- 1 | #include "BasicSettingsPage.h" 2 | #include "Util/PathUtils.h" 3 | #include "UI/ImGui_TF2BotDetector.h" 4 | 5 | using namespace std::string_view_literals; 6 | using namespace tf2_bot_detector; 7 | 8 | template 9 | static bool InternalValidateSettings(const T& settings) 10 | { 11 | if (auto steamDir = settings.GetSteamDir(); steamDir.empty() || !ValidateSteamDir(steamDir)) 12 | return false; 13 | 14 | if (auto tfDir = settings.GetTFDir(); tfDir.empty() || !ValidateTFDir(tfDir)) 15 | return false; 16 | 17 | if (!settings.GetLocalSteamID().IsValid()) 18 | return false; 19 | 20 | return true; 21 | } 22 | 23 | auto BasicSettingsPage::ValidateSettings(const Settings& settings) const -> ValidateSettingsResult 24 | { 25 | return InternalValidateSettings(settings) ? ValidateSettingsResult::Success : ValidateSettingsResult::TriggerOpen; 26 | } 27 | 28 | auto BasicSettingsPage::OnDraw(const DrawState& ds) -> OnDrawResult 29 | { 30 | ImGui::TextFmt("Due to your configuration, some settings could not be automatically detected."); 31 | ImGui::NewLine(); 32 | 33 | // 1. Steam directory 34 | bool any = false; 35 | //if (auto autodetected = GetCurrentSteamDir(); autodetected.empty()) 36 | { 37 | any = true; 38 | ImGui::TextFmt("Location of your Steam directory:"); 39 | ImGui::NewLine(); 40 | ImGuiDesktop::ScopeGuards::Indent indent; 41 | ImGui::TextFmt("Your Steam directory is needed to execute commands in TF2, read console logs, and determine your current Steam ID."); 42 | 43 | InputTextSteamDirOverride("##SetupFlow_SteamDir", m_Settings.m_SteamDirOverride); 44 | 45 | ImGui::NewLine(); 46 | } 47 | 48 | //if (FindTFDir(m_Settings.GetSteamDir()).empty()) 49 | { 50 | any = true; 51 | ImGui::TextFmt("Location of your tf directory:"); 52 | ImGui::NewLine(); 53 | ImGui::Indent(); 54 | ImGui::TextFmt("Your tf directory is needed so this tool knows where to read console output from. It is also needed to automatically run commands in the game."); 55 | 56 | InputTextTFDirOverride("##SetupFlow_TFDir", m_Settings.m_TFDirOverride, FindTFDir(m_Settings.GetSteamDir())); 57 | 58 | ImGui::Unindent(); 59 | } 60 | 61 | // 2. Steam ID 62 | //if (!GetCurrentActiveSteamID().IsValid()) 63 | { 64 | any = true; 65 | ImGui::TextFmt("Your Steam ID:"sv); 66 | ImGui::NewLine(); 67 | ImGui::Indent(); 68 | ImGui::TextFmt("Your Steam ID is needed to identify who can be votekicked (same team) and who is on the other team. You can find your Steam ID using sites like steamidfinder.com."); 69 | 70 | InputTextSteamIDOverride("##SetupFlow_SteamID", m_Settings.m_LocalSteamIDOverride); 71 | 72 | ImGui::Unindent(); 73 | ImGui::NewLine(); 74 | } 75 | 76 | return any ? OnDrawResult::ContinueDrawing : OnDrawResult::EndDrawing; 77 | } 78 | 79 | void BasicSettingsPage::Init(const InitState& is) 80 | { 81 | m_Settings = is.m_Settings; 82 | } 83 | 84 | bool BasicSettingsPage::CanCommit() const 85 | { 86 | return InternalValidateSettings(m_Settings); 87 | } 88 | 89 | void BasicSettingsPage::Commit(const CommitState& cs) 90 | { 91 | static_cast(cs.m_Settings) = m_Settings; 92 | } 93 | -------------------------------------------------------------------------------- /tf2_bot_detector_updater/CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\..\\out\\build_updater\\${name}", 9 | "installRoot": "${projectDir}\\..\\out\\install_updater\\${name}", 10 | "cmakeCommandArgs": "", 11 | "buildCommandArgs": "", 12 | "ctestCommandArgs": "", 13 | "variables": [ 14 | { 15 | "name": "TF2BD_ENABLE_TESTS", 16 | "value": "True", 17 | "type": "BOOL" 18 | }, 19 | { 20 | "name": "VCPKG_TARGET_TRIPLET", 21 | "value": "x64-windows-static", 22 | "type": "STRING" 23 | } 24 | ], 25 | "cmakeToolchain": "../submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 26 | }, 27 | { 28 | "name": "x64-Release", 29 | "generator": "Ninja", 30 | "configurationType": "Release", 31 | "buildRoot": "${projectDir}\\..\\out\\build_updater\\${name}", 32 | "installRoot": "${projectDir}\\..\\out\\install_updater\\${name}", 33 | "cmakeCommandArgs": "", 34 | "buildCommandArgs": "", 35 | "ctestCommandArgs": "", 36 | "inheritEnvironments": [ "msvc_x64_x64" ], 37 | "variables": [ 38 | { 39 | "name": "TF2BD_ENABLE_TESTS", 40 | "value": "True", 41 | "type": "BOOL" 42 | }, 43 | { 44 | "name": "VCPKG_TARGET_TRIPLET", 45 | "value": "x64-windows-static", 46 | "type": "STRING" 47 | } 48 | ], 49 | "cmakeToolchain": "../submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 50 | }, 51 | { 52 | "name": "x86-Release", 53 | "generator": "Ninja", 54 | "configurationType": "Release", 55 | "buildRoot": "${projectDir}\\..\\out\\build_updater\\${name}", 56 | "installRoot": "${projectDir}\\..\\out\\install_updater\\${name}", 57 | "cmakeCommandArgs": "", 58 | "buildCommandArgs": "", 59 | "ctestCommandArgs": "", 60 | "inheritEnvironments": [ "msvc_x86_x64" ], 61 | "variables": [ 62 | { 63 | "name": "TF2BD_ENABLE_TESTS", 64 | "value": "True", 65 | "type": "BOOL" 66 | }, 67 | { 68 | "name": "VCPKG_TARGET_TRIPLET", 69 | "value": "x86-windows-static", 70 | "type": "STRING" 71 | } 72 | ], 73 | "cmakeToolchain": "../submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 74 | }, 75 | { 76 | "name": "x86-Debug", 77 | "generator": "Ninja", 78 | "configurationType": "Debug", 79 | "buildRoot": "${projectDir}\\..\\out\\build_updater\\${name}", 80 | "installRoot": "${projectDir}\\..\\out\\install_updater\\${name}", 81 | "cmakeCommandArgs": "", 82 | "buildCommandArgs": "", 83 | "ctestCommandArgs": "", 84 | "inheritEnvironments": [ "msvc_x86_x64" ], 85 | "variables": [ 86 | { 87 | "name": "TF2BD_ENABLE_TESTS", 88 | "value": "True", 89 | "type": "BOOL" 90 | }, 91 | { 92 | "name": "VCPKG_TARGET_TRIPLET", 93 | "value": "x86-windows-static", 94 | "type": "STRING" 95 | } 96 | ], 97 | "cmakeToolchain": "../submodules/vcpkg/scripts/buildsystems/vcpkg.cmake" 98 | } 99 | ] 100 | } 101 | -------------------------------------------------------------------------------- /tf2_bot_detector/Networking/GithubAPI.cpp: -------------------------------------------------------------------------------- 1 | #include "GithubAPI.h" 2 | #include "Util/JSONUtils.h" 3 | #include "Log.h" 4 | #include "Version.h" 5 | #include "HTTPClient.h" 6 | #include "HTTPHelpers.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace std::chrono_literals; 20 | using namespace std::string_literals; 21 | using namespace tf2_bot_detector; 22 | using namespace tf2_bot_detector::GithubAPI; 23 | 24 | namespace 25 | { 26 | struct InternalRelease : NewVersionResult::Release 27 | { 28 | bool m_IsPrerelease; 29 | }; 30 | } 31 | 32 | static mh::generator GetAllReleases(const HTTPClient& client) 33 | { 34 | auto str = co_await client.GetStringAsync("https://api.github.com/repos/PazerOP/tf2_bot_detector/releases"); 35 | if (str.empty()) 36 | throw std::runtime_error("Autoupdate: response string was empty"); 37 | 38 | auto j = nlohmann::json::parse(str); 39 | if (j.empty()) 40 | throw std::runtime_error("Autoupdate: Response json was empty"); 41 | 42 | if (!j.is_array()) 43 | throw std::runtime_error("Autoupdate: Response json was not an array"); 44 | 45 | for (auto& releases : j) 46 | { 47 | InternalRelease retVal; 48 | retVal.m_IsPrerelease = releases.at("prerelease"); 49 | retVal.m_URL = releases.at("html_url"); 50 | 51 | std::string versionTag = releases.at("tag_name"); 52 | if (auto version = Version::Parse(versionTag.c_str())) 53 | retVal.m_Version = *version; 54 | else 55 | { 56 | DebugLogWarning("Release id "s << releases.at("id") << " has invalid tag_name version " << versionTag); 57 | continue; 58 | } 59 | 60 | co_yield retVal; 61 | } 62 | } 63 | 64 | static mh::task GetLatestVersion(const HTTPClient& client) 65 | { 66 | DebugLog("GetLatestVersion()"); 67 | try 68 | { 69 | NewVersionResult retVal; 70 | 71 | for (auto release : GetAllReleases(client)) 72 | { 73 | DebugLog("GetLatestVersion(): version = {}, url = {}", release.m_Version, release.m_URL); 74 | 75 | if (release.m_Version <= VERSION) 76 | { 77 | DebugLog("GetLatestVersion(): break"); 78 | break; 79 | } 80 | 81 | if (release.m_IsPrerelease) 82 | retVal.m_Preview = release; 83 | else 84 | retVal.m_Stable = release; 85 | 86 | if (retVal.m_Stable.has_value()) 87 | break; 88 | } 89 | 90 | co_return retVal; 91 | } 92 | catch (const nlohmann::json::parse_error& e) 93 | { 94 | LogError("Autoupdate: Failed to parse json: "s << e.what()); 95 | co_return NewVersionResult{ .m_Error = true }; 96 | } 97 | catch (const std::exception& e) 98 | { 99 | LogError("Autoupdate: Unknown error of type "s << typeid(e).name() << ": " << e.what()); 100 | co_return NewVersionResult{ .m_Error = true }; 101 | } 102 | } 103 | 104 | auto GithubAPI::CheckForNewVersion(const HTTPClient& client) -> mh::task 105 | { 106 | return GetLatestVersion(client); 107 | } 108 | -------------------------------------------------------------------------------- /staging/licenses/glad2.txt: -------------------------------------------------------------------------------- 1 | The glad source code: 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2013-2020 David Herberth 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | 25 | The Khronos Specifications: 26 | 27 | Copyright (c) 2013-2020 The Khronos Group Inc. 28 | 29 | Licensed under the Apache License, Version 2.0 (the "License"); 30 | you may not use this file except in compliance with the License. 31 | You may obtain a copy of the License at 32 | 33 | http://www.apache.org/licenses/LICENSE-2.0 34 | 35 | Unless required by applicable law or agreed to in writing, software 36 | distributed under the License is distributed on an "AS IS" BASIS, 37 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 38 | See the License for the specific language governing permissions and 39 | limitations under the License. 40 | 41 | 42 | The EGL Specification and various headers: 43 | 44 | Copyright (c) 2007-2016 The Khronos Group Inc. 45 | 46 | Permission is hereby granted, free of charge, to any person obtaining a 47 | copy of this software and/or associated documentation files (the 48 | "Materials"), to deal in the Materials without restriction, including 49 | without limitation the rights to use, copy, modify, merge, publish, 50 | distribute, sublicense, and/or sell copies of the Materials, and to 51 | permit persons to whom the Materials are furnished to do so, subject to 52 | the following conditions: 53 | 54 | The above copyright notice and this permission notice shall be included 55 | in all copies or substantial portions of the Materials. 56 | 57 | THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 58 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 59 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 60 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 61 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 62 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 63 | MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 64 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/Actions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | #include "ICommandSource.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace tf2_bot_detector 15 | { 16 | enum class ActionType 17 | { 18 | GenericCommand, 19 | Kick, 20 | ChatMessage, 21 | LobbyUpdate, 22 | StatusUpdate, 23 | 24 | COUNT, 25 | }; 26 | 27 | class IAction : public ICommandSource 28 | { 29 | public: 30 | virtual ~IAction() = default; 31 | 32 | virtual duration_t GetMinInterval() const { return {}; } 33 | virtual ActionType GetType() const = 0; 34 | virtual size_t GetMaxQueuedCount() const { return size_t(-1); } 35 | }; 36 | 37 | class GenericCommandAction : public IAction 38 | { 39 | public: 40 | explicit GenericCommandAction(std::string cmd, std::string args = std::string()); 41 | 42 | ActionType GetType() const override { return ActionType::GenericCommand; } 43 | void WriteCommands(ICommandWriter& writer) const; 44 | 45 | private: 46 | std::string m_Command; 47 | std::string m_Args; 48 | }; 49 | 50 | class LobbyUpdateAction : public IAction 51 | { 52 | public: 53 | ActionType GetType() const override { return ActionType::LobbyUpdate; } 54 | void WriteCommands(ICommandWriter& writer) const override; 55 | }; 56 | 57 | enum class KickReason 58 | { 59 | Other, 60 | Cheating, 61 | Idle, 62 | Scamming, 63 | }; 64 | 65 | void to_json(nlohmann::json& j, const KickReason& d); 66 | void from_json(const nlohmann::json& j, KickReason& d); 67 | 68 | class KickAction final : public GenericCommandAction 69 | { 70 | public: 71 | KickAction(uint16_t userID, KickReason reason); 72 | 73 | duration_t GetMinInterval() const override; 74 | ActionType GetType() const override { return ActionType::Kick; } 75 | size_t GetMaxQueuedCount() const override { return 1; } 76 | 77 | private: 78 | static std::string MakeArgs(uint16_t userID, KickReason reason); 79 | }; 80 | 81 | enum class ChatMessageType 82 | { 83 | Public, 84 | Team, 85 | Party, 86 | }; 87 | 88 | class ChatMessageAction final : public GenericCommandAction 89 | { 90 | public: 91 | ChatMessageAction(const std::string_view& message, ChatMessageType type = ChatMessageType::Public); 92 | 93 | duration_t GetMinInterval() const override; 94 | ActionType GetType() const override { return ActionType::ChatMessage; } 95 | size_t GetMaxQueuedCount() const override { return 2; } 96 | 97 | private: 98 | static std::string_view GetCommand(ChatMessageType type); 99 | static std::string ScrubMessage(std::string msg); 100 | }; 101 | 102 | #if 0 103 | class StatusUpdateAction final : public GenericCommandAction 104 | { 105 | public: 106 | StatusUpdateAction(bool shortStatus = false); 107 | 108 | duration_t GetMinInterval() const override; 109 | ActionType GetType() const override { return ActionType::StatusUpdate; } 110 | }; 111 | #endif 112 | } 113 | 114 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::KickReason) 115 | MH_ENUM_REFLECT_VALUE(Cheating) 116 | MH_ENUM_REFLECT_VALUE(Idle) 117 | MH_ENUM_REFLECT_VALUE(Other) 118 | MH_ENUM_REFLECT_VALUE(Scamming) 119 | MH_ENUM_REFLECT_END() 120 | -------------------------------------------------------------------------------- /tf2_bot_detector/Platform/Platform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ReleaseChannel.h" 4 | #include "SteamID.h" 5 | #include "Version.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace tf2_bot_detector 16 | { 17 | class IHTTPClient; 18 | struct BuildInfo; 19 | 20 | inline namespace Platform 21 | { 22 | std::filesystem::path GetCurrentSteamDir(); 23 | SteamID GetCurrentActiveSteamID(); 24 | 25 | std::filesystem::path GetCurrentExeDir(); 26 | std::filesystem::path GetRootLocalAppDataDir(); 27 | std::filesystem::path GetRootRoamingAppDataDir(); 28 | std::filesystem::path GetLegacyAppDataDir(); 29 | std::filesystem::path GetRootTempDataDir(); 30 | bool IsPortAvailable(uint16_t port); 31 | 32 | bool IsDebuggerAttached(); 33 | 34 | enum class OS 35 | { 36 | Windows, 37 | Linux, 38 | }; 39 | OS GetOS(); 40 | 41 | enum class Arch 42 | { 43 | x86, 44 | x64, 45 | }; 46 | Arch GetArch(); 47 | 48 | namespace InstallUpdate 49 | { 50 | struct Success {}; 51 | 52 | // We think we've started the update, but there's no way to determine if it succeeded or not. 53 | struct StartedNoFeedback {}; 54 | 55 | struct NeedsUpdateTool 56 | { 57 | std::string m_UpdateToolArgs; 58 | }; 59 | 60 | using Result = std::variant< 61 | Success, 62 | StartedNoFeedback, 63 | NeedsUpdateTool>; 64 | }; 65 | 66 | bool CanInstallUpdate(const BuildInfo& bi); 67 | mh::task BeginInstallUpdate(const BuildInfo& bi, const IHTTPClient& client); 68 | bool IsInstalled(); // As opposed to portable 69 | 70 | bool NeedsElevationToWrite(const std::filesystem::path& path, bool recursive = false); 71 | 72 | namespace Processes 73 | { 74 | bool IsTF2Running(); 75 | mh::task> GetTF2CommandLineArgsAsync(); 76 | bool IsSteamRunning(); 77 | bool IsProcessRunning(const std::string_view& processName); 78 | void RequireTF2NotRunning(); 79 | 80 | void Launch(const std::filesystem::path& executable, const std::vector& args = {}, 81 | bool elevated = false); 82 | void Launch(const std::filesystem::path& executable, const std::string_view& args = {}, 83 | bool elevated = false); 84 | int GetCurrentProcessID(); 85 | 86 | size_t GetCurrentRAMUsage(); 87 | } 88 | 89 | namespace Shell 90 | { 91 | std::vector SplitCommandLineArgs(const std::string_view& cmdline); 92 | std::filesystem::path BrowseForFolderDialog(); 93 | void ExploreToAndSelect(std::filesystem::path path); 94 | 95 | void ExploreTo(const std::filesystem::path& path); 96 | void OpenURL(const char* url); 97 | inline void OpenURL(const std::string& url) { return OpenURL(url.c_str()); } 98 | } 99 | 100 | namespace ErrorCodes 101 | { 102 | extern const std::error_code PRIVILEGE_NOT_HELD; 103 | } 104 | } 105 | } 106 | 107 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::Platform::OS) 108 | MH_ENUM_REFLECT_VALUE(Windows) 109 | MH_ENUM_REFLECT_VALUE(Linux) 110 | MH_ENUM_REFLECT_END() 111 | 112 | MH_ENUM_REFLECT_BEGIN(tf2_bot_detector::Platform::Arch) 113 | MH_ENUM_REFLECT_VALUE(x86) 114 | MH_ENUM_REFLECT_VALUE(x64) 115 | MH_ENUM_REFLECT_END() 116 | -------------------------------------------------------------------------------- /tf2_bot_detector/SetupFlow/NetworkSettingsPage.cpp: -------------------------------------------------------------------------------- 1 | #include "NetworkSettingsPage.h" 2 | #include "Config/Settings.h" 3 | #include "UI/ImGui_TF2BotDetector.h" 4 | #include "ReleaseChannel.h" 5 | #include "Version.h" 6 | #include "UpdateManager.h" 7 | 8 | #include 9 | 10 | using namespace tf2_bot_detector; 11 | 12 | template 13 | static bool InternalValidateSettings(const T& settings) 14 | { 15 | if (!settings.m_AllowInternetUsage.has_value()) 16 | return false; 17 | if (!settings.m_ReleaseChannel.has_value()) 18 | return false; 19 | #if 0 20 | if (settings.m_ProgramUpdateCheckMode == ProgramUpdateCheckMode::Releases && VERSION.m_Preview != 0) 21 | return false; 22 | #endif 23 | 24 | return true; 25 | } 26 | 27 | auto NetworkSettingsPage::ValidateSettings(const Settings& settings) const -> ValidateSettingsResult 28 | { 29 | return InternalValidateSettings(settings) ? ValidateSettingsResult::Success : ValidateSettingsResult::TriggerOpen; 30 | } 31 | 32 | auto NetworkSettingsPage::OnDraw(const DrawState& ds) -> OnDrawResult 33 | { 34 | ImGui::TextFmt("This tool can optionally connect to the internet to automatically update."); 35 | 36 | ImGui::Indent(); 37 | { 38 | if (bool allow = m_Settings.m_AllowInternetUsage.value_or(false); ImGui::Checkbox("Allow Internet Connectivity", &allow)) 39 | m_Settings.m_AllowInternetUsage = allow; 40 | 41 | if (m_Settings.m_AllowInternetUsage.value_or(false)) 42 | ImGui::TextFmt({ 1, 1, 0, 1 }, "If you use antivirus software, connecting to the internet may trigger warnings."); 43 | } 44 | ImGui::Unindent(); 45 | ImGui::NewLine(); 46 | 47 | const bool enabled = m_Settings.m_AllowInternetUsage.value_or(false); 48 | ImGui::EnabledSwitch(enabled, [&](bool enabled) 49 | { 50 | ImGui::BeginGroup(); 51 | ImGui::TextFmt("This tool can also check for updated functionality and bugfixes on startup."); 52 | ImGui::Indent(); 53 | { 54 | std::optional mode = enabled ? m_Settings.m_ReleaseChannel : ReleaseChannel::None; 55 | if (Combo("##SetupFlow_UpdateCheckingMode", mode)) 56 | m_Settings.m_ReleaseChannel = mode; 57 | 58 | if (m_Settings.m_ReleaseChannel == ReleaseChannel::None) 59 | ImGui::TextFmt("You can always check for updates manually via the Help menu."); 60 | } 61 | ImGui::Unindent(); 62 | ImGui::EndGroup(); 63 | }, "Requires \"Allow Internet Connectivity\""); 64 | 65 | return OnDrawResult::ContinueDrawing; 66 | } 67 | 68 | void NetworkSettingsPage::Init(const InitState& is) 69 | { 70 | m_Settings.m_AllowInternetUsage = is.m_Settings.m_AllowInternetUsage.value_or(true); 71 | m_Settings.m_ReleaseChannel = is.m_Settings.m_ReleaseChannel; 72 | if (!m_Settings.m_ReleaseChannel.has_value()) 73 | m_Settings.m_ReleaseChannel = ReleaseChannel::Public; 74 | } 75 | 76 | bool NetworkSettingsPage::CanCommit() const 77 | { 78 | return InternalValidateSettings(m_Settings); 79 | } 80 | 81 | void NetworkSettingsPage::Commit(const CommitState& cs) 82 | { 83 | const bool oldClient = !!cs.m_Settings.GetHTTPClient(); 84 | cs.m_Settings.m_AllowInternetUsage = m_Settings.m_AllowInternetUsage; 85 | cs.m_Settings.m_ReleaseChannel = m_Settings.m_ReleaseChannel; 86 | 87 | if (!oldClient && cs.m_Settings.GetHTTPClient()) 88 | { 89 | DebugLog(MH_SOURCE_LOCATION_CURRENT(), "Re-checking for updates, user has chosen to allow internet connectivity"); 90 | 91 | // Trigger an update check, user has enabled 92 | if (mh_ensure(cs.m_UpdateManager)) 93 | cs.m_UpdateManager->QueueUpdateCheck(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tf2_bot_detector/SteamID.cpp: -------------------------------------------------------------------------------- 1 | #include "SteamID.h" 2 | #include "Util/RegexUtils.h" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace std::string_literals; 11 | using namespace tf2_bot_detector; 12 | 13 | SteamID::SteamID(const std::string_view& str) 14 | { 15 | ID64 = 0; 16 | 17 | // Steam3 18 | static std::regex s_SteamID3Regex(R"regex(\[([a-zA-Z]):(\d):(\d+)(?::(\d+))?\])regex", std::regex::optimize); 19 | if (std::match_results result; 20 | std::regex_match(str.begin(), str.end(), result, s_SteamID3Regex)) 21 | { 22 | const char firstChar = *result[1].first; 23 | switch (firstChar) 24 | { 25 | case 'U': Type = SteamAccountType::Individual; break; 26 | case 'M': Type = SteamAccountType::Multiseat; break; 27 | case 'G': Type = SteamAccountType::GameServer; break; 28 | case 'A': Type = SteamAccountType::AnonGameServer; break; 29 | case 'P': Type = SteamAccountType::Pending; break; 30 | case 'C': Type = SteamAccountType::ContentServer; break; 31 | case 'g': Type = SteamAccountType::Clan; break; 32 | case 'a': Type = SteamAccountType::AnonUser; break; 33 | 34 | case 'T': 35 | case 'L': 36 | case 'c': 37 | Type = SteamAccountType::Chat; break; 38 | 39 | case 'I': 40 | Type = SteamAccountType::Invalid; 41 | return; // We're totally done trying to parse 42 | 43 | default: 44 | throw std::invalid_argument(mh::format("Invalid SteamID3: Unknown SteamAccountType '{}'", firstChar)); 45 | } 46 | 47 | { 48 | uint32_t universe; 49 | if (auto parseResult = from_chars(result[2], universe); !parseResult) 50 | throw std::invalid_argument(mh::format("Out-of-range value for SteamID3 universe: {}", result[2].str())); 51 | 52 | Universe = static_cast(universe); 53 | } 54 | 55 | { 56 | uint32_t id; 57 | if (auto parseResult = from_chars(result[3], id); !parseResult) 58 | throw std::invalid_argument(mh::format("Out-of-range value for SteamID3 ID: {}", result[3].str())); 59 | 60 | ID = id; 61 | } 62 | 63 | if (result[4].matched) 64 | { 65 | uint32_t instance; 66 | if (auto parseResult = from_chars(result[4], instance); !parseResult) 67 | throw std::invalid_argument(mh::format("Out-of-range value for SteamID3 account instance: {}", result[4].str())); 68 | 69 | Instance = static_cast(instance); 70 | } 71 | else 72 | { 73 | Instance = SteamAccountInstance::Desktop; 74 | } 75 | 76 | return; 77 | } 78 | 79 | // Steam64 80 | if (std::all_of(str.begin(), str.end(), [](char c) { return std::isdigit(c) || std::isspace(c); })) 81 | { 82 | uint64_t result; 83 | if (auto parseResult = mh::from_chars(str, result); !parseResult) 84 | throw std::invalid_argument(mh::format("Out-of-range SteamID64: {}", str)); 85 | 86 | ID64 = result; 87 | return; 88 | } 89 | 90 | throw std::invalid_argument("SteamID string does not match any known formats"); 91 | } 92 | 93 | std::string SteamID::str() const 94 | { 95 | return mh::format("{}", *this); 96 | } 97 | 98 | void tf2_bot_detector::to_json(nlohmann::json& j, const SteamID& d) 99 | { 100 | j = d.str(); 101 | } 102 | 103 | void tf2_bot_detector::from_json(const nlohmann::json& j, SteamID& d) 104 | { 105 | if (j.is_number_unsigned()) 106 | d = SteamID(j.get()); 107 | else 108 | d = SteamID(j.get()); 109 | } 110 | -------------------------------------------------------------------------------- /tf2_bot_detector/Actions/Actions.cpp: -------------------------------------------------------------------------------- 1 | #include "Actions.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using namespace tf2_bot_detector; 11 | using namespace std::chrono_literals; 12 | using namespace std::string_literals; 13 | using namespace std::string_view_literals; 14 | 15 | KickAction::KickAction(uint16_t userID, KickReason reason) : 16 | GenericCommandAction("callvote", MakeArgs(userID, reason)) 17 | { 18 | } 19 | 20 | duration_t KickAction::GetMinInterval() const 21 | { 22 | return 5s; 23 | } 24 | 25 | std::string KickAction::MakeArgs(uint16_t userID, KickReason reason) 26 | { 27 | const char* reasonString = [&]() 28 | { 29 | switch (reason) 30 | { 31 | case KickReason::Other: return "other"; 32 | case KickReason::Cheating: return "cheating"; 33 | case KickReason::Idle: return "idle"; 34 | case KickReason::Scamming: return "scamming"; 35 | default: throw std::runtime_error("Unknown/invalid reason"); 36 | } 37 | }(); 38 | 39 | return "kick \""s << userID << ' ' << reasonString << '"'; 40 | } 41 | 42 | GenericCommandAction::GenericCommandAction(std::string cmd, std::string args) : 43 | m_Command(std::move(cmd)), m_Args(std::move(args)) 44 | { 45 | } 46 | 47 | void GenericCommandAction::WriteCommands(ICommandWriter& writer) const 48 | { 49 | writer.Write(m_Command, m_Args); 50 | } 51 | 52 | ChatMessageAction::ChatMessageAction(const std::string_view& message, ChatMessageType type) : 53 | GenericCommandAction(std::string(GetCommand(type)), ScrubMessage(std::string(message))) 54 | { 55 | } 56 | 57 | duration_t ChatMessageAction::GetMinInterval() const 58 | { 59 | return 1s; // Actual cooldown is 0.66 seconds 60 | } 61 | 62 | std::string_view ChatMessageAction::GetCommand(ChatMessageType type) 63 | { 64 | switch (type) 65 | { 66 | case ChatMessageType::Public: return "say"sv; 67 | case ChatMessageType::Team: return "say_team"sv; 68 | case ChatMessageType::Party: return "say_party"sv; 69 | 70 | default: 71 | throw std::invalid_argument("Unhandled/invalid ChatMessageType"); 72 | } 73 | } 74 | 75 | std::string ChatMessageAction::ScrubMessage(std::string msg) 76 | { 77 | msg.erase(std::remove_if(msg.begin(), msg.end(), 78 | [](char c) 79 | { 80 | return 81 | c == '\r' || 82 | c == '\0' || 83 | c == '\n'; 84 | }), msg.end()); 85 | 86 | for (auto& c : msg) 87 | { 88 | if (c == '"') 89 | c = '\''; 90 | } 91 | 92 | return "\""s << msg << '"'; 93 | } 94 | 95 | void LobbyUpdateAction::WriteCommands(ICommandWriter& writer) const 96 | { 97 | writer.Write("tf_lobby_debug"); 98 | } 99 | 100 | void tf2_bot_detector::to_json(nlohmann::json& j, const KickReason& d) 101 | { 102 | switch (d) 103 | { 104 | case KickReason::Other: j = "other"; break; 105 | case KickReason::Cheating: j = "cheating"; break; 106 | case KickReason::Idle: j = "idle"; break; 107 | case KickReason::Scamming: j = "scamming"; break; 108 | } 109 | } 110 | 111 | void tf2_bot_detector::from_json(const nlohmann::json& j, KickReason& d) 112 | { 113 | const auto value = j.get(); 114 | 115 | if (value == "other") 116 | d = KickReason::Other; 117 | else if (value == "cheating") 118 | d = KickReason::Cheating; 119 | else if (value == "idle") 120 | d = KickReason::Idle; 121 | else if (value == "scamming") 122 | d = KickReason::Scamming; 123 | else 124 | throw std::invalid_argument(mh::format("{}: unexpected value {}", MH_SOURCE_LOCATION_CURRENT(), std::quoted(value))); 125 | } 126 | -------------------------------------------------------------------------------- /tf2_bot_detector/ConsoleLog/IConsoleLine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Clock.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tf2_bot_detector 10 | { 11 | class MainWindow; 12 | class Settings; 13 | class IWorldState; 14 | 15 | enum class ConsoleLineType 16 | { 17 | Generic, 18 | Chat, 19 | Ping, 20 | LobbyStatusFailed, 21 | LobbyChanged, 22 | DifferingLobbyReceived, 23 | LobbyHeader, 24 | LobbyMember, 25 | PartyHeader, 26 | PlayerStatus, 27 | PlayerStatusIP, 28 | PlayerStatusShort, 29 | PlayerStatusCount, 30 | PlayerStatusMapPosition, 31 | ClientReachedServerSpawn, 32 | KillNotification, 33 | CvarlistConvar, 34 | VoiceReceive, 35 | EdictUsage, 36 | SplitPacket, 37 | SVC_UserMessage, 38 | ConfigExec, 39 | TeamsSwitched, 40 | Connecting, 41 | HostNewGame, 42 | GameQuit, 43 | QueueStateChange, 44 | InQueue, 45 | ServerJoin, 46 | ServerDroppedPlayer, 47 | MatchmakingBannedTime, 48 | 49 | NetStatusConfig, 50 | NetLatency, 51 | NetLoss, 52 | NetPacketsTotal, 53 | NetPacketsPerClient, 54 | NetDataTotal, 55 | NetDataPerClient, 56 | 57 | NetChannelOnline, 58 | NetChannelReliable, 59 | NetChannelLatencyLoss, 60 | NetChannelPackets, 61 | NetChannelChoke, 62 | NetChannelFlow, 63 | NetChannelTotal, 64 | }; 65 | 66 | enum class ConsoleLineOrdering 67 | { 68 | Auto, 69 | First, 70 | Last, 71 | }; 72 | 73 | struct ConsoleLineTryParseArgs 74 | { 75 | std::string_view m_Text; 76 | time_point_t m_Timestamp; 77 | IWorldState& m_World; 78 | }; 79 | 80 | class IConsoleLine : public std::enable_shared_from_this 81 | { 82 | public: 83 | IConsoleLine(time_point_t timestamp); 84 | virtual ~IConsoleLine() = default; 85 | 86 | virtual ConsoleLineType GetType() const = 0; 87 | virtual bool ShouldPrint() const { return true; } 88 | 89 | struct PrintArgs 90 | { 91 | const Settings& m_Settings; 92 | IWorldState& m_WorldState; 93 | MainWindow& m_MainWindow; 94 | }; 95 | virtual void Print(const PrintArgs& args) const = 0; 96 | 97 | static std::shared_ptr ParseConsoleLine(const std::string_view& text, time_point_t timestamp, IWorldState& world); 98 | 99 | time_point_t GetTimestamp() const { return m_Timestamp; } 100 | 101 | protected: 102 | using TryParseFunc = std::shared_ptr(*)(const ConsoleLineTryParseArgs& args); 103 | struct ConsoleLineTypeData 104 | { 105 | TryParseFunc m_TryParseFunc = nullptr; 106 | const std::type_info* m_TypeInfo = nullptr; 107 | 108 | size_t m_AutoParseSuccessCount = 0; 109 | bool m_AutoParse = true; 110 | }; 111 | 112 | //static const ConsoleLineTypeData* GetTypeData() { return s_TypeData; } 113 | static void AddTypeData(ConsoleLineTypeData data); 114 | 115 | private: 116 | time_point_t m_Timestamp; 117 | 118 | static std::list& GetTypeData(); 119 | inline static ConsoleLineTypeData* s_TypeData = nullptr; 120 | inline static size_t s_TotalParseCount = 0; 121 | }; 122 | 123 | template 124 | class ConsoleLineBase : public IConsoleLine 125 | { 126 | public: 127 | ConsoleLineBase(time_point_t timestamp) : IConsoleLine(timestamp) {} 128 | 129 | private: 130 | struct AutoRegister 131 | { 132 | AutoRegister() 133 | { 134 | AddTypeData(ConsoleLineTypeData 135 | { 136 | .m_TryParseFunc = &TSelf::TryParse, 137 | .m_TypeInfo = &typeid(TSelf), 138 | .m_AutoParse = AutoParse 139 | }); 140 | } 141 | 142 | } inline static s_AutoRegister; 143 | }; 144 | } 145 | --------------------------------------------------------------------------------