#pragma semicolon 1 #pragma newdecls required #define DEBUG #define PLUGIN_AUTHOR "jenz" #define PLUGIN_VERSION "2.2" #define generic_length 256 #include #include #include #include #include #include #include #include #include #include int target_client[5]; //4 autism bots. // ~5.3s of history at 66 tick. Plenty of headroom for approach + backlog + // resync cycles. At ~48 bytes/frame this is only a few hundred KB total. #define BUFFER_SIZE 350 enum struct FrameData { int buttons; float vel[3]; float angles[3]; float origin[3]; // where the real client actually was when this frame was recorded int impulse; int weaponSlot; // 0=none/untracked, 1=primary, 2=secondary, 3=knife, 4=grenade } FrameData g_Buffer[MAXPLAYERS+1][BUFFER_SIZE]; int g_WriteHead[MAXPLAYERS+1]; // where were writing next, indexed by REAL client int g_FrameCount[MAXPLAYERS+1]; // total frames captured, indexed by REAL client // --- movement state machine (bots only) --- #define BOT_STATE_IDLE 0 #define BOT_STATE_APPROACH 1 #define BOT_STATE_MIMIC 2 #define ARRIVE_RADIUS 18.0 // horizontal distance to anchor that counts as "arrived" #define ARRIVE_Z_SLACK 256.0 // max height difference to anchor that still counts as "arrived" #define APPROACH_SPEED 250.0 // forward move value while walking to the anchor // Must stay comfortably bigger than ARRIVE_RADIUS or the bot can flap // between MIMIC and APPROACH right at the boundary. #define DESYNC_RADIUS 64.0 // if actual pos strays this far from the frame its about to play, resync // Anti-loop / anti-stuck tuning #define RESYNC_COOLDOWN 3.0 // min seconds between two resyncs; drift during cooldown just keeps mimicking #define MAX_RESYNC_STRIKES 2 // this many resyncs on the same target -> give up, pick a new target #define STUCK_CHECK_INTERVAL 1.0 // seconds between stuck checks while approaching #define STUCK_MOVE_EPSILON 30.0 // if the bot moved less than this between checks, it counts as stuck #define MAX_STUCK_STRIKES 2 // first strike re-anchors + jumps, second strike drops the target int g_BotState[MAXPLAYERS+1]; // indexed by BOT client float g_AnchorPos[MAXPLAYERS+1][3]; // indexed by BOT client - the single fixed point it walks to int g_ReadHead[MAXPLAYERS+1]; // indexed by BOT client - read index into g_Buffer[target][] int g_LastWeaponSlot[MAXPLAYERS+1]; // indexed by BOT client - last weaponSlot category we told it to switch to // Anti-loop / anti-stuck state, all indexed by BOT client float g_LastResyncTime[MAXPLAYERS+1]; // GetGameTime() of the last drift-triggered resync int g_ResyncStrikes[MAXPLAYERS+1]; // consecutive resyncs against the current target float g_StuckCheckTime[MAXPLAYERS+1]; // GetGameTime() of the last stuck check float g_StuckCheckPos[MAXPLAYERS+1][3]; // where the bot was at the last stuck check int g_StuckStrikes[MAXPLAYERS+1]; // consecutive stuck detections while approaching bool g_WantJump[MAXPLAYERS+1]; // one-shot: press jump on the next approach tick (unstick aid) // Weapon category is cheap to get slightly stale, so we only actually // re-check it every WEAPON_CHECK_INTERVAL seconds per real client instead of // every tick, and just keep writing the cached value into each frame. #define WEAPON_CHECK_INTERVAL 5.0 int g_CachedWeaponSlot[MAXPLAYERS+1]; // indexed by REAL client - last computed category float g_NextWeaponCheck[MAXPLAYERS+1]; // indexed by REAL client - GetGameTime() of next allowed check int ports[7] = {48470, 48471, 48472, 48473, 48474, 48479, 48480}; //last three indexes are ports its sending udp from in plugin int server_ports[2] = {27015, 27035}; //server ports: ze, ze2 bool chat_cooldown = false; //socket for bot input Handle global_socket; //timer handle Handle g_hTimer_pressing = null; Handle g_hTimer_bot_connect = null; Handle g_hTimer_bot_connect2 = null; Handle g_hTimer_bot_connect3 = null; Handle g_hTimer_bot_connect4 = null; int specific_bot_player[MAXPLAYERS + 1]; public Plugin myinfo = { name = "autism bot simulate player movement", author = PLUGIN_AUTHOR, description = "just copies player movement to the autism bots. now also AI slopped.", version = PLUGIN_VERSION, url = "" }; public void OnPluginStart() { //talking RegConsoleCmd("sm_autism", cmd_talk, "talking to the bot through java application"); RegConsoleCmd("sm_botrtv", cmd_botrtv, "making bots rtv"); RegAdminCmd("sm_botstate", cmd_botstate, ADMFLAG_GENERIC, "debug: show state of each autism bot"); HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); //UDP connection connect_socket(); chat_cooldown = false; g_hTimer_bot_connect = CreateTimer(60.0, bot_check_connect, _, TIMER_REPEAT); CreateTimer(5.0, activate_timer2); CreateTimer(10.0, activate_timer3); CreateTimer(15.0, activate_timer4); g_hTimer_pressing = CreateTimer(2.0, check_team, _, TIMER_REPEAT); target_client[1] = -1; target_client[2] = -1; target_client[3] = -1; target_client[4] = -1; for (int i = 1; i <= MaxClients; i++) { if (IsValidClient(i)) { OnClientPostAdminCheck(i); } } } public Action activate_timer2(Handle timer, any data) { g_hTimer_bot_connect2 = CreateTimer(60.0, bot_check_connect2, _, TIMER_REPEAT); return Plugin_Continue; } public Action activate_timer3(Handle timer, any data) { g_hTimer_bot_connect3 = CreateTimer(60.0, bot_check_connect3, _, TIMER_REPEAT); return Plugin_Continue; } public Action activate_timer4(Handle timer, any data) { g_hTimer_bot_connect4 = CreateTimer(60.0, bot_check_connect4, _, TIMER_REPEAT); return Plugin_Continue; } public void OnPluginEnd() { if (g_hTimer_pressing != null) { delete g_hTimer_pressing; } if (g_hTimer_bot_connect != null) { delete g_hTimer_bot_connect; } if (g_hTimer_bot_connect2 != null) { delete g_hTimer_bot_connect2; } if (g_hTimer_bot_connect3 != null) { delete g_hTimer_bot_connect3; } if (g_hTimer_bot_connect4 != null) { delete g_hTimer_bot_connect4; } } // Classifies the clients currently held weapon into a slot category. This // servers loadout is fixed: raw inventory slot 0=primary, 1=secondary, // 2=knife, 3=HE grenade (no smoke/flash, no slot 4 ever exists), so we can // just compare the active weapon against each fixed slot directly. stock int GetActiveWeaponSlotCategory(int client) { int activeWeapon = GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon", 0); if (activeWeapon <= -1 || !IsValidEntity(activeWeapon)) { return 0; } for (int rawSlot = 0; rawSlot <= 3; rawSlot++) { if (activeWeapon == GetPlayerWeaponSlot(client, rawSlot)) { return rawSlot + 1; // 1=primary, 2=secondary, 3=knife, 4=grenade } } return 0; } public void OnPlayerRunCmdPost(int client, int buttons, int impulse, const float vel[3], const float angles[3], int weapon, int subtype, int cmdnum, int tickcount, int seed, const int mouse[2]) { if (!IsValidClient(client) || IsFakeClient(client)) { return; } int head = g_WriteHead[client]; g_Buffer[client][head].buttons = buttons; g_Buffer[client][head].impulse = impulse; g_Buffer[client][head].vel = vel; g_Buffer[client][head].angles = angles; GetClientAbsOrigin(client, g_Buffer[client][head].origin); float now = GetGameTime(); if (now >= g_NextWeaponCheck[client]) { g_CachedWeaponSlot[client] = GetActiveWeaponSlotCategory(client); g_NextWeaponCheck[client] = now + WEAPON_CHECK_INTERVAL; } g_Buffer[client][head].weaponSlot = g_CachedWeaponSlot[client]; g_WriteHead[client] = (head + 1) % BUFFER_SIZE; g_FrameCount[client]++; // If any bot currently mimicking/approaching this client is about to have // its unread backlog overwritten by this write, nudge its read head // forward so it never reads stale/overwritten frames. With 4 bots max // this loop is cheap. for (int slot = 1; slot <= 4; slot++) { if (target_client[slot] != client) { continue; } int bot = GetBotClientForSlot(slot); if (bot == -1 || g_BotState[bot] == BOT_STATE_IDLE) { continue; } if (g_ReadHead[bot] == g_WriteHead[client]) { g_ReadHead[bot] = (g_ReadHead[bot] + 1) % BUFFER_SIZE; } } } // Horizontal (XY) distance between two points. The old 3D check made bots // standing directly under/over an anchor on another floor "never arrive", // which also produced the crazy spinning (near-vertical direction vectors // have unstable yaw). stock float GetHorizontalDistance(const float a[3], const float b[3]) { float dx = a[0] - b[0]; float dy = a[1] - b[1]; return SquareRoot(dx * dx + dy * dy); } // Faces the bot toward `anchor` (yaw from horizontal components only, so a // mostly-vertical offset cant destabilize it) and pushes it forward. Shared // by the normal APPROACH state and by the mid-mimic resync path below. void ApplyApproachMovement(int bot, const float anchor[3], int &buttons, int &impulse, float vel[3], float angles[3]) { float botPos[3]; GetClientAbsOrigin(bot, botPos); float dir[3]; SubtractVectors(anchor, botPos, dir); dir[2] = 0.0; // yaw only - never derive facing from the vertical component impulse = 0; vel[1] = 0.0; vel[2] = 0.0; angles[0] = 0.0; angles[2] = 0.0; if (GetVectorLength(dir) < 1.0) { // Horizontally on top of the anchor but not "arrived" (must be a // height mismatch). Running forward would just grind into geometry // and the yaw would be garbage - hold position, keep current yaw, // and let stuck detection deal with it. buttons = 0; vel[0] = 0.0; return; } float faceAngles[3]; GetVectorAngles(dir, faceAngles); angles[1] = faceAngles[1]; buttons = IN_FORWARD; vel[0] = APPROACH_SPEED; if (g_WantJump[bot]) { buttons |= IN_JUMP; g_WantJump[bot] = false; } } void SyncWeaponSlot(int bot, int wantSlot) { if (wantSlot == 0 || wantSlot == g_LastWeaponSlot[bot]) { return; } int weapon = GetPlayerWeaponSlot(bot, wantSlot - 1); if (weapon != -1 && Weapon_IsValid(weapon)) { Client_SetActiveWeapon(bot, weapon); } g_LastWeaponSlot[bot] = wantSlot; } // Drops the bots current target entirely and goes idle. check_team runs // every 2 seconds and will hand it a fresh target (possibly the same player, // but with a brand-new anchor and clean strike counters). void GiveUpOnTarget(int bot) { int slot = specific_bot_player[bot]; if (slot != -1) { target_client[slot] = -1; } g_BotState[bot] = BOT_STATE_IDLE; } 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]) { if (specific_bot_player[client] == -1 || !IsValidClient(target_client[specific_bot_player[client]])) { return Plugin_Continue; } int target = target_client[specific_bot_player[client]]; float now = GetGameTime(); if (g_BotState[client] == BOT_STATE_APPROACH) { float botPos[3]; GetClientAbsOrigin(client, botPos); // Anti-stuck: if the bot has barely moved since the last check, its // wedged on geometry. First strike: re-anchor to the targets CURRENT // position (theyve moved on; the fresh anchor may have a clear path) // and hop to clear small ledges. Second strike: give up on this // target and let check_team assign a new one. if (now - g_StuckCheckTime[client] >= STUCK_CHECK_INTERVAL) { if (GetVectorDistance(botPos, g_StuckCheckPos[client]) < STUCK_MOVE_EPSILON) { g_StuckStrikes[client]++; if (g_StuckStrikes[client] >= MAX_STUCK_STRIKES) { GiveUpOnTarget(client); return Plugin_Continue; } GetClientAbsOrigin(target, g_AnchorPos[client]); g_ReadHead[client] = g_WriteHead[target]; g_WantJump[client] = true; } else { g_StuckStrikes[client] = 0; } g_StuckCheckPos[client] = botPos; g_StuckCheckTime[client] = now; } // Arrival is judged horizontally with generous height slack. A pure // 3D radius made anchors on other floors unreachable forever. if (GetHorizontalDistance(botPos, g_AnchorPos[client]) <= ARRIVE_RADIUS && FloatAbs(botPos[2] - g_AnchorPos[client][2]) <= ARRIVE_Z_SLACK) { // arrived - start draining whatever the target did while we were walking here g_BotState[client] = BOT_STATE_MIMIC; g_StuckStrikes[client] = 0; // fall through into mimic logic below on this same tick } else { ApplyApproachMovement(client, g_AnchorPos[client], buttons, impulse, vel, angles); return Plugin_Changed; } } if (g_BotState[client] != BOT_STATE_MIMIC) { return Plugin_Continue; } // Drift check: compare where the bot actually is against where the // target actually was for the frame were about to play. If its too // far off (missed a knockback, got stuck on geometry mid-mimic, etc.), // drop back into APPROACH toward the targets current live position // instead of continuing to mimic from a wrong spot. // // Two guards against the approach<->mimic ping-pong loop: // - a cooldown, so drift during the cooldown window just keeps // mimicking instead of instantly re-approaching // - a strike counter, so a target whose replay keeps desyncing on the // same geometry gets dropped entirely instead of looping forever if (g_ReadHead[client] != g_WriteHead[target]) { int nextSlot = g_ReadHead[client]; float botPos[3]; GetClientAbsOrigin(client, botPos); if (GetVectorDistance(botPos, g_Buffer[target][nextSlot].origin) > DESYNC_RADIUS && now - g_LastResyncTime[client] >= RESYNC_COOLDOWN) { g_ResyncStrikes[client]++; if (g_ResyncStrikes[client] >= MAX_RESYNC_STRIKES) { GiveUpOnTarget(client); return Plugin_Continue; } g_LastResyncTime[client] = now; GetClientAbsOrigin(target, g_AnchorPos[client]); g_BotState[client] = BOT_STATE_APPROACH; g_ReadHead[client] = g_WriteHead[target]; // drop the stale backlog, start fresh once we arrive // reset the stuck tracker for the fresh approach leg g_StuckStrikes[client] = 0; g_StuckCheckTime[client] = now; GetClientAbsOrigin(client, g_StuckCheckPos[client]); ApplyApproachMovement(client, g_AnchorPos[client], buttons, impulse, vel, angles); return Plugin_Changed; } } if (g_ReadHead[client] == g_WriteHead[target]) { // fully drained - nothing new recorded yet, hold still until more frames arrive return Plugin_Continue; } int slot = g_ReadHead[client]; buttons = g_Buffer[target][slot].buttons; impulse = g_Buffer[target][slot].impulse; vel = g_Buffer[target][slot].vel; angles = g_Buffer[target][slot].angles; SyncWeaponSlot(client, g_Buffer[target][slot].weaponSlot); // "cleans them out after processing" - advancing the read head past this // slot means it will never be handed out again, which is the queue // equivalent of clearing it. g_ReadHead[client] = (slot + 1) % BUFFER_SIZE; return Plugin_Changed; } // Looks up which bot client (if any) currently occupies a given bot slot (1-4). int GetBotClientForSlot(int slot) { for (int i = 1; i <= MaxClients; i++) { if (IsValidClient(i) && specific_bot_player[i] == slot) { return i; } } return -1; } // Assigns `target` as the client that `bot` should mimic. Captures a single, // fixed anchor point (the targets position right now) for the bot to walk // to, and marks the point in the targets buffer where recording of the // "walk-over" backlog begins. No teleport happens here. void SetBotTarget(int bot, int target) { int slot = specific_bot_player[bot]; if (slot == -1) { return; } target_client[slot] = target; g_LastWeaponSlot[bot] = 0; // force weapon re-evaluation once mimicking starts // clean slate for the anti-loop/anti-stuck trackers g_ResyncStrikes[bot] = 0; g_StuckStrikes[bot] = 0; g_LastResyncTime[bot] = 0.0; g_StuckCheckTime[bot] = GetGameTime(); g_WantJump[bot] = false; if (IsValidClient(bot)) { GetClientAbsOrigin(bot, g_StuckCheckPos[bot]); } if (!IsValidClient(target)) { g_BotState[bot] = BOT_STATE_IDLE; return; } GetClientAbsOrigin(target, g_AnchorPos[bot]); g_BotState[bot] = BOT_STATE_APPROACH; g_ReadHead[bot] = g_WriteHead[target]; // everything the target does from now on gets replayed once we arrive } // Debug command: prints each bots state machine status so loops/stuck // situations can be diagnosed live instead of guessed at. public Action cmd_botstate(int client, int args) { static const char stateNames[][] = { "IDLE", "APPROACH", "MIMIC" }; bool found = false; for (int i = 1; i <= MaxClients; i++) { if (!IsValidClient(i) || specific_bot_player[i] == -1) { continue; } found = true; int slot = specific_bot_player[i]; int target = target_client[slot]; int backlog = 0; char targetName[64]; strcopy(targetName, sizeof(targetName), ""); if (IsValidClient(target)) { GetClientName(target, targetName, sizeof(targetName)); backlog = (g_WriteHead[target] - g_ReadHead[i] + BUFFER_SIZE) % BUFFER_SIZE; } ReplyToCommand(client, "[bot %d] %N | state=%s | target=%s | backlog=%d frames | resync_strikes=%d | stuck_strikes=%d", slot, i, stateNames[g_BotState[i]], targetName, backlog, g_ResyncStrikes[i], g_StuckStrikes[i]); } if (!found) { ReplyToCommand(client, "no autism bots currently connected"); } return Plugin_Handled; } public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast) { target_client[1] = -1; target_client[2] = -1; target_client[3] = -1; target_client[4] = -1; chat_cooldown = false; for (int i = 1; i <= MaxClients; i++) { if (IsValidClient(i) && IsPlayerAlive(i) && specific_bot_player[i] != -1) { g_BotState[i] = BOT_STATE_IDLE; g_LastWeaponSlot[i] = 0; g_ResyncStrikes[i] = 0; g_StuckStrikes[i] = 0; g_WantJump[i] = false; } } } public void cmd_talk_help(int port, int client, char[] info) { char msg[generic_length * 5]; chat_cooldown = true; char magic_code[16]; Format(magic_code, sizeof(magic_code), autism_bot_code); //included with unloze.secrets simply. Format(msg, sizeof(msg), "clientmessage:%N %s %s", client, magic_code, info); send_socket_msg(msg, strlen(msg), port); CreateTimer(2.0, bot_chat_cooldown); } public void is_autism_bot1(int client) { char auth[50]; GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth)); if (StrEqual("[U:1:120378081]", auth, false) || StrEqual("STEAM_0:1:60189040", auth, false)) { specific_bot_player[client] = 1; } } public void is_autism_bot2(int client) { char auth[50]; GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth)); if (StrEqual("[U:1:1036189204]", auth, false) || StrEqual("STEAM_0:0:518094602", auth, false)) { specific_bot_player[client] = 2; } } public void is_autism_bot3(int client) { char auth[50]; GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth)); if (StrEqual("[U:1:408797742]", auth, false) || StrEqual("STEAM_0:0:204398871", auth, false)) { specific_bot_player[client] = 3; } } public void is_autism_bot4(int client) { char auth[50]; GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth)); if (StrEqual("[U:1:1221121532]", auth, false) || StrEqual("STEAM_0:0:610560766", auth, false)) { specific_bot_player[client] = 4; } } public Action cmd_botrtv(int client, int args) { if ((CheckCommandAccess(client, "sm_kick", ADMFLAG_KICK)) || (CheckCommandAccess(client, "sm_reserved", ADMFLAG_RESERVATION))) { for (int i = 1; i <= MaxClients; i++) { if (IsValidClient(i) && specific_bot_player[i] != -1) { FakeClientCommandEx(i, "say rtv"); } } } return Plugin_Handled; } public Action cmd_talk(int client, int args) { char info[generic_length]; GetCmdArgString(info, sizeof(info)); if (strlen(info) == 0) { PrintToChat(client, "Add a message to the command if autism bot is ingame and running on discord"); return Plugin_Handled; } if (specific_bot_player[client] == -1) { return Plugin_Handled; } if (chat_cooldown) { PrintToChat(client, "spamming bot too much, applying cooldown"); return Plugin_Handled; } bool bot_found = false; for (int i = 1; i <= MaxClients; i++) { if (IsValidClient(i) && specific_bot_player[i] != -1) { int port_number = specific_bot_player[i] - 1; cmd_talk_help(ports[port_number], client, info); bot_found = true; } } if (!bot_found) PrintToChat(client, "bot not connected to server"); return Plugin_Handled; } public Action bot_chat_cooldown(Handle timer, any data) { chat_cooldown = false; return Plugin_Continue; } public void send_socket_msg(char[] query_msg, int len, int port) { //LogMessage("query_msg: %s port: %i", query_msg, port); if (global_socket != INVALID_HANDLE && SocketIsConnected(global_socket)) SocketSendTo(global_socket, query_msg, len, "127.0.0.1", port); //udp } public void OnMapVoteEnd(const char[] map) { if (StrEqual(map, "##extend##") || StrEqual(map, "##dontchange##")) { return; } char msg[generic_length]; Format(msg, sizeof(msg), "download map:%s", map); send_socket_msg(msg, strlen(msg), ports[0]); } public Action bot_check_connect(Handle timer, any data) { char nextmap[64]; char msg[generic_length]; if (GetNextMap(nextmap, sizeof(nextmap))) { Format(msg, sizeof(msg), "download map:%s", nextmap); send_socket_msg(msg, strlen(msg), ports[0]); } GetCurrentMap(nextmap, sizeof(nextmap)); Format(msg, sizeof(msg), "download map:%s", nextmap); send_socket_msg(msg, strlen(msg), ports[0]); //this is designed for being ran from several css servers int client_count = GetClientCount(false); int i_port = GetConVarInt(FindConVar("hostport")); bool bot1_connected = false; for (int i = 1; i <= MaxClients; i++) if (IsValidClient(i) && !IsFakeClient(i)) { if (specific_bot_player[i] == 1) bot1_connected = true; } if (i_port == server_ports[0]) { int max_allowed_players = 55; char current_map[128]; GetCurrentMap(current_map, sizeof(current_map)); if (client_count > max_allowed_players) { Format(msg, sizeof(msg), "connect to ze2"); send_socket_msg(msg, strlen(msg), ports[0]); } else if (client_count < max_allowed_players - 5) { Format(msg, sizeof(msg), "connect to ze"); if (!bot1_connected) send_socket_msg(msg, strlen(msg), ports[0]); } } else { //in case the bot is not connected to ze2 anymore make sure that is_bot_connected_to_ze2 is set to false again Format(msg, sizeof(msg), "not connected to ze2"); if (!bot1_connected) send_socket_msg(msg, strlen(msg), ports[0]); } return Plugin_Continue; } public Action bot_check_connect2(Handle timer, any data) { char nextmap[64]; char msg[generic_length]; if (GetNextMap(nextmap, sizeof(nextmap))) { Format(msg, sizeof(msg), "download map:%s", nextmap); send_socket_msg(msg, strlen(msg), ports[0]); } GetCurrentMap(nextmap, sizeof(nextmap)); Format(msg, sizeof(msg), "download map:%s", nextmap); send_socket_msg(msg, strlen(msg), ports[0]); //this is designed for being ran from several css servers int client_count = GetClientCount(false); int i_port = GetConVarInt(FindConVar("hostport")); bool bot2_connected = false; for (int i = 1; i <= MaxClients; i++) if (IsValidClient(i) && !IsFakeClient(i)) { if (specific_bot_player[i] == 2) bot2_connected = true; } if (i_port == server_ports[0]) { int max_allowed_players = 55; char current_map[128]; GetCurrentMap(current_map, sizeof(current_map)); if (client_count > max_allowed_players) { Format(msg, sizeof(msg), "connect to ze2"); send_socket_msg(msg, strlen(msg), ports[1]); } else if (client_count < max_allowed_players - 5) { Format(msg, sizeof(msg), "connect to ze"); if (!bot2_connected) send_socket_msg(msg, strlen(msg), ports[1]); } } else { //in case the bot is not connected to ze2 anymore make sure that is_bot_connected_to_ze2 is set to false again Format(msg, sizeof(msg), "not connected to ze2"); if (!bot2_connected) send_socket_msg(msg, strlen(msg), ports[1]); } return Plugin_Continue; } public Action bot_check_connect3(Handle timer, any data) { char nextmap[64]; char msg[generic_length]; if (GetNextMap(nextmap, sizeof(nextmap))) { Format(msg, sizeof(msg), "download map:%s", nextmap); send_socket_msg(msg, strlen(msg), ports[0]); } GetCurrentMap(nextmap, sizeof(nextmap)); Format(msg, sizeof(msg), "download map:%s", nextmap); //this is designed for being ran from several css servers int client_count = GetClientCount(false); int i_port = GetConVarInt(FindConVar("hostport")); bool bot3_connected = false; for (int i = 1; i <= MaxClients; i++) if (IsValidClient(i) && !IsFakeClient(i)) { if (specific_bot_player[i] == 3) bot3_connected = true; } if (i_port == server_ports[0]) { int max_allowed_players = 55; char current_map[128]; GetCurrentMap(current_map, sizeof(current_map)); if (client_count > max_allowed_players) { Format(msg, sizeof(msg), "connect to ze2"); send_socket_msg(msg, strlen(msg), ports[2]); } else if (client_count < max_allowed_players - 5) { Format(msg, sizeof(msg), "connect to ze"); if (!bot3_connected) send_socket_msg(msg, strlen(msg), ports[2]); } } else { //in case the bot is not connected to ze2 anymore make sure that is_bot_connected_to_ze2 is set to false again Format(msg, sizeof(msg), "not connected to ze2"); if (!bot3_connected) send_socket_msg(msg, strlen(msg), ports[2]); } return Plugin_Continue; } public Action bot_check_connect4(Handle timer, any data) { char nextmap[64]; char msg[generic_length]; if (GetNextMap(nextmap, sizeof(nextmap))) { Format(msg, sizeof(msg), "download map:%s", nextmap); send_socket_msg(msg, strlen(msg), ports[0]); } GetCurrentMap(nextmap, sizeof(nextmap)); Format(msg, sizeof(msg), "download map:%s", nextmap); //this is designed for being ran from several css servers int client_count = GetClientCount(false); int i_port = GetConVarInt(FindConVar("hostport")); bool bot4_connected = false; for (int i = 1; i <= MaxClients; i++) if (IsValidClient(i) && !IsFakeClient(i)) { if (specific_bot_player[i] == 4) bot4_connected = true; } if (i_port == server_ports[0]) { int max_allowed_players = 55; char current_map[128]; GetCurrentMap(current_map, sizeof(current_map)); if (client_count > max_allowed_players) { Format(msg, sizeof(msg), "connect to ze2"); send_socket_msg(msg, strlen(msg), ports[3]); } else if (client_count < max_allowed_players - 5) { Format(msg, sizeof(msg), "connect to ze"); if (!bot4_connected) send_socket_msg(msg, strlen(msg), ports[3]); } } else { //in case the bot is not connected to ze2 anymore make sure that is_bot_connected_to_ze2 is set to false again Format(msg, sizeof(msg), "not connected to ze2"); if (!bot4_connected) send_socket_msg(msg, strlen(msg), ports[3]); } return Plugin_Continue; } public Action check_team(Handle timer, any data) { for (int client = 1; client <= MaxClients; client++) { if (!IsValidClient(client)) { continue; } if (specific_bot_player[client] != -1 && GetClientTeam(client) <= CS_TEAM_SPECTATOR) { ChangeClientTeam(client, CS_TEAM_T); CreateTimer(1.0, zspawn, GetClientSerial(client)); continue; } if (specific_bot_player[client] != -1) { if (IsPlayerAlive(client)) { int newTarget = GetClosestClient_option1(client); if (newTarget != target_client[specific_bot_player[client]]) { // only reset the walk-over/mimic state when the target // actually changes - otherwise a bot re-targeting the // same player every 2 seconds would keep getting yanked // back into APPROACH state SetBotTarget(client, newTarget); } } } } bool found_valid_ct = false; for (int client = 1; client <= MaxClients; client++) { if (!IsValidClient(client)) continue; if (specific_bot_player[client] == -1 && GetClientTeam(client) > CS_TEAM_T && IsPlayerAlive(client)) { found_valid_ct = true; break; } } if (!found_valid_ct) { for (int client = 1; client <= MaxClients; client++) { if (!IsValidClient(client)) continue; if (specific_bot_player[client] != -1 && IsPlayerAlive(client)) { ForcePlayerSuicide(client); } } } return Plugin_Continue; } public Action zspawn(Handle hTimer, int Serial) { int client; if ((client = GetClientFromSerial(Serial)) == 0) { return Plugin_Handled; } if (IsValidClient(client)) { FakeClientCommandEx(client, "joinclass"); FakeClientCommandEx(client, "say /zspawn"); } return Plugin_Handled; } stock bool IsValidClient(int client) { if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client)) return true; return false; } public int GetClosestClient_option1(int autism_client) { float nearestdistance = -1.0; int nearest = -1; for (int i = 1; i <= MaxClients; i++) { if (IsValidClient(i) && !IsFakeClient(i) && IsPlayerAlive(i) && i != autism_client) { if (GetClientIdleTime(i) > 3 || GetClientTeam(autism_client) != GetClientTeam(i) || !PM_IsPlayerSteam(i)) //not following nosteamers is a bit quality check { continue; } float pos[3]; GetEntPropVector(i, Prop_Send, "m_vecOrigin", pos); float dist_target = get_power_distance(autism_client, pos); if (specific_bot_player[i] != -1) { //just making sure bots have lowest priority dist_target = 9000000.0; } if (nearestdistance < 0 || dist_target < nearestdistance) { nearest = i; nearestdistance = dist_target; } } } return nearest; } public float get_power_distance(int target_player, float pos[3]) { float vec[3]; GetClientAbsOrigin(target_player, vec); return GetVectorDistance(vec, pos); } public void OnClientPutInServer(int client) { specific_bot_player[client] = -1; g_BotState[client] = BOT_STATE_IDLE; g_LastWeaponSlot[client] = 0; g_NextWeaponCheck[client] = 0.0; // force an immediate recheck rather than waiting out a stale timer g_ResyncStrikes[client] = 0; g_StuckStrikes[client] = 0; g_WantJump[client] = false; } public void OnClientPostAdminCheck(int client) { specific_bot_player[client] = -1; g_BotState[client] = BOT_STATE_IDLE; g_LastWeaponSlot[client] = 0; is_autism_bot1(client); is_autism_bot2(client); is_autism_bot3(client); is_autism_bot4(client); //checks if ze or ze2 int i_port = GetConVarInt(FindConVar("hostport")); char msg[generic_length]; if (i_port == server_ports[0]) { Format(msg, sizeof(msg), "autismo connected to ze"); } else { Format(msg, sizeof(msg), "autismo connected to ze2"); } if (specific_bot_player[client] != -1) { send_socket_msg(msg, strlen(msg), ports[specific_bot_player[client] - 1]); } } public void OnSocketError(Handle socket, const int errorType, const int errorNum, any args) { CloseHandle(socket); LogError("[MR] Socket error: %d (errno %d)", errorType, errorNum); CreateTimer(10.0, TimerConnect, INVALID_HANDLE, TIMER_HNDL_CLOSE); } stock void connect_socket() { if (global_socket == INVALID_HANDLE || !SocketIsConnected(global_socket)) { int i_port = GetConVarInt(FindConVar("hostport")); int target_port = ports[4]; //default ze if (i_port == server_ports[1]) { //ze2 target_port = ports[5]; } //socket otherwise declare in public OnConfigsExecuted(){} global_socket = SocketCreate(SOCKET_UDP, OnSocketError); SocketSetOption(global_socket, SocketReuseAddr, 1); SocketBind(global_socket, "127.0.0.0", target_port); SocketConnect(global_socket, OnSocketConnected, OnSocketReceive, OnSocketDisconnected, "127.0.0.1", target_port); //48474 on ze } } public void OnClientDisconnect(int client) { specific_bot_player[client] = -1; g_BotState[client] = BOT_STATE_IDLE; g_LastWeaponSlot[client] = 0; // if this client was somebodys target, put that bot back to idle so it // doesnt keep trying to read/approach a client thats gone for (int slot = 1; slot <= 4; slot++) { if (target_client[slot] == client) { target_client[slot] = -1; int bot = GetBotClientForSlot(slot); if (bot != -1) { g_BotState[bot] = BOT_STATE_IDLE; g_LastWeaponSlot[bot] = 0; } } } } //Socket callback public void OnSocketConnected(Handle socket, int arg) { } //manage message public void OnSocketReceive(Handle socket, char receiveData [3500], int dataSize, int hFile) { } public void OnSocketDisconnected(Handle socket, int arg) { CreateTimer(10.0, TimerConnect, INVALID_HANDLE, TIMER_HNDL_CLOSE); } public Action TimerConnect(Handle timer, any arg) { connect_socket(); return Plugin_Handled; }