making the simulate movement plugin obsolete and moving all the connection management related content into a separate plugin so that its isolated from other functionality

This commit is contained in:
jenz
2026-10-09 15:05:32 +02:00
parent 65ca4fda29
commit dd44cb5e49
2 changed files with 400 additions and 0 deletions
@@ -0,0 +1,400 @@
#pragma semicolon 1
#pragma newdecls required
#define PLUGIN_AUTHOR "jenz"
#define PLUGIN_VERSION "1.0"
#define generic_length 256
#include <sourcemod>
#include <cstrike>
#include <socket>
#include <sdktools>
#include <UNLOZE.secret>
#include <mapchooser_extended>
#include <BotTargeting>
#define MAX_AUTISM_BOTS 4
int g_BotIndex[MAXPLAYERS + 1]; // 0 = not an autism bot, 1-4 = autism bot index
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 communication
Handle global_socket = null;
ConVar g_cvHostPort;
public Plugin myinfo =
{
name = "autism bot connection",
author = PLUGIN_AUTHOR,
description = "connection state management for the autism bots",
version = PLUGIN_VERSION,
url = ""
};
public void OnPluginStart()
{
g_cvHostPort = FindConVar("hostport");
//talking
RegConsoleCmd("sm_autism", cmd_talk, "talking to the bot through java application");
RegConsoleCmd("sm_botrtv", cmd_botrtv, "making bots rtv");
HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy);
//UDP connection
connect_socket();
chat_cooldown = false;
// map downloads go to the first port only, once per cycle
CreateTimer(60.0, Timer_MapDownload, _, TIMER_REPEAT);
// per-bot connect checks, staggered 5 seconds apart
for (int index = 1; index <= MAX_AUTISM_BOTS; index++)
{
CreateTimer(float(index - 1) * 5.0 + 0.1, Timer_StartConnectCheck, index);
}
// team joining / killing bots when no human is left alive
CreateTimer(2.0, Timer_CheckTeam, _, TIMER_REPEAT);
// late load
for (int i = 1; i <= MaxClients; i++)
{
if (IsValidClient(i) && IsClientAuthorized(i))
{
OnClientPostAdminCheck(i);
}
}
}
// ---------------------------------------------------------------------------
// bot identification
// ---------------------------------------------------------------------------
int GetBotClientForIndex(int index)
{
for (int i = 1; i <= MaxClients; i++)
{
if (IsValidClient(i) && g_BotIndex[i] == index)
{
return i;
}
}
return -1;
}
public void OnClientPutInServer(int client)
{
g_BotIndex[client] = 0;
}
public void OnClientPostAdminCheck(int client)
{
g_BotIndex[client] = 0;
if (IsFakeClient(client))
{
return;
}
int index = GetClientAutismBotIndex(client);
if (index < 1 || index > MAX_AUTISM_BOTS)
{
return;
}
g_BotIndex[client] = index;
//checks if ze or ze2
char msg[generic_length];
if (g_cvHostPort.IntValue == server_ports[0])
{
Format(msg, sizeof(msg), "autismo connected to ze");
}
else if (g_cvHostPort.IntValue == server_ports[1])
{
Format(msg, sizeof(msg), "autismo connected to ze2");
}
else
{
return;
}
send_socket_msg(msg, strlen(msg), ports[index - 1]);
}
public void OnClientDisconnect(int client)
{
g_BotIndex[client] = 0;
}
// ---------------------------------------------------------------------------
// team joining
// ---------------------------------------------------------------------------
public Action Timer_CheckTeam(Handle timer, any data)
{
for (int client = 1; client <= MaxClients; client++)
{
if (!IsValidClient(client) || g_BotIndex[client] == 0)
{
continue;
}
if (GetClientTeam(client) <= CS_TEAM_SPECTATOR)
{
ChangeClientTeam(client, CS_TEAM_T);
CreateTimer(1.0, zspawn, GetClientSerial(client));
}
}
// no real human alive on CT anymore -> kill the bots so they dont hold up the round
bool found_valid_ct = false;
for (int client = 1; client <= MaxClients; client++)
{
if (!IsValidClient(client)) continue;
if (g_BotIndex[client] == 0 && 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 (g_BotIndex[client] != 0 && 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;
}
// ---------------------------------------------------------------------------
// chat / rtv
// ---------------------------------------------------------------------------
public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast)
{
chat_cooldown = 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 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;
}
// kept from the original plugin: only the bot accounts themselves can use sm_autism
if (g_BotIndex[client] == 0)
{
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) && g_BotIndex[i] != 0)
{
cmd_talk_help(ports[g_BotIndex[i] - 1], 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 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) && g_BotIndex[i] != 0)
{
FakeClientCommandEx(i, "say rtv");
}
}
}
return Plugin_Handled;
}
// ---------------------------------------------------------------------------
// connection management
// ---------------------------------------------------------------------------
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 Timer_MapDownload(Handle timer, any data)
{
char map[64];
char msg[generic_length];
if (GetNextMap(map, sizeof(map)))
{
Format(msg, sizeof(msg), "download map:%s", map);
send_socket_msg(msg, strlen(msg), ports[0]);
}
GetCurrentMap(map, sizeof(map));
Format(msg, sizeof(msg), "download map:%s", map);
send_socket_msg(msg, strlen(msg), ports[0]);
return Plugin_Continue;
}
public Action Timer_StartConnectCheck(Handle timer, int index)
{
CreateTimer(60.0, Timer_ConnectCheck, index, TIMER_REPEAT);
return Plugin_Stop;
}
//this is designed for being ran from several css servers
public Action Timer_ConnectCheck(Handle timer, int index)
{
char msg[generic_length];
int client_count = GetClientCount(false);
bool connected = GetBotClientForIndex(index) != -1;
int port = ports[index - 1];
if (g_cvHostPort.IntValue == server_ports[0])
{
int max_allowed_players = 55;
if (client_count > max_allowed_players)
{
Format(msg, sizeof(msg), "connect to ze2");
send_socket_msg(msg, strlen(msg), port);
}
else if (client_count < max_allowed_players - 5 && !connected)
{
Format(msg, sizeof(msg), "connect to ze");
send_socket_msg(msg, strlen(msg), port);
}
}
else if (!connected)
{
//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");
send_socket_msg(msg, strlen(msg), port);
}
return Plugin_Continue;
}
// ---------------------------------------------------------------------------
// socket
// ---------------------------------------------------------------------------
public void send_socket_msg(char[] query_msg, int len, int port)
{
if (global_socket != null && SocketIsConnected(global_socket))
{
SocketSendTo(global_socket, query_msg, len, "127.0.0.1", port); //udp
}
}
stock void connect_socket()
{
if (global_socket == null || !SocketIsConnected(global_socket))
{
int target_port = ports[4]; //default ze
if (g_cvHostPort.IntValue == server_ports[1])
{
//ze2
target_port = ports[5];
}
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);
}
}
public void OnSocketError(Handle socket, const int errorType, const int errorNum, any args)
{
CloseHandle(socket);
if (socket == global_socket)
{
global_socket = null; // otherwise connect_socket would query a closed handle
}
LogError("[autism bot] Socket error: %d (errno %d)", errorType, errorNum);
CreateTimer(10.0, TimerConnect);
}
public void OnSocketConnected(Handle socket, int arg)
{
}
public void OnSocketReceive(Handle socket, char receiveData[3500], int dataSize, int hFile)
{
}
public void OnSocketDisconnected(Handle socket, int arg)
{
CreateTimer(10.0, TimerConnect);
}
public Action TimerConnect(Handle timer, any arg)
{
connect_socket();
return Plugin_Stop;
}
stock bool IsValidClient(int client)
{
return client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client);
}