diff --git a/ai.sp b/ai.sp new file mode 100644 index 0000000..f4495d2 --- /dev/null +++ b/ai.sp @@ -0,0 +1,339 @@ +#pragma semicolon 1 +#pragma newdecls required + +/* SM Includes */ +#include +#include +#include +#define TIME_TO_TICK(%1) (RoundToNearest((%1) / GetTickInterval())) +#define TICK_TO_TIME(%1) ((%1) * GetTickInterval()) +#define IS_CLIENT(%1) (1 <= %1 <= MaxClients) +#define ABS(%1) ((%1)>0 ? (%1) : -(%1)) +/* Plugin Info */ +public Plugin myinfo = +{ + name = "Crow Aimbot Detector", + author = "huoji", + description = "Analyzes clients to detect aimbots", + version = "0.0.1", + url = "http://key08.com/" +}; + +/* Globals */ +#define AIM_ANGLE_CHANGE 15 // Max angle change that a player should snap +#define AIM_BAN_MIN 4 // Minimum number of detections before an auto-ban is allowed +#define AIM_MIN_DISTANCE 50.0 // Minimum distance acceptable for a detection. +Handle g_IgnoreWeapons = INVALID_HANDLE; + +float g_fEyeAngles[MAXPLAYERS+1][64][3]; + +float g_Sensitivity[MAXPLAYERS + 1]; +float g_mYaw[MAXPLAYERS + 1]; +float g_mPitch[MAXPLAYERS + 1]; + +int g_iEyeIndex[MAXPLAYERS+1]; +int g_iMouse[MAXPLAYERS+1][64][2]; +int g_iAimDetections[MAXPLAYERS+1]; +int g_iAimbotBan = 0; +int g_iMaxAngleHistory; +int g_cheat = 0; +bool g_bIsShoting[MAXPLAYERS+1][64]; +/* Plugin Functions */ +public void OnPluginStart() +{ + // Store no more than 300ms worth of angle history. + if ((g_iMaxAngleHistory = TIME_TO_TICK(0.3)) > sizeof(g_fEyeAngles[])) + { + g_iMaxAngleHistory = sizeof(g_fEyeAngles[]); + } + + // Weapons to ignore when analyzing. + g_IgnoreWeapons = CreateTrie(); + + SetTrieValue(g_IgnoreWeapons, "weapon_knife", 1); + SetTrieValue(g_IgnoreWeapons, "weapon_taser", 1); + + // Hooks. + HookEntityOutput("trigger_teleport", "OnEndTouch", Teleport_OnEndTouch); + HookEvent("player_spawn", Event_PlayerSpawn, EventHookMode_Post); + HookEvent("player_death", Event_PlayerDeath, EventHookMode_Post); + HookEventEx("cs_intermission", Event_Intermission); + g_cheat = CreateConVar("sm_crowai_cheat", "0", "0 = no cheat , 1 = cheating.", _, true, 0.0, true, 1.0); + PrintToServer("[CrowAi] Init Success!"); +} +void OnFileUploadCallBack(HTTPStatus status, any value) +{ + if (status != HTTPStatus_OK) { + PrintToServer("文件上传失败"); + return; + } + + PrintToServer("Upload complete"); +} +public Action Event_Intermission(Handle event, const char[] name, bool dontBroadcast) +{ + PrintToChatAll("比赛结束!上传反作弊数据中!"); + //上传数据 + return Plugin_Continue; +} +stock bool IsClientNew(int client) +{ + // Client must be ingame. + return IsFakeClient(client) || GetGameTime() > GetClientTime(client); +} + +stock void ZeroVector(float vec[3]) +{ + vec[0] = vec[1] = vec[2] = 0.0; +} + +stock bool IsVectorZero(const float vec[3]) +{ + return vec[0] == 0.0 && vec[1] == 0.0 && vec[2] == 0.0; +} + +public void OnClientPutInServer(int client) +{ + if (IsClientNew(client)) + { + g_iAimDetections[client] = 0; + Aimbot_ClearAngles(client); + } +} + + +public void Teleport_OnEndTouch(const char[] output, int caller, int activator, float delay) +{ + /* A client is being teleported in the map. */ + if (IS_CLIENT(activator) && IsClientConnected(activator)) + { + Aimbot_ClearAngles(activator); + CreateTimer(0.1 + delay, Timer_ClearAngles, GetClientUserId(activator), TIMER_FLAG_NO_MAPCHANGE); + } +} + +public Action Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) +{ + int userid = event.GetInt("userid"); + int client = GetClientOfUserId(userid); + + if (IS_CLIENT(client)) + { + Aimbot_ClearAngles(client); + CreateTimer(0.1, Timer_ClearAngles, userid, TIMER_FLAG_NO_MAPCHANGE); + } +} + +public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) +{ + char sWeapon[32], dummy; + //GetEventString(event, "weapon", sWeapon, sizeof(sWeapon)); + event.GetString("weapon", sWeapon, sizeof(sWeapon)); + + if (GetTrieValue(g_IgnoreWeapons, sWeapon, dummy)) + { + return; + } + if (GameRules_GetProp("m_bWarmupPeriod") == 1) + { + return; + } + int victim = GetClientOfUserId(event.GetInt("userid")); + int attacker = GetClientOfUserId(event.GetInt("attacker")); + if (IS_CLIENT(attacker) && victim != attacker && IsClientInGame(attacker)) + { + float vVictim[3], vAttacker[3]; + GetClientAbsOrigin(victim, vVictim); + GetClientAbsOrigin(attacker, vAttacker); + if (GetVectorDistance(vVictim, vAttacker) >= AIM_MIN_DISTANCE) + { + Aimbot_AnalyzeAngles(attacker); + } + } +} + +public Action Timer_ClearAngles(Handle timer, any userid) +{ + /* Delayed because the client's angles can sometimes "spin" after being teleported. */ + int client = GetClientOfUserId(userid); + + if (IS_CLIENT(client)) + { + Aimbot_ClearAngles(client); + } + + return Plugin_Stop; +} + +public Action Timer_DecreaseCount(Handle timer, any userid) +{ + /* Decrease the detection count by 1. */ + int client = GetClientOfUserId(userid); + + if (IS_CLIENT(client) && g_iAimDetections[client]) + { + g_iAimDetections[client]--; + } + + return Plugin_Stop; +} +int m_iShotsFired(int client) +{ + return GetEntPropEnt(client, Prop_Send, "m_iShotsFired"); +} +float m_aimPunchAngleX(int client) +{ + float punch[3]; + GetEntPropVector(client, Prop_Send, "m_aimPunchAngle",punch); + return punch[0]; +} +float m_aimPunchAngleY(int client) +{ + float punch[3]; + GetEntPropVector(client, Prop_Send, "m_aimPunchAngle",punch); + return punch[1]; +} +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]) +{ + g_fEyeAngles[client][g_iEyeIndex[client]] = angles; + g_iMouse[client][g_iEyeIndex[client]] = mouse; + g_bIsShoting[client][g_iEyeIndex[client]] = false; + if(m_iShotsFired(client) > 2) + { + g_fEyeAngles[client][g_iEyeIndex[client]][0] += m_aimPunchAngleX(client) * 2; + g_fEyeAngles[client][g_iEyeIndex[client]][1] += m_aimPunchAngleY(client) * 2; + g_bIsShoting[client][g_iEyeIndex[client]] = true; + } + + if (++g_iEyeIndex[client] == g_iMaxAngleHistory) + { + g_iEyeIndex[client] = 0; + } + return Plugin_Continue; +} +public void ConVar_QueryClient(QueryCookie cookie, int client, ConVarQueryResult result, const char[] cvarName, const char[] cvarValue) +{ + if(!IsFakeClient(client)) + { + if(result == ConVarQuery_Okay) + { + if(StrEqual("sensitivity", cvarName)) + { + g_Sensitivity[client] = StringToFloat(cvarValue); + }else if(StrEqual("m_yaw", cvarName)) + { + g_mYaw[client] = StringToFloat(cvarValue); + } + else if(StrEqual("m_pitch", cvarName)) + { + g_mPitch[client] = StringToFloat(cvarValue); + } + } + } +} + +void Aimbot_AnalyzeAngles(int client) +{ + if(IsFakeClient(client)) + return; + /* Analyze the client to see if their angles snapped. */ + float vLastAngles[3], vAngles[3], fAngleDiff,Last_AngleDiff; + int idx = g_iEyeIndex[client]; + QueryClientConVar(client, "sensitivity", ConVar_QueryClient, client); + QueryClientConVar(client, "m_yaw", ConVar_QueryClient, client); + QueryClientConVar(client, "m_pitch", ConVar_QueryClient, client); + int num_of_detect = 0; + int num_of_detect_smooth = 0; + //PrintToChatAll("[CrowAntiCheat] 开始分析 %N",client); + char steamid[64]; + GetClientAuthId(client, AuthId_SteamID64, steamid, sizeof(steamid)); + char path_name[PLATFORM_MAX_PATH]; + Format(path_name, sizeof(path_name), "addons\\sourcemod\\ai\\ai_%s.csv",steamid); + bool file_exists = true; + if(!FileExists(path_name)){ + file_exists = false; + } + Handle handle_file = OpenFile(path_name, "a+"); + char file_head[19999]; + if(file_exists == false){ + bool first_meme = false; + for (int i = 0; i < g_iMaxAngleHistory; i++) + { + if (i == 0) + { + continue; + } + if(first_meme == false){ + first_meme = true; + Format(file_head, sizeof(file_head), "%d_x,%d_y,%d_diff,%d_mx,%d_my",i,i,i,i,i); + }else{ + Format(file_head, sizeof(file_head), "%s,%d_x,%d_y,%d_diff,%d_mx,%d_my",file_head,i,i,i,i,i); + } + + } + WriteFileLine(handle_file, "%s,is_cheat",file_head); + //PrintToChatAll("max_angle_tick: %d",g_iMaxAngleHistory); + } + bool first_meme2 = false; + char cheat_data[19999]; + for (int i = 0; i < g_iMaxAngleHistory; i++) + { + if (idx == g_iMaxAngleHistory) + { + idx = 0; + } + if (IsVectorZero(g_fEyeAngles[client][idx])) + { + break; + } + // Nothing to compare on the first iteration. + if (i == 0) + { + vLastAngles = g_fEyeAngles[client][idx]; + idx++; + continue; + } + vAngles = g_fEyeAngles[client][idx]; + fAngleDiff = GetVectorDistance(vLastAngles, vAngles); + // If the difference is being reported higher than 180, get the 'real' value. + if (fAngleDiff > 180) + { + fAngleDiff = FloatAbs(fAngleDiff - 360); + } + + if (fAngleDiff > AIM_ANGLE_CHANGE) + { + PrintToChatAll("[CrowAI] %N 暴力自瞄",client); + break; + } + int mouse[2]; + mouse = g_iMouse[client][idx]; + if(first_meme2 == false){ + first_meme2 = true; + Format(cheat_data, sizeof(cheat_data), "%f,%f,%f,%d,%d",g_fEyeAngles[client][idx][0],g_fEyeAngles[client][idx][1],fAngleDiff,mouse[0],mouse[1]); + }else{ + Format(cheat_data, sizeof(cheat_data), "%s,%f,%f,%f,%d,%d",cheat_data,g_fEyeAngles[client][idx][0],g_fEyeAngles[client][idx][1],fAngleDiff,mouse[0],mouse[1]); + } + + vLastAngles = vAngles; + Last_AngleDiff = fAngleDiff; + idx++; + } + int is_cheat = GetConVarInt(g_cheat); + WriteFileLine(handle_file, "%s,%d",cheat_data,is_cheat); + //PrintToChatAll("[CrowAI] %N 分析完毕",client); + CloseHandle(handle_file); +} + +void Aimbot_ClearAngles(int client) +{ + /* Clear angle history and reset the index. */ + g_iEyeIndex[client] = 0; + + for (int i = 0; i < g_iMaxAngleHistory; i++) + { + ZeroVector(g_fEyeAngles[client][i]); + g_iMouse[client][i][0] = 0; + g_iMouse[client][i][1] = 0; + } +} diff --git a/test.py b/test.py new file mode 100644 index 0000000..8ef48aa --- /dev/null +++ b/test.py @@ -0,0 +1,107 @@ +import os +import tensorflow as tf +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import functools +import csv +test_file_path = [ + r"I:\csgoserver\steamcmd\steamapps\common\Counter-Strike Global Offensive Beta - Dedicated Server\csgo\addons\sourcemod\测试数据1.csv"] + +train_file_path = [ + r"I:\csgoserver\steamcmd\steamapps\common\Counter-Strike Global Offensive Beta - Dedicated Server\csgo\addons\sourcemod\训练数据.csv"] + +csv_data = [] +csv_data_x = [] +csv_data_y = [] +csv_data_all_num = [] +csv_data_y_avg = {} +with open(train_file_path[0], 'r') as f: + reader = csv.reader(f) + for row in reader: + csv_data.append(row) +csv_data_x = csv_data[0] +for index in range(1, len(csv_data)): + for zeus in range(len(csv_data[index])): + if csv_data_x[zeus] == 'is_cheat': + continue + if csv_data_x[zeus] in csv_data_y_avg.keys(): + csv_data_y_avg[csv_data_x[zeus]] = float( + csv_data_y_avg[csv_data_x[zeus]]) + float(csv_data[index][zeus]) + + else: + csv_data_y_avg[csv_data_x[zeus]] = float(csv_data[index][zeus]) +for key in csv_data_y_avg: + csv_data_y_avg[key] = float(csv_data_y_avg[key]) / float(len(csv_data) - 1) + +print(csv_data_y_avg) + + +def process_continuous_data(mean, data): + # 标准化数据 + data = tf.cast(data, tf.float32) * 1/(2*mean) + return tf.reshape(data, [-1, 1]) + + +def create_model(): + numerical_columns = [] + for feature in csv_data_y_avg.keys(): + num_col = tf.feature_column.numeric_column( + feature, normalizer_fn=functools.partial(process_continuous_data, csv_data_y_avg[feature])) + numerical_columns.append(num_col) + preprocessing_layer = tf.keras.layers.DenseFeatures(numerical_columns) + model = tf.keras.models.Sequential([ + preprocessing_layer, + tf.keras.layers.Dense(128, activation='relu'), + tf.keras.layers.Dense(128, activation='relu'), + tf.keras.layers.Dense(1, activation='sigmoid'), + ]) + + model.compile(optimizer='adam', + loss=tf.losses.BinaryCrossentropy( + from_logits=True), + metrics=['accuracy']) + + return model + + +def get_dataset(file_path): + dataset = tf.data.experimental.make_csv_dataset( + file_path, + batch_size=32, + label_name='is_cheat', + na_value="?", + num_epochs=1, + ignore_errors=True) + return dataset + + +def start_predict(): + test_data = get_dataset(test_file_path) + print(test_data) + test_loss, test_accuracy = model.evaluate(test_data) + predict_data = model.predict(test_data) + print("anti-aimbot model, accuracy:{:5.2f}%".format(100 * test_accuracy)) + num_cheat = 0 + for pre, result in zip(predict_data[:20], list(test_data)[0][1][:20]): + num_access = 100 * pre[0] + if num_access >= 60: + num_cheat = num_cheat + 1 + print("player is aimbot :{:5.2f}% ".format( + num_access), " is cheat: ", ("yes" if bool(result) else "no")) + + print("result:", num_cheat) + if len(predict_data) < 20: + print("数据太少,无法辨别") + if num_cheat >= (len(predict_data) / 2) - 2: + print("此人微自瞄") + else: + print("此人不是微自瞄") + + +model = create_model() +model.load_weights('./save/model_weight') +start_predict() +while True: + input("按任意键开始预测") + start_predict() diff --git a/train.py b/train.py new file mode 100644 index 0000000..d49bb17 --- /dev/null +++ b/train.py @@ -0,0 +1,103 @@ +import tensorflow as tf +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt +import functools +import csv +import time +from tensorflow.keras.callbacks import TensorBoard + + +LABEL_COLUMN = 'is_cheat' + +# "I:\csgoserver\steamcmd\steamapps\common\Counter-Strike Global Offensive Beta - Dedicated Server\csgo\addons\sourcemod\训练数据.csv" +train_file_path = [ + r"I:\csgoserver\steamcmd\steamapps\common\Counter-Strike Global Offensive Beta - Dedicated Server\csgo\addons\sourcemod\训练数据.csv"] +test_file_path = [ + r"I:\csgoserver\steamcmd\steamapps\common\Counter-Strike Global Offensive Beta - Dedicated Server\csgo\addons\sourcemod\测试数据1.csv"] +csv_data = [] +csv_data_x = [] +csv_data_y = [] +csv_data_all_num = [] +csv_data_y_avg = {} +with open(train_file_path[0], 'r') as f: + reader = csv.reader(f) + for row in reader: + csv_data.append(row) +csv_data_x = csv_data[0] +for index in range(1, len(csv_data)): + for zeus in range(len(csv_data[index])): + if csv_data_x[zeus] == 'is_cheat': + continue + if csv_data_x[zeus] in csv_data_y_avg.keys(): + csv_data_y_avg[csv_data_x[zeus]] = float( + csv_data_y_avg[csv_data_x[zeus]]) + float(csv_data[index][zeus]) + + else: + csv_data_y_avg[csv_data_x[zeus]] = float(csv_data[index][zeus]) +for key in csv_data_y_avg: + csv_data_y_avg[key] = float(csv_data_y_avg[key]) / float(len(csv_data) - 1) + +print(csv_data_y_avg) + + +def get_dataset(file_path): + dataset = tf.data.experimental.make_csv_dataset( + file_path, + batch_size=32, + label_name=LABEL_COLUMN, + na_value="?", + num_epochs=1, + ignore_errors=True) + return dataset + + +def process_continuous_data(mean, data): + # 标准化数据 + data = tf.cast(data, tf.float32) * 1/(2*mean) + return tf.reshape(data, [-1, 1]) + + +raw_train_data = get_dataset(train_file_path) +raw_test_data = get_dataset(test_file_path) +examples, labels = next(iter(raw_train_data)) # 第一个批次 +print("EXAMPLES: \n", examples, "\n") +print("LABELS: \n", labels) + +numerical_columns = [] +for feature in csv_data_y_avg.keys(): + num_col = tf.feature_column.numeric_column( + feature, normalizer_fn=functools.partial(process_continuous_data, csv_data_y_avg[feature])) + numerical_columns.append(num_col) + +preprocessing_layer = tf.keras.layers.DenseFeatures(numerical_columns) +model = tf.keras.Sequential([ + preprocessing_layer, + tf.keras.layers.Dense(128, activation='relu'), + tf.keras.layers.Dense(128, activation='relu'), + tf.keras.layers.Dense(1, activation='sigmoid'), +]) + +model_name = "anti_aimbot-{}".format(int(time.time())) +TensorBoardcallback = TensorBoard( + log_dir='logs/{}'.format(model_name), + histogram_freq=1, batch_size=32, + write_graph=True, write_grads=False, write_images=True, + embeddings_freq=0, embeddings_layer_names=None, + embeddings_metadata=None, embeddings_data=None, update_freq=500 +) + +model.compile( + loss='binary_crossentropy', + optimizer='adam', + metrics=['accuracy']) +train_data = raw_train_data.shuffle(1500) +test_data = raw_test_data +history = model.fit(train_data, epochs=85, callbacks=[TensorBoardcallback]) +test_loss, test_accuracy = model.evaluate(test_data) +predict_data = model.predict(test_data) +print('anti-aimbot: \n\nTest Loss {}, Test Accuracy {}'.format(test_loss, test_accuracy)) +for pre, result in zip(predict_data[:20], list(test_data)[0][1][:20]): + print("player is aimbot :{:5.2f}% ".format( + 100 * pre[0]), " is cheat: ", ("yes" if bool(result) else "no")) +model.save_weights('./save/model_weight')