Flags spinbot/anti-aim from impossible yaw rotation speed or an illegal pitch outside -89..89.
csb_spinbot_detect.sma v1.0.0
1
/*
2
* CSB Spinbot Detection
3
* Copyright (C) 2026 counter-strike-boost.com
4
*
5
* Samples pev_v_angle each frame and flags spinbot / anti-aim: a yaw that
6
* rotates faster than a human can move the mouse, or a pitch outside the legal
7
* -89..89 range, over several sustained frames.
8
*
9
* This program is free software: you can redistribute it and/or modify it under
10
* the terms of the GNU General Public License as published by the Free Software
11
* Foundation, either version 3 of the License, or (at your option) any later
12
* version. It is distributed in the hope that it will be useful, but WITHOUT
13
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
* FOR A PARTICULAR PURPOSE. See <https://www.gnu.org/licenses/> for details.
15
*/
16
17
#include <amxmodx>
18
#include <fakemeta>
19
20
new const PLUGIN[] = "CSB Spinbot Detection"
21
new const VERSION[] = "1.0.0"
22
new const AUTHOR[] = "counter-strike-boost.com"
23
24
new g_pEnabled, g_pMaxYaw, g_pStrikes, g_pAction
25
26
new Float:g_fLastYaw[33]
27
new bool:g_bHasLast[33]
28
new g_iStrikes[33]
29
30
public plugin_init()
31
{
32
register_plugin(PLUGIN, VERSION, AUTHOR)
33
34
g_pEnabled = register_cvar("csb_spin_enabled", "1")
35
g_pMaxYaw = register_cvar("csb_spin_maxyaw", "50.0")
36
g_pStrikes = register_cvar("csb_spin_strikes", "10")
37
g_pAction = register_cvar("csb_spin_action", "0")
38
39
register_forward(FM_PlayerPreThink, "fwPreThink")
40
}
41
42
public client_putinserver(id)
43
{
44
g_bHasLast[id] = false
45
g_iStrikes[id] = 0
46
}
47
48
public fwPreThink(id)
49
{
50
if (!get_pcvar_num(g_pEnabled) || !is_user_alive(id) || is_user_bot(id))
51
return FMRES_IGNORED
52
53
new Float:angles[3]
54
pev(id, pev_v_angle, angles)
55
56
new bool:bad = false
57
new reason[24]
58
59
// legal pitch is clamped to [-89, 89]; anything beyond is a hack angle
60
if (angles[0] > 89.5 || angles[0] < -89.5)
61
{
62
bad = true
63
copy(reason, charsmax(reason), "illegal pitch")
64
}
65
else if (g_bHasLast[id])
66
{
67
new Float:delta = angles[1] - g_fLastYaw[id]
68
69
// normalise to the shortest signed turn
70
while (delta > 180.0) delta -= 360.0
71
while (delta < -180.0) delta += 360.0
72
73
if (delta < 0.0)
74
delta = -delta
75
76
if (delta > get_pcvar_float(g_pMaxYaw))
77
{
78
bad = true
79
copy(reason, charsmax(reason), "impossible yaw speed")
80
}
81
}
82
83
g_fLastYaw[id] = angles[1]
84
g_bHasLast[id] = true
85
86
if (bad)
87
{
88
g_iStrikes[id]++
89
90
if (g_iStrikes[id] >= get_pcvar_num(g_pStrikes))
91
{
92
flag(id, reason)
93
g_iStrikes[id] = 0
94
}
95
}
96
else if (g_iStrikes[id] > 0)
97
{
98
g_iStrikes[id]--
99
}
100
101
return FMRES_IGNORED
102
}
103
104
flag(id, const reason[])
105
{
106
new name[32], authid[35]
107
get_user_name(id, name, charsmax(name))
108
get_user_authid(id, authid, charsmax(authid))
109
110
log_amx("[CSB Spinbot] %s <%s> flagged: %s", name, authid, reason)
111
112
if (get_pcvar_num(g_pAction) == 1)
113
{
114
server_cmd("kick #%d ^"CSB: spinbot / anti-aim detected^"", get_user_userid(id))
115
server_exec()
116
}
117
else
118
{
119
client_print(0, print_chat, "[CSB] %s is suspected of using spinbot / anti-aim (%s).", name, reason)
120
}
121
}
122









