1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "server.h"
26 
27 master_sv_t	master_status[MAX_MASTERS];	// status of master servers
28 
29 client_t	*sv_client;			// current client
30 
31 cvar_t	*sv_paused;
32 cvar_t	*sv_timedemo;
33 
34 cvar_t	*sv_enforcetime;
35 
36 cvar_t	*timeout;				// seconds without any message
37 cvar_t	*zombietime;			// seconds to sink messages after disconnect
38 
39 cvar_t	*rcon_password;			// password for remote server commands
40 
41 cvar_t	*allow_download;
42 cvar_t	*allow_download_players;
43 cvar_t	*allow_download_models;
44 cvar_t	*allow_download_sounds;
45 cvar_t	*allow_download_maps;
46 
47 cvar_t	*sv_airaccelerate;
48 
49 cvar_t	*sv_joustmode;
50 cvar_t	*sv_tactical;
51 cvar_t  *sv_excessive;
52 
53 cvar_t	*sv_noreload;			// don't reload level state when reentering
54 
55 cvar_t	*maxclients;			// FIXME: rename sv_maxclients
56 cvar_t	*sv_showclamp;
57 
58 cvar_t	*hostname;
59 cvar_t	*public_server;			// should heartbeats be sent
60 
61 cvar_t	*sv_reconnect_limit;	// minimum seconds between connect messages
62 
63 cvar_t	*sv_ratelimit_status;   //new security measures
64 cvar_t	*sv_iplimit;
65 
66 cvar_t	*sv_downloadurl;
67 
68 cvar_t	*sv_iplogfile;		// Log file by IP address
69 
70 int		sv_numbots;
71 
72 void Master_Shutdown (void);
73 
74 short   ShortSwap (short l);
75 
76 //============================================================================
77 
78 
79 /*
80 =====================
81 SV_LogEvent
82 
83 Logs an event to the IP log.
84 =====================
85 */
SV_LogEvent(netadr_t address,const char * event,const char * name)86 static void SV_LogEvent( netadr_t address , const char * event , const char * name )
87 {
88 	FILE * file;
89 
90 	if ( !( sv_iplogfile && sv_iplogfile->string[0] ) )
91 		return;
92 
93 
94 	file = fopen( sv_iplogfile->string , "a" );
95 	if ( !file ) {
96 		Com_DPrintf( "Failed to write to IP log file '%s'\n" , sv_iplogfile->string );
97 		return;
98 	}
99 	fprintf( file , "%s\t%s\t%d\t%s\r\n" , NET_AdrToString(address) , event ,
100 			( name != NULL ) , ( name != NULL ) ? name : "" );
101 	fclose( file );
102 }
103 
104 
105 
106 
107 /*
108 =====================
109 SV_DropClient
110 
111 Called when the player is totally leaving the server, either willingly
112 or unwillingly.  This is NOT called if the entire server is quiting
113 or crashing.
114 =====================
115 */
SV_DropClient(client_t * drop)116 void SV_DropClient (client_t *drop)
117 {
118 	// add the disconnect
119 
120 	MSG_WriteByte (&drop->netchan.message, svc_disconnect);
121 
122 	if (drop->state == cs_spawned)
123 	{
124 		// call the prog function for removing a client
125 		// this will remove the body, among other things
126 		ge->ClientDisconnect (drop->edict);
127 	}
128 
129 	if (drop->download)
130 	{
131 		FS_FreeFile (drop->download);
132 		drop->download = NULL;
133 	}
134 
135 	SV_LogEvent( drop->netchan.remote_address , "DCN" , drop->name );
136 	drop->state = cs_zombie;		// become free in a few seconds
137 	drop->name[0] = 0;
138 }
139 
140 
141 
142 /*
143 ==============================================================================
144 
145 CONNECTIONLESS COMMANDS
146 
147 ==============================================================================
148 */
149 
150 /*
151 ===============
152 SV_StatusString
153 
154 Builds the string that is sent as heartbeats and status replies
155 ===============
156 */
SV_StatusString(void)157 char *SV_StatusString (void)
158 {
159 	static char status[MAX_MSGLEN - 16]; // static buffer for the status string
160 	qboolean msg_overflow = false;
161 
162 	char      player[MAX_INFO_STRING];
163 	client_t *cl;
164 	size_t    count;
165 
166 	// server info string. MAX_INFO_STRING is always < sizeof(status)
167 	strcpy( status, Cvar_Serverinfo() );
168 	strcat( status, "\n" );
169 
170 	// real player score info
171 	for ( cl=svs.clients, count=maxclients->integer ; count-- ; cl++ )
172 	{
173 		if (cl->state == cs_connected || cl->state == cs_spawned )
174 		{
175 			/* send score of 0 for spectators and players not yet in game
176 			 *  statistics program uses this data when polling for scores
177 			 */
178 			int cl_score = 0;
179 			if ( cl->state == cs_spawned
180 					&& cl->edict->client->ps.stats[STAT_SPECTATOR] == 0 )
181 			{
182 				cl_score = cl->edict->client->ps.stats[STAT_FRAGS];;
183 			}
184 			/*
185 			 * send color characters
186 			 * do not send actual ip addresses for security/privacy reasons
187 			 */
188 			Com_sprintf( player, sizeof(player),
189 					"%i %i \"%s\" \"127.0.0.1\"\n",
190 					cl_score, cl->ping, cl->name);
191 			if ( (strlen(status) + strlen(player) + 1) < sizeof(status) )
192 			{
193 				strncat( status, player, strlen(player) );
194 			}
195 			else
196 			{
197 				msg_overflow = true;
198 				break;
199 			}
200 		}
201 	}
202 
203 	// bot score info
204 	if ( !msg_overflow )
205 	{
206 		for ( cl=svs.clients, count=maxclients->integer ; count-- ; cl++ )
207 		{
208 			size_t bot_count;
209 			bot_count = cl->edict->client->ps.botnum;
210 			if ( bot_count )
211 			{ // normally, first client contains the bot names and scores
212 				bot_t* ps_bot;
213 				for ( ps_bot = cl->edict->client->ps.bots ; bot_count-- ; ps_bot++ )
214 				{
215 					int bot_score = ps_bot->score;
216 					Com_sprintf( player, sizeof(player),
217 							"%i %i \"%s\" \"127.0.0.1\"\n",
218 							bot_score,
219 							0, // bot ping
220 							ps_bot->name );
221 					if ( (strlen(status) + strlen(player) + 1) < sizeof(status) )
222 					{
223 						strncat( status, player, strlen(player) );
224 					}
225 					else
226 					{
227 						msg_overflow = true;
228 						break;
229 					}
230 				}
231 			}
232 			break;
233 		}
234 	}
235 	if ( msg_overflow )
236 	{
237 		Com_DPrintf("SV_StatusString overflowed\n");
238 	}
239 
240 	return status;
241 }
242 
243 /*
244 ================
245 SVC_Status
246 
247 Responds with all the info that qplug or qspy can see
248 ================
249 */
250 /*void SVC_Status (void)
251 {
252 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString());
253 #if 0
254 	Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
255 	Com_Printf (SV_StatusString());
256 	Com_EndRedirect ();
257 #endif
258 }*/
RateLimited(ratelimit_t * limit,int maxCount)259 static qboolean RateLimited (ratelimit_t *limit, int maxCount)
260 {
261 	int diff;
262 
263 	diff = sv.time - limit->time;
264 
265 	//a new sampling period
266 	if (diff > limit->period || diff < 0)
267 	{
268 		limit->time = sv.time;
269 		limit->count = 0;
270 	}
271 	else
272 	{
273 		if (limit->count >= maxCount)
274 			return true;
275 	}
276 
277 	return false;
278 }
279 
RateSample(ratelimit_t * limit)280 static void RateSample (ratelimit_t *limit)
281 {
282 	int diff;
283 
284 	diff = sv.time - limit->time;
285 
286 	//a new sampling period
287 	if (diff > limit->period || diff < 0)
288 	{
289 		limit->time = sv.time;
290 		limit->count = 1;
291 	}
292 	else
293 	{
294 		limit->count++;
295 	}
296 }
297 
SVC_Status(void)298 static void SVC_Status (void)
299 {
300 
301 	RateSample (&svs.ratelimit_status);
302 
303 	if (RateLimited (&svs.ratelimit_status, sv_ratelimit_status->integer))
304 	{
305 		Com_DPrintf ("SVC_Status: Dropped status request from %s\n", NET_AdrToString (net_from));
306 		return;
307 	}
308 
309 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString());
310 }
311 
312 /*
313 ================
314 SVC_Ack
315 
316 ================
317 */
SVC_Ack(void)318 void SVC_Ack (void)
319 {
320 	int		i;
321 	Com_Printf ("Ping acknowledge from %s\n", NET_AdrToString(net_from));
322 	for ( i = 0 ; i < MAX_MASTERS ; i ++ ) {
323 		if ( master_status[i].name[0] == 0 )
324 			break;
325 
326 		if ( master_status[i].addr.port == 0 )
327 			continue;
328 
329 		if ( NET_CompareAdr (master_status[i].addr, net_from) )
330 			master_status[i].last_ping_ack = 2;
331 	}
332 }
333 
334 /*
335 ================
336 SVC_Info
337 
338 Responds with short info for broadcast scans
339 The second parameter should be the current protocol version number.
340 ================
341 */
SVC_Info(void)342 void SVC_Info (void)
343 {
344 	char	string[64];
345 	int		i, count;
346 	int		version;
347 	client_t	*cl;
348 
349 	if (maxclients->integer == 1)
350 		return;		// ignore in single player
351 
352 	version = atoi (Cmd_Argv(1));
353 
354 	if (version != PROTOCOL_VERSION) {
355 		Com_sprintf (string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string));
356 		//r1: return instead of sending another packet. prevents spoofed udp packet
357 		//    causing server <-> server info loops.
358 		return;
359 	}
360 	else
361 	{
362 		count = 0;
363 		for (i=0 ; i<maxclients->integer ; i++)
364 			if (svs.clients[i].state >= cs_connected)
365 				count++;
366 
367 		//bot score info
368 		for (i=0 ; i<maxclients->integer ; i++)
369 		{
370 			cl = &svs.clients[i];
371 			if(cl->edict->client->ps.botnum > 0)
372 			{
373 				count += cl->edict->client->ps.botnum; //add the bots
374 				break;
375 			}
376 		}
377 		//end bot score info
378 
379 		Com_sprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, maxclients->integer);
380 	}
381 
382 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "info\n%s", string);
383 }
384 
385 /*
386 ================
387 SVC_Ping
388 
389 Just responds with an acknowledgement
390 ================
391 */
SVC_Ping(void)392 void SVC_Ping (void)
393 {
394 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "ack");
395 }
396 
SV_KickClient(client_t * cl,const char * reason,const char * cprintf)397 void SV_KickClient (client_t *cl, const char /*@null@*/*reason, const char /*@null@*/*cprintf)
398 {
399 	if (reason && cl->state == cs_spawned && cl->name[0])
400 		SV_BroadcastPrintf (PRINT_HIGH, "%s was dropped: %s\n", cl->name, reason);
401 	if (cprintf)
402 		SV_ClientPrintf (cl, PRINT_HIGH, "%s", cprintf);
403 	Com_Printf ("Dropping %s, %s.\n", cl->name, reason ? reason : "SV_KickClient");
404 	SV_DropClient (cl);
405 }
406 
407 /*
408 =================
409 SVC_GetChallenge
410 
411 Returns a challenge number that can be used
412 in a subsequent client_connect command.
413 We do this to prevent denial of service attacks that
414 flood the server with invalid connection IPs.  With a
415 challenge, they must give a valid IP address.
416 =================
417 */
SVC_GetChallenge(void)418 void SVC_GetChallenge (void)
419 {
420 	int		i;
421 	int		oldest;
422 	int		oldestTime;
423 
424 	oldest = 0;
425 	oldestTime = 0x7fffffff;
426 
427 	// see if we already have a challenge for this ip
428 	for (i = 0 ; i < MAX_CHALLENGES ; i++)
429 	{
430 		if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
431 			break;
432 		if (svs.challenges[i].time < oldestTime)
433 		{
434 			oldestTime = svs.challenges[i].time;
435 			oldest = i;
436 		}
437 	}
438 
439 	if (i == MAX_CHALLENGES)
440 	{
441 		// overwrite the oldest
442 		svs.challenges[oldest].challenge = rand() & 0x7fff;
443 		svs.challenges[oldest].adr = net_from;
444 		svs.challenges[oldest].time = curtime;
445 		i = oldest;
446 	}
447 
448 	// send it back
449 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge);
450 }
451 
452 /*
453 ==================
454 SVC_DirectConnect
455 
456 A connection request that did not come from the master
457 ==================
458 */
SVC_DirectConnect(void)459 void SVC_DirectConnect (void)
460 {
461 	char		userinfo[MAX_INFO_STRING];
462 	netadr_t	adr;
463 	int			i;
464 	client_t	*cl, *newcl;
465 	client_t	temp;
466 	edict_t		*ent;
467 	int			edictnum;
468 	int			version;
469 	int			qport;
470 	int			challenge;
471 	int			previousclients;
472 	int			botnum, botkick;
473 
474 	adr = net_from;
475 
476 	Com_DPrintf ("SVC_DirectConnect ()\n");
477 
478 	version = atoi(Cmd_Argv(1));
479 	if (version != PROTOCOL_VERSION)
480 	{
481 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is protocol version %s\n", PROTOCOL_VERSION);
482 		Com_DPrintf ("    rejected connect from protocol version %i\n", version);
483 		SV_LogEvent( adr , "RVR" , NULL );
484 		return;
485 	}
486 
487 	qport = atoi(Cmd_Argv(2));
488 
489 	challenge = atoi(Cmd_Argv(3));
490 
491 	//security, overflow fixes
492 
493 	//limit connections from a single IP
494 	previousclients = 0;
495 	for (i=0,cl=svs.clients ; i<maxclients->integer ; i++,cl++)
496 	{
497 		if (cl->state == cs_free)
498 			continue;
499 		if (NET_CompareBaseAdr (adr, cl->netchan.remote_address))
500 		{
501 			//zombies are less dangerous
502 			if (cl->state == cs_zombie)
503 				previousclients++;
504 			else
505 				previousclients += 2;
506 		}
507 	}
508 
509 	if (previousclients >= sv_iplimit->integer * 2)
510 	{
511 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nToo many connections from your host.\n");
512 		Com_DPrintf ("    too many connections\n");
513 		SV_LogEvent( adr , "R00" , NULL );
514 		return;
515 	}
516 
517 	// sku - reserve 32 bytes for the IP address
518 	strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-32);
519 	userinfo[sizeof(userinfo) - 32] = 0;
520 
521 	//check it is not overflowed, save enough bytes for /ip/111.222.333.444:55555
522 	if (strlen(userinfo) + 25 >= sizeof(userinfo)-1)
523 	{
524 		Com_DPrintf ("    userinfo length exceeded\n");
525 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nUserinfo string length exceeded.\n");
526 		SV_LogEvent( adr , "R01" , NULL );
527 		return;
528 	}
529 	else if (!userinfo[0])
530 	{
531 		Com_DPrintf ("    empty userinfo string\n");
532 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad userinfo string.\n");
533 		SV_LogEvent( adr , "R02" , NULL );
534 		return;
535 	}
536 
537 	//block anyone trying to use the end-of-message-in-string exploit
538 	if (strchr(userinfo, '\xFF'))
539 	{
540 		char		*ptr;
541 		ptr = strchr (userinfo, '\xFF');
542 		ptr -= 8;
543 		if (ptr < userinfo)
544 			ptr = userinfo;
545 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused due to attempted exploit!\n");
546 		SV_LogEvent( adr , "R03" , NULL );
547 		return;
548 	}
549 	if (Info_KeyExists (userinfo, "ip"))
550 	{
551 		char	*p;
552 		p = Info_ValueForKey(userinfo, "ip");
553 		Com_Printf ("EXPLOIT: Client %s attempted to spoof IP address: %s\n", Info_ValueForKey (userinfo, "name"), NET_AdrToString(adr));
554 		SV_LogEvent( adr , "R04" , NULL );
555 		return;
556 	}
557 
558 	// force the IP key/value pair so the game can filter based on ip
559 	Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from));
560 
561 	// attractloop servers are ONLY for local clients
562 	if (sv.attractloop)
563 	{
564 		if (!NET_IsLocalAddress (adr))
565 		{
566 			Com_Printf ("Remote connect in attract loop.  Ignored.\n");
567 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n");
568 			SV_LogEvent( adr , "R05" , NULL );
569 			return;
570 		}
571 	}
572 
573 	// see if the challenge is valid
574 	if (!NET_IsLocalAddress (adr))
575 	{
576 		for (i=0 ; i<MAX_CHALLENGES ; i++)
577 		{
578 			if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
579 			{
580 				if (challenge == svs.challenges[i].challenge)
581 					break;		// good
582 				Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n");
583 				SV_LogEvent( adr , "R06" , NULL );
584 				return;
585 			}
586 		}
587 		if (i == MAX_CHALLENGES)
588 		{
589 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n");
590 			SV_LogEvent( adr , "R07" , NULL );
591 			return;
592 		}
593 	}
594 
595 	newcl = &temp;
596 	memset (newcl, 0, sizeof(client_t));
597 
598 	// if there is already a slot for this ip, reuse it
599 	for (i=0,cl=svs.clients ; i<maxclients->integer ; i++,cl++)
600 	{
601 		if (cl->state == cs_free)
602 			continue;
603 
604 		if (NET_CompareAdr (adr, cl->netchan.remote_address))
605 		{
606 
607 			//r1: !! fix nasty bug where non-disconnected clients (from dropped disconnect
608 			//packets) could be overwritten!
609 			if (cl->state != cs_zombie)
610 			{
611 				Com_DPrintf ("    client already found\n");
612 				Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nPlayer '%s' is already connected from %s.\n", cl->name, NET_AdrToString(adr));
613 				SV_LogEvent( adr , "R08" , cl->name );
614 				return;
615 			}
616 
617 			if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->integer * 1000))
618 			{
619 				Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr));
620 				SV_LogEvent( adr , "R09" , NULL );
621 				return;
622 			}
623 			Com_Printf ("%s:reconnect\n", NET_AdrToString (adr));
624 			SV_LogEvent( adr , "RCN" , NULL );
625 
626 			newcl = cl;
627 			goto gotnewcl;
628 		}
629 	}
630 
631 	// find a client slot
632 
633 	//get number of bots
634 	for (i=botnum=0 ; i<maxclients->integer ; i++)
635 	{
636 		cl = &svs.clients[i];
637 		botnum = cl->edict->client->ps.botnum;
638 		if(botnum > 0)
639 			break;
640 	}
641 
642 	//still need to reserve one slot
643 	newcl = NULL;
644 
645 	//are we using botkickthreshold?
646 	botkick = Cvar_VariableValue("sv_botkickthreshold");
647 
648 	//prevent client slot overwrites with bots rejoining after map change
649 	if(botkick) {
650 
651 		if(botkick < sv_numbots)
652 			botnum = botkick;
653 		else
654 			botnum = sv_numbots;
655 	}
656 
657 	for (i=0,cl=svs.clients ; i<maxclients->integer-botnum; i++,cl++)
658 	{
659 		if (cl->state == cs_free)
660 		{
661 			newcl = cl;
662 			break;
663 		}
664 	}
665 	if (!newcl)
666 	{
667 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n");
668 		Com_DPrintf ("Rejected a connection.\n");
669 		SV_LogEvent( adr , "R10" , NULL );
670 		return;
671 	}
672 	SV_LogEvent( adr , "NCN" , NULL );
673 
674 gotnewcl:
675 
676 	// build a new connection
677 	// accept the new client
678 	// this is the only place a client_t is ever initialized
679 	*newcl = temp;
680 	sv_client = newcl;
681 	edictnum = (newcl-svs.clients)+1;
682 	ent = EDICT_NUM(edictnum);
683 	newcl->edict = ent;
684 	newcl->challenge = challenge; // save challenge for checksumming
685 
686 	// get the game a chance to reject this connection or modify the userinfo
687 
688 	if (!(ge->ClientConnect (ent, userinfo)))
689 	{
690 
691 		if (*Info_ValueForKey (userinfo, "rejmsg"))
692 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n",
693 				Info_ValueForKey (userinfo, "rejmsg"));
694 		else
695 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" );
696 		SV_LogEvent( adr , "GRJ" , NULL );
697 		Com_DPrintf ("Game rejected a connection.\n");
698 		return;
699 	}
700 
701 	// parse some info from the info strings
702 	Q_strncpyz2( newcl->userinfo, userinfo, sizeof(newcl->userinfo) );
703 	SV_UserinfoChanged (newcl);
704 	SV_LogEvent( adr , "UUS" , newcl->name );
705 
706 	// send the connect packet to the client
707 	Netchan_OutOfBandPrint(NS_SERVER, adr, "client_connect %s", sv_downloadurl->string);
708 
709 	Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport);
710 
711 	newcl->state = cs_connected;
712 
713 	SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) );
714 	SZ_SetName (&newcl->datagram, va("Datagram buffer %s", NET_AdrToString(adr)), true);
715 
716 	newcl->datagram.allowoverflow = true;
717 	newcl->lastmessage = svs.realtime;	// don't timeout
718 	newcl->lastconnect = svs.realtime;
719 
720 	ge->ForceExitIntermission ();
721 	Cbuf_Execute ();
722 }
723 
Rcon_Validate(void)724 int Rcon_Validate (void)
725 {
726 	if (!strlen (rcon_password->string))
727 		return 0;
728 
729 	if (strcmp (Cmd_Argv(1), rcon_password->string) )
730 		return 0;
731 
732 	return 1;
733 }
734 
735 /*
736 ===============
737 SVC_RemoteCommand
738 
739 A client issued an rcon command.
740 Shift down the remaining args
741 Redirect all printfs
742 ===============
743 */
SVC_RemoteCommand(void)744 void SVC_RemoteCommand (void)
745 {
746 	int		i;
747 	char	remaining[1024];
748 
749 	i = Rcon_Validate ();
750 
751 	if (i == 0)
752 		Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
753 	else
754 		Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
755 
756 	Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
757 
758 	if (!Rcon_Validate ())
759 	{
760 		Com_Printf ("Bad rcon_password.\n");
761 	}
762 	else
763 	{
764 		remaining[0] = 0;
765 
766 		for (i=2 ; i<Cmd_Argc() ; i++)
767 		{
768 			/* If spaces present in args, quote them in the remaining string */
769 			if(strchr(Cmd_Argv(i), ' '))
770 			{
771 				strcat (remaining, "\"");
772 				strcat (remaining, Cmd_Argv(i) );
773 				strcat (remaining, "\"");
774 			}
775 			else
776 			{
777 				strcat (remaining, Cmd_Argv(i) );
778 			}
779 			strcat (remaining, " ");
780 		}
781 		if ( strncmp( remaining, "killserver", 10 ) )
782 		{ // not too surprising, rcon killserver does bad things
783 			Cmd_ExecuteString (remaining);
784 		}
785 		else
786 		{
787 			Com_Printf("Cannot do that.\n");
788 		}
789 	}
790 
791 	Com_EndRedirect ();
792 }
793 
794 /*
795 =================
796 SV_ConnectionlessPacket
797 
798 A connectionless packet has four leading 0xff
799 characters to distinguish it from a game channel.
800 Clients that are in the game can still send
801 connectionless packets.
802 =================
803 */
SV_ConnectionlessPacket(void)804 void SV_ConnectionlessPacket (void)
805 {
806 	char	*s;
807 	char	*c;
808 
809 	//r1: make sure we never talk to ourselves
810 	if (NET_IsLocalAddress (net_from) && !NET_IsLocalHost(&net_from) && ShortSwap(net_from.port) == server_port)
811 	{
812 		Com_DPrintf ("dropped %d byte connectionless packet from self! (spoofing attack?)\n", net_message.cursize);
813 		return;
814 	}
815 
816 
817 	MSG_BeginReading (&net_message);
818 	MSG_ReadLong (&net_message);		// skip the -1 marker
819 
820 	s = MSG_ReadStringLine (&net_message);
821 
822 	Cmd_TokenizeString (s, false);
823 
824 	c = Cmd_Argv(0);
825 	Com_DPrintf ("Packet %s : %s\n", NET_AdrToString(net_from), c);
826 
827 	if (!strcmp(c, "ping"))
828 		SVC_Ping ();
829 	else if (!strcmp(c, "ack"))
830 		SVC_Ack ();
831 	else if (!strcmp(c,"status"))
832 		SVC_Status ();
833 	else if (!strcmp(c,"info"))
834 		SVC_Info ();
835 	else if (!strcmp(c,"getchallenge"))
836 		SVC_GetChallenge ();
837 	else if (!strcmp(c,"connect"))
838 		SVC_DirectConnect ();
839 	else if (!strcmp(c, "rcon"))
840 		SVC_RemoteCommand ();
841 	else if (!strcmp(c, "teamgame")) {
842 		Netchan_OutOfBandPrint (NS_SERVER, net_from, "teamgame %f", Cvar_VariableValue ("g_teamgame"));
843 	} else
844 		Com_Printf ("bad connectionless packet from %s:\n%s\n"
845 		, NET_AdrToString (net_from), s);
846 }
847 
848 
849 //============================================================================
850 
851 /*
852 ===================
853 SV_CalcPings
854 
855 Updates the cl->ping variables
856 ===================
857 */
SV_CalcPings(void)858 void SV_CalcPings (void)
859 {
860 	int			i, j;
861 	client_t	*cl;
862 	int			total, count;
863 
864 	for (i=0 ; i<maxclients->integer ; i++)
865 	{
866 		cl = &svs.clients[i];
867 		if (cl->state != cs_spawned )
868 			continue;
869 
870 #if 0
871 		if (cl->lastframe > 0)
872 			cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl->lastframe + 1;
873 		else
874 			cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0;
875 #endif
876 
877 		total = 0;
878 		count = 0;
879 		for (j=0 ; j<LATENCY_COUNTS ; j++)
880 		{
881 			if (cl->frame_latency[j] > 0)
882 			{
883 				count++;
884 				total += cl->frame_latency[j];
885 			}
886 		}
887 		if (!count)
888 			cl->ping = 0;
889 		else
890 #if 0
891 			cl->ping = total*100/count - 100;
892 #else
893 			cl->ping = total / count;
894 #endif
895 
896 		// let the game dll know about the ping
897 		cl->edict->client->ping = cl->ping;
898 	}
899 }
900 
901 
902 /*
903 ===================
904 SV_GiveMsec
905 
906 Every few frames, gives all clients an allotment of milliseconds
907 for their command moves.  If they exceed it, assume cheating.
908 ===================
909 */
SV_GiveMsec(void)910 void SV_GiveMsec (void)
911 {
912 	int			i;
913 	client_t	*cl;
914 
915 	if (sv.framenum & 15)
916 		return;
917 
918 	for (i=0 ; i<maxclients->integer ; i++)
919 	{
920 		cl = &svs.clients[i];
921 		if (cl->state == cs_free )
922 			continue;
923 
924 		cl->commandMsec = 1800;		// 1600 + some slop
925 	}
926 }
927 
928 
929 /*
930 =================
931 SV_ReadPackets
932 =================
933 */
934 extern int sys_msec_as_of_packet_read;
SV_ReadPackets(void)935 void SV_ReadPackets (void)
936 {
937 	int			i;
938 	client_t	*cl;
939 	int			qport;
940 	sys_msec_as_of_packet_read = Sys_Milliseconds ();
941 
942 	while (NET_GetPacket (NS_SERVER, &net_from, &net_message))
943 	{
944 		// check for connectionless packet (0xffffffff) first
945 		if (*(int *)net_message.data == -1)
946 		{
947 			SV_ConnectionlessPacket ();
948 			continue;
949 		}
950 
951 		// read the qport out of the message so we can fix up
952 		// stupid address translating routers
953 		MSG_BeginReading (&net_message);
954 		MSG_ReadLong (&net_message);		// sequence number
955 		MSG_ReadLong (&net_message);		// sequence number
956 		qport = MSG_ReadShort (&net_message) & 0xffff;
957 
958 		// check for packets from connected clients
959 		for (i=0, cl=svs.clients ; i<maxclients->integer ; i++,cl++)
960 		{
961 			if (cl->state == cs_free)
962 				continue;
963 
964 			if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
965 				continue;
966 			if (cl->netchan.qport != qport)
967 				continue;
968 			if (cl->netchan.remote_address.port != net_from.port)
969 			{
970 				Com_Printf ("SV_ReadPackets: fixing up a translated port\n");
971 				cl->netchan.remote_address.port = net_from.port;
972 			}
973 
974 			if (Netchan_Process(&cl->netchan, &net_message))
975 			{	// this is a valid, sequenced packet, so process it
976 				if (cl->state != cs_zombie)
977 				{
978 					cl->lastmessage = svs.realtime;	// don't timeout
979 					SV_ExecuteClientMessage (cl);
980 				}
981 			}
982 			break;
983 		}
984 
985 		if (i != maxclients->integer)
986 			continue;
987 	}
988 }
989 
990 /*
991 ==================
992 SV_CheckTimeouts
993 
994 If a packet has not been received from a client for timeout->value
995 seconds, drop the conneciton.  Server frames are used instead of
996 realtime to avoid dropping the local client while debugging.
997 
998 When a client is normally dropped, the client_t goes into a zombie state
999 for a few seconds to make sure any final reliable message gets resent
1000 if necessary
1001 ==================
1002 */
SV_CheckTimeouts(void)1003 void SV_CheckTimeouts (void)
1004 {
1005 	int		i;
1006 	client_t	*cl;
1007 	int			droppoint;
1008 	int			zombiepoint;
1009 
1010 	droppoint = svs.realtime - 1000*timeout->value;
1011 	zombiepoint = svs.realtime - 1000*zombietime->value;
1012 
1013 	for (i=0,cl=svs.clients ; i<maxclients->integer ; i++,cl++)
1014 	{
1015 
1016 
1017 		// message times may be wrong across a changelevel
1018 		if (cl->lastmessage > svs.realtime)
1019 			cl->lastmessage = svs.realtime;
1020 
1021 		if (cl->state == cs_zombie
1022 		&& cl->lastmessage < zombiepoint)
1023 		{
1024 			cl->state = cs_free;	// can now be reused
1025 			continue;
1026 		}
1027 		if ( (cl->state == cs_connected || cl->state == cs_spawned)
1028 			&& cl->lastmessage < droppoint)
1029 		{
1030 			SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
1031 			SV_DropClient (cl);
1032 			cl->state = cs_free;	// don't bother with zombie state
1033 		}
1034 	}
1035 }
1036 
1037 /*
1038 ================
1039 SV_PrepWorldFrame
1040 
1041 This has to be done before the world logic, because
1042 player processing happens outside RunWorldFrame
1043 ================
1044 */
SV_PrepWorldFrame(void)1045 void SV_PrepWorldFrame (void)
1046 {
1047 	edict_t	*ent;
1048 	int		i;
1049 
1050 	for (i=0 ; i<ge->num_edicts ; i++, ent++)
1051 	{
1052 		ent = EDICT_NUM(i);
1053 		// events only last for a single message
1054 		ent->s.event = 0;
1055 	}
1056 
1057 }
1058 
1059 
1060 /*
1061 =================
1062 SV_RunGameFrame
1063 =================
1064 */
SV_RunGameFrame(void)1065 void SV_RunGameFrame (void)
1066 {
1067 	if (host_speeds->integer)
1068 		time_before_game = Sys_Milliseconds ();
1069 
1070 	// we always need to bump framenum, even if we
1071 	// don't run the world, otherwise the delta
1072 	// compression can get confused when a client
1073 	// has the "current" frame
1074 	sv.framenum++;
1075 	sv.time = sv.framenum*100;
1076 
1077 	// don't run if paused
1078 	if (!sv_paused->integer || maxclients->integer > 1)
1079 	{
1080 		ge->RunFrame ();
1081 
1082 		// never get more than one tic behind
1083 		if (sv.time < svs.realtime)
1084 		{
1085 			if (sv_showclamp->integer)
1086 				Com_Printf ("sv highclamp\n");
1087 			svs.realtime = sv.time;
1088 		}
1089 	}
1090 
1091 	if (host_speeds->integer)
1092 		time_after_game = Sys_Milliseconds ();
1093 
1094 }
1095 
1096 /*
1097 ==================
1098 SV_Frame
1099 
1100 ==================
1101 */
1102 extern int sys_lasthang;
SV_Frame(int msec)1103 void SV_Frame (int msec)
1104 {
1105 	int tmp_systime, tmp_hangtime;
1106 	static int old_systime = 0;
1107 	if (!old_systime)
1108 		old_systime = Sys_Milliseconds ();
1109 	time_before_game = time_after_game = 0;
1110 
1111 	// if server is not active, do nothing
1112 	if (!svs.initialized)
1113 		return;
1114 
1115     svs.realtime += msec;
1116 
1117 	// keep the random time dependent
1118 	rand ();
1119 
1120 	// check timeouts
1121 	SV_CheckTimeouts ();
1122 
1123 	// get packets from clients
1124 	SV_ReadPackets ();
1125 
1126 	// move autonomous things around if enough time has passed
1127 	if (!sv_timedemo->integer && svs.realtime < sv.time)
1128 	{
1129 		// never let the time get too far off
1130 		if (sv.time - svs.realtime > 100)
1131 		{
1132 			if (sv_showclamp->integer)
1133 				Com_Printf ("sv lowclamp\n");
1134 			svs.realtime = sv.time - 100;
1135 		}
1136 		NET_Sleep(sv.time - svs.realtime);
1137 		return;
1138 	}
1139 
1140 	// update ping based on the last known frame from all clients
1141 	SV_CalcPings ();
1142 
1143 	// give the clients some timeslices
1144 	SV_GiveMsec ();
1145 
1146 	// let everything in the world think and move
1147 	SV_RunGameFrame ();
1148 
1149 	// send messages back to the clients that had packets read this frame
1150 	SV_SendClientMessages ();
1151 
1152 	// save the entire world state if recording a serverdemo
1153 	SV_RecordDemoMessage ();
1154 
1155 	// send a heartbeat to the master if needed
1156 	Master_Heartbeat ();
1157 
1158 	// clear teleport flags, etc for next frame
1159 	SV_PrepWorldFrame ();
1160 
1161 	tmp_systime = Sys_Milliseconds ();
1162 	tmp_hangtime = tmp_systime-old_systime;
1163 	old_systime = tmp_systime;
1164 	if (tmp_hangtime > 150) {
1165 		sys_lasthang = tmp_systime;
1166 	}
1167 
1168 }
1169 
1170 //============================================================================
1171 
1172 /*
1173 ================
1174 Master_Heartbeat
1175 
1176 Send a message to the master every few minutes to
1177 let it know we are alive, and log information
1178 ================
1179 */
1180 #define	HEARTBEAT_SECONDS	300
Master_Heartbeat(void)1181 void Master_Heartbeat (void)
1182 {
1183 	char		string[MAX_MSGLEN];
1184 
1185 	// pgm post3.19 change, cvar pointer not validated before dereferencing
1186 
1187 	if(!public_server || !public_server->integer)
1188 		return;
1189 
1190 	// check for time wraparound
1191 	if (svs.last_heartbeat > svs.realtime)
1192 		svs.last_heartbeat = svs.realtime;
1193 
1194 	if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000)
1195 		return;		// not time to send yet
1196 
1197 	svs.last_heartbeat = svs.realtime;
1198 
1199 	// send the same string that we would give for a status OOB command
1200 	Com_sprintf (string, MAX_MSGLEN, "heartbeat\n%s", SV_StatusString ());
1201 	SV_HandleMasters (string, "heartbeat");
1202 }
1203 
1204 /*
1205 =================
1206 Master_Shutdown
1207 
1208 Informs all masters that this server is going down
1209 =================
1210 */
Master_Shutdown(void)1211 void Master_Shutdown (void)
1212 {
1213 
1214 	// pgm post3.19 change, cvar pointer not validated before dereferencing
1215 	if (!public_server || !public_server->integer)
1216 		return;		// a private dedicated game
1217 
1218 	SV_HandleMasters ("shutdown", "shutdown");
1219 }
1220 
1221 
1222 /*
1223 =================
1224 SV_HandleMasters
1225 
1226 Sends a message to all master servers, looking up the
1227 master servers' addresses if appropriate.
1228 =================
1229 */
SV_HandleMasters(const char * message,const char * console_message)1230 void SV_HandleMasters (const char *message, const char *console_message)
1231 {
1232 	int		i;
1233 	qboolean	updated_master;
1234 
1235 	// if the server is not dedicated, we need to check cl_master
1236 	if ( !( dedicated && dedicated->integer ) )
1237 	{
1238 		if ( !sv_master )
1239 		{
1240 			sv_master = Cvar_Get ("cl_master", "master.corservers.com", CVAR_ARCHIVE);
1241 			updated_master = true;
1242 		}
1243 		else if ( sv_master->modified )
1244 		{
1245 			sv_master->modified = false;
1246 			updated_master = true;
1247 		}
1248 		else
1249 		{
1250 			updated_master = false;
1251 		}
1252 
1253 		if ( updated_master )
1254 		{
1255 			memset (&master_status[0], 0, sizeof(master_sv_t));
1256 			strncpy (master_status[0].name, sv_master->string, MAX_MASTER_LEN);
1257 		}
1258 	}
1259 
1260 	// first we need to loop through the master servers
1261 	// in order to find the ones that need (re-)resolving
1262 	for ( i = 0; i < MAX_MASTERS ; i ++ )
1263 	{
1264 		if ( master_status[i].name[0] == 0 )
1265 			break;
1266 
1267 		// if we already sent a ping and didn't get
1268 		// any acknowledgement packet, we need to try
1269 		// re-resolving
1270 		if ( master_status[i].resolved && master_status[i].last_ping_sent > master_status[i].last_ping_ack )
1271 		{
1272 			Com_Printf ("No acknowledgement from %s - re-resolving\n", master_status[i].name);
1273 			master_status[i].resolved = false;
1274 		}
1275 
1276 		if ( master_status[i].resolved )
1277 			continue;
1278 
1279 		if (!NET_StringToAdr (master_status[i].name, &master_status[i].addr))
1280 		{
1281 			// resolution failed, did we say so already?
1282 			if ( !master_status[i].failed )
1283 			{
1284 				Com_Printf ("Bad master address: %s\n", master_status[i].name);
1285 				master_status[i].failed = true;
1286 			}
1287 			master_status[i].addr.port = 0;
1288 		}
1289 		else
1290 		{
1291 			master_status[i].failed = false;
1292 			master_status[i].resolved = true;
1293 
1294 			if (master_status[i].addr.port == 0)
1295 				master_status[i].addr.port = BigShort (PORT_MASTER);
1296 
1297 			Com_Printf ("Master server at %s\n", NET_AdrToString (master_status[i].addr));
1298 		}
1299 	}
1300 
1301 	// send the message we needed to send
1302 	for ( i = 0 ; i < MAX_MASTERS ; i ++ )
1303 	{
1304 		if ( master_status[i].name[0] == 0 )
1305 			break;
1306 
1307 		if ( master_status[i].addr.port == 0 )
1308 			continue;
1309 
1310 		Com_Printf ("Sending %s to %s\n", console_message, NET_AdrToString (master_status[i].addr));
1311 		Netchan_OutOfBandPrint (NS_SERVER, master_status[i].addr, "%s", message);
1312 		master_status[i].last_ping_sent = 1;
1313 		master_status[i].last_ping_ack = 0;
1314 	}
1315 }
1316 
1317 
1318 //============================================================================
1319 
1320 
1321 /*
1322 =================
1323 SV_UserinfoChanged
1324 
1325 Pull specific info from a newly changed userinfo string
1326 into a more C freindly form.
1327 =================
1328 */
SV_UserinfoChanged(client_t * cl)1329 void SV_UserinfoChanged (client_t *cl)
1330 {
1331 	char	*val;
1332 	int		i;
1333 
1334 	// call prog code to allow overrides
1335 	ge->ClientUserinfoChanged (cl->edict, cl->userinfo, 0);
1336 
1337 	// name for C code
1338 	Q_strncpyz2( cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name) );
1339 	// mask off high bit
1340 	for (i=0 ; i<sizeof(cl->name) ; i++)
1341 		cl->name[i] &= 127;
1342 
1343 	// rate command
1344 	val = Info_ValueForKey (cl->userinfo, "rate");
1345 	if (strlen(val))
1346 	{
1347 		i = atoi(val);
1348 		cl->rate = i;
1349 		if (cl->rate < 100)
1350 			cl->rate = 100;
1351 		if (cl->rate > 15000)
1352 			cl->rate = 15000;
1353 	}
1354 	else
1355 		cl->rate = 5000;
1356 
1357 	// msg command
1358 	val = Info_ValueForKey (cl->userinfo, "msg");
1359 	if (strlen(val))
1360 	{
1361 		cl->messagelevel = atoi(val);
1362 	}
1363 
1364 }
1365 
1366 
1367 //============================================================================
1368 
1369 /*
1370 ===============
1371 SV_Init
1372 
1373 Only called at quake2.exe startup, not for each game
1374 ===============
1375 */
SV_Init(void)1376 void SV_Init (void)
1377 {
1378 	SV_InitOperatorCommands	();
1379 
1380 	rcon_password = Cvar_Get ("rcon_password", "", 0);
1381 	Cvar_Get ("skill", "1", 0);
1382 	Cvar_Get ("deathmatch", "1", CVAR_LATCH); //Alien Arena is *always* deathmatch
1383 	Cvar_Get ("ctf", "0", CVAR_LATCH);
1384 	Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS+DF_BOT_LEVELAD), CVAR_SERVERINFO);
1385 	Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO);
1386 	Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
1387 	Cvar_Get ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
1388 	Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET);;
1389 	maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
1390 	hostname = Cvar_Get ("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE);
1391 	timeout = Cvar_Get ("timeout", "125", 0);
1392 	zombietime = Cvar_Get ("zombietime", "2", 0);
1393 	sv_showclamp = Cvar_Get ("showclamp", "0", 0);
1394 	sv_paused = Cvar_Get ("paused", "0", 0);
1395 	sv_timedemo = Cvar_Get ("timedemo", "0", 0);
1396 	sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0);
1397 	allow_download = Cvar_Get ("allow_download", "1", CVAR_ARCHIVE);
1398 	allow_download_players  = Cvar_Get ("allow_download_players", "0", CVAR_ARCHIVE);
1399 	allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE);
1400 	allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE);
1401 	allow_download_maps	  = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE);
1402 	sv_downloadurl = Cvar_Get("sv_downloadurl", "http://red.planetarena.org/sv_downloadurl", CVAR_SERVERINFO);
1403 
1404 	sv_iplogfile = Cvar_Get("sv_iplogfile" , "" , CVAR_ARCHIVE);
1405 
1406 	sv_noreload = Cvar_Get ("sv_noreload", "0", 0);
1407 
1408 	sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH);
1409 
1410 	sv_joustmode = Cvar_Get("sv_joustmode", "0", CVAR_SERVERINFO);
1411 	sv_tactical = Cvar_Get("g_tactical", "0", CVAR_LATCH | CVAR_GAMEINFO);
1412 	sv_excessive = Cvar_Get("excessive", "0", CVAR_LATCH | CVAR_GAMEINFO);
1413 
1414 	public_server = Cvar_Get ("sv_public", "1", 0);
1415 
1416 	sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE);
1417 
1418 	sv_ratelimit_status = Cvar_Get ("sv_ratelimit_status", "15", 0);
1419 
1420 	sv_iplimit = Cvar_Get ("sv_iplimit", "3", 0);
1421 
1422 	SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer));
1423 	SZ_SetName (&net_message, "Net message buffer", true);
1424 
1425 	remoteserver_runspeed = 300; //default
1426 }
1427 
1428 /*
1429 ==================
1430 SV_FinalMessage
1431 
1432 Used by SV_Shutdown to send a final message to all
1433 connected clients before the server goes down.  The messages are sent immediately,
1434 not just stuck on the outgoing message list, because the server is going
1435 to totally exit after returning from this function.
1436 ==================
1437 */
SV_FinalMessage(char * message,qboolean reconnect)1438 void SV_FinalMessage (char *message, qboolean reconnect)
1439 {
1440 	int			i;
1441 	client_t	*cl;
1442 
1443 	SZ_Clear (&net_message);
1444 	MSG_WriteByte (&net_message, svc_print);
1445 	MSG_WriteByte (&net_message, PRINT_HIGH);
1446 	MSG_WriteString (&net_message, message);
1447 
1448 	if (reconnect)
1449 		MSG_WriteByte (&net_message, svc_reconnect);
1450 	else
1451 		MSG_WriteByte (&net_message, svc_disconnect);
1452 
1453 	// send it twice
1454 	// stagger the packets to crutch operating system limited buffers
1455 
1456 	for (i=0, cl = svs.clients ; i<maxclients->integer ; i++, cl++)
1457 
1458 		if (cl->state >= cs_connected)
1459 			Netchan_Transmit (&cl->netchan, net_message.cursize
1460 			, net_message.data);
1461 
1462 	for (i=0, cl = svs.clients ; i<maxclients->integer ; i++, cl++)
1463 
1464 		if (cl->state >= cs_connected)
1465 			Netchan_Transmit (&cl->netchan, net_message.cursize
1466 			, net_message.data);
1467 }
1468 
1469 
1470 
1471 /*
1472 ================
1473 SV_Shutdown
1474 
1475 Called when each game quits,
1476 before Sys_Quit or Sys_Error
1477 ================
1478 */
SV_Shutdown(char * finalmsg,qboolean reconnect)1479 void SV_Shutdown (char *finalmsg, qboolean reconnect)
1480 {
1481 	extern void Con_Clear_f (void);
1482 
1483 	if (svs.clients)
1484 		SV_FinalMessage (finalmsg, reconnect);
1485 
1486 	Master_Shutdown ();
1487 	SV_ShutdownGameProgs ();
1488 
1489 	// free current level
1490 	if (sv.demofile)
1491 		fclose (sv.demofile);
1492 	memset (&sv, 0, sizeof(sv));
1493 	Com_SetServerState (sv.state);
1494 
1495 	// free server static data
1496 	if (svs.clients)
1497 		Z_Free (svs.clients);
1498 	if (svs.client_entities)
1499 		Z_Free (svs.client_entities);
1500 	if (svs.demofile)
1501 		fclose (svs.demofile);
1502 	memset (&svs, 0, sizeof(svs));
1503 }
1504 
IsVisible(vec3_t org1,vec3_t org2)1505 qboolean IsVisible(vec3_t org1,vec3_t org2)
1506 {
1507 	trace_t	trace;
1508 
1509 	trace = SV_Trace2 (org1, NULL, NULL, org2, NULL, MASK_VISIBILILITY);
1510 
1511 	if (trace.fraction != 1)
1512 		return false;
1513 	return true;
1514 }
1515