/* SourceMod Anti-Cheat Copyright (C) 2011-2016 SMAC Development Team Copyright (C) 2007-2011 CodingDirect LLC This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #pragma semicolon 1 #pragma newdecls required /* SM Includes */ #include #include #include /* Plugin Info */ public Plugin myinfo = { name = "SMAC AutoTrigger Detector", author = SMAC_AUTHOR, description = "Detects cheats that automatically press buttons for players", version = SMAC_VERSION, url = SMAC_URL }; /* Globals */ #define TRIGGER_DETECTIONS 20 // Amount of detections needed to perform action. #define MIN_JUMP_TIME 0.500 // Minimum amount of air-time for a jump to count. // Detection methods. #define METHOD_BUNNYHOP 0 #define METHOD_AUTOFIRE 1 #define METHOD_MAX 2 ConVar g_hCvarBan = null; int g_iDetections[METHOD_MAX][MAXPLAYERS+1]; int g_iAttackMax = 66; /* Plugin Functions */ public void OnPluginStart() { LoadTranslations("smac.phrases"); // Convars. g_hCvarBan = SMAC_CreateConVar("smac_autotrigger_ban", "0", "Automatically ban players on auto-trigger detections.", 0, true, 0.0, true, 1.0); // Initialize. g_iAttackMax = RoundToNearest(1.0 / GetTickInterval() / 3.0); CreateTimer(4.0, Timer_DecreaseCount, _, TIMER_REPEAT); } public void OnClientDisconnect_Post(int client) { for (int i = 0; i < METHOD_MAX; i++) { g_iDetections[i][client] = 0; } } public Action Timer_DecreaseCount(Handle timer) { for (int i = 0; i < METHOD_MAX; i++) { for (int j = 1; j <= MaxClients; j++) { if (g_iDetections[i][j]) { g_iDetections[i][j]--; } } } return Plugin_Continue; } public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) { static int iPrevButtons[MAXPLAYERS+1]; /* BunnyHop */ static float fCheckTime[MAXPLAYERS+1]; // Player didn't jump immediately after the last jump. if (!(buttons & IN_JUMP) && (GetEntityFlags(client) & FL_ONGROUND) && fCheckTime[client] > 0.0) { fCheckTime[client] = 0.0; } // Ignore this jump if the player is in a tight space or stuck in the ground. if ((buttons & IN_JUMP) && !(iPrevButtons[client] & IN_JUMP)) { // Player is on the ground and about to trigger a jump. if (GetEntityFlags(client) & FL_ONGROUND) { float fGameTime = GetGameTime(); // Player jumped on the exact frame that allowed it. if (fCheckTime[client] > 0.0 && fGameTime > fCheckTime[client]) { AutoTrigger_Detected(client, METHOD_BUNNYHOP); } else { fCheckTime[client] = fGameTime + MIN_JUMP_TIME; } } else { fCheckTime[client] = 0.0; } } /* Auto-Fire */ static int iAttackAmt[MAXPLAYERS+1]; static bool bResetNext[MAXPLAYERS+1]; if (((buttons & IN_ATTACK) && !(iPrevButtons[client] & IN_ATTACK)) || (!(buttons & IN_ATTACK) && (iPrevButtons[client] & IN_ATTACK))) { if (++iAttackAmt[client] >= g_iAttackMax) { AutoTrigger_Detected(client, METHOD_AUTOFIRE); iAttackAmt[client] = 0; } bResetNext[client] = false; } else if (bResetNext[client]) { iAttackAmt[client] = 0; bResetNext[client] = false; } else { bResetNext[client] = true; } iPrevButtons[client] = buttons; return Plugin_Continue; } void AutoTrigger_Detected(int client,int method) { if (!IsFakeClient(client) && IsPlayerAlive(client) && ++g_iDetections[method][client] >= TRIGGER_DETECTIONS) { char sMethod[32]; switch (method) { case METHOD_BUNNYHOP: { strcopy(sMethod, sizeof(sMethod), "BunnyHop"); } case METHOD_AUTOFIRE: { strcopy(sMethod, sizeof(sMethod), "Auto-Fire"); } } Handle info = CreateKeyValues(""); KvSetString(info, "method", sMethod); if (SMAC_CheatDetected(client, Detection_AutoTrigger, info) == Plugin_Continue) { SMAC_PrintAdminNotice("%t", "SMAC_AutoTriggerDetected", client, sMethod); if (GetConVarBool(g_hCvarBan)) { SMAC_LogAction(client, "was banned for using auto-trigger cheat: %s", sMethod); SMAC_Ban(client, "AutoTrigger Detection: %s", sMethod); } else { SMAC_LogAction(client, "is suspected of using auto-trigger cheat: %s", sMethod); } } CloseHandle(info); g_iDetections[method][client] = 0; } }