Код
/*
Title: Play or Be Kicked
Author: Brad Jones
Current Version: 1.5.243
Release Date: 2008-JUL-23
Compatibility: AMXX 1.8 or higher
This plugin has the ability to kick players for any of the following three events:
* not joining a team or spectator mode in time when initially joining the server
* spectatating too long
* being AFK too long
Which events your server looks for is configurable, as is the amount of time allowed for each event.
INSTALLATION
File Locations:
.\configs\pbk.cfg
.\data\lang\pbk.txt
.\plugins\pbk.amxx
CHANGE LOG:
2008-JUL-23 1.5.243
- Removed all vestiges of the engine module and used fakemeta instead.
- Optimized various portions of code to be more efficient.
- No longer comparing Z-axis to see if a player moved. This fixes an issue where
players' AFK meters could get reset upon round start if you had a low (2 seconds
or less) freeze time. Also fixes an issue on maps where players can be constantly
moving along the Z-axis, even when AFK (for example, ka_bungee).
- Replaced "pbk_immunity" with three new CVARs: "pbk_spec_immunity_flags",
"pbk_afk_immunity_flags", and "pbk_join_immunity_flags". Allows you to set
one or more flags that should have immunity from each kick event.
- Added plugin-specific config file, "pbk.cfg".
- Added ability to redirect players to a different server upon kicking them. Uses the
new CVARs, "pbk_kick2_ip" and "pbk_kick2_port".
- Implemented optional feature whereas, if this plugin detects someone in spectator
mode, and the plugin is configured to kick for too much time in spectator mode, it can
periodically query the player to see if they're actually at the keyboard or not.
2005-DEC-11 1.4
- Fixed inconsistencies between what the docs said the default values for certain
CVARs were and what they were set as in the code. The docs were correct,
the code was wrong.
- Replaced "pbk_min_players" with three new CVARs: "pbk_join_min_players",
"pbk_spec_min_players", and "pbk_afk_min_players". This allows you to have
finer control over when players should be kicked.
2005-NOV-11 1.3
- Fixed issue where player would be able to choose a team but not a model
and then sit there indefinitely.
- Added CVAR "pbk_log" which lets you specify how to log kicks. Options are to log
in the AMXX log (as was previously done), the chat log (allows kicks to be seen in
programs like HLSW that show you the chat log), and in their own file (pbkMM.log
where MM is the two-digit month). The default is to log in the AMXX and chat logs.
- Added CVAR "pbk_log_cnt" which lets you specify how many months of logs to keep if
you are logging kicks into their own file.
- Added functionality to kick AFK users via new CVAR "pbk_afk_time". A value of 0 will
disable checking of AFK status.
- Renamed CVAR "pbk_un_time" to "pbk_join_time". Added the ability to specify 0 to
disable checking of initial join status.
- Added ability to specify 0 for the "pbk_spec_time" CVAR to disable checking of
spectator status.
- Removed CVAR "pbk_restrict". Functionality that it provided is now being provided
via "pbk_join_time", "pbk_spec_time", and "pbk_afk_time".
- Renamed CVAR "pbk_allow_immunity" to "pbk_immunity".
- Changed the options for "pbk_immunity" to allow indication of what events an
immune player can be immune from being kicked. Options are "joining", "spectating",
and "being AFK".
- Added CVAR "pbk_immunity_warning" to indicate whether players with immunity should
be shown the warning countdown. The default is to show the countdown.
- Fixed issue where the time length wasn't multilingual.
2005-JUL-16 1.1
- Will ignore HLTV users.
- Added CVAR to allow immune players to be kicked.
- Replaced CVAR "pbk_time" with "pbk_spec_time" and "pbk_un_time".
2005-JUL-10 1.0
- Initial release.
*/
#include <amxmodx>
#include <amxmisc>
#include <fakemeta>
#include <logging>
#include <time>
new const PLUGIN[] = "Play or Be Kicked";
new const RELEASE[] = "1.5.243";
new const AUTHOR[] = "Brad Jones";
#define CHECK_FREQ 5
// team flags
#define TEAM_T 1
#define TEAM_CT 2
// event flags
#define EVENT_JOIN 1
#define EVENT_SPEC 2
#define EVENT_AFK 4
// coordinate info
#define MAX_COORD_CNT 3
#define COORD_X 0
#define COORD_Y 1
// player
#define MAX_PLAYER_CNT 33 // really 32, but 32 is 0-31 and we want 1-32, so... 33
new g_playerJoined[MAX_PLAYER_CNT], g_playerSpawned[MAX_PLAYER_CNT];
new g_timeJoin[MAX_PLAYER_CNT], g_timeSpec[MAX_PLAYER_CNT], g_timeAFK[MAX_PLAYER_CNT], g_timeSpecQuery[MAX_PLAYER_CNT];
new g_prevCoords[MAX_PLAYER_CNT][MAX_COORD_CNT];
new g_joinImmunity[32], g_specImmunity[32], g_afkImmunity[32];
new bool:g_roundInProgress = false;
new g_cvar_joinMinPlayers, g_cvar_joinTime, g_cvar_joinImmunity;
new g_cvar_specMinPlayers, g_cvar_specTime, g_cvar_specImmunity, g_cvar_specQuery;
new g_cvar_afkMinPlayers, g_cvar_afkTime, g_cvar_afkImmunity;
new g_cvar_immunityWarning, g_cvar_warningTime;
new g_cvar_log, g_cvar_logCnt;
new g_cvar_kick2ip, g_cvar_kick2port;
public plugin_init()
{
register_plugin(PLUGIN, RELEASE, AUTHOR);
register_cvar("pbk_debug", "-1");
register_cvar("pbk_version", RELEASE, FCVAR_SERVER|FCVAR_SPONLY); // For GameSpy/HLSW and such
register_dictionary("pbk.txt");
register_dictionary("time.txt");
register_event("ResetHUD", "event_resethud", "be");
register_forward(FM_PlayerPostThink, "fm_playerPostThink");
register_logevent("event_round_start", 2, "0=World triggered", "1=Round_Start");
register_logevent("event_round_end", 2, "0=World triggered", "1=Round_End");
g_cvar_joinMinPlayers = register_cvar("pbk_join_min_players", "4");
g_cvar_joinTime = register_cvar("pbk_join_time", "120");
g_cvar_joinImmunity = register_cvar("pbk_join_immunity_flags", "");
g_cvar_specMinPlayers = register_cvar("pbk_spec_min_players", "4");
g_cvar_specTime = register_cvar("pbk_spec_time", "120");
g_cvar_specImmunity = register_cvar("pbk_spec_immunity_flags", "");
g_cvar_specQuery = register_cvar("pbk_spec_query", "0");
g_cvar_afkMinPlayers = register_cvar("pbk_afk_min_players", "4");
g_cvar_afkTime = register_cvar("pbk_afk_time", "90");
g_cvar_afkImmunity = register_cvar("pbk_afk_immunity_flags", "");
g_cvar_immunityWarning = register_cvar("pbk_immunity_warning", "7");
g_cvar_warningTime = register_cvar("pbk_warning_time", "20");
g_cvar_log = register_cvar("pbk_log", "3");
g_cvar_logCnt = register_cvar("pbk_log_cnt", "2");
g_cvar_kick2ip = register_cvar("pbk_kick2_ip", "");
g_cvar_kick2port = register_cvar("pbk_kick2_port", "27015");
}
public plugin_cfg()
{
new configDir[255];
formatex(configDir[get_configsdir(configDir, sizeof(configDir)-1)], sizeof(configDir)-1, "/");
server_cmd("exec %spbk.cfg", configDir);
server_exec();
get_pcvar_string(g_cvar_joinImmunity, g_joinImmunity, sizeof(g_joinImmunity)-1);
get_pcvar_string(g_cvar_specImmunity, g_specImmunity, sizeof(g_specImmunity)-1);
get_pcvar_string(g_cvar_afkImmunity, g_afkImmunity, sizeof(g_afkImmunity)-1);
cycle_log_files("pbk", clamp(get_pcvar_num(g_cvar_logCnt), 0, 11)); // must keep between 0 and 11 months
register_menucmd(register_menuid("pbk_AreYouThere"), (1<<0)|(1<<1), "query_answered");
set_task(float(CHECK_FREQ), "check_players", _, _, _, "b"); set_task(float(CHECK_FREQ), "check_players", _, _, _, "b");
}
public fm_playerPostThink(id)
{
if (!g_playerJoined[id])
{
// if the player is on the T or CT team or is spectating, they have "fully joined"
new team[2], teamID = get_user_team(id, team, 1);
if (teamID == TEAM_T || teamID == TEAM_CT || team[0] == 'S') g_playerJoined[id] = true;
}
return PLUGIN_CONTINUE;
}
public client_disconnect(id)
{
g_playerJoined[id] = false;
g_playerSpawned[id] = false;
g_timeJoin[id] = 0;
g_timeSpec[id] = 0;
g_timeAFK[id] = 0;
}
public event_resethud(id)
{
if (!g_playerSpawned[id]) g_playerSpawned[id] = true;
}
public event_round_end()
{
g_roundInProgress = false;
}
public event_round_start()
{
// reset the coords of each player (for use in AFK checking)
new players[32], playerCnt, id;
get_players(players, playerCnt, "ch"); // skip bots and hltv
for (new playerIdx = 0; playerIdx < playerCnt; playerIdx++)
{
id = players[playerIdx];
get_user_origin(id, g_prevCoords[id], 0);
}
// note that the round has started
g_roundInProgress = true;
}
public check_players()
{
new playerCnt = get_playersnum();
new team[2], eventType, curCoords[MAX_COORD_CNT], prevCoords[MAX_COORD_CNT];
new bool:checkJoinStatus = (get_pcvar_num(g_cvar_joinTime) && playerCnt >= get_pcvar_num(g_cvar_joinMinPlayers));
new bool:checkSpecStatus = (get_pcvar_num(g_cvar_specTime) && playerCnt >= get_pcvar_num(g_cvar_specMinPlayers));
new bool:checkAFKStatus = (get_pcvar_num(g_cvar_afkTime) && playerCnt >= get_pcvar_num(g_cvar_afkMinPlayers) && g_roundInProgress);
new players[32], id;
get_players(players, playerCnt, "ch"); // skip bots and hltv
for (new playerIdx = 0; playerIdx < playerCnt; playerIdx++)
{
id = players[playerIdx];
if (g_playerJoined[id])
{
get_user_team(id, team, 1);
eventType = (team[0] == 'S') ? EVENT_SPEC : EVENT_AFK;
if (eventType == EVENT_AFK && checkAFKStatus && g_playerSpawned[id] && is_user_alive(id))
{
// grab the current position of the player
get_user_origin(id, curCoords, 0);
// compare to previous coords
prevCoords = g_prevCoords[id];
if (prevCoords[COORD_X] == curCoords[COORD_X] && prevCoords[COORD_Y] == curCoords[COORD_Y])
{
g_timeAFK[id] += CHECK_FREQ;
}
else
{
g_prevCoords[id] = curCoords;
g_timeAFK[id] = 0;
}
}
else if (eventType == EVENT_SPEC && checkSpecStatus)
{
g_timeSpec[id] += determine_spec_time_elapsed(id);
}
else continue;
}
else
{
eventType = EVENT_JOIN;
if (checkJoinStatus) g_timeJoin[id] += CHECK_FREQ;
else continue;
}
handle_time_elapsed(id, eventType);
}
}
determine_spec_time_elapsed(id)
{
new timeElapsed = 0;
if (get_pcvar_num(g_cvar_specQuery))
{
g_timeSpecQuery[id] += CHECK_FREQ;
if (g_timeSpecQuery[id] == 45)
{
display_spec_query(id);
}
else if (g_timeSpecQuery[id] >= 55)
{
timeElapsed = g_timeSpecQuery[id] - CHECK_FREQ;
g_timeSpecQuery[id] = CHECK_FREQ;
}
}
else
{
timeElapsed = CHECK_FREQ;
}
return timeElapsed;
}
display_spec_query(id)
{
new query[192];
formatex(query, sizeof(query)-1, "\r%L\R^n^n\y1.\w %L^n\y2.\w %L", id, "KICK_SPEC_AREYOUTHERE", id, "YES", id, "NO");
show_menu(id, (1<<0)|(1<<1), query, 4, "pbk_AreYouThere");
}
public query_answered(id, key)
{
//g_timeSpec[id] -= g_timeSpecQuery[id];
g_timeSpecQuery[id] = 0;
}
public handle_time_elapsed(id, eventType)
{
new warningFlag = get_pcvar_num(g_cvar_immunityWarning);
new maxSeconds, elapsedSeconds, eventImmunity, showWarning;
if (eventType == EVENT_JOIN)
{
maxSeconds = get_pcvar_num(g_cvar_joinTime);
elapsedSeconds = g_timeJoin[id];
eventImmunity = has_flag(id, g_joinImmunity);
showWarning = eventImmunity ? warningFlag & EVENT_JOIN : 1;
}
else if (eventType == EVENT_SPEC)
{
maxSeconds = get_pcvar_num(g_cvar_specTime);
elapsedSeconds = g_timeSpec[id];
eventImmunity = has_flag(id, g_specImmunity);
showWarning = eventImmunity ? warningFlag & EVENT_SPEC : 1;
}
else if (eventType == EVENT_AFK)
{
maxSeconds = get_pcvar_num(g_cvar_afkTime);
elapsedSeconds = g_timeAFK[id];
eventImmunity = has_flag(id, g_afkImmunity);
showWarning = eventImmunity ? warningFlag & EVENT_AFK : 1;
}
else return;
new warningStartSeconds = maxSeconds - get_pcvar_num(g_cvar_warningTime);
if (elapsedSeconds >= maxSeconds)
{
// if players have immunity for this event abort
if (eventImmunity) return;
// get the correct message formats for this event type
new msgReason[32], msgAnnounce[32];
switch (eventType)
{
case EVENT_JOIN:
{
copy(msgReason, 31, "KICK_JOIN_REASON");
copy(msgAnnounce, 31, "KICK_JOIN_ANNOUNCE");
}
case EVENT_SPEC:
{
copy(msgReason, 31, "KICK_SPEC_REASON");
copy(msgAnnounce, 31, "KICK_SPEC_ANNOUNCE");
}
case EVENT_AFK:
{
copy(msgReason, 31, "KICK_AFK_REASON");
copy(msgAnnounce, 31, "KICK_AFK_ANNOUNCE");
}
}
new maxTime[128];
get_time_length(id, maxSeconds, timeunit_seconds, maxTime, 127);
new kick2ip[32];
get_pcvar_string(g_cvar_kick2ip, kick2ip, sizeof(kick2ip)-1);
if (kick2ip[0] == 0)
{
// kick the player into the nether
server_cmd("kick #%d %L", get_user_userid(id), id, msgReason, maxTime);
}
else
{
// kick the player into another server
new kick2port[16];
get_pcvar_string(g_cvar_kick2port, kick2port, sizeof(kick2port)-1);
client_cmd(id, "connect %s:%s", kick2ip, kick2port);
}
// announce the kick to the rest of the world
new players[32], playerCnt;
get_players(players, playerCnt, "c");
new playerName[32];
get_user_name(id, playerName, 31);
new playerID;
for (new playerIdx = 0; playerIdx < playerCnt; playerIdx++)
{
playerID = players[playerIdx];
get_time_length(playerID, maxSeconds, timeunit_seconds, maxTime, 127);
client_print(playerID, print_chat, "[PBK] %L", playerID, msgAnnounce, playerName, maxTime);
}
// log the kick
new logFlags = get_pcvar_num(g_cvar_log);
if (logFlags)
{
get_time_length(0, maxSeconds, timeunit_seconds, maxTime, 127);
new logText[128];
format(logText, 127, "%L", LANG_SERVER, msgAnnounce, "", maxTime);
// remove the single space that not providing a name added
trim(logText);
create_log_entry(id, "PBK", logFlags, logText);
}
}
else if (warningStartSeconds <= elapsedSeconds && showWarning)
{
// get the correct message format for this event type
new msgWarning[32];
switch (eventType)
{
case EVENT_JOIN: copy(msgWarning, 31, "KICK_JOIN_WARNING");
case EVENT_SPEC: copy(msgWarning, 31, "KICK_SPEC_WARNING");
case EVENT_AFK: copy(msgWarning, 31, "KICK_AFK_WARNING");
}
new timeLeft[128]
get_time_length(id, maxSeconds - elapsedSeconds, timeunit_seconds, timeLeft, 127);
// warn the user about their impending departure
client_print(id, print_chat, "[PBK] %L", id, msgWarning, timeLeft);
}
}