/********************************************************************************
* AMX Mod X Script.
*
* Speed Hack Detector
* Formatright (C) 2009 OT
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
**********************************************************************************/
/* [Plugin Link]
*/
/* [Changelog]
- 2.0 - fixed everything, perfected the detection method, fixed the server lag issue that would ban players! Fixed cvar detections and added clockwindow detection.
- 1.6 - fixed a problem where the plugin would not detect the developer cvar
- 1.5 - added cvar for limit, improved logging
- 1.4 - added developer cvar kick
- 1.3 - improved logging, increaced tolerance
- 1.2 - added more cvars for customization
- 1.1 - added one more option to punishtype cvar
- 1.0 - initial release
*/
/* [Credits]
Empower - posted all the false bans, suggested improvements, server lag false detection problem, clockwindow information
Connor - info about corectly detecting developer cvar
*/
#include <amxmodx>
#include <fakemeta>
#include <hamsandwich>
#define add_bot_property(%1) gBS_cl_bot |= (1<<(%1 - 1))
#define del_bot_property(%1) gBS_cl_bot &= ~(1<<(%1 - 1))
#define has_bot_property(%1) (gBS_cl_bot & (1<<(%1 - 1)))
#define add_alive_property(%1) gBS_cl_alive |= (1<<(%1 - 1))
#define del_alive_property(%1) gBS_cl_alive &= ~(1<<(%1 - 1))
#define has_alive_property(%1) (gBS_cl_alive & (1<<(%1 - 1)))
const gC_MaxIdle = 2500
const gC_MaxSlots = 32
new gBS_cl_alive, gBS_cl_bot
new gPV_punishtype, gPV_enable, gCV_enable, gPV_bantime, gPV_adminonly, gPV_logbans, gPV_limit
new gCLI_count[gC_MaxSlots + 1]
new gCLI_loss[gC_MaxSlots + 1]
new gCLI_ping[gC_MaxSlots + 1]
new gCLI_buttons[gC_MaxSlots + 1]
new gCLI_idlecount[gC_MaxSlots + 1]
new Float:gCLF_lasttime[gC_MaxSlots + 1]
new Float:gCLV_views[gC_MaxSlots + 1][3]
new Float:gCLV_pviews[gC_MaxSlots + 1][3]
public plugin_init()
{
register_plugin("Speed Hack Detector", "2.0", "OT")
gPV_enable = register_cvar("spd_enable", "1")
gPV_punishtype = register_cvar("spd_punishtype", "0")
gPV_bantime = register_cvar("spd_bantime", "0")
gPV_adminonly = register_cvar("spd_adminonly", "1")
gPV_logbans = register_cvar("spd_logbans", "1")
gPV_limit = register_cvar("spd_limit", "150")
RegisterHam(Ham_Spawn, "player", "pfw_PlayerHandleAD", 1)
RegisterHam(Ham_Killed, "player", "pfw_PlayerHandleAD", 1)
register_forward(FM_CmdStart, "pfw_CmdStart", 1)
set_task(1.0, "tsk_ChacheCvars", 0, "", 0, "b", 0)
}
public client_putinserver(id)
{
del_alive_property(id)
if (is_user_bot(id))
add_bot_property(id)
else
del_bot_property(id)
}
public client_disconnect(id)
{
del_alive_property(id)
del_bot_property(id)
}
public tsk_ChacheCvars(id)
{
// The !! means that we want or 0 or 1 not 34183 values
gCV_enable = !!get_pcvar_num(gPV_enable)
return PLUGIN_CONTINUE
}
public pfw_CmdStart(id, pUC, seed)
{
if (!gCV_enable)
return FMRES_IGNORED
if (!has_alive_property(id) || has_bot_property(id))
return FMRES_IGNORED
new Float:fGameTime, iButtons
new Float:vView[3]
pev(id, pev_v_angle, vView)
fGameTime = get_gametime()
get_uc(pUC, UC_Buttons, iButtons)
if (gCLI_count[id] > 0)
{
if (gCLI_buttons[id] == iButtons)
{
if (gCLV_pviews[id][0] == vView[0] && gCLV_pviews[id][1] == vView[1] && gCLV_pviews[id][2] == vView[2])
gCLI_idlecount[id]++
else
gCLI_idlecount[id] = 0
if (gCLI_idlecount[id] > gC_MaxIdle)
{
gCLV_views[id][0] == -8000.0
gCLI_idlecount[id] = 0
}
}
else
{
gCLI_idlecount[id] = 0
gCLI_buttons[id] = iButtons
}
gCLV_pviews[id][0] = vView[0]
gCLV_pviews[id][1] = vView[1]
gCLV_pviews[id][2] = vView[2]
}
if (gCLI_count[id] < 0)
{
gCLI_idlecount[id] = 0
gCLI_buttons[id] = iButtons
gCLV_pviews[id][0] = vView[0]
gCLV_pviews[id][1] = vView[1]
gCLV_pviews[id][2] = vView[2]
}
if (gCLV_views[id][0] == -8000.0 && gCLI_count[id] != -9)
{
gCLI_count[id] = -9
return FMRES_IGNORED
}
if (gCLI_count[id] < -2)
{
gCLI_buttons[id] = iButtons
gCLV_views[id][0] = vView[0]
gCLV_views[id][1] = vView[1]
gCLV_views[id][2] = vView[2]
gCLI_count[id]++
return FMRES_IGNORED
}
// If the user has begun targeting players
if (gCLV_views[id][0] == vView[0] && gCLV_views[id][1] == vView[1] && gCLV_views[id][2] == vView[2])
{
gCLI_count[id] = -2
return FMRES_IGNORED
}
if (fGameTime - gCLF_lasttime[id] > 1.0)
{
if (gCLI_count[id] > get_pcvar_num(gPV_limit))
punish_player(id)
if (gCLI_count[id] < 0)
gCLI_count[id] ++
else
gCLI_count[id] = 0
gCLF_lasttime[id] = fGameTime
return FMRES_IGNORED
}
if (gCLI_count[id] >= 0)
{
new loss, ping
get_user_ping(id, ping, loss)
gCLI_loss[id] = floatround(float(gCLI_loss[id] * gCLI_count[id] + loss)/float(gCLI_count[id]))
gCLI_ping[id] = floatround(float(gCLI_ping[id] * gCLI_count[id] + ping)/float(gCLI_count[id]))
gCLI_count[id]++
if (gCLI_count[id] % 10 == 0)
{
query_client_cvar(id, "developer", "qcv_developer")
query_client_cvar(id, "clockwindow", "qcv_clockwindow")
}
}
return FMRES_IGNORED
}
public pfw_PlayerHandleAD(id)
{
if (is_user_alive(id))
{
if (!has_bot_property(id))
{
query_client_cvar(id, "developer", "qcv_developer")
query_client_cvar(id, "clockwindow", "qcv_clockwindow")
}
gCLF_lasttime[id] = get_gametime()
gCLV_views[id][0] = -8000.0
gCLI_idlecount[id] = 0
add_alive_property(id)
}
else
del_alive_property(id)
return HAM_IGNORED
}
public qcv_developer(id, const cvar[], const value[])
{
// Thank you Connor!
new len, counter, c, i
len = strlen(value)
counter = 0
for(i=0; i<len; i++)
{
c = value[i]
if( c != 48 )
{
if( c == 46 )
{
if( ++counter > 1 )
{
kick_user(id, "Kicked by Speed Hack Detector", "Reason: You cannot play with developer different than 0", "To connect set developer to 0 and leave it that way or else you will be banned.")
}
}
else
{
kick_user(id, "Kicked by Speed Hack Detector", "Reason: You cannot play with developer different than 0", "To connect set developer to 0 and leave it that way or else you will be banned.")
}
}
}
}
public qcv_clockwindow(id, const cvar[], const value[])
{
if (value[0] != '0' || value[1] != '.' || value[2] != '5' || value[3] != '^0')
kick_user(id, "Kicked by Speed Hack Detector", "Reason: You cannot play with clockwindow different than 0.5", "To connect set clockwindow to 0.5 and leave it that way or else you will be banned.")
}
punish_player(id)
{
new name[32]
get_user_name(id, name, 31)
new steamid[32]
get_user_authid(id, steamid, 31)
new ping, loss
get_user_ping(id, ping, loss)
// No ping = Lag period (probably false detect). Thanks Empower!
if(!ping)
return 0
switch (get_pcvar_num(gPV_punishtype))
{
case 1: // AMX BANS support
{
server_cmd("amx_ban %d #%d ^"Speed Hack^"", abs(get_pcvar_num(gPV_bantime)), get_user_userid(id))
}
case 2: // The normal ban included in admincmd.sma
{
server_cmd("amx_ban #%d %d ^"Speed Hack^"", get_user_userid(id), abs(get_pcvar_num(gPV_bantime)))
}
case 3: // The same as previous, but without extern plugins need.
{
server_cmd("kick #%d ^"Speed Hack (Banned permanently)^";wait;banid ^"%d.0^" ^"%s^";wait;writeid", get_user_userid(id), abs(get_pcvar_num(gPV_bantime)), steamid)
}
case 4: // IP ban, included in admincmd.sma
{
server_cmd("amx_banip #%d %d ^"Speed Hack^"", get_user_userid(id), abs(get_pcvar_num(gPV_bantime)))
}
default:
{
if (get_pcvar_num(gPV_adminonly))
print_toadmin(name)
else
client_print(0, print_chat, "[SHD] Player %s was detected using speed hack.", name)
return 1
}
}
new message[200]
formatex(message, charsmax(message), "[SHD] Name: %s | Steam ID: %s | Counts: %d | Ping: %d | Loss: %d", name, steamid, gCLI_count[id], gCLI_ping[id], gCLI_loss[id])
if (get_pcvar_num(gPV_logbans))
log_to_file("shd_bans.log", message)
console_print(0, message)
if (get_pcvar_num(gPV_adminonly))
print_toadmin(name)
else
client_print(0, print_chat, "[SHD] Player %s", name, (0 < get_pcvar_num(gPV_punishtype) <= 4) ? "was banned because of the speed hack": "has been detected using speed hack")
return 1
}
print_toadmin(name[])
{
new players[32], num, id
get_players(players, num)
for (new i=0;i<num;i++)
{
id = players[i]
if (get_user_flags(id) & ADMIN_CHAT)
client_print(id, print_chat, "[SHD][Admin] Player %s", name, (0 < get_pcvar_num(gPV_punishtype) <= 4) ? "was banned because of the speed hack": "has been detected using speed hack")
}
}
kick_user(id, line1[] = "", line2[] = "", line3[] = "")
{
new msg_content[1024], pl_name[32], pl_userid, pl_authid[35]
/* grab logging infos */
pl_userid = get_user_userid(id)
get_user_name(id, pl_name, 31)
get_user_authid(id, pl_authid, 34)
/* do kick the player */
format(msg_content, 1023, "%s^n%s^n%s", line1, line2, line3)
message_begin(MSG_ONE, SVC_DISCONNECT, {0,0,0}, id)
write_string(msg_content)
message_end()
/* log the kick as <kick> command do */
log_message("Kick_ML: ^"%s<%d><%s><>^" was kicked by ^"Console^" (message ^"%s^" ^"%s^" ^"%s^")", pl_name, pl_userid, pl_authid, line1, line2, line3)
return 1
}