]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/anticheat.qc
Merge remote-tracking branch 'origin/master' into samual/notification_rewrite
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / anticheat.qc
1 .float anticheat_jointime;
2
3 void mean_accumulate(entity e, .float a, .float c, float mean, float value, float weight)
4 {
5         if(weight == 0)
6                 return;
7         if(mean == 0)
8                 e.a *= pow(value, weight);
9         else
10                 e.a += pow(value, mean) * weight;
11         e.c += weight;
12 }
13
14 float mean_evaluate(entity e, .float a, .float c, float mean)
15 {
16         if(e.c == 0)
17                 return 0;
18         if(mean == 0)
19                 return pow(e.a, 1.0 / e.c);
20         else
21                 return pow(e.a / e.c, 1.0 / mean);
22 }
23
24 #define MEAN_ACCUMULATE(prefix,v,w) mean_accumulate(self,prefix##_accumulator,prefix##_count,prefix##_mean,v,w)
25 #define MEAN_EVALUATE(prefix) mean_evaluate(self,prefix##_accumulator,prefix##_count,prefix##_mean)
26 #define MEAN_DECLARE(prefix,m) float prefix##_mean = m; .float prefix##_count, prefix##_accumulator
27
28 float anticheat_div0_evade_evasion_delta;
29 .float anticheat_div0_evade_offset;
30 .vector anticheat_div0_evade_v_angle;
31 .vector anticheat_div0_evade_forward_initial;
32 MEAN_DECLARE(anticheat_div0_evade, 5);
33
34 .vector anticheat_div0_strafebot_movement_prev;
35 MEAN_DECLARE(anticheat_div0_strafebot_old, 5);
36
37 .vector anticheat_div0_strafebot_forward_prev;
38 MEAN_DECLARE(anticheat_div0_strafebot_new, 5);
39
40 .float anticheat_speedhack_offset;
41 .float anticheat_speedhack_movetime, anticheat_speedhack_movetime_count, anticheat_speedhack_movetime_frac;
42 MEAN_DECLARE(anticheat_speedhack, 5);
43
44 float movement_oddity(vector m0, vector m1)
45 {
46         float cosangle = normalize(m0) * normalize(m1);
47         if(cosangle >= 0)
48                 return 0;
49         return 0.5 - 0.5 * cos(cosangle * cosangle * 4 * M_PI);
50                 // returns 0 for: -1, -sqrt(0.5), 0 (angles that commonly happen with kbd)
51 }
52
53 void anticheat_physics()
54 {
55         float f, wishspeed;
56         vector wishvel;
57
58         // div0_evade -> SPECTATORS
59         makevectors(self.v_angle);
60         if(self.anticheat_div0_evade_offset == 0)
61         {
62                 f = fabs(anticheat_div0_evade_evasion_delta - floor(anticheat_div0_evade_evasion_delta) - 0.5) * 2; // triangle function
63                 self.anticheat_div0_evade_offset = time + sys_frametime * (3 * f - 1);
64                 self.anticheat_div0_evade_v_angle = self.v_angle;
65                 self.anticheat_div0_evade_forward_initial = v_forward;
66                 MEAN_ACCUMULATE(anticheat_div0_evade, 0, 1);
67         }
68         else
69         {
70                 if(time < self.anticheat_div0_evade_offset)
71                         self.anticheat_div0_evade_v_angle = self.v_angle;
72                 MEAN_ACCUMULATE(anticheat_div0_evade, 0.5 - 0.5 * (self.anticheat_div0_evade_forward_initial * v_forward), 1);
73         }
74
75         MEAN_ACCUMULATE(anticheat_div0_strafebot_old, movement_oddity(self.movement, self.anticheat_div0_strafebot_movement_prev), 1);
76         self.anticheat_div0_strafebot_movement_prev = self.movement;
77
78         if(vlen(self.anticheat_div0_strafebot_forward_prev))
79                 MEAN_ACCUMULATE(anticheat_div0_strafebot_new, 0.5 - 0.5 * (self.anticheat_div0_strafebot_forward_prev * v_forward), 1);
80         self.anticheat_div0_strafebot_forward_prev = v_forward;
81
82         // generic speedhack detection: correlate anticheat_speedhack_movetime (UPDATED BEFORE THIS) and server time
83         self.anticheat_speedhack_movetime_frac += frametime;
84         f = floor(self.anticheat_speedhack_movetime_frac);
85         self.anticheat_speedhack_movetime_frac -= f;
86         self.anticheat_speedhack_movetime_count += f;
87         self.anticheat_speedhack_movetime = self.anticheat_speedhack_movetime_frac + self.anticheat_speedhack_movetime_count;
88         f = self.anticheat_speedhack_movetime - servertime;
89         if(self.anticheat_speedhack_offset == 0)
90                 self.anticheat_speedhack_offset = f;
91         else
92         {
93                 MEAN_ACCUMULATE(anticheat_speedhack, max(0, f - self.anticheat_speedhack_offset), 1);
94                 self.anticheat_speedhack_offset += (f - self.anticheat_speedhack_offset) * frametime * 0.1;
95         }
96
97         // race/CTS: force kbd movement for fairness
98         if(g_race || g_cts)
99         {
100                 // if record times matter
101                 // ensure nothing EVIL is being done (i.e. div0_evade)
102                 // this hinders joystick users though
103                 // but it still gives SOME analog control
104                 wishvel_x = fabs(self.movement_x);
105                 wishvel_y = fabs(self.movement_y);
106                 if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y)
107                 {
108                         wishvel_z = 0;
109                         wishspeed = vlen(wishvel);
110                         if(wishvel_x >= 2 * wishvel_y)
111                         {
112                                 // pure X motion
113                                 if(self.movement_x > 0)
114                                         self.movement_x = wishspeed;
115                                 else
116                                         self.movement_x = -wishspeed;
117                                 self.movement_y = 0;
118                         }
119                         else if(wishvel_y >= 2 * wishvel_x)
120                         {
121                                 // pure Y motion
122                                 self.movement_x = 0;
123                                 if(self.movement_y > 0)
124                                         self.movement_y = wishspeed;
125                                 else
126                                         self.movement_y = -wishspeed;
127                         }
128                         else
129                         {
130                                 // diagonal
131                                 if(self.movement_x > 0)
132                                         self.movement_x = M_SQRT1_2 * wishspeed;
133                                 else
134                                         self.movement_x = -M_SQRT1_2 * wishspeed;
135                                 if(self.movement_y > 0)
136                                         self.movement_y = M_SQRT1_2 * wishspeed;
137                                 else
138                                         self.movement_y = -M_SQRT1_2 * wishspeed;
139                         }
140                 }
141         }
142 }
143
144 void anticheat_spectatecopy(entity spectatee)
145 {
146         // div0_evade -> SPECTATORS
147         self.angles = spectatee.anticheat_div0_evade_v_angle;
148 }
149
150 void anticheat_prethink()
151 {
152         // div0_evade -> SPECTATORS
153         self.anticheat_div0_evade_offset = 0;
154 }
155
156 string anticheat_display(float f, float tmin, float mi, float ma)
157 {
158         string s;
159         s = ftos(f);
160         if(f <= mi)
161                 return strcat(s, ":N");
162         if(f >= ma)
163                 return strcat(s, ":Y");
164         return strcat(s, ":-");
165 }
166
167 void anticheat_report()
168 {
169         if(!autocvar_sv_eventlog)
170                 return;
171         GameLogEcho(strcat(":anticheat:_time:", ftos(self.playerid), ":", ftos(servertime - self.anticheat_jointime)));
172         GameLogEcho(strcat(":anticheat:speedhack:", ftos(self.playerid), ":", anticheat_display(MEAN_EVALUATE(anticheat_speedhack), 240, 0.1, 0.15)));
173         GameLogEcho(strcat(":anticheat:div0_strafebot_old:", ftos(self.playerid), ":", anticheat_display(MEAN_EVALUATE(anticheat_div0_strafebot_old), 120, 0.3, 0.4)));
174         GameLogEcho(strcat(":anticheat:div0_strafebot_new:", ftos(self.playerid), ":", anticheat_display(MEAN_EVALUATE(anticheat_div0_strafebot_new), 120, 0.3, 0.4)));
175         GameLogEcho(strcat(":anticheat:div0_evade:", ftos(self.playerid), ":", anticheat_display(MEAN_EVALUATE(anticheat_div0_evade), 120, 0.1, 0.2)));
176 }
177
178 void anticheat_serverframe()
179 {
180         anticheat_div0_evade_evasion_delta += frametime * (0.5 + random());
181 }
182
183 void anticheat_init()
184 {
185         self.anticheat_speedhack_offset = 0;
186         self.anticheat_jointime = servertime;
187 }
188
189 void anticheat_shutdown()
190 {
191 }