Files
projects-jenz/AutismBotIngame/scripting/autism_bot_simulate_movement.sp
T

1121 lines
36 KiB
SourcePawn
Executable File

#pragma semicolon 1
#pragma newdecls required
#define DEBUG
#define PLUGIN_AUTHOR "jenz"
#define PLUGIN_VERSION "2.2"
#define generic_length 256
#include <sourcemod>
#include <sdktools>
#include <AFKManager>
#include <sdkhooks>
#include <PlayerManager>
#include <cstrike>
#include <socket>
#include <UNLOZE.secret>
#include <mapchooser_extended>
#include <smlib>
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), "<none>");
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;
}