is_lagging(client * cl)1 inline bool is_lagging(client *cl)
2 {
3 return ( cl->spj > 50 || cl->ping > 500 || cl->ldt > 80 ); // do not change this except if you really know what are you doing
4 }
5
outside_border(vec & po)6 inline bool outside_border(vec &po)
7 {
8 return (po.x < 0 || po.y < 0 || po.x >= maplayoutssize || po.y >= maplayoutssize);
9 }
10
checkclientpos(client * cl)11 inline void checkclientpos(client *cl)
12 {
13 vec &po = cl->state.o;
14 if( outside_border(po) || maplayout[((int) po.x) + (((int) po.y) << maplayout_factor)] > po.z + 3)
15 {
16 if(gamemillis > 10000 && (servmillis - cl->connectmillis) > 10000) cl->mapcollisions++;
17 if(cl->mapcollisions && !(cl->mapcollisions % 25))
18 {
19 logline(ACLOG_INFO, "[%s] %s is colliding with the map", cl->hostname, cl->name);
20 }
21 }
22 }
23
24 #define POW2XY(A,B) (pow2(A.x-B.x)+pow2(A.y-B.y))
25
26 extern inline void addban(client *cl, int reason, int type = BAN_AUTO);
27
28 #ifdef ACAC
29 #include "anticheat.h"
30 #endif
31
32 #define MINELINE 50
33
34 //FIXME
35 /* There are smarter ways to implement this function, but most probably they will be very complex */
getmaxarea(int inversed_x,int inversed_y,int transposed,int ml_factor,char * ml)36 int getmaxarea(int inversed_x, int inversed_y, int transposed, int ml_factor, char *ml)
37 {
38 int ls = (1 << ml_factor);
39 int xi = 0, oxi = 0, xf = 0, oxf = 0, fx = 0, fy = 0;
40 int area = 0, maxarea = 0;
41 bool sav_x = false, sav_y = false;
42
43 if (transposed) fx = ml_factor;
44 else fy = ml_factor;
45
46 // walk on x for each y
47 for ( int y = (inversed_y ? ls-1 : 0); (inversed_y ? y >= 0 : y < ls); (inversed_y ? y-- : y++) ) {
48
49 /* Analyzing each cube of the line */
50 for ( int x = (inversed_x ? ls-1 : 0); (inversed_x ? x >= 0 : x < ls); (inversed_x ? x-- : x++) ) {
51 if ( ml[ ( x << fx ) + ( y << fy ) ] != 127 ) { // if it is not solid
52 if ( sav_x ) { // if the last cube was saved
53 xf = x; // new end for this line
54 }
55 else {
56 xi = x; // new begin of the line
57 sav_x = true; // accumulating cubes from now
58 }
59 } else { // solid
60 if ( xf - xi > MINELINE ) break; // if the empty line is greater than a minimum, get out
61 sav_x = false; // stop the accumulation of cubes
62 }
63 }
64
65 /* Analyzing this line with the previous one */
66 if ( xf - xi > MINELINE ) { // if the line has the minimun threshold of emptiness
67 if ( sav_y ) { // if the last line was saved
68 if ( 2*oxi + MINELINE < 2*xf &&
69 2*xi + MINELINE < 2*oxf ) { // if the last line intersect this one
70 area += xf - xi;
71 } else {
72 oxi = xi; // new area vertices
73 oxf = xf;
74 }
75 }
76 else {
77 oxi = xi;
78 oxf = xf;
79 sav_y = true; // accumulating lines from now
80 }
81 } else {
82 sav_y = false; // stop the accumulation of lines
83 if (area > maxarea) maxarea = area; // new max area
84 area=0;
85 }
86
87 sav_x = false; // reset x
88 xi = xf = 0;
89 }
90 return maxarea;
91 }
92
checkarea(int maplayout_factor,char * maplayout)93 int checkarea(int maplayout_factor, char *maplayout)
94 {
95 int area = 0, maxarea = 0;
96 for (int i=0; i < 8; i++) {
97 area = getmaxarea((i & 1),(i & 2),(i & 4), maplayout_factor, maplayout);
98 if ( area > maxarea ) maxarea = area;
99 }
100 return maxarea;
101 }
102
103 /**
104 This part is related to medals system. WIP
105 */
106
107 const char * medal_messages[] = { "defended the flag", "covered the flag stealer", "defended the dropped flag", "covered the flag keeper", "covered teammate" };
108 enum { CTFLDEF, CTFLCOV, HTFLDEF, HTFLCOV, COVER, MEDALMESSAGENUM };
print_medal_messages(client * c,int n)109 inline void print_medal_messages(client *c, int n)
110 {
111 if (n<0 || n>=MEDALMESSAGENUM) return;
112 logline(ACLOG_VERBOSE, "[%s] %s %s", c->hostname, c->name, medal_messages[n]);
113 }
114
115 inline void addpt(client *c, int points, int n = -1) {
116 c->state.points += points;
117 c->md.dpt += points;
118 c->md.updated = true;
119 c->md.upmillis = gamemillis + 240; // about 2 AR shots
120 print_medal_messages(c,n);
121 }
122
123 /** cnumber is the number of players in the game, at a max value of 12 */
124 #define CTFPICKPT cnumber // player picked the flag (ctf)
125 #define CTFDROPPT -cnumber // player dropped the flag to other player (probably)
126 #define HTFLOSTPT -2*cnumber // penalty
127 #define CTFLOSTPT cnumber*distance/100 // bonus: 1/4 of the flag score bonus
128 #define CTFRETURNPT cnumber // flag return
129 #define CTFSCOREPT (cnumber*distance/25+10) // flag score
130 #define HTFSCOREPT (cnumber*4+10)
131 #define KTFSCOREPT (cnumber*2+10)
132 #define COMBOPT 5 // player frags with combo
133 #define REPLYPT 2 // reply success
134 #define TWDONEPT 5 // team work done
135 #define CTFLDEFPT cnumber // player defended the flag in the base (ctf)
136 #define CTFLCOVPT cnumber*2 // player covered the flag stealer (ctf)
137 #define HTFLDEFPT cnumber // player defended a droped flag (htf)
138 #define HTFLCOVPT cnumber*3 // player covered the flag keeper (htf)
139 #define COVERPT cnumber*2 // player covered teammate
140 #define DEATHPT -4 // player died
141 #define BONUSPT target->state.points/400 // bonus (for killing high level enemies :: beware with exponential behavior!)
142 #define FLBONUSPT target->state.points/300 // bonus if flag team mode
143 #define TMBONUSPT target->state.points/200 // bonus if team mode (to give some extra reward for playing tdm modes)
144 #define HTFFRAGPT cnumber/2 // player frags while carrying the flag
145 #define CTFFRAGPT 2*cnumber // player frags the flag stealer
146 #define FRAGPT 10 // player frags (normal)
147 #define HEADSHOTPT 15 // player gibs with head shot
148 #define KNIFEPT 20 // player gibs with the knife
149 #define SHOTGPT 12 // player gibs with the shotgun
150 #define TKPT -20 // player tks
151 #define FLAGTKPT -2*(10+cnumber) // player tks the flag keeper/stealer
152
flagpoints(client * c,int message)153 void flagpoints(client *c, int message)
154 {
155 float distance = 0;
156 int cnumber = totalclients < 13 ? totalclients : 12;
157 switch (message)
158 {
159 case FM_PICKUP:
160 c->md.flagpos.x = c->state.o.x;
161 c->md.flagpos.y = c->state.o.y;
162 c->md.flagmillis = servmillis;
163 if (m_ctf) addpt(c, CTFPICKPT);
164 break;
165 case FM_DROP:
166 if (m_ctf) addpt(c, CTFDROPPT);
167 break;
168 case FM_LOST:
169 if (m_htf) addpt(c, HTFLOSTPT);
170 else if (m_ctf) {
171 distance = sqrt(POW2XY(c->state.o,c->md.flagpos));
172 if (distance > 200) distance = 200; // ~200 is the distance between the flags in ac_depot
173 addpt(c, CTFLOSTPT);
174 }
175 break;
176 case FM_RETURN:
177 addpt(c, CTFRETURNPT);
178 break;
179 case FM_SCORE:
180 if (m_ctf) {
181 distance = sqrt(POW2XY(c->state.o,c->md.flagpos));
182 if (distance > 200) distance = 200;
183 #ifdef ACAC
184 if ( validflagscore(distance,c) )
185 #endif
186 addpt(c, CTFSCOREPT);
187 } else addpt(c, HTFSCOREPT);
188 break;
189 case FM_KTFSCORE:
190 addpt(c, KTFSCOREPT);
191 break;
192 default:
193 break;
194 }
195 }
196
197
minhits2combo(int gun)198 inline int minhits2combo(int gun)
199 {
200 switch (gun)
201 {
202 case GUN_SUBGUN:
203 case GUN_AKIMBO:
204 return 4;
205 case GUN_GRENADE:
206 case GUN_ASSAULT:
207 return 3;
208 default:
209 return 2;
210 }
211 }
212
checkcombo(client * target,client * actor,int damage,int gun)213 void checkcombo(client *target, client *actor, int damage, int gun)
214 {
215 int diffhittime = servmillis - actor->md.lasthit;
216 actor->md.lasthit = servmillis;
217 if ((gun == GUN_SHOTGUN || gun == GUN_GRENADE) && damage < 20)
218 {
219 actor->md.lastgun = gun;
220 return;
221 }
222
223 if ( diffhittime < 750 ) {
224 if ( gun == actor->md.lastgun )
225 {
226 if ( diffhittime * 2 < guns[gun].attackdelay * 3 )
227 {
228 actor->md.combohits++;
229 actor->md.combotime+=diffhittime;
230 actor->md.combodamage+=damage;
231 int mh2c = minhits2combo(gun);
232 if ( actor->md.combohits > mh2c && actor->md.combohits % mh2c == 1 )
233 {
234 if (actor->md.combo < 5) actor->md.combo++;
235 actor->md.ncombos++;
236 }
237 }
238 }
239 else
240 {
241 switch (gun)
242 {
243 case GUN_KNIFE:
244 case GUN_PISTOL:
245 if ( guns[actor->md.lastgun].isauto ) break;
246 case GUN_SNIPER:
247 if ( actor->md.lastgun > GUN_PISTOL ) break;
248 default:
249 actor->md.combohits++;
250 actor->md.combotime+=diffhittime;
251 actor->md.combodamage+=damage;
252 if (actor->md.combo < 5) actor->md.combo++;
253 actor->md.ncombos++;
254 break;
255 }
256 }
257 }
258 else
259 {
260 actor->md.combo = 0;
261 actor->md.combofrags = 0;
262 actor->md.combotime = 0;
263 actor->md.combodamage = 0;
264 actor->md.combohits = 0;
265 }
266 actor->md.lastgun = gun;
267 }
268
269 #define COVERDIST 2000 // about 45 cubes
270 #define REPLYDIST 8000 // about 90 cubes
271 float coverdist = COVERDIST;
272
checkteamrequests(int sender)273 int checkteamrequests(int sender)
274 {
275 int dtime, besttime = -1;
276 int bestid = -1;
277 client *ant = clients[sender];
278 loopv(clients) {
279 client *prot = clients[i];
280 if ( i!=sender && prot->type != ST_EMPTY && prot->team == ant->team &&
281 prot->state.state == CS_ALIVE && prot->md.askmillis > gamemillis && prot->md.ask >= 0 ) {
282 float dist = POW2XY(ant->state.o,prot->state.o);
283 if ( dist < REPLYDIST && (dtime=prot->md.askmillis-gamemillis) > besttime) {
284 bestid = i;
285 besttime = dtime;
286 }
287 }
288 }
289 if ( besttime >= 0 ) return bestid;
290 return -1;
291 }
292
293 /** WIP */
checkteamplay(int s,int sender)294 void checkteamplay(int s, int sender)
295 {
296 client *actor = clients[sender];
297
298 if ( actor->state.state != CS_ALIVE ) return;
299 switch(s){
300 case S_IMONDEFENSE: // informs
301 actor->md.linkmillis = gamemillis + 20000;
302 actor->md.linkreason = s;
303 break;
304 case S_COVERME: // demands
305 case S_STAYTOGETHER:
306 case S_STAYHERE:
307 actor->md.ask = s;
308 actor->md.askmillis = gamemillis + 5000;
309 break;
310 case S_AFFIRMATIVE: // replies
311 case S_ALLRIGHTSIR:
312 case S_YES:
313 {
314 int id = checkteamrequests(sender);
315 if ( id >= 0 ) {
316 client *sgt = clients[id];
317 actor->md.linked = id;
318 if ( actor->md.linkmillis < gamemillis ) addpt(actor,REPLYPT);
319 actor->md.linkmillis = gamemillis + 30000;
320 actor->md.linkreason = sgt->md.ask;
321 sendf(actor->clientnum, 1, "ri2", SV_HUDEXTRAS, HE_NUM+id);
322 switch( actor->md.linkreason ) { // check demands
323 case S_STAYHERE:
324 actor->md.pos = sgt->state.o;
325 addpt(sgt,REPLYPT);
326 break;
327 }
328 }
329 break;
330 }
331 }
332 }
333
computeteamwork(int team,int exclude)334 void computeteamwork(int team, int exclude) // testing
335 {
336 loopv(clients)
337 {
338 client *actor = clients[i];
339 if ( i == exclude || actor->type == ST_EMPTY || actor->team != team || actor->state.state != CS_ALIVE || actor->md.linkmillis < gamemillis ) continue;
340 vec position;
341 bool teamworkdone = false;
342 switch( actor->md.linkreason )
343 {
344 case S_IMONDEFENSE:
345 position = actor->spawnp;
346 teamworkdone = true;
347 break;
348 case S_STAYTOGETHER:
349 if ( valid_client(actor->md.linked) ) position = clients[actor->md.linked]->state.o;
350 teamworkdone = true;
351 break;
352 case S_STAYHERE:
353 position = actor->md.pos;
354 teamworkdone = true;
355 break;
356 }
357 if ( teamworkdone )
358 {
359 float dist = POW2XY(actor->state.o,position);
360 if (dist < COVERDIST)
361 {
362 addpt(actor,TWDONEPT);
363 sendf(actor->clientnum, 1, "ri2", SV_HUDEXTRAS, HE_TEAMWORK);
364 }
365 }
366 }
367 }
368
369 float a2c = 0, c2t = 0, a2t = 0; // distances: actor to covered, covered to target and actor to target
370
testcover(int msg,int factor,client * actor)371 inline bool testcover(int msg, int factor, client *actor)
372 {
373 if ( a2c < coverdist && c2t < coverdist && a2t < coverdist )
374 {
375 sendf(actor->clientnum, 1, "ri2", SV_HUDEXTRAS, msg);
376 addpt(actor, factor);
377 actor->md.ncovers++;
378 return true;
379 }
380 return false;
381 }
382
383 #define CALCCOVER(C) \
384 a2c = POW2XY(actor->state.o,C);\
385 c2t = POW2XY(C,target->state.o);\
386 a2t = POW2XY(actor->state.o,target->state.o)
387
validlink(client * actor,int cn)388 bool validlink(client *actor, int cn)
389 {
390 return actor->md.linked >= 0 && actor->md.linked == cn && gamemillis < actor->md.linkmillis && valid_client(actor->md.linked);
391 }
392
393 /** WIP */
checkcover(client * target,client * actor)394 void checkcover(client *target, client *actor)
395 {
396 int team = actor->team;
397 int oteam = team_opposite(team);
398
399 bool covered = false;
400 int coverid = -1;
401
402 int cnumber = totalclients < 13 ? totalclients : 12;
403
404 if ( m_flags ) {
405 sflaginfo &f = sflaginfos[team];
406 sflaginfo &of = sflaginfos[oteam];
407
408 if ( m_ctf )
409 {
410 if ( f.state == CTFF_INBASE )
411 {
412 CALCCOVER(f);
413 if ( testcover(HE_FLAGDEFENDED, CTFLDEFPT, actor) ) print_medal_messages(actor,CTFLDEF);
414 }
415 if ( of.state == CTFF_STOLEN && actor->clientnum != of.actor_cn )
416 {
417 covered = true; coverid = of.actor_cn;
418 CALCCOVER(clients[of.actor_cn]->state.o);
419 if ( testcover(HE_FLAGCOVERED, CTFLCOVPT, actor) ) print_medal_messages(actor,CTFLCOV);
420 }
421 }
422 else if ( m_htf )
423 {
424 if ( actor->clientnum != f.actor_cn )
425 {
426 if ( f.state == CTFF_DROPPED )
427 {
428 struct { short x, y; } nf;
429 nf.x = f.pos[0]; nf.y = f.pos[1];
430 CALCCOVER(nf);
431 if ( testcover(HE_FLAGDEFENDED, HTFLDEFPT, actor) ) print_medal_messages(actor,HTFLDEF);
432 }
433 if ( f.state == CTFF_STOLEN )
434 {
435 covered = true; coverid = f.actor_cn;
436 CALCCOVER(clients[f.actor_cn]->state.o);
437 if ( testcover(HE_FLAGCOVERED, HTFLCOVPT, actor) ) print_medal_messages(actor,HTFLCOV);
438 }
439 }
440 }
441 }
442
443 if ( !(covered && actor->md.linked==coverid) && validlink(actor,actor->md.linked) )
444 {
445 CALCCOVER(clients[actor->md.linked]->state.o);
446 if ( testcover(HE_COVER, COVERPT, actor) ) print_medal_messages(actor,COVER);
447 }
448
449 }
450
451 #undef CALCCOVER
452
453 /** WiP */
checkfrag(client * target,client * actor,int gun,bool gib)454 void checkfrag(client *target, client *actor, int gun, bool gib)
455 {
456 int targethasflag = clienthasflag(target->clientnum);
457 int actorhasflag = clienthasflag(actor->clientnum);
458 int cnumber = totalclients < 13 ? totalclients : 12;
459 addpt(target,DEATHPT);
460 if(target!=actor) {
461 if(!isteam(target->team, actor->team)) {
462
463 if (m_teammode) {
464 if(!m_flags) addpt(actor, TMBONUSPT);
465 else addpt(actor, FLBONUSPT);
466
467 checkcover (target, actor);
468 if ( m_htf && actorhasflag >= 0 ) addpt(actor, HTFFRAGPT);
469
470 if ( m_ctf && targethasflag >= 0 ) {
471 addpt(actor, CTFFRAGPT);
472 }
473 }
474 else addpt(actor, BONUSPT);
475
476 if (gib && gun != GUN_GRENADE) {
477 if ( gun == GUN_SNIPER ) {
478 addpt(actor, HEADSHOTPT);
479 actor->md.nhs++;
480 }
481 else if ( gun == GUN_KNIFE ) addpt(actor, KNIFEPT);
482 else if ( gun == GUN_SHOTGUN ) addpt(actor, SHOTGPT);
483 }
484 else addpt(actor, FRAGPT);
485
486 if ( actor->md.combo ) {
487 actor->md.combofrags++;
488 addpt(actor,COMBOPT);
489 actor->md.combosend = true;
490 }
491
492 } else {
493
494 if ( targethasflag >= 0 ) addpt(actor, FLAGTKPT);
495 else addpt(actor, TKPT);
496
497 }
498 }
499 }
500
501 int next_afk_check = 200;
502
503 /* this function is managed to the PUBS, id est, many people playing in an open server */
check_afk()504 void check_afk()
505 {
506 next_afk_check = servmillis + 7 * 1000;
507 /* if we have few people (like 2x2), or it is not a teammode with the server not full: do nothing! */
508 if ( totalclients < 5 || ( totalclients < scl.maxclients && !m_teammode) ) return;
509 loopv(clients)
510 {
511 client &c = *clients[i];
512 if ( c.type != ST_TCPIP || c.connectmillis + 60 * 1000 > servmillis ||
513 c.inputmillis + scl.afk_limit > servmillis || clienthasflag(c.clientnum) != -1 ) continue;
514 if ( ( c.state.state == CS_DEAD && !m_arena && c.state.lastdeath + 45 * 1000 < gamemillis) ||
515 ( c.state.state == CS_ALIVE && c.upspawnp ) /*||
516 ( c.state.state == CS_SPECTATE && totalclients >= scl.maxclients ) // only kick spectator if server is full - 2011oct16:flowtron: mmh, that seems reasonable enough .. still, kicking spectators for inactivity seems harsh! disabled ATM, kick them manually if you must.
517 */
518 )
519 {
520 logline(ACLOG_INFO, "[%s] %s %s", c.hostname, c.name, "is afk");
521 defformatstring(msg)("%s is afk", c.name);
522 sendservmsg(msg);
523 disconnect_client(c.clientnum, DISC_AFK);
524 }
525 }
526 }
527
528 /** This function counts how much non-killing-damage the player does to any teammates
529 The damage limit is 100 hp per minute, which is about 2 tks per minute in a normal game
530 In normal games, the players go over 6 tks only in the worst cases */
check_ffire(client * target,client * actor,int damage)531 void check_ffire(client *target, client *actor, int damage)
532 {
533 if ( mastermode != MM_OPEN ) return;
534 actor->ffire += damage;
535 if ( actor->ffire > 300 && actor->ffire * 600 > gamemillis) {
536 logline(ACLOG_INFO, "[%s] %s %s", actor->hostname, actor->name, "kicked for excessive friendly fire");
537 defformatstring(msg)("%s %s", actor->name, "kicked for excessive friendly fire");
538 sendservmsg(msg);
539 disconnect_client(actor->clientnum, DISC_FFIRE);
540 }
541 }
542
check_pdist(client * c,float & dist)543 inline int check_pdist(client *c, float & dist) // pick up distance
544 {
545 // ping 1000ms at max velocity can produce an error of 20 cubes
546 float delay = 9.0f + (float)c->ping * 0.02f + (float)c->spj * 0.025f; // at pj/ping 40/100, delay = 12
547 if ( dist > delay )
548 {
549 if ( dist < 1.5f * delay ) return 1;
550 #ifdef ACAC
551 pickup_checks(c,dist-delay);
552 #endif
553 return 2;
554 }
555 return 0;
556 }
557
558 /**
559 If you read README.txt you must know that AC does not have cheat protection implemented.
560 However this file is the sketch to a very special kind of cheat detection tools in server side.
561
562 This is not based in program tricks, i.e., encryption, secret bytes, nor monitoring/scanning tools.
563
564 The idea behind these cheat detections is to check (or reproduce) the client data, and verify if
565 this data is expected or possible. Also, there is no need to check all clients all time, and
566 one coding this kind of check must pay a special attention to the lag effect and how it can
567 affect the data observed. This is not a trivial task, and probably it is the main reason why
568 such tools were never implemented.
569
570 This part is here for compatibility purposes.
571 If you know nothing about these detections, please, just ignore it.
572 */
573
checkmove(client * cl)574 inline void checkmove(client *cl)
575 {
576 cl->ldt = gamemillis - cl->lmillis;
577 cl->lmillis = gamemillis;
578 if ( cl->ldt < 40 ) cl->ldt = 40;
579 cl->t += cl->ldt;
580 cl->spj = (( 7 * cl->spj + cl->ldt ) >> 3);
581
582 if ( cl->input != cl->f )
583 {
584 cl->input = cl->f;
585 cl->inputmillis = servmillis;
586 }
587
588 if(maplayout) checkclientpos(cl);
589
590 #ifdef ACAC
591 m_engine(cl);
592 #endif
593
594 if ( !cl->upspawnp )
595 {
596 cl->spawnp = cl->state.o;
597 cl->upspawnp = true;
598 cl->spj = cl->ldt = 40;
599 }
600
601 return;
602 }
603
checkshoot(int & cn,gameevent & shot,int & hits,int & tcn)604 inline void checkshoot(int & cn, gameevent & shot, int & hits, int & tcn)
605 {
606 #ifdef ACAC
607 s_engine(cn, shot, hits, tcn);
608 #endif
609 return;
610 }
611
checkweapon(int & type,int & var)612 inline void checkweapon(int & type, int & var)
613 {
614 #ifdef ACAC
615 w_engine(type,var);
616 #endif
617 return;
618 }
619
validdamage(client * & target,client * & actor,int & damage,int & gun,bool & gib)620 bool validdamage(client *&target, client *&actor, int &damage, int &gun, bool &gib)
621 {
622 #ifdef ACAC
623 if (!d_engine(target, actor, damage, gun, gib)) return false;
624 #endif
625 return true;
626 }
627
checkmessage(client * c,int type)628 inline int checkmessage(client *c, int type)
629 {
630 #ifdef ACAC
631 type = p_engine(c,type);
632 #endif
633 return type;
634 }
635
636