Код:
#include <amxmodx>
#include <amxmisc>
#include <regex>
#include <sockets>
#include <string>
#define PLUGIN "Check online player"
#define AUTHOR ""
#define VERSION "0.1"
const SOCKET_TASK_ID = 57343;
const Float:RECEVIE_DELAY = 0.1; // Time in seconds
const WAIT_FOR_RESPONSE = 4; // Time in seconds * RECEIVE_DELAY
const Float:QUERY_INTERVAL = 10.0; // Time in seconds
enum _:SERVERLIST
{
IP[16] = 0,
PORT[6],
RCON[64]
}
new g_serverlist[][SERVERLIST] =
{
{"IP","PORT","RCON"}
};
enum _:PLAYERSLIST
{
ID = 0,
NICKNAME[32],
USERID[32],
UNIQUEID[32],
FRAG[4],
TIME[8],
PING,
LOSS,
ADR[32]
}
new g_playerlist[][][PLAYERSLIST];
new socket = 0;
new err;
new command[] = "status";
new pcvar_rcon;
new pcvar_ip;
new pcvar_port;
new pcvar_serverid;
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR);
set_task(QUERY_INTERVAL, "query_servers", SOCKET_TASK_ID, _, 0, "b");
}
public query_servers()
{
for(pcvar_serverid = 0; pcvar_serverid < sizeof(g_serverlist); pcvar_serverid++)
{
pcvar_ip = g_serverlist[pcvar_serverid][IP];
pcvar_port = g_serverlist[pcvar_serverid][PORT];
pcvar_rcon = g_serverlist[pcvar_serverid][RCON];
executeCommand("status");
}
return PLUGIN_CONTINUE;
}
public socktest()
{
new ret = executeCommand("status");
server_print("Return: %i", ret);
return PLUGIN_CONTINUE;
}
executeCommand(const cmd[])
{
if (cmd[0] == 0) return 0;
if (socket > 0) return -1;
// Open socket.
new ip[32];
new port_str[10];
get_pcvar_string(pcvar_ip, ip, sizeof(ip) - 1);
get_pcvar_string(pcvar_port, port_str, sizeof(port_str) - 1);
new port = str_to_num(port_str);
socket = socket_open(ip, port, SOCKET_UDP, err);
// Execute command directly in console if socket failed.
if (socket <= 0)
{
log_amx("Got error when opening socket for server %s:%i", ip, port);
return 2;
}
// Copy command.
copy (command, sizeof(command) - 1, cmd);
// Start rcon challenging.
set_task(RECEVIE_DELAY, "socketReceive", SOCKET_TASK_ID, _, _, "b")
return 1;
}
public socketReceive()
{
static challenge[11];
static buffer[2048] = "FFFF";
static response[4096];
static response_size = 0;
static rcon_state = 0;
static wait_count = 0;
static reset_rcon = 0;
switch(rcon_state)
{
case 0: // Start challenge.
{
// Send query for challenge string.
buffer[0] = buffer[1] = buffer[2] = buffer[3] = 255;
copy(buffer[4], sizeof(buffer) - 5, "challenge rcon^n");
err = socket_send(socket, buffer, sizeof(buffer) - 1);
response_size = 0;
wait_count = 0;
rcon_state = 1;
}
case 1: // Receive challenge, execute command.
{
// Check if response is received.
err = socket_change(socket, 1000);
if (err != 0)
{
// Read received data.
err = socket_recv(socket, buffer, sizeof(buffer) - 1);
// Stop if challenge is not received.
if (!equal(buffer[4], "challenge rcon", 14))
{
rcon_state = 5; // Error.
return PLUGIN_CONTINUE;
}
// Retrive challenge string.
new challenge_len = strlen(buffer[19]) - 1; // Remove closing ^n
if (challenge_len > sizeof(challenge) - 1) challenge_len = sizeof(challenge) - 1;
copy(challenge, challenge_len, buffer[19]);
// Get rcon password.
new rcon_password[64];
get_pcvar_string(pcvar_rcon, rcon_password, sizeof(rcon_password) - 1);
// Generate and set rcon password if it is empty.
if (rcon_password[0] == 0)
{
reset_rcon = 1;
float_to_str(random_float(0.0, 10.0), rcon_password, sizeof(rcon_password) - 1);
set_pcvar_string(pcvar_rcon, rcon_password);
}
// Execute command on rcon.
format(buffer[4], sizeof(buffer) - 1, "rcon %s ^"%s^" %s", challenge, rcon_password, command);
err = socket_send(socket, buffer, sizeof(buffer) - 1);
response_size = 0;
wait_count = 0;
rcon_state = 3;
}
else
{
// Count retries to receive response.
wait_count++;
if (wait_count > WAIT_FOR_RESPONSE)
{
rcon_state = 5; // Error.
wait_count = 0;
}
}
}
case 3: // Collect response.
{
// Check if response is received.
err = socket_change(socket, 1000);
if (err != 0)
{
// Read received data and store it in response buffer.
err = socket_recv(socket, buffer, sizeof(buffer) - 1);
copy(response[response_size], sizeof(response) - response_size - 1, buffer[5]); // +1 to skip 'l'
response_size += err - 7; // Skip trailing two 0x00 and leading 0xFFFFFFFF and lead 'l'
}
else
{
// Count retries to receive response.
wait_count++;
if (wait_count > WAIT_FOR_RESPONSE)
{
rcon_state = 4; // End.
wait_count = 0;
}
}
}
case 4: // Close socket, process response.
{
// Stop pocessing socket and close it.
remove_task(SOCKET_TASK_ID);
err = socket_close(socket);
socket = 0;
// Reset rcon password if it was empty.
if (reset_rcon)
{
set_pcvar_string(pcvar_rcon, "");
reset_rcon = 0;
}
// Return to state 0.
rcon_state = 0;
// Do something with response.
if (response_size > 0)
{
if (response[response_size - 1] == 10) // 0x0A
{
response[response_size - 1] = 0;
response_size--;
}
/* TODO: parse every line of response! -- как я понимаю, оно должно быть примерно вот так. Но все равно надо отбросить первые 5 строк, символ "#" в начале строки и т.д.
while ()
{
new p_id;
parse (response, p_id, 2)
parse (response,
g_playerlist[pcvar_serverid][p_id][ID], 2,
g_playerlist[pcvar_serverid][p_id][NICKNAME], 32,
g_playerlist[pcvar_serverid][p_id][USERID], 32,
g_playerlist[pcvar_serverid][p_id][UNIQUEID], 32,
...
)
} */
}
}
case 5: // Error happend.
{
// Stop pocessing socket and close it.
remove_task(SOCKET_TASK_ID);
err = socket_close(socket);
socket = 0;
// Reset rcon password if it was empty.
if (reset_rcon)
{
set_pcvar_string(pcvar_rcon, "");
reset_rcon = 0;
}
// Return to state 0.
rcon_state = 0;
// Execute command directly in console.
server_cmd(command);
}
}
return PLUGIN_CONTINUE;
}