1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2010 COR Entertainment, LLC.
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 
14 See the GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #if defined HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 
29 #if defined HAVE_SYS_STAT_H
30 #include <sys/stat.h>
31 #endif
32 
33 #if !defined HAVE__STRDUP
34 #define _strdup strdup
35 #endif
36 
37 #include "g_local.h"
38 
39 game_locals_t	game;
40 level_locals_t	level;
41 game_import_t	gi;
42 game_export_t	globals;
43 spawn_temp_t	st;
44 g_vote_t		playervote;
45 tactical_t		tacticalScore;
46 
47 int	sm_meat_index;
48 int meansOfDeath;
49 int bot_won;
50 
51 char *winningmap;
52 
53 edict_t		*g_edicts;
54 
55 cvar_t	*deathmatch;
56 cvar_t  *ctf;
57 cvar_t  *tca;
58 cvar_t  *cp;
59 //mutators
60 cvar_t  *instagib;
61 cvar_t  *rocket_arena;
62 cvar_t  *insta_rockets;
63 cvar_t  *low_grav;
64 cvar_t  *regeneration;
65 cvar_t  *vampire;
66 cvar_t  *excessive;
67 cvar_t  *grapple;
68 cvar_t  *classbased;
69 
70 //duel mode
71 cvar_t	*g_duel;
72 
73 //tactical mode
74 cvar_t	*g_tactical;
75 
76 cvar_t	*g_losehealth;
77 cvar_t	*g_losehealth_num;
78 
79 //weapons
80 cvar_t	*wep_selfdmgmulti;
81 
82 //health/max health/max ammo
83 cvar_t	*g_spawnhealth;
84 cvar_t	*g_maxhealth;
85 cvar_t	*g_maxbullets;
86 cvar_t	*g_maxshells;
87 cvar_t	*g_maxrockets;
88 cvar_t	*g_maxgrenades;
89 cvar_t	*g_maxcells;
90 cvar_t	*g_maxslugs;
91 cvar_t	*g_maxseekers;
92 cvar_t	*g_maxbombs;
93 
94 //quick weapon change
95 cvar_t  *quickweap;
96 
97 //anti-camp
98 cvar_t  *anticamp;
99 cvar_t  *camptime;
100 
101 //show player lights for visibility even in non-team games
102 cvar_t	*g_dmlights;
103 
104 /** @brief Anti-camp frames
105  *
106  * This CVar controls the amount of frames velocity is accumulated for when
107  * trying to determine if a player is camping. Its valid range is 1-100,
108  * anything outside this range will default it to the value of the
109  * G_ANTICAMP_FRAMES constant.
110  *
111  * @note If the value is 1, the anti-camp will work exactly as it used to.
112  *
113  * @note This CVar's value should be high enough for the anti-camp to work
114  *	correctly (the higher it is, the more the system "remembers" about
115  *	players' previous velocities), but low enough and for it not to
116  *	punish players for camping long after they've resumed moving.
117  */
118 cvar_t	*ac_frames;
119 
120 /** @brief Anti-camp threshold
121  *
122  * This CVar contains the speed below which the timeout to "suicide" is no
123  * longer updated. It will default to G_ANTICAMP_THRESHOLD if its value is
124  * not in the ]0;500] range.
125  *
126  * @note In excessive mode, this value is multiplied by 1.5
127  */
128 cvar_t	*ac_threshold;
129 
130 //random quad
131 cvar_t  *g_randomquad;
132 
133 //warmup
134 cvar_t  *warmuptime;
135 
136 //spawn protection
137 cvar_t  *g_spawnprotect;
138 
139 //joust mode
140 cvar_t  *joustmode;
141 
142 //map voting
143 cvar_t	*g_mapvote;
144 cvar_t	*g_voterand;
145 cvar_t	*g_votemode;
146 cvar_t	*g_votesame;
147 
148 //call voting
149 cvar_t	*g_callvote;
150 
151 //reward point threshold
152 cvar_t	*g_reward;
153 
154 //force autobalanced teams
155 cvar_t	*g_autobalance;
156 
157 cvar_t	*dmflags;
158 cvar_t	*skill;
159 cvar_t	*fraglimit;
160 cvar_t	*timelimit;
161 cvar_t	*password;
162 cvar_t	*spectator_password;
163 cvar_t	*needpass;
164 cvar_t	*g_maxclients;
165 cvar_t	*maxspectators;
166 cvar_t	*maxentities;
167 cvar_t	*g_select_empty;
168 cvar_t	*g_dedicated;
169 cvar_t	*motdfile;
170 
171 /** @brief CVar that forces MOTD display
172  *
173  * This CVar should contain an integer, which will indicate how client frames
174  * the MOTD should be forced on for. If it is 0, then the MOTD will not be
175  * forced on.
176  */
177 cvar_t	*motdforce;
178 
179 cvar_t	*filterban;
180 
181 cvar_t	*sv_maxvelocity;
182 cvar_t	*sv_gravity;
183 
184 cvar_t	*sv_rollspeed;
185 cvar_t	*sv_rollangle;
186 cvar_t	*gun_x;
187 cvar_t	*gun_y;
188 cvar_t	*gun_z;
189 
190 cvar_t	*run_pitch;
191 cvar_t	*run_roll;
192 cvar_t	*bob_up;
193 cvar_t	*bob_pitch;
194 cvar_t	*bob_roll;
195 
196 cvar_t	*sv_cheats;
197 
198 cvar_t	*flood_msgs;
199 cvar_t	*flood_persecond;
200 cvar_t	*flood_waitdelay;
201 
202 cvar_t	*sv_maplist;
203 
204 cvar_t  *g_background_music;
205 
206 cvar_t  *sv_botkickthreshold;
207 cvar_t  *sv_custombots;
208 
209 //unlagged
210 cvar_t	*g_antilag;
211 cvar_t	*g_antilagdebug;
212 cvar_t	*g_antilagprojectiles;
213 
214 void SpawnEntities (char *mapname, char *entities, char *spawnpoint);
215 void ClientThink (edict_t *ent, usercmd_t *cmd);
216 qboolean ClientConnect (edict_t *ent, char *userinfo);
217 void ClientUserinfoChanged (edict_t *ent, char *userinfo, int whereFrom);
218 void ClientDisconnect (edict_t *ent);
219 void ClientBegin (edict_t *ent);
220 void ClientCommand (edict_t *ent);
221 void RunEntity (edict_t *ent);
222 void InitGame (void);
223 void G_RunFrame (void);
224 int ACESP_FindBotNum(void);
225 extern long filelength(int);
226 extern void ED_CallSpawn (edict_t *ent);
227 
228 static size_t szr;
229 
g_filelength(FILE * f)230 static int g_filelength( FILE *f )
231 {
232 	int length = -1;
233 
234 #if defined HAVE_FILELENGTH
235 
236 	length = filelength( fileno( f ) );
237 
238 #elif defined HAVE_FSTAT
239 
240 	struct stat statbfr;
241 	int result;
242 
243 	result = fstat( fileno(f), &statbfr );
244 	if ( result != -1 )
245 	{
246 		length = (int)statbfr.st_size;
247 	}
248 
249 #else
250 
251 	long int pos;
252 
253 	pos = ftell( f );
254 	fseek( f, 0L, SEEK_END );
255 	length = ftell( f );
256 	fseek( f, pos, SEEK_SET );
257 
258 #endif
259 
260 	return length;
261 }
262 
263 
264 //===================================================================
265 
266 
ShutdownGame(void)267 void ShutdownGame (void)
268 {
269 	gi.dprintf ("==== ShutdownGame ====\n");
270 
271 	gi.FreeTags (TAG_LEVEL);
272 	gi.FreeTags (TAG_GAME);
273 }
274 
275 /*
276 =============
277 G_ForceExitIntermission
278 
279 To prevent those annoying occurrences when you join a server which has been
280 idling at intermission for a long time, then it instantly switches maps as
281 soon as the old one is done loading.
282 
283 Now new player joins will force intermission to end after twenty seconds, even
284 if there are still players in the server.
285 =============
286 */
287 void ExitLevel (void);
G_ForceExitIntermission(void)288 void G_ForceExitIntermission (void)
289 {
290 	if (level.time < level.intermissiontime + 20.0 || !level.intermissiontime)
291 		return;
292 	ExitLevel();
293 }
294 
295 /*
296 =================
297 GetGameAPI
298 
299 Returns a pointer to the structure with all entry points
300 and global variables
301 =================
302 */
GetGameAPI(game_import_t * import)303 game_export_t *GetGameAPI (game_import_t *import)
304 {
305 	gi = *import;
306 
307 	globals.apiversion = GAME_API_VERSION;
308 	globals.Init = InitGame;
309 	globals.Shutdown = ShutdownGame;
310 	globals.SpawnEntities = SpawnEntities;
311 
312 	globals.ClientThink = ClientThink;
313 	globals.ClientConnect = ClientConnect;
314 	globals.ClientUserinfoChanged = ClientUserinfoChanged;
315 	globals.ClientDisconnect = ClientDisconnect;
316 	globals.ClientBegin = ClientBegin;
317 	globals.ClientCommand = ClientCommand;
318 	globals.ACESP_FindBotNum = ACESP_FindBotNum;
319 
320 	globals.RunFrame = G_RunFrame;
321 	globals.ForceExitIntermission = G_ForceExitIntermission;
322 
323 	globals.ServerCommand = ServerCommand;
324 
325 	globals.edict_size = sizeof(edict_t);
326 
327 	return &globals;
328 }
329 
330 
331 #define Z_MAGIC		0x1d1d
332 
333 typedef struct zhead_s
334 {
335 	struct	zhead_s  *prev, *next;
336 	short   magic;
337 	short   tag;
338 	int	size;
339 } zhead_t;
340 
341 zhead_t		z_chain;
342 int		z_count, z_bytes;
343 
344 
345 /*
346 =================
347 ClientEndServerFrames
348 =================
349 */
ClientEndServerFrames(void)350 void ClientEndServerFrames (void)
351 {
352 	int		i;
353 	edict_t	*ent;
354 
355 	/* make sure bot info in first client is up to date */
356 	ACESP_UpdateBots();
357 
358 	// calc the player views now that all pushing
359 	// and damage has been added
360 	for (i=0 ; i<g_maxclients->value ; i++)
361 	{
362 		ent = g_edicts + 1 + i;
363 		if (!ent->inuse || !ent->client)
364 			continue;
365 		ClientEndServerFrame (ent);
366 	}
367 
368 }
369 
370 /*
371 =================
372 CreateTargetChangeLevel
373 
374 Returns the created target changelevel
375 =================
376 */
CreateTargetChangeLevel(char * map)377 edict_t *CreateTargetChangeLevel(char *map)
378 {
379 	edict_t *ent;
380 
381 	ent = G_Spawn ();
382 	ent->classname = "target_changelevel";
383 	Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map);
384 	ent->map = level.nextmap;
385 	return ent;
386 }
387 
388 
389 /**
390  * \brief Log scores and stats
391  *
392  * \detail This is a somewhat crude formatted dump of game information to
393  *         the server console and log file. This version is mostly intended
394  *         to collect some data about bot targeting accuracy.
395  *
396  *         The odds that the array indexes for the weapon hit data and weapon
397  *         accuracy data are correctly cross-referenced are slim.
398  *
399  *  --- from hud stat report (p_hud.c) ---
400  * --- currently def'd out ---
401  * 0: "blaster"
402  * 1: "disruptor"
403  * 2: "smartgun"
404  * 3: "chaingun"
405  * 4: "flame"
406  * 5: "rocket"
407  * 6: "beamgun"
408  * 7: "vaporizer"
409  * 8: "violator"
410  *
411  *--- weapon_hit[] incremented ---
412  * 0 : blasterball_touch  : Weapon_Beamgun, Weapon_Blaster, Weapon_Hover
413  * 0 : fire_blaster_beam  : Weapon_Beamgun, Weapon_Blaster, Weapon_Alienblaster, Weapon_Strafer
414  * 1 : fire_disruptor     : Weapon_Disruptor
415  * 2 : smartgrenade_think : Weapon_Smartgun
416  * 3 : fire_lead          : Weapon_Chain
417  * 4 : fireball_touch     : Weapon_Flame
418  * 4 : flame_touch        : Weapon_Flame
419  * 5 : rocket_touch       : Weapon_RocketLauncher, Weapon_Bomber, Weapon_Strafer
420  * 6 : fire_blaster       : Weapon_Beamgun, Weapon_Blaster
421  * 7 : bomb_touch         : Weapon_Bomber, Weapon_Vaporizer
422  * 7 : fire_vaporizer     : Weapon_Vaporizer
423  * 8 : fire_violator      : Weapon_Violator
424 
425  * --- from sample.cfg accuracy
426  * weapon_hit[] :to: bot weapacc[]
427  * 0: 1: blaster
428  * 1: 2: alien disruptor
429  * 3: 3: pulse rifle (chaingun)
430  * 4: 4: flame thrower
431  * x: 5: homing rocket launcher (not implemented)
432  * 5: 6: rocket launcher
433  * 2: 7: alien smartgun
434  * 6: 8: alien beamgun
435  * 7: 9: alien vaporizer
436  * 8: x: violator
437  */
game_report(void)438 static void game_report( void )
439 {
440 	static const char* weapname[] =
441 	{
442 		 "   blaster", // 0 blaster, hover
443 		 " disruptor", // 1
444 		 "  smartgun", // 2
445 		 "  chaingun", // 3
446 		 "     flame", // 4
447 		 "    rocket", // 5 rocketlauncher, bomber, strafer
448 		 "   beamgun", // 6 beamgun
449 		 " vaporizer", // 7 +bomber's bomb
450 		 "  violator"  // 8
451 	};
452 	/* cross-reference from weapon_hit to weapacc[] */
453 	static const int accxref[] =
454 	{
455 		1, /* 0 blaster   */
456 		2, /* 1 disruptor */
457 		7, /* 2 smartgun  */
458 		3, /* 3 chaingun  */
459 		4, /* 4 flame     */
460 		6, /* 5 rocket    */
461 		8, /* 6 beamgun   */
462 		9, /* 7 vaporizer */
463 		0  /* 8 violator  */ /* no weapacc[] for this */
464 	};
465 
466 	edict_t *pclient;
467 	int i;
468 	char *client_name;
469 
470 	gi.dprintf( "<GAME-REPORT-BEGIN>\n" );
471 
472 	for ( i = 0, pclient = &g_edicts[1] ; i < game.maxclients ; ++pclient, i++ )
473 	{ // for each possible client, human or bot
474 		if ( pclient->inuse && game.clients[i].resp.spectator == 0 )
475 		{
476 			int weap;
477 
478 			/* scoring - UNSORTED */
479 			client_name = Info_ValueForKey( pclient->client->pers.userinfo, "name" );
480 			if ( pclient->is_bot )
481 			{
482 				gi.dprintf( "%s (%i,%0.2f): %i\n",
483 						client_name,
484 						pclient->skill, pclient->awareness,
485 						game.clients[i].resp.score );
486 			}
487 			else
488 			{
489 				gi.dprintf( "%s: %i\n",
490 						client_name,
491 						game.clients[i].resp.score );
492 			}
493 
494 			/* weapon skill */
495 			for ( weap = 0; weap < 9; weap++ )
496 			{
497 				if ( pclient->client->resp.weapon_shots[weap] != 0 )
498 				{
499 					int hits  = pclient->client->resp.weapon_hits[weap];
500 					int shots = pclient->client->resp.weapon_shots[weap];
501 					int percent = (100 * hits) / shots;
502 
503 					if ( pclient->is_bot )
504 					{
505 						if ( weap == 8 )
506 						{ /* violator - no weapacc[], accuracy always 1.0 */
507 							gi.dprintf( " %s (1.0): %i/%i = %i%%\n",
508 								weapname[weap], hits, shots, percent );
509 						}
510 						else
511 						{
512 							gi.dprintf( " %s (%0.2f): %i/%i = %i%%\n",
513 								weapname[weap],
514 								pclient->weapacc[accxref[weap]],
515 								hits, shots, percent );
516 						}
517 					}
518 					else
519 					{
520 						gi.dprintf( " %s : %i/%i = %i%%\n",
521 								weapname[weap], hits, shots, percent );
522 					}
523 				}
524 			}
525 		}
526 	}
527 	gi.dprintf("<GAME-REPORT-END>\n");
528 
529 }
530 
531 
532 /*
533 =================
534 EndDMLevel
535 
536 The timelimit or fraglimit has been exceeded
537 =================
538 */
EndDMLevel(void)539 void EndDMLevel (void)
540 {
541 	edict_t		*ent;
542 	char *s, *t, *f;
543 	static const char *seps = " ,\n\r";
544 	char *buffer;
545 	char  mapsname[MAX_OSPATH];
546 	int length;
547 	static char **mapnames;
548 	static int	  nummaps;
549 	int i;
550 	FILE *fp;
551 
552 	// log game scoring, statistics. currently crude for bot testing mostly.
553 	if ( g_dedicated && g_dedicated->integer != 0 )
554 	{
555 		game_report();
556 	}
557 
558 	/* Search for dead players in order to remove DeathCam and free mem */
559 	for (i=0 ; i<g_maxclients->value ; i++)
560 	{
561 		ent = g_edicts + i + 1;
562 		if (!ent->inuse || ent->client->resp.spectator)
563 			continue;
564 		if(!ent->is_bot && ent->deadflag)
565 			DeathcamRemove (ent, "off");
566 	}
567 
568 	//call voting
569 	if(g_callvote->value)
570 	{
571 		playervote.called = false;
572 		playervote.yay = 0;
573 		playervote.nay = 0;
574 		playervote.command[0] = 0;
575 	}
576 
577 	//map voting
578 	if(g_mapvote->value)
579 	{
580 		level.changemap = level.mapname;
581 
582 		// initialise map list using the current map's name
583 		// this is done in all modes just in case
584 		for ( i = 0 ; i < 4 ; i ++ )
585 		{
586 			strcpy(votedmap[i].mapname, level.mapname);
587 			votedmap[i].tally = 0;
588 		}
589 
590 		// if there is a map list, time to choose
591 		if ( sv_maplist && sv_maplist->string && *(sv_maplist->string) )
592 		{
593 			char * names[200]; // 200 map names should be fine
594 			int n_maps = 0;
595 			int same_index = -1;
596 			int mode = ( g_votemode ? g_votemode->value : 0 );
597 			qboolean same = ( g_votesame ? (g_votesame->value == 1) : 1 );
598 
599 			memset( names, 0 , sizeof(names) );
600 			s = _strdup( sv_maplist->string );
601 			t = strtok( s, seps );
602 			do {
603 				// if using the same map is disallowed, skip it
604 				if ( !Q_strcasecmp(t, level.mapname) )
605 				{
606 					if ( same_index == -1 )
607 						same_index = n_maps;
608 					if ( !same )
609 						continue;
610 				}
611 
612 				// avoid duplicates
613 				for ( i = 0 ; i < n_maps ; i ++ )
614 				{
615 					if ( ! Q_strcasecmp( t , names[i] ) )
616 						break;
617 				}
618 				if ( i < n_maps )
619 					continue;
620 
621 				names[n_maps ++] = t;
622 			} while ( (t = strtok( NULL, seps)) != NULL );
623 
624 			if ( n_maps > 0 )
625 			{
626 				// if the map list has been changed and the
627 				// current map is no longer in it, prevent
628 				// screw-ups
629 				if ( same_index == -1 )
630 					same_index = 0;
631 
632 				if ( mode == 0 )
633 				{
634 					// standard vote mode, take the next
635 					// 4 maps from the list
636 					for ( i = 0 ; i < 4 ; i ++ )
637 						strcpy( votedmap[i].mapname, names[(same_index + i) % n_maps] );
638 				}
639 				else
640 				{
641 					// random selection
642 					for ( i = 0 ; i < 4 && n_maps > 0 ; i ++ )
643 					{
644 						int map = random() * (n_maps - 1);
645 						strcpy( votedmap[i].mapname, names[map] );
646 						names[map] = names[n_maps - 1];
647 						n_maps --;
648 					}
649 				}
650 			}
651 
652 			free(s);
653 		}
654 	}
655 
656 	// stay on same level flag
657 	if (dmflags->integer & DF_SAME_LEVEL)
658 	{
659 		BeginIntermission (CreateTargetChangeLevel (level.mapname) );
660 		return;
661 	}
662 
663 	if (bot_won && !(dmflags->integer & DF_BOT_LEVELAD) && !ctf->value)
664 	{
665 		BeginIntermission (CreateTargetChangeLevel (level.mapname) );
666 		return;
667 	}
668 
669 	if( ( ctf->integer || cp->integer ) && ! g_dedicated->integer )
670 	{ //ctf will just stay on same level unless specified by dedicated list
671 		BeginIntermission (CreateTargetChangeLevel (level.mapname));
672 		return;
673 	}
674 	// see if it's in the map list
675 	if (*sv_maplist->string) {
676 		s = _strdup(sv_maplist->string);
677 		f = NULL;
678 		t = strtok(s, seps);
679 		while (t != NULL) {
680 			if (Q_strcasecmp(t, level.mapname) == 0) {
681 				// it's in the list, go to the next one
682 				t = strtok(NULL, seps);
683 				if (t == NULL) { // end of list, go to first one
684 					if (f == NULL) // there isn't a first one, same level
685 						BeginIntermission (CreateTargetChangeLevel (level.mapname) );
686 					else
687 						BeginIntermission (CreateTargetChangeLevel (f) );
688 				} else
689 					BeginIntermission (CreateTargetChangeLevel (t) );
690 				free(s);
691 				return;
692 			}
693 			if (!f)
694 				f = t;
695 			t = strtok(NULL, seps);
696 		}
697 		free(s);
698 	}
699 
700 	if(ctf->integer) { //wasn't in the dedicated list
701 		BeginIntermission (CreateTargetChangeLevel (level.mapname));
702 		return;
703 	}
704 
705     //check the maps.lst file and read in those, which will overide anything in the level
706 	//write this code here
707 
708 	/*
709 	** load the list of map names
710 	*/
711 	if ( !gi.FullPath( mapsname, sizeof(mapsname), "maps.lst" ) )
712 	{ // no maps.lst.
713 		// note: originally this was looked for in "./data1/" only
714 		//   hope it is ok to use FullPath() search path.
715 		BeginIntermission (CreateTargetChangeLevel (level.mapname) );
716 		return;
717 	}
718 	if ( ( fp = fopen( mapsname, "rb" ) ) == 0 )
719 	{
720 		BeginIntermission (CreateTargetChangeLevel (level.mapname) );
721 			return;
722 	}
723 
724 	length = g_filelength( fp );
725 	buffer = malloc( length + 1 );
726 	szr = fread( buffer, length, 1, fp );
727 	buffer[length] = 0;
728 
729 	s = buffer;
730 
731 	i = 0;
732 	while ( i < length )
733 	{
734 		if ( s[i] == '\r' )
735 			nummaps++;
736 		i++;
737 	}
738 
739 	mapnames = malloc( sizeof( char * ) * ( nummaps + 1 ) );
740 	memset( mapnames, 0, sizeof( char * ) * ( nummaps + 1 ) );
741 
742 	s = buffer;
743 
744 	for ( i = 0; i < nummaps; i++ )
745 	{
746     char  shortname[MAX_TOKEN_CHARS];
747     char  longname[MAX_TOKEN_CHARS];
748 		char  scratch[200];
749 #if defined WIN32_VARIANT
750 		int  j;
751 #endif
752 		int l;
753 
754 		strcpy( shortname, COM_Parse( &s ) );
755 		l = strlen(shortname);
756 #if defined WIN32_VARIANT
757 		for (j=0 ; j<l ; j++)
758 			shortname[j] = toupper(shortname[j]);
759 #endif
760 		strcpy( longname, COM_Parse( &s ) );
761 		Com_sprintf( scratch, sizeof( scratch ), "%s", shortname );
762 
763 		mapnames[i] = malloc( strlen( scratch ) + 1 );
764 		strcpy( mapnames[i], scratch );
765 	}
766 	mapnames[nummaps] = 0;
767 
768 	fclose(fp);
769 	free( buffer );
770 
771 	//find map, goto next map - if one doesn't exist, repeat list
772 	//stick something in here to filter out CTF, and just make it loop back
773 	for (i = 0; i < nummaps; i++) {
774 		if (Q_strcasecmp(mapnames[i], level.mapname) == 0) {
775 			if(mapnames[i+1])
776 			{
777 				if(mapnames[i+1][0])
778 					BeginIntermission (CreateTargetChangeLevel (mapnames[i+1]) );
779 				else if(mapnames[0][0]) //no more maps, repeat list
780 					BeginIntermission (CreateTargetChangeLevel (mapnames[0]) );
781 			}
782 			else
783 				BeginIntermission (CreateTargetChangeLevel (mapnames[0]) );
784 		}
785 	}
786 
787 	if (level.nextmap[0]) // go to a specific map
788 		BeginIntermission (CreateTargetChangeLevel (level.nextmap) );
789 	else {	// search for a changelevel
790 		ent = G_Find (NULL, FOFS(classname), "target_changelevel");
791 		if (!ent)
792 		{	// the map designer didn't include a changelevel,
793 			// so create a fake ent that goes back to the same level
794 			BeginIntermission (CreateTargetChangeLevel (level.mapname) );
795 			return;
796 		}
797 		BeginIntermission (ent);
798 	}
799 }
800 
801 
802 /*
803 =================
804 CheckNeedPass
805 =================
806 */
CheckNeedPass(void)807 void CheckNeedPass (void)
808 {
809 	int need;
810 
811 	// if password or spectator_password has changed, update needpass
812 	// as needed
813 	if (password->modified || spectator_password->modified)
814 	{
815 		password->modified = spectator_password->modified = false;
816 
817 		need = 0;
818 
819 		if (*password->string && Q_strcasecmp(password->string, "none"))
820 			need |= 1;
821 		if (*spectator_password->string && Q_strcasecmp(spectator_password->string, "none"))
822 			need |= 2;
823 
824 		gi.cvar_set("needpass", va("%d", need));
825 	}
826 }
827 /*
828 =================
829 ResetLevel
830 =================
831 */
ResetLevel(qboolean keepscores)832 void ResetLevel (qboolean keepscores) //for resetting players and items after warmup
833 {
834 	int i, backup_score = 0; //initialize to shut gcc up
835 	edict_t	*ent;
836 	gitem_t *item;
837 
838 	for (i=0 ; i<g_maxclients->value ; i++)
839 	{
840 		ent = g_edicts + i + 1;
841 		if (!ent->inuse || ent->client->resp.spectator)
842 			continue;
843 		if (keepscores)
844 			backup_score = ent->client->resp.score;
845 		// locate ent at a spawn point and reset everything
846 		InitClientResp (ent->client);
847 		if(ent->is_bot)
848 		{
849 			// respawn bots after warmup
850 			ACESP_PutClientInServer( ent, true );
851 		}
852 		else {
853 			if(ent->deadflag)
854 				DeathcamRemove (ent, "off");
855 			PutClientInServer (ent);
856 		}
857 		ent->client->homing_shots = 0;
858 		if (keepscores)
859 			ent->client->resp.score = backup_score;
860 	}
861 
862 	ACESP_SaveBots(); // update bots.tmp and client bot information
863 
864 	blue_team_score = 0;
865 	red_team_score = 0;
866 
867 	if(g_tactical->integer)
868 	{
869 		tacticalScore.alienAmmoDepot =
870 			tacticalScore.alienComputer =
871 			tacticalScore.alienPowerSource =
872 			tacticalScore.alienBackupGen =
873 			tacticalScore.humanAmmoDepot =
874 			tacticalScore.humanComputer =
875 			tacticalScore.humanPowerSource =
876 			tacticalScore.humanBackupGen =
877 			true;
878 	}
879 
880 	mindEraserTime = level.time;
881 
882 	//reset level items
883 	for (i=1, ent=g_edicts+i ; i < globals.num_edicts ; i++,ent++) {
884 
885 		if (!ent->inuse)
886 			continue;
887 		if(ent->client) //not players
888 			continue;
889 
890 		//only items, not triggers, trains, etc
891 		for (i=0,item=itemlist ; i<game.num_items ; i++,item++)
892 		{
893 			if (!item->classname)
894 				continue;
895 
896 			if (!strcmp(item->classname, ent->classname))
897 			{	// found it
898 				DoRespawn(ent);
899 				break;
900 			}
901 		}
902 
903 		if (!strcmp(ent->classname, "misc_bluenode") || !strcmp(ent->classname, "misc_rednode"))
904 			ent->powered = true;
905 		if (!strcmp(ent->classname, "misc_redspidernode"))
906 		{
907 			gi.unlinkentity (ent);
908 			SP_misc_redspidernode (ent);
909 		}
910 		if (!strcmp(ent->classname, "misc_bluespidernode"))
911 		{
912 			gi.unlinkentity (ent);
913 			SP_misc_bluespidernode (ent);
914 		}
915 	}
916 
917 	if(g_callvote->integer)
918 		safe_bprintf(PRINT_HIGH, "Call voting is ^2ENABLED\n");
919 	else
920 		safe_bprintf(PRINT_HIGH, "Call voting is ^1DISABLED\n");
921 
922 	if(g_antilag->integer)
923 		safe_bprintf(PRINT_HIGH, "Antilag is ^2ENABLED\n");
924 	else
925 		safe_bprintf(PRINT_HIGH, "Antilag is ^1DISABLED\n");
926 
927 	return;
928 }
929 
930 /*
931 =================
932 CheckDMRules
933 =================
934 */
CheckDMRules(void)935 void CheckDMRules (void)
936 {
937 	int        i, top_score;
938 	gclient_t  *cl;
939 	edict_t    *cl_ent;
940 	float      countdown_time;
941 	static int warmup_state = 0;
942 
943 	gi.cvar_set ("g_teamgame", va("%d", TEAM_GAME));
944 
945 	if ( !g_tactical->integer && !tca->integer && !ctf->integer && !cp->integer && !(dmflags->integer & DF_SKINTEAMS) )
946 	{
947 		/*--- non-team game warmup ---*/
948 		/*
949 		 * note: broadcast sounds require first client to have sound info
950 		 * whether or not first client is inuse (bot or not, also not important)
951 		 */
952 		if ( (warmup_state == 0) && (level.time <= warmuptime->value) )
953 		{ /* start warmup countdown */
954 			countdown_time = warmuptime->value - level.time;
955 			warmup_state = (int)(ceil( countdown_time ));
956 		}
957 
958 		if ( warmup_state > 0 )
959 		{ /* warmup in progress */
960 			countdown_time = warmuptime->value - level.time;
961 			if ( (float)warmup_state > ceil( countdown_time ) )
962 			{ /* next state of countdown */
963 				--warmup_state;
964 				switch ( warmup_state )
965 				{
966 					case 3:
967 						gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/three.wav" ), 1, ATTN_NONE, 0 );
968 						break;
969 					case 2:
970 						gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/two.wav" ), 1, ATTN_NONE, 0 );
971 						break;
972 					case 1:
973 						gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/one.wav" ), 1, ATTN_NONE, 0 );
974 						break;
975 					case 0:
976 						gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "misc/fight.wav" ), 1, ATTN_NONE, 0 );
977 						break;
978 					default:
979 						break;
980 				}
981 				if ( warmup_state > 0 )
982 				{
983 					for ( i = 1; i <= g_maxclients->integer; i++ )
984 					{
985 						safe_centerprintf( &g_edicts[i], "%i...\n", warmup_state );
986 					}
987 				}
988 				else
989 				{ /* end of warmup */
990 					for ( i = 1; i <= g_maxclients->integer; i++ )
991 					{
992 						safe_centerprintf( &g_edicts[i], "FIGHT!\n" );
993 					}
994 					ResetLevel(false);
995 				}
996 			}
997 		}
998 	}
999 
1000 	if (level.intermissiontime)
1001 		return;
1002 
1003 	if (!deathmatch->integer)
1004 		return;
1005 
1006 	if (timelimit->value)
1007 	{
1008 		if (level.time >= timelimit->value*60.0 && ((tca->integer || ctf->integer  || cp->integer || (dmflags->integer & DF_SKINTEAMS)) || level.time > warmuptime->value))
1009 		{
1010 			safe_bprintf (PRINT_HIGH, "Timelimit hit.\n");
1011 			EndDMLevel ();
1012 			return;
1013 		}
1014 	}
1015 
1016 	if (fraglimit->integer && ((tca->integer || ctf->integer || cp->integer || (dmflags->integer & DF_SKINTEAMS)) || level.time > warmuptime->value))
1017 	{
1018 		//team scores
1019 		if ((dmflags->integer & DF_SKINTEAMS) || ctf->integer || cp->integer) //it's all about the team!
1020 		{
1021 			if(blue_team_score >= fraglimit->integer)
1022 			{
1023 				safe_bprintf(PRINT_HIGH, "Blue Team wins!\n");
1024 				bot_won = 0; //we don't care if it's a bot that wins
1025 				EndDMLevel();
1026 				return;
1027 			}
1028 			if(red_team_score >= fraglimit->integer)
1029 			{
1030 				safe_bprintf(PRINT_HIGH, "Red Team wins!\n");
1031 				bot_won = 0; //we don't care if it's a bot that wins
1032 				EndDMLevel();
1033 				return;
1034 			}
1035 		}
1036 		else
1037 		{
1038 			top_score = 0;
1039 			for (i=0 ; i<g_maxclients->integer ; i++)
1040 			{
1041 				cl = game.clients + i;
1042 				if (!g_edicts[i+1].inuse)
1043 					continue;
1044 
1045 				if(cl->resp.score > top_score)
1046 					top_score = cl->resp.score; //grab the top score
1047 
1048 				if (cl->resp.score >= fraglimit->integer)
1049 				{
1050 					if(cl->is_bot)
1051 					{
1052 						bot_won = 1; //a bot has won the match
1053 						safe_bprintf (PRINT_HIGH, "Fraglimit hit by bot.\n");
1054 					}
1055 					else
1056 					{
1057 						bot_won = 0;
1058 						safe_bprintf (PRINT_HIGH, "Fraglimit hit.\n");
1059 					}
1060 
1061 					EndDMLevel ();
1062 					return;
1063 				}
1064 			}
1065 			if(!tca->integer && !ctf->integer && !cp->integer)
1066 			{
1067 				i = fraglimit->integer - top_score;
1068 				switch(i)
1069 				{
1070 					case 3:
1071 						if(!print3)
1072 						{
1073 							for (i=0 ; i<g_maxclients->integer ; i++)
1074 							{
1075 								cl_ent = g_edicts + 1 + i;
1076 								if (!cl_ent->inuse || cl_ent->is_bot)
1077 									continue;
1078 								if(i == 0)
1079 									gi.sound (cl_ent, CHAN_AUTO, gi.soundindex("misc/3frags.wav"), 1, ATTN_NONE, 0);
1080 								safe_centerprintf(cl_ent, "3 frags remain!\n");
1081 							}
1082 							print3 = true;
1083 						}
1084 						break;
1085 					case 2:
1086 						if(!print2)
1087 						{
1088 							for (i=0 ; i<g_maxclients->integer ; i++)
1089 							{
1090 								cl_ent = g_edicts + 1 + i;
1091 								if (!cl_ent->inuse || cl_ent->is_bot)
1092 									continue;
1093 								if(i == 0)
1094 									gi.sound (cl_ent, CHAN_AUTO, gi.soundindex("misc/2frags.wav"), 1, ATTN_NONE, 0);
1095 								safe_centerprintf(cl_ent, "2 frags remain!\n");
1096 							}
1097 							print2 = true;
1098 						}
1099 						break;
1100 					case 1:
1101 						if(!print1)
1102 						{
1103 							for (i=0 ; i<g_maxclients->integer ; i++)
1104 							{
1105 								cl_ent = g_edicts + 1 + i;
1106 								if (!cl_ent->inuse || cl_ent->is_bot)
1107 									continue;
1108 								if(i == 0)
1109 									gi.sound (cl_ent, CHAN_AUTO, gi.soundindex("misc/1frags.wav"), 1, ATTN_NONE, 0);
1110 								safe_centerprintf(cl_ent, "1 frag remains!\n");
1111 							}
1112 							print1 = true;
1113 						}
1114 						break;
1115 					default:
1116 						break;
1117 				}
1118 			}
1119 		}
1120 	}
1121 
1122 	if(tca->integer)
1123 	{
1124 		if(red_team_matches == 2)
1125 		{
1126 			safe_bprintf(PRINT_HIGH, "Red Team wins!\n");
1127 			bot_won = 0; //we don't care if it's a bot that wins
1128 			EndDMLevel();
1129 			return;
1130 		}
1131 		if(blue_team_matches == 2)
1132 		{
1133 			safe_bprintf(PRINT_HIGH, "Blue Team wins!\n");
1134 			bot_won = 0; //we don't care if it's a bot that wins
1135 			EndDMLevel();
1136 			return;
1137 		}
1138 		if (blue_team_score == 0)
1139 		{
1140 			safe_bprintf(PRINT_HIGH, "Red Team wins match %d out of 3!\n", red_team_matches);
1141 			bot_won = 0;
1142 			ResetLevel(true);
1143 			blue_team_score = red_team_score = 4;
1144 			return;
1145 		}
1146 		if (red_team_score == 0)
1147 		{
1148 			safe_bprintf(PRINT_HIGH, "Blue Team wins match %d out of 3!\n", blue_team_matches);
1149 			bot_won = 0;
1150 			ResetLevel(true);
1151 			blue_team_score = red_team_score = 4;
1152 			return;
1153 		}
1154 	}
1155 
1156 	if(g_tactical->integer)
1157 	{
1158 		if(!tacticalScore.alienAmmoDepot && !tacticalScore.alienComputer && !tacticalScore.alienPowerSource)
1159 		{
1160 			safe_bprintf(PRINT_HIGH, "The Humans have defeated the Aliens!\n");
1161 			bot_won = 0; //we don't care if it's a bot that wins
1162 			EndDMLevel();
1163 			return;
1164 		}
1165 		if(!tacticalScore.humanAmmoDepot && !tacticalScore.humanComputer && !tacticalScore.humanPowerSource)
1166 		{
1167 			safe_bprintf(PRINT_HIGH, "The Aliens have defeated the Humans!\n");
1168 			bot_won = 0; //we don't care if it's a bot that wins
1169 			EndDMLevel();
1170 			return;
1171 		}
1172 	}
1173 }
1174 
1175 /*
1176 =============
1177 ExitLevel
1178 =============
1179 */
ExitLevel(void)1180 void ExitLevel (void)
1181 {
1182 	int			i;
1183 	edict_t		*ent;
1184 	char		command [256];
1185 
1186 	if(strcmp(level.mapname, level.changemap) || timelimit->value || g_tactical->value)
1187 	{
1188 		Com_sprintf( command, sizeof(command), "map \"%s\"\n", level.changemap );
1189 		gi.AddCommandString( command );
1190 	}
1191 
1192     //Note-- whenever the map command fails (for instance, misspelled bsp name
1193     //in the server cfg,) it will just play another game on the same map. As
1194     //of right here in the code, there is no way to detect if that is going to
1195     //happen, so we initialize another game on this map just in case. This
1196     //fixes many longstanding bugs where misspelled map names in the cfg would
1197     //cause servers to glitch out.
1198     // -Max
1199 
1200 	level.changemap = NULL;
1201 	level.exitintermission = 0;
1202 	level.intermissiontime = 0;
1203 	ClientEndServerFrames ();
1204 	EndIntermission();
1205 
1206 	// clear some things before going to next level
1207 	for (i=0 ; i<g_maxclients->integer ; i++)
1208 	{
1209 		ent = g_edicts + 1 + i;
1210 		if (!ent->inuse)
1211 			continue;
1212 		if (ent->health > ent->client->pers.max_health)
1213 			ent->health = ent->client->pers.max_health;
1214 
1215 		ent->client->resp.score = 0;
1216 		ent->client->resp.deaths = 0;
1217 		ent->client->resp.reward_pts = 0;
1218 		ent->client->homing_shots = 0;
1219 		ent->takedamage = DAMAGE_AIM;
1220 		ent->solid = SOLID_BBOX;
1221 		ent->deadflag = DEAD_NO;
1222 		if(ent->is_bot)
1223 		{
1224 			ACESP_PutClientInServer( ent, true );
1225 		}
1226 		else
1227 		{
1228 			PutClientInServer (ent);
1229 		}
1230 		if(g_duel->integer)
1231 		{
1232 			ClientPlaceInQueue(ent);
1233 			ClientCheckQueue(ent);
1234 		}
1235 	}
1236 	for (i=1, ent=g_edicts+i ; i < globals.num_edicts ; i++,ent++)
1237 	{
1238 
1239 		if (!ent->inuse || ent->client)
1240 			continue;
1241 		//remove podiums
1242 		if(!strcmp(ent->classname, "pad"))
1243 			G_FreeEdict(ent);
1244 		if(tca->integer)
1245 		{
1246 		    if(strstr(ent->classname, "spidernode"))
1247 		        ED_CallSpawn (ent);
1248 		    ent->powered = true;
1249 		}
1250 	}
1251 	if(tca->integer)
1252 	{
1253 		blue_team_score = red_team_score = 4;
1254 		red_team_matches = blue_team_matches = 0;
1255 	}
1256 	else
1257 	{
1258 		red_team_score = 0;
1259 		blue_team_score = 0;
1260 	}
1261 
1262 	if(g_tactical->integer)
1263 	{
1264 		tacticalScore.alienAmmoDepot =
1265 			tacticalScore.alienComputer =
1266 			tacticalScore.alienPowerSource =
1267 			tacticalScore.alienBackupGen =
1268 			tacticalScore.humanAmmoDepot =
1269 			tacticalScore.humanComputer =
1270 			tacticalScore.humanPowerSource =
1271 			tacticalScore.humanBackupGen =
1272 			true;
1273 
1274 		tacticalScore.alienAmmoDepotHealth =
1275 			tacticalScore.alienComputerHealth =
1276 			tacticalScore.alienPowerSourceHealth =
1277 			tacticalScore.humanAmmoDepotHealth =
1278 			tacticalScore.humanComputerHealth =
1279 			tacticalScore.humanPowerSourceHealth =
1280 			100;
1281 	}
1282 
1283 	print1 = print2 = print3 = false;
1284 
1285 }
1286 
1287 /*
1288 ================
1289 G_Ban
1290 
1291 Ban player
1292 ================
1293 */
1294 /*
1295 =================
1296 SV_WriteIP_f
1297 =================
1298 */
G_Ban(char * ip)1299 void G_Ban (char *ip)
1300 {
1301 	FILE	*f;
1302 	char	name[MAX_OSPATH];
1303 	cvar_t	*game;
1304 	int		i;
1305 
1306 	//add to banlist file
1307 	game = gi.cvar("game", "", 0);
1308 
1309 	if (!*game->string)
1310 		sprintf (name, "%s/listip.cfg", GAMEVERSION);
1311 	else
1312 		sprintf (name, "%s/listip.cfg", game->string);
1313 
1314 	safe_cprintf (NULL, PRINT_HIGH, "Writing %s.\n", name);
1315 
1316 	f = fopen (name, "ab");
1317 	if (!f)
1318 	{
1319 		safe_cprintf (NULL, PRINT_HIGH, "Couldn't open %s\n", name);
1320 		return;
1321 	}
1322 
1323 	fprintf (f, "sv addip %s\n", ip);
1324 
1325 	fclose (f);
1326 
1327 	//add to current ban list
1328 	for (i=0 ; i<numipfilters ; i++)
1329 		if (ipfilters[i].compare == 0xffffffff)
1330 			break;		// free spot
1331 	if (i == numipfilters)
1332 	{
1333 		if (numipfilters == MAX_IPFILTERS)
1334 		{
1335 			safe_cprintf (NULL, PRINT_HIGH, "IP filter list is full\n");
1336 			return;
1337 		}
1338 		numipfilters++;
1339 	}
1340 
1341 	if (!StringToFilter (ip, &ipfilters[i]))
1342 		ipfilters[i].compare = 0xffffffff;
1343 }
1344 
1345 /*
1346 ================
1347 G_NameMatch
1348 
1349  A name entered in the console may (unlikely) or may not have color codes.
1350  This is a case-sensitive match, because a case-insensitive match would
1351    complicate matters.
1352 
1353  Used by sv removebot <name> and callvote kicks
1354 
1355 ================
1356 */
G_NameMatch(const char * netname,const char * kickname)1357 qboolean G_NameMatch( const char* netname, const char *kickname )
1358 {
1359 	const char* p_netname = netname;
1360 	const char* p_kickname = kickname;
1361 	int char_count = 16;
1362 	int len_count = 49; // maximum length of 16 char color name (16 * 3 + nul)
1363 	qboolean matched = false;
1364 
1365 	while ( len_count > 0 && char_count > 0)
1366 	{
1367 		if ( *p_netname != *p_kickname )
1368 		{
1369 			if ( Q_IsColorString( p_netname ) )
1370 			{ // netname has color code, kickname does not, match still possible
1371 				p_netname += 2;
1372 				len_count -= 2;
1373 			}
1374 			else if ( Q_IsColorString( p_kickname ))
1375 			{ // kickname has color code, netname does not, match still possible
1376 				p_kickname += 2;
1377 				len_count -= 2;
1378 			}
1379 			else
1380 			{ // no match. mismatched chars, or end of one or the other
1381 				break;
1382 			}
1383 		}
1384 		else if ( !*p_netname && !*p_kickname )
1385 		{ // end of both strings,
1386 			matched = true;
1387 			break;
1388 		}
1389 		else
1390 		{ // chars matched
1391 			if ( Q_IsColorString( p_netname ) && Q_IsColorString( p_kickname ) )
1392 			{ // color does not have to match
1393 				p_netname += 2;
1394 				p_kickname += 2;
1395 				len_count -= 2;
1396 			}
1397 			else
1398 			{
1399 				++p_netname;
1400 				++p_kickname;
1401 				--len_count;
1402 				--char_count;
1403 			}
1404 		}
1405 	}
1406 
1407 	return matched;
1408 }
1409 
1410 /*
1411 ================
1412 G_ParseVoteCommand
1413 
1414 Parse and execute command
1415 ================
1416 */
1417 // helper function
find_kick_target(const char * name)1418 static edict_t* find_kick_target( const char *name )
1419 {
1420 	edict_t* kick_target = NULL;
1421 	edict_t* ent;
1422 	int i;
1423 
1424 	for ( i = 1 ; i <= g_maxclients->integer ; i++ )
1425 	{
1426 		ent = &g_edicts[i];
1427 		if ( !ent->inuse )
1428 			continue;
1429 		if ( ent->client )
1430 		{
1431 			if ( G_NameMatch( ent->client->pers.netname, name ) )
1432 			{
1433 				kick_target = ent;
1434 				break;
1435 			}
1436 		}
1437 	}
1438 
1439 	return kick_target;
1440 }
1441 
G_ParseVoteCommand(void)1442 void G_ParseVoteCommand (void)
1443 {
1444 	int i, j;
1445 	char command[128];
1446 	char args[128];
1447 	edict_t *ent;
1448 	qboolean donearg = false;
1449 	char	*value;
1450 
1451 	//separate command from args
1452 	i = j = 0;
1453 	while(i < 128) {
1454 
1455 		if(playervote.command[i] == ' ')
1456 			donearg = true;
1457 
1458 		if(!donearg)
1459 			command[i] = playervote.command[i];
1460 		else
1461 			command[i] = 0;
1462 
1463 		if(donearg && i < 127) { //skip the space between command and arg
1464 			args[j] = playervote.command[i+1];
1465 			j++;
1466 		}
1467 		i++;
1468 	}
1469 
1470 	if ( !strcmp(command, "kick") )
1471 	{ // kick player or bot
1472 		ent = find_kick_target( args );
1473 		if ( ent )
1474 		{
1475 			if ( ent->is_bot )
1476 			{
1477 				if ( sv_botkickthreshold && sv_botkickthreshold->integer )
1478 				{
1479 					safe_bprintf(PRINT_HIGH,
1480 						"Auto bot kick enabled, callvote kick <bot> disabled.\n%s not kicked.\n",
1481 						 ent->client->pers.netname);
1482 				}
1483 				else
1484 				{
1485 					ACESP_KickBot( ent );
1486 				}
1487 			}
1488 			else
1489 			{
1490 				safe_bprintf(PRINT_HIGH, "%s was kicked\n", args);
1491 				ClientDisconnect (ent);
1492 			}
1493 		}
1494 		else
1495 		{
1496 			safe_bprintf( PRINT_HIGH, "Did not find %s here.\n", args );
1497 		}
1498 	}
1499 	else if ( !strcmp( command, "kickban") )
1500 	{ // kickban player
1501 		ent = find_kick_target( args );
1502 		if ( ent )
1503 		{
1504 			if ( ent->is_bot )
1505 			{ //
1506 				safe_bprintf(PRINT_HIGH,
1507 					"%s is a bot, use \"kick\", not \"kickban.\"\n", ent->client->pers.netname);
1508 			}
1509 			else
1510 			{
1511 				safe_bprintf(PRINT_HIGH, "%s was kickbanned\n", args);
1512 				ClientDisconnect (ent);
1513 				value = Info_ValueForKey (ent->client->pers.userinfo, "ip");
1514 				G_Ban(value);
1515 			}
1516 		}
1517 		else
1518 		{
1519 			safe_bprintf( PRINT_HIGH, "Did not find %s here.\n", args );
1520 		}
1521 	}
1522 	else if(!strcmp(command, "fraglimit")) { //change fraglimit
1523 		gi.cvar_set("fraglimit", args);
1524 		safe_bprintf(PRINT_HIGH, "Fraglimit changed to %s\n", args);
1525 	}
1526 	else if(!strcmp(command, "timelimit")) { //change timelimit
1527 		gi.cvar_set("timelimit", args);
1528 		safe_bprintf(PRINT_HIGH, "Timelimit changed to %s\n", args);
1529 	}
1530 	else if(!strcmp(command, "map")) { //change map
1531 		Com_sprintf (command, sizeof(command), "map \"%s\"\n", args);
1532 		gi.AddCommandString (command);
1533 	}
1534 	else
1535 		safe_bprintf(PRINT_HIGH, "Invalid command!");
1536 
1537 }
1538 
1539 
1540 /*
1541 ================
1542 G_RunFrame
1543 
1544 Advances the world by 0.1 seconds
1545 ================
1546 */
1547 extern void ACEND_DrawPath(void);
G_RunFrame(void)1548 void G_RunFrame (void)
1549 {
1550 	int		i, numActiveClients = 0;
1551 	edict_t	*ent;
1552 
1553 	level.previousTime = gi.Sys_Milliseconds() - 100;
1554 
1555 	level.framenum++;
1556 	level.time = level.framenum*FRAMETIME;
1557 
1558 	/*
1559 	 * update bot info in first client always, and in other active clients
1560 	 */
1561 	ACESP_UpdateBots();
1562 
1563 	// choose a client for monsters to target this frame
1564 	AI_SetSightClient ();
1565 
1566 	// exit intermissions
1567 
1568 	if (level.exitintermission)
1569 	{
1570 		ExitLevel ();
1571 		return;
1572 	}
1573 
1574 	//
1575 	// treat each object in turn
1576 	// even the world gets a chance to think
1577 	//
1578 
1579 	ent = &g_edicts[0];
1580 	for (i=0 ; i<globals.num_edicts ; i++, ent++)
1581 	{
1582 		if (!ent->inuse)
1583 			continue;
1584 
1585 		level.current_entity = ent;
1586 
1587 		VectorCopy (ent->s.origin, ent->s.old_origin);
1588 
1589 		// if the ground entity moved, make sure we are still on it
1590 		if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount))
1591 		{
1592 			ent->groundentity = NULL;
1593 			if ( !(ent->flags & (FL_SWIM|FL_FLY)) && (ent->svflags & SVF_MONSTER) )
1594 			{
1595 				M_CheckGround (ent);
1596 			}
1597 		}
1598 
1599 		if (i > 0 && i <= g_maxclients->integer)
1600 		{
1601 			ClientBeginServerFrame (ent);
1602 		}
1603 
1604 		if(ent->inuse && ent->client && !ent->is_bot)
1605 		{
1606 			if ( ent->s.number <= g_maxclients->integer )
1607 			{ // count actual players, not deathcam entities
1608 				numActiveClients++;
1609 			}
1610 		}
1611 
1612 		//this next block of code may not be practical for a server running at 10fps
1613 /*		if(ent->movetype & MOVETYPE_FLYMISSILE) {
1614 			//unlagged
1615 			if ( g_antilag->integer)
1616 				G_TimeShiftAllClients( level.previousTime, NULL );
1617 
1618 			G_RunEntity (ent);
1619 
1620 			//unlagged
1621 			if ( g_antilag->integer)
1622 		 		G_UnTimeShiftAllClients( NULL );
1623 		}
1624 		else*/
1625 		G_RunEntity (ent, FRAMETIME);
1626 	}
1627 
1628 	// see if it is time to end a deathmatch
1629 	CheckDMRules ();
1630 
1631 	// see if needpass needs updated
1632 	CheckNeedPass ();
1633 
1634 	// build the playerstate_t structures for all players
1635 	ClientEndServerFrames ();
1636 
1637 	// For bot debugging
1638 	ACEND_DrawPath();
1639 
1640 	//unlagged
1641 	if ( g_antilag->integer)
1642 		level.frameStartTime = level.time;
1643 
1644 	//call voting
1645 	if(g_callvote->integer && playervote.called) {
1646 
1647 		playervote.time = level.time;
1648 		if(playervote.time-playervote.starttime > 15 ){ //15 seconds
1649 			//execute command if votes are sufficient
1650 			if(numActiveClients <= 2 && playervote.yay > playervote.nay+1) { //minimum of 2 votes to pass
1651 				safe_bprintf(PRINT_HIGH, "Vote ^2Passed\n");
1652 
1653 				//parse command(we will allow kick, map, fraglimit, timelimit).
1654 				G_ParseVoteCommand();
1655 
1656 			}
1657 			else if(playervote.yay > 2 && playervote.yay > playervote.nay+1) { //3-1 minimum to pass
1658 				safe_bprintf(PRINT_HIGH, "Vote ^2Passed\n");
1659 
1660 				//parse command(we will allow kick, map, fraglimit, timelimit).
1661 				G_ParseVoteCommand();
1662 
1663 			}
1664 			else
1665 				safe_bprintf(PRINT_HIGH, "Vote ^1Failed\n");
1666 
1667 			//clear
1668 			playervote.called = false;
1669 			playervote.yay = playervote.nay = 0;
1670 			playervote.command[0] = 0;
1671 
1672 			//do each ent
1673 			for (i=0 ; i<g_maxclients->integer ; i++)
1674 			{
1675 				ent = g_edicts + 1 + i;
1676 				if (!ent->inuse || ent->is_bot)
1677 					continue;
1678 				ent->client->resp.voted = false;
1679 			}
1680 		}
1681 	}
1682 }
1683 
1684