#pragma semicolon 1
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <reapi>
#define ClearBit(%0,%1) ((%0) &= ~(1 << (%1)))
#define IsSetBit(%0,%1) ((%0) & (1 << (%1)))
#define InvertBit(%0,%1) ((%0) ^= (1 << (%1)))
#define ACCESS_LEVEL ADMIN_CFG // access for menu & full check
#define MAX_METHODS 3
#define MAX_FIRST_WARNS 8
#define MAX_SECOND_WARNS 15
#define MAX_THIRD_WARNS 7
const Float:ClearWarns_Time = 100.0;
enum {
FIRST_TYPE = 0,
SECOND_TYPE,
THIRD_TYPE
};
new g_iChecker[MAX_PLAYERS + 1], bool:g_bFullChecks[MAX_PLAYERS + 1];
new g_iAttention[MAX_METHODS][MAX_PLAYERS + 1], g_iWarns[MAX_METHODS][MAX_PLAYERS + 1];
new g_iBitCanChecks, g_iBitCheckHolding;
new g_iMenuPage[MAX_PLAYERS + 1];
new g_iMenuPlayers[MAX_PLAYERS + 1][MAX_PLAYERS];
public plugin_init() {
register_plugin("Easy KBChecker", "1.0", "bristol");
register_clcmd("easy_kb", "@Command_Menu", ACCESS_LEVEL);
register_clcmd("kb_menu", "@Command_Menu", ACCESS_LEVEL);
register_clcmd("kb", "@Command_Menu", ACCESS_LEVEL);
register_clcmd("say /kb", "@Command_Menu", ACCESS_LEVEL);
register_concmd("kb_full_button", "@FullChecks", ACCESS_LEVEL, "<name or #userid>");
RegisterHookChain(RG_CBasePlayer_TakeDamage, "@CBasePlayer_TakeDamage_Pre", false);
register_forward(FM_AddToFullPack, "@FM_AddToFullPack_Pre", false);
register_forward(FM_CmdStart, "@Fwd_CmdStart", true);
register_menucmd(register_menuid("EasyKB_Players"), 1023, "@HandleMenu_Players");
set_task(ClearWarns_Time, "@ClearWarns", 7319916815 ,_ ,_ ,"b");
}
public client_disconnected(id) {
ResetItems(id);
}
public client_putinserver(id) {
ResetItems(id);
}
ResetItems(id) {
g_iChecker[id] = 0;
g_bFullChecks[id] = false;
ClearBit(g_iBitCanChecks, id);
ClearBit(g_iBitCheckHolding, id);
for (new i; i < MAX_METHODS; i++) {
g_iWarns[i][id] = 0;
g_iAttention[i][id] = 0;
}
}
@FM_AddToFullPack_Pre(es, e, ent, host, flags, player, pSet) {
if(player && host == ent && IsSetBit(g_iBitCanChecks, host)) {
g_iChecker[host]++;
}
return FMRES_IGNORED;
}
@CBasePlayer_TakeDamage_Pre(id, inflictor, attacker, Float:damage, bitsDamageType) {
if (!is_user_connected(attacker)) {
return;
}
if (get_entvar(attacker, var_button) & IN_ATTACK || get_member(attacker, m_iTeam) == get_member(id, m_iTeam)) {
return;
}
if (is_nullent(inflictor)) {
return;
}
if (bitsDamageType & DMG_BLAST || bitsDamageType & DMG_GRENADE) {
return;
}
if (Float:get_entvar(id, var_health) > 65.0) {
return;
}
InvertBit(g_iBitCanChecks, attacker);
}
@Fwd_CmdStart(id, uc_handle, random_seed) {
static buttons;
static oldbuttons;
buttons = get_uc(uc_handle, UC_Buttons);
oldbuttons = get_entvar(id, var_oldbuttons);
if (buttons & IN_ATTACK2 && !(oldbuttons & IN_ATTACK2)) {
if (IsSetBit(g_iBitCheckHolding, id)) {
ClearBit(g_iBitCanChecks, id);
}
}
if (buttons & IN_ATTACK2 && oldbuttons & IN_ATTACK2) {
if (IsSetBit(g_iBitCanChecks, id)) {
InvertBit(g_iBitCheckHolding, id);
}
}
if (oldbuttons & IN_ATTACK2 && !(buttons & IN_ATTACK2)) {
if (IsSetBit(g_iBitCanChecks, id)) {
@CheckPlayer_Knifebot(id);
}
ClearBit(g_iBitCanChecks, id);
ClearBit(g_iBitCheckHolding, id);
g_iChecker[id] = 0;
}
}
@CheckPlayer_Knifebot(id) {
new name[MAX_NAME_LENGTH];
new authid[32], ip[32];
get_entvar(id, var_netname, name, charsmax(name));
get_user_authid(id, authid, charsmax(authid));
get_user_ip(id, ip, charsmax(ip), 1);
//Type #1
if (g_iChecker[id] < 2) {
g_iAttention[FIRST_TYPE][id]++;
g_iWarns[FIRST_TYPE][id]++;
if (g_iAttention[FIRST_TYPE][id] > 30) {
g_iWarns[FIRST_TYPE][id]++;
}
if (g_iWarns[FIRST_TYPE][id] >= MAX_FIRST_WARNS) {
Checker_Punishment(id, FIRST_TYPE, name, authid, ip);
}
Checker_LogAttacks(id, FIRST_TYPE, name, authid);
}
//Type #2
else if (g_iChecker[id] == 4) {
g_iAttention[SECOND_TYPE][id]++;
g_iWarns[SECOND_TYPE][id]++;
if (g_iWarns[SECOND_TYPE][id] >= MAX_SECOND_WARNS) {
Checker_Punishment(id, SECOND_TYPE, name, authid, ip);
}
Checker_LogAttacks(id, SECOND_TYPE, name, authid);
}
//Type #3
else if (g_iChecker[id] >= 95 && g_iChecker[id] <= 110) {
g_iAttention[THIRD_TYPE][id]++;
g_iWarns[THIRD_TYPE][id]++;
if (g_iWarns[THIRD_TYPE][id] >= MAX_THIRD_WARNS) {
Checker_Punishment(id, THIRD_TYPE, name, authid, ip);
}
Checker_LogAttacks(id, THIRD_TYPE, name, authid);
@ReduceExtraWarns(id);
}
else {
@ReduceExtraWarns(id);
}
if (g_bFullChecks[id]) {
Log("FullChecks: %s *%s* [attack: %d] [w1: %d] [w2: %d] [w3: %d]", name, authid, g_iChecker[id], g_iWarns[FIRST_TYPE][id], g_iWarns[SECOND_TYPE][id], g_iWarns[THIRD_TYPE][id]);
for (new i = 1; i <= MaxClients; i++) {
if (!is_user_connected(i)) {
continue;
}
if (!(get_user_flags(i) & ACCESS_LEVEL)) {
continue;
}
client_print_color(i, print_team_red, "^1[^4AC^1] FullChecks: ^3%s ^1[attack: ^4%d^1] [w1: %s%d^1] [w2: %s%d^1] [w3: %s%d^1]",
name,
g_iChecker[id],
g_iWarns[FIRST_TYPE][id] > 0 ? "^4" : "^1",
g_iWarns[FIRST_TYPE][id],
g_iWarns[SECOND_TYPE][id] > 0 ? "^4" : "^1",
g_iWarns[SECOND_TYPE][id],
g_iWarns[THIRD_TYPE][id] > 0 ? "^4" : "^1",
g_iWarns[THIRD_TYPE][id]
);
}
}
}
Checker_LogAttacks(id, type, name[], authid[]) {
if (!g_bFullChecks[id]) {
Log("[Att_%d]: %s *%s* [attack: %d] [w1: %d] [w2: %d] [w3: %d] [att%d: %d]",
type+1, name, authid, g_iChecker[id], g_iWarns[FIRST_TYPE][id], g_iWarns[SECOND_TYPE][id], g_iWarns[THIRD_TYPE][id], type+1, g_iAttention[type][id]);
}
if (g_iWarns[FIRST_TYPE][id] > 4 || g_iWarns[SECOND_TYPE][id] > 7 || g_iWarns[THIRD_TYPE][id] > 3) {
for (new i = 1; i <= MaxClients; i++) {
if (!is_user_connected(i)) {
continue;
}
if (!(get_user_flags(i) & ACCESS_LEVEL)) {
continue;
}
client_print_color(i, print_team_red, "^1[^4AC^1] KnifeBot detected on player ^3%s ^1[^4say /kb^1]", name);
}
}
}
Checker_Punishment(id, type, name[], authid[], ip[]) {
Log("[Banned_%d]: %s *%s* *%s* [attack: %d] [w1: %d] [w2: %d] [w3: %d]",
type+1, name, authid, ip, g_iChecker[id], g_iWarns[FIRST_TYPE][id], g_iWarns[SECOND_TYPE][id], g_iWarns[THIRD_TYPE][id]);
server_cmd("fb_ban 0 #%d ^"[UK AntiCheat] KnifeBot^"", get_user_userid(id));
server_exec();
}
@ReduceExtraWarns(id) {
if (g_iWarns[FIRST_TYPE][id] > 3) {
g_iWarns[FIRST_TYPE][id]--;
}
if (g_iWarns[SECOND_TYPE][id] > 2) {
g_iWarns[SECOND_TYPE][id]--;
}
}
@Command_Menu(id, bitsLevel) {
if (!(get_user_flags(id) & bitsLevel)) {
return PLUGIN_CONTINUE;
}
KB_PlayersMenu(id);
return PLUGIN_HANDLED;
}
@FullChecks(id, level, cid) {
if (!cmd_access(id, level, cid, 2)) {
return PLUGIN_HANDLED;
}
new arg[32];
read_argv(1, arg, charsmax(arg));
new player = cmd_target(id, arg, CMDTARGET_ALLOW_SELF);
if (!player) {
return PLUGIN_HANDLED;
}
new adminName[MAX_NAME_LENGTH], playerName[MAX_NAME_LENGTH];
get_entvar(id, var_netname, adminName, charsmax(adminName));
get_entvar(player, var_netname, playerName, charsmax(playerName));
g_bFullChecks[player] = !g_bFullChecks[player];
Log("[AC] %s %s FullChecks mode for player %s", adminName, g_bFullChecks[player] ? "enabled" : "disabled", playerName);
for (new i = 1; i <= MaxClients; i++) {
if (!is_user_connected(i)) {
continue;
}
if (!(get_user_flags(i) & ACCESS_LEVEL)) {
continue;
}
client_print_color(i, print_team_red, "^1[^4AC^1] ^3%s ^4%s ^1FullChecks mode for player ^3%s", adminName, g_bFullChecks[player] ? "enabled" : "disabled", playerName);
}
return PLUGIN_HANDLED;
}
KB_PlayersMenu(id, page = 0) {
if (page < 0) {
return PLUGIN_HANDLED;
}
new playersArray[MAX_PLAYERS];
new playersNum;
new name[MAX_NAME_LENGTH];
new text[MAX_MENU_LENGTH];
new target;
for (target = 1; target <= MaxClients; target++) {
if (!is_user_connected(target) || is_user_bot(target) || g_iWarns[FIRST_TYPE][target] < 1 && g_iWarns[SECOND_TYPE][target] < 1 && g_iWarns[THIRD_TYPE][target] < 1) {
continue;
}
playersArray[playersNum++] = target;
}
new i = min(page * 8, playersNum);
new start = i - (i % 8);
new end = min(start + 8, playersNum);
page = start / 8;
g_iMenuPlayers[id] = playersArray;
g_iMenuPage[id] = page;
new len = formatex(text, charsmax(text), "\yKnifeBot Detector \d%d/%d^n\d*\rButton detect\d*^n^n", page + 1, ((playersNum - 1) / 8) + 1);
new keys = MENU_KEY_0;
new item;
for (i = start; i < end; i++) {
target = playersArray[i];
get_entvar(target, var_netname, name, charsmax(name));
len += formatex(text[len], charsmax(text) - len, "\r%d. \w%s \r[\d%d/%d \r|\d %d/%d \r|\d %d/%d\r]^n", ++item, name, g_iWarns[FIRST_TYPE][target], MAX_FIRST_WARNS, g_iWarns[SECOND_TYPE][target], MAX_SECOND_WARNS, g_iWarns[THIRD_TYPE][target], MAX_THIRD_WARNS);
}
if (end < playersNum) {
formatex(text[len], charsmax(text) - len, "^n\r9. \wNext^n\r0. \w%s", page ? "Back" : "Exit");
keys |= MENU_KEY_9;
}
else {
formatex(text[len], charsmax(text) - len, "^n\r0. \w%s", page ? "Back" : "Exit");
}
show_menu(id, keys, text, -1, "EasyKB_Players");
return PLUGIN_HANDLED;
}
@HandleMenu_Players(id, key) {
switch (key) {
case 8: KB_PlayersMenu(id, ++g_iMenuPage[id]);
case 9: KB_PlayersMenu(id, --g_iMenuPage[id]);
}
return PLUGIN_HANDLED;
}
@ClearWarns() {
for (new i = 1; i <= MaxClients; i++) {
if (!is_user_connected(i)) {
continue;
}
for (new j; j < MAX_METHODS; j++) {
if (g_iWarns[j][i] > 0) {
g_iWarns[j][i]--;
}
}
}
log_amx("[Easy KB] Clear Warns!");
}
Log(const message_fmt[], any:...) {
static message[256];
vformat(message, sizeof(message) - 1, message_fmt, 2);
static filename[96];
static dir[64];
if(!dir[0]) {
get_basedir(dir, sizeof(dir) - 1);
add(dir, sizeof(dir) - 1, "/logs/knifebot_checker");
}
format_time(filename, sizeof(filename) - 1, "%Y_%m_%d");
format(filename, sizeof(filename) - 1, "%s/%s.log", dir, filename);
log_to_file(filename, "%s", message);
}