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 #include "server.h"
22 
23 netadr_t	master_adr[MAX_MASTERS];	// address of group servers
24 
25 client_t	*sv_client;			// current client
26 
27 cvar_t	*sv_paused;
28 cvar_t	*sv_timedemo;
29 
30 cvar_t	*sv_enforcetime;
31 
32 cvar_t	*timeout;				// seconds without any message
33 cvar_t	*zombietime;			// seconds to sink messages after disconnect
34 
35 cvar_t	*rcon_password;			// password for remote server commands
36 
37 cvar_t	*allow_download;
38 cvar_t *allow_download_players;
39 cvar_t *allow_download_models;
40 cvar_t *allow_download_sounds;
41 cvar_t *allow_download_maps;
42 
43 cvar_t *sv_airaccelerate;
44 
45 cvar_t	*sv_noreload;			// don't reload level state when reentering
46 
47 cvar_t	*maxclients;			// FIXME: rename sv_maxclients
48 cvar_t	*sv_showclamp;
49 
50 cvar_t	*hostname;
51 cvar_t	*public_server;			// should heartbeats be sent
52 
53 cvar_t	*sv_reconnect_limit;	// minimum seconds between connect messages
54 
55 void Master_Shutdown (void);
56 
57 
58 //============================================================================
59 
60 
61 /*
62 =====================
63 SV_DropClient
64 
65 Called when the player is totally leaving the server, either willingly
66 or unwillingly.  This is NOT called if the entire server is quiting
67 or crashing.
68 =====================
69 */
70 /*
71 =====================
72 GetClientFromAdr
73 
74 Given an netadr_t, returns the matching client.
75 =====================
76 */
GetClientFromAdr(netadr_t address)77 client_t *GetClientFromAdr (netadr_t address)
78 {
79    client_t   *cl;
80    int      i;
81 
82    for (i = 0; i < maxclients->value; i++)
83    {
84       cl = &svs.clients[i];
85       if (NET_CompareBaseAdr(cl->netchan.remote_address, address))
86          break;
87    }
88    return cl;
89 }
90 
SV_DropClient(client_t * drop)91 void SV_DropClient (client_t *drop)
92 {
93 	// add the disconnect
94 	MSG_WriteByte (&drop->netchan.message, svc_disconnect);
95 
96 	if (drop->state == cs_spawned)
97 	{
98 		// call the prog function for removing a client
99 		// this will remove the body, among other things
100 		ge->ClientDisconnect (drop->edict);
101 	}
102 
103 	if (drop->download)
104 	{
105 		FS_FreeFile (drop->download);
106 		drop->download = NULL;
107 	}
108 
109 	drop->state = cs_zombie;		// become free in a few seconds
110 	drop->name[0] = 0;
111 }
112 
113 
114 
115 /*
116 ==============================================================================
117 
118 CONNECTIONLESS COMMANDS
119 
120 ==============================================================================
121 */
122 
123 /*
124 ===============
125 SV_StatusString
126 
127 Builds the string that is sent as heartbeats and status replies
128 ===============
129 */
SV_StatusString(void)130 char	*SV_StatusString (void)
131 {
132 	char	player[1024];
133 	static char	status[MAX_MSGLEN - 16];
134 	int		i;
135 	client_t	*cl;
136 	int		statusLength;
137 	int		playerLength;
138 
139 	strcpy (status, Cvar_Serverinfo());
140 	strcat (status, "\n");
141 	statusLength = strlen(status);
142 
143 	for (i=0 ; i<maxclients->value ; i++)
144 	{
145 		cl = &svs.clients[i];
146 		if (cl->state == cs_connected || cl->state == cs_spawned )
147 		{
148 			Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n",
149 				cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, cl->name);
150 			playerLength = strlen(player);
151 			if (statusLength + playerLength >= sizeof(status) )
152 				break;		// can't hold any more
153 			strcpy (status + statusLength, player);
154 			statusLength += playerLength;
155 		}
156 	}
157 
158 	return status;
159 }
160 
161 /*
162 ================
163 SVC_Status
164 
165 Responds with all the info that qplug or qspy can see
166 ================
167 */
SVC_Status(void)168 void SVC_Status (void)
169 {
170 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString());
171 #if 0
172 	Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
173 	Com_Printf (SV_StatusString());
174 	Com_EndRedirect ();
175 #endif
176 }
177 
178 /*
179 ================
180 SVC_Ack
181 
182 ================
183 */
SVC_Ack(void)184 void SVC_Ack (void)
185 {
186 	Com_Printf ("Ping acknowledge from %s\n", NET_AdrToString(net_from));
187 }
188 
189 /*
190 ================
191 SVC_Info
192 
193 Responds with short info for broadcast scans
194 The second parameter should be the current protocol version number.
195 ================
196 */
SVC_Info(void)197 void SVC_Info (void)
198 {
199 	char	string[64];
200 	int		i, count;
201 	int		version;
202 
203 	if (maxclients->value == 1)
204 		return;		// ignore in single player
205 
206 	version = atoi (Cmd_Argv(1));
207 
208 	if (version != PROTOCOL_VERSION)
209 		Com_sprintf (string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string));
210 	else
211 	{
212 		count = 0;
213 		for (i=0 ; i<maxclients->value ; i++)
214 			if (svs.clients[i].state >= cs_connected)
215 				count++;
216 
217 		Com_sprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value);
218 	}
219 
220 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "info\n%s", string);
221 }
222 
223 /*
224 ================
225 SVC_Ping
226 
227 Just responds with an acknowledgement
228 ================
229 */
SVC_Ping(void)230 void SVC_Ping (void)
231 {
232 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "ack");
233 }
234 
235 
236 /*
237 =================
238 SVC_GetChallenge
239 
240 Returns a challenge number that can be used
241 in a subsequent client_connect command.
242 We do this to prevent denial of service attacks that
243 flood the server with invalid connection IPs.  With a
244 challenge, they must give a valid IP address.
245 =================
246 */
SVC_GetChallenge(void)247 void SVC_GetChallenge (void)
248 {
249 	int		i;
250 	int		oldest;
251 	int		oldestTime;
252 
253 	oldest = 0;
254 	oldestTime = 0x7fffffff;
255 
256 	// see if we already have a challenge for this ip
257 	for (i = 0 ; i < MAX_CHALLENGES ; i++)
258 	{
259 		if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
260 			break;
261 		if (svs.challenges[i].time < oldestTime)
262 		{
263 			oldestTime = svs.challenges[i].time;
264 			oldest = i;
265 		}
266 	}
267 
268 	if (i == MAX_CHALLENGES)
269 	{
270 		// overwrite the oldest
271 		svs.challenges[oldest].challenge = rand() & 0x7fff;
272 		svs.challenges[oldest].adr = net_from;
273 		svs.challenges[oldest].time = curtime;
274 		i = oldest;
275 	}
276 
277 	// send it back
278 	Netchan_OutOfBandPrint (NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge);
279 }
280 
281 /*
282 ==================
283 SVC_DirectConnect
284 
285 A connection request that did not come from the master
286 ==================
287 */
SVC_DirectConnect(void)288 void SVC_DirectConnect (void)
289 {
290 	char		userinfo[MAX_INFO_STRING];
291 	netadr_t	adr;
292 	int			i;
293 	client_t	*cl, *newcl;
294 	client_t	temp;
295 	edict_t		*ent;
296 	int			edictnum;
297 	int			version;
298 	int			qport;
299 	int			challenge;
300 
301 	adr = net_from;
302 
303 	Com_DPrintf ("SVC_DirectConnect ()\n");
304 
305 	version = atoi(Cmd_Argv(1));
306 	if (version != PROTOCOL_VERSION)
307 	{
308 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %s.\n", VERSION);
309 		Com_DPrintf ("    rejected connect from version %i\n", version);
310 		return;
311 	}
312 
313 	qport = atoi(Cmd_Argv(2));
314 
315 	challenge = atoi(Cmd_Argv(3));
316 
317 	strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1);
318 	userinfo[sizeof(userinfo) - 1] = 0;
319 
320 	// force the IP key/value pair so the game can filter based on ip
321 	Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from));
322 
323 	// attractloop servers are ONLY for local clients
324 	if (sv.attractloop)
325 	{
326 		if (!NET_IsLocalAddress (adr))
327 		{
328 			Com_Printf ("Remote connect in attract loop.  Ignored.\n");
329 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n");
330 			return;
331 		}
332 	}
333 
334 	// see if the challenge is valid
335 	if (!NET_IsLocalAddress (adr))
336 	{
337 		for (i=0 ; i<MAX_CHALLENGES ; i++)
338 		{
339 			if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
340 			{
341 				if (challenge == svs.challenges[i].challenge)
342 					break;		// good
343 				Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n");
344 				return;
345 			}
346 		}
347 		if (i == MAX_CHALLENGES)
348 		{
349 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n");
350 			return;
351 		}
352 	}
353 
354 	newcl = &temp;
355 	memset (newcl, 0, sizeof(client_t));
356 
357 	// if there is already a slot for this ip, reuse it
358 	for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
359 	{
360 		if (cl->state == cs_free)
361 			continue;
362 		if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
363 			&& ( cl->netchan.qport == qport
364 			|| adr.port == cl->netchan.remote_address.port ) )
365 		{
366 			if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000))
367 			{
368 				Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr));
369 				return;
370 			}
371 			Com_Printf ("%s:reconnect\n", NET_AdrToString (adr));
372 			newcl = cl;
373 			goto gotnewcl;
374 		}
375 	}
376 
377 	// find a client slot
378 	newcl = NULL;
379 	for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
380 	{
381 		if (cl->state == cs_free)
382 		{
383 			newcl = cl;
384 			break;
385 		}
386 	}
387 	if (!newcl)
388 	{
389 		Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n");
390 		Com_DPrintf ("Rejected a connection.\n");
391 		return;
392 	}
393 
394 gotnewcl:
395 	// build a new connection
396 	// accept the new client
397 	// this is the only place a client_t is ever initialized
398 	*newcl = temp;
399 	sv_client = newcl;
400 	edictnum = (newcl-svs.clients)+1;
401 	ent = EDICT_NUM(edictnum);
402 	newcl->edict = ent;
403 	newcl->challenge = challenge; // save challenge for checksumming
404 
405 	// get the game a chance to reject this connection or modify the userinfo
406 	if (!(ge->ClientConnect (ent, userinfo)))
407 	{
408 		if (*Info_ValueForKey (userinfo, "rejmsg"))
409 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n",
410 				Info_ValueForKey (userinfo, "rejmsg"));
411 		else
412 			Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" );
413 		Com_DPrintf ("Game rejected a connection.\n");
414 		return;
415 	}
416 
417 	// parse some info from the info strings
418 	strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1);
419 	SV_UserinfoChanged (newcl);
420 
421 	// send the connect packet to the client
422 	Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect");
423 
424 	Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport);
425 
426 	newcl->state = cs_connected;
427 
428 	SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) );
429 	newcl->datagram.allowoverflow = true;
430 	newcl->lastmessage = svs.realtime;	// don't timeout
431 	newcl->lastconnect = svs.realtime;
432 }
433 
Rcon_Validate(void)434 int Rcon_Validate (void)
435 {
436 	if (!strlen (rcon_password->string))
437 		return 0;
438 
439 	if (strcmp (Cmd_Argv(1), rcon_password->string) )
440 		return 0;
441 
442 	return 1;
443 }
444 
445 /*
446 ===============
447 SVC_RemoteCommand
448 
449 A client issued an rcon command.
450 Shift down the remaining args
451 Redirect all printfs
452 ===============
453 */
SVC_RemoteCommand(void)454 void SVC_RemoteCommand (void)
455 {
456 	int		i;
457 	char	remaining[1024];
458 
459 	i = Rcon_Validate ();
460 
461 	if (i == 0)
462 		Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
463 	else
464 		Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
465 
466 	Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
467 
468 	if (!Rcon_Validate ())
469 	{
470 		Com_Printf ("Bad rcon_password.\n");
471 	}
472 	else
473 	{
474 		remaining[0] = 0;
475 
476 		for (i=2 ; i<Cmd_Argc() ; i++)
477 		{
478 			strcat (remaining, Cmd_Argv(i) );
479 			strcat (remaining, " ");
480 		}
481 
482 		Cmd_ExecuteString (remaining);
483 	}
484 
485 	Com_EndRedirect ();
486 }
487 
488 /*
489 =================
490 SV_ConnectionlessPacket
491 
492 A connectionless packet has four leading 0xff
493 characters to distinguish it from a game channel.
494 Clients that are in the game can still send
495 connectionless packets.
496 =================
497 */
SV_ConnectionlessPacket(void)498 void SV_ConnectionlessPacket (void)
499 {
500 	char	*s;
501 	char	*c;
502 
503 	MSG_BeginReading (&net_message);
504 	MSG_ReadLong (&net_message);		// skip the -1 marker
505 
506 	s = MSG_ReadStringLine (&net_message);
507 
508 	Cmd_TokenizeString (s, false);
509 
510 	c = Cmd_Argv(0);
511 	Com_DPrintf ("Packet %s : %s\n", NET_AdrToString(net_from), c);
512 
513 	if (!strcmp(c, "ping"))
514 		SVC_Ping ();
515 	else if (!strcmp(c, "ack"))
516 		SVC_Ack ();
517 	else if (!strcmp(c,"status"))
518 		SVC_Status ();
519 	else if (!strcmp(c,"info"))
520 		SVC_Info ();
521 	else if (!strcmp(c,"getchallenge"))
522 		SVC_GetChallenge ();
523 	else if (!strcmp(c,"connect"))
524 		SVC_DirectConnect ();
525 	else if (!strcmp(c, "rcon"))
526 		SVC_RemoteCommand ();
527 	else
528 		Com_Printf ("bad connectionless packet from %s:\n%s\n"
529 		, NET_AdrToString (net_from), s);
530 }
531 
532 
533 //============================================================================
534 
535 /*
536 ===================
537 SV_CalcPings
538 
539 Updates the cl->ping variables
540 ===================
541 */
SV_CalcPings(void)542 void SV_CalcPings (void)
543 {
544 	int			i, j;
545 	client_t	*cl;
546 	int			total, count;
547 
548 	for (i=0 ; i<maxclients->value ; i++)
549 	{
550 		cl = &svs.clients[i];
551 		if (cl->state != cs_spawned )
552 			continue;
553 
554 #if 0
555 		if (cl->lastframe > 0)
556 			cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl->lastframe + 1;
557 		else
558 			cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0;
559 #endif
560 
561 		total = 0;
562 		count = 0;
563 		for (j=0 ; j<LATENCY_COUNTS ; j++)
564 		{
565 			if (cl->frame_latency[j] > 0)
566 			{
567 				count++;
568 				total += cl->frame_latency[j];
569 			}
570 		}
571 		if (!count)
572 			cl->ping = 0;
573 		else
574 #if 0
575 			cl->ping = total*100/count - 100;
576 #else
577 			cl->ping = total / count;
578 #endif
579 
580 		// let the game dll know about the ping
581 		cl->edict->client->ping = cl->ping;
582 	}
583 }
584 
585 
586 /*
587 ===================
588 SV_GiveMsec
589 
590 Every few frames, gives all clients an allotment of milliseconds
591 for their command moves.  If they exceed it, assume cheating.
592 ===================
593 */
SV_GiveMsec(void)594 void SV_GiveMsec (void)
595 {
596 	int			i;
597 	client_t	*cl;
598 
599 	if (sv.framenum & 15)
600 		return;
601 
602 	for (i=0 ; i<maxclients->value ; i++)
603 	{
604 		cl = &svs.clients[i];
605 		if (cl->state == cs_free )
606 			continue;
607 
608 		cl->commandMsec = 1800;		// 1600 + some slop
609 	}
610 }
611 
612 
613 /*
614 =================
615 SV_ReadPackets
616 =================
617 */
SV_ReadPackets(void)618 void SV_ReadPackets (void)
619 {
620 	int			i;
621 	client_t	*cl;
622 	int			qport;
623 
624 	while (NET_GetPacket (NS_SERVER, &net_from, &net_message))
625 	{
626 		// check for connectionless packet (0xffffffff) first
627 		if (*(int *)net_message.data == -1)
628 		{
629 			SV_ConnectionlessPacket ();
630 			continue;
631 		}
632 
633 		// read the qport out of the message so we can fix up
634 		// stupid address translating routers
635 		MSG_BeginReading (&net_message);
636 		MSG_ReadLong (&net_message);		// sequence number
637 		MSG_ReadLong (&net_message);		// sequence number
638 		qport = MSG_ReadShort (&net_message) & 0xffff;
639 
640 		// check for packets from connected clients
641 		for (i=0, cl=svs.clients ; i<maxclients->value ; i++,cl++)
642 		{
643 			if (cl->state == cs_free)
644 				continue;
645 			if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
646 				continue;
647 			if (cl->netchan.qport != qport)
648 				continue;
649 			if (cl->netchan.remote_address.port != net_from.port)
650 			{
651 				Com_Printf ("SV_ReadPackets: fixing up a translated port\n");
652 				cl->netchan.remote_address.port = net_from.port;
653 			}
654 
655 			if (Netchan_Process(&cl->netchan, &net_message))
656 			{	// this is a valid, sequenced packet, so process it
657 				if (cl->state != cs_zombie)
658 				{
659 					cl->lastmessage = svs.realtime;	// don't timeout
660 					SV_ExecuteClientMessage (cl);
661 				}
662 			}
663 			break;
664 		}
665 
666 		if (i != maxclients->value)
667 			continue;
668 	}
669 }
670 
671 /*
672 ==================
673 SV_CheckTimeouts
674 
675 If a packet has not been received from a client for timeout->value
676 seconds, drop the conneciton.  Server frames are used instead of
677 realtime to avoid dropping the local client while debugging.
678 
679 When a client is normally dropped, the client_t goes into a zombie state
680 for a few seconds to make sure any final reliable message gets resent
681 if necessary
682 ==================
683 */
SV_CheckTimeouts(void)684 void SV_CheckTimeouts (void)
685 {
686 	int		i;
687 	client_t	*cl;
688 	int			droppoint;
689 	int			zombiepoint;
690 
691 	droppoint = svs.realtime - 1000*timeout->value;
692 	zombiepoint = svs.realtime - 1000*zombietime->value;
693 
694 	for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
695 	{
696 		// message times may be wrong across a changelevel
697 		if (cl->lastmessage > svs.realtime)
698 			cl->lastmessage = svs.realtime;
699 
700 		if (cl->state == cs_zombie
701 		&& cl->lastmessage < zombiepoint)
702 		{
703 			cl->state = cs_free;	// can now be reused
704 			continue;
705 		}
706 		if ( (cl->state == cs_connected || cl->state == cs_spawned)
707 			&& cl->lastmessage < droppoint)
708 		{
709 			SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
710 			SV_DropClient (cl);
711 			cl->state = cs_free;	// don't bother with zombie state
712 		}
713 	}
714 }
715 
716 /*
717 ================
718 SV_PrepWorldFrame
719 
720 This has to be done before the world logic, because
721 player processing happens outside RunWorldFrame
722 ================
723 */
SV_PrepWorldFrame(void)724 void SV_PrepWorldFrame (void)
725 {
726 	edict_t	*ent;
727 	int		i;
728 
729 	for (i=0 ; i<ge->num_edicts ; i++, ent++)
730 	{
731 		ent = EDICT_NUM(i);
732 		// events only last for a single message
733 		ent->s.event = 0;
734 	}
735 
736 }
737 
738 
739 /*
740 =================
741 SV_RunGameFrame
742 =================
743 */
SV_RunGameFrame(void)744 void SV_RunGameFrame (void)
745 {
746 	if (host_speeds->value)
747 		time_before_game = Sys_Milliseconds ();
748 
749 	// we always need to bump framenum, even if we
750 	// don't run the world, otherwise the delta
751 	// compression can get confused when a client
752 	// has the "current" frame
753 	sv.framenum++;
754 	sv.time = sv.framenum*100;
755 
756 	// don't run if paused
757 	if (!sv_paused->value || maxclients->value > 1)
758 	{
759 		ge->RunFrame ();
760 
761 		// never get more than one tic behind
762 		if (sv.time < svs.realtime)
763 		{
764 			if (sv_showclamp->value)
765 				Com_Printf ("sv highclamp\n");
766 			svs.realtime = sv.time;
767 		}
768 	}
769 
770 	if (host_speeds->value)
771 		time_after_game = Sys_Milliseconds ();
772 
773 }
774 
775 /*
776 ==================
777 SV_Frame
778 
779 ==================
780 */
SV_Frame(int msec)781 void SV_Frame (int msec)
782 {
783 	time_before_game = time_after_game = 0;
784 
785 	// if server is not active, do nothing
786 	if (!svs.initialized)
787 		return;
788 
789     svs.realtime += msec;
790 
791 	// keep the random time dependent
792 	rand ();
793 
794 	// check timeouts
795 	SV_CheckTimeouts ();
796 
797 	// get packets from clients
798 	SV_ReadPackets ();
799 
800 	// move autonomous things around if enough time has passed
801 	if (!sv_timedemo->value && svs.realtime < sv.time)
802 	{
803 		// never let the time get too far off
804 		if (sv.time - svs.realtime > 100)
805 		{
806 			if (sv_showclamp->value)
807 				Com_Printf ("sv lowclamp\n");
808 			svs.realtime = sv.time - 100;
809 		}
810 		NET_Sleep(sv.time - svs.realtime);
811 		return;
812 	}
813 
814 	// update ping based on the last known frame from all clients
815 	SV_CalcPings ();
816 
817 	// give the clients some timeslices
818 	SV_GiveMsec ();
819 
820 	// let everything in the world think and move
821 	SV_RunGameFrame ();
822 
823 	// send messages back to the clients that had packets read this frame
824 	SV_SendClientMessages ();
825 
826 	// save the entire world state if recording a serverdemo
827 	SV_RecordDemoMessage ();
828 
829 	// send a heartbeat to the master if needed
830 	Master_Heartbeat ();
831 
832 	// clear teleport flags, etc for next frame
833 	SV_PrepWorldFrame ();
834 
835 }
836 
837 //============================================================================
838 
839 /*
840 ================
841 Master_Heartbeat
842 
843 Send a message to the master every few minutes to
844 let it know we are alive, and log information
845 ================
846 */
847 #define	HEARTBEAT_SECONDS	300
Master_Heartbeat(void)848 void Master_Heartbeat (void)
849 {
850 	char		*string;
851 	int			i;
852 
853 	// pgm post3.19 change, cvar pointer not validated before dereferencing
854 	if (!dedicated || !dedicated->value)
855 		return;		// only dedicated servers send heartbeats
856 
857 	// pgm post3.19 change, cvar pointer not validated before dereferencing
858 	if (!public_server || !public_server->value)
859 		return;		// a private dedicated game
860 
861 	// check for time wraparound
862 	if (svs.last_heartbeat > svs.realtime)
863 		svs.last_heartbeat = svs.realtime;
864 
865 	if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000)
866 		return;		// not time to send yet
867 
868 	svs.last_heartbeat = svs.realtime;
869 
870 	// send the same string that we would give for a status OOB command
871 	string = SV_StatusString();
872 
873 	// send to group master
874 	for (i=0 ; i<MAX_MASTERS ; i++)
875 		if (master_adr[i].port)
876 		{
877 			Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
878 			Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "heartbeat\n%s", string);
879 		}
880 }
881 
882 /*
883 =================
884 Master_Shutdown
885 
886 Informs all masters that this server is going down
887 =================
888 */
Master_Shutdown(void)889 void Master_Shutdown (void)
890 {
891 	int			i;
892 
893 	// pgm post3.19 change, cvar pointer not validated before dereferencing
894 	if (!dedicated || !dedicated->value)
895 		return;		// only dedicated servers send heartbeats
896 
897 	// pgm post3.19 change, cvar pointer not validated before dereferencing
898 	if (!public_server || !public_server->value)
899 		return;		// a private dedicated game
900 
901 	// send to group master
902 	for (i=0 ; i<MAX_MASTERS ; i++)
903 		if (master_adr[i].port)
904 		{
905 			if (i > 0)
906 				Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
907 			Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "shutdown");
908 		}
909 }
910 
911 //============================================================================
912 
913 
914 /*
915 =================
916 SV_UserinfoChanged
917 
918 Pull specific info from a newly changed userinfo string
919 into a more C freindly form.
920 =================
921 */
SV_UserinfoChanged(client_t * cl)922 void SV_UserinfoChanged (client_t *cl)
923 {
924 	char	*val;
925 	int		i;
926 
927 	// call prog code to allow overrides
928 	ge->ClientUserinfoChanged (cl->edict, cl->userinfo);
929 
930 	// name for C code
931 	strncpy (cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name)-1);
932 	// mask off high bit
933 	for (i=0 ; i<sizeof(cl->name) ; i++)
934 		cl->name[i] &= 127;
935 
936 	// rate command
937 	val = Info_ValueForKey (cl->userinfo, "rate");
938 	if (strlen(val))
939 	{
940 		i = atoi(val);
941 		cl->rate = i;
942 		if (cl->rate < 100)
943 			cl->rate = 100;
944 		if (cl->rate > 15000)
945 			cl->rate = 15000;
946 	}
947 	else
948 		cl->rate = 5000;
949 
950 	// msg command
951 	val = Info_ValueForKey (cl->userinfo, "msg");
952 	if (strlen(val))
953 	{
954 		cl->messagelevel = atoi(val);
955 	}
956 
957 }
958 
959 
960 //============================================================================
961 
962 /*
963 ===============
964 SV_Init
965 
966 Only called at quake2.exe startup, not for each game
967 ===============
968 */
SV_Init(void)969 void SV_Init (void)
970 {
971 	SV_InitOperatorCommands	();
972 
973 	rcon_password = Cvar_Get ("rcon_password", "", 0);
974 	Cvar_Get ("skill", "1", 0);
975 	Cvar_Get ("deathmatch", "0", CVAR_LATCH);
976 	Cvar_Get ("coop", "0", CVAR_LATCH);
977 	Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO);
978 	Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO);
979 	Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
980 	Cvar_Get ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
981 	Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET);;
982 	maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
983 	hostname = Cvar_Get ("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE);
984 	timeout = Cvar_Get ("timeout", "125", 0);
985 	zombietime = Cvar_Get ("zombietime", "2", 0);
986 	sv_showclamp = Cvar_Get ("showclamp", "0", 0);
987 	sv_paused = Cvar_Get ("paused", "0", 0);
988 	sv_timedemo = Cvar_Get ("timedemo", "0", 0);
989 	sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0);
990 	allow_download = Cvar_Get ("allow_download", "0", CVAR_ARCHIVE);
991 	allow_download_players  = Cvar_Get ("allow_download_players", "0", CVAR_ARCHIVE);
992 	allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE);
993 	allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE);
994 	allow_download_maps	  = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE);
995 
996 	sv_noreload = Cvar_Get ("sv_noreload", "0", 0);
997 
998 	sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH);
999 
1000 	public_server = Cvar_Get ("public", "0", 0);
1001 
1002 	sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE);
1003 
1004 	SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer));
1005 }
1006 
1007 /*
1008 ==================
1009 SV_FinalMessage
1010 
1011 Used by SV_Shutdown to send a final message to all
1012 connected clients before the server goes down.  The messages are sent immediately,
1013 not just stuck on the outgoing message list, because the server is going
1014 to totally exit after returning from this function.
1015 ==================
1016 */
SV_FinalMessage(char * message,qboolean reconnect)1017 void SV_FinalMessage (char *message, qboolean reconnect)
1018 {
1019 	int			i;
1020 	client_t	*cl;
1021 
1022 	SZ_Clear (&net_message);
1023 	MSG_WriteByte (&net_message, svc_print);
1024 	MSG_WriteByte (&net_message, PRINT_HIGH);
1025 	MSG_WriteString (&net_message, message);
1026 
1027 	if (reconnect)
1028 		MSG_WriteByte (&net_message, svc_reconnect);
1029 	else
1030 		MSG_WriteByte (&net_message, svc_disconnect);
1031 
1032 	// send it twice
1033 	// stagger the packets to crutch operating system limited buffers
1034 
1035 	for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
1036 		if (cl->state >= cs_connected)
1037 			Netchan_Transmit (&cl->netchan, net_message.cursize
1038 			, net_message.data);
1039 
1040 	for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
1041 		if (cl->state >= cs_connected)
1042 			Netchan_Transmit (&cl->netchan, net_message.cursize
1043 			, net_message.data);
1044 }
1045 
1046 
1047 
1048 /*
1049 ================
1050 SV_Shutdown
1051 
1052 Called when each game quits,
1053 before Sys_Quit or Sys_Error
1054 ================
1055 */
SV_Shutdown(char * finalmsg,qboolean reconnect)1056 void SV_Shutdown (char *finalmsg, qboolean reconnect)
1057 {
1058 	if (svs.clients)
1059 		SV_FinalMessage (finalmsg, reconnect);
1060 
1061 	Master_Shutdown ();
1062 	SV_ShutdownGameProgs ();
1063 
1064 	// free current level
1065 	if (sv.demofile)
1066 		fclose (sv.demofile);
1067 	memset (&sv, 0, sizeof(sv));
1068 	Com_SetServerState (sv.state);
1069 
1070 	// free server static data
1071 	if (svs.clients)
1072 		Z_Free (svs.clients);
1073 	if (svs.client_entities)
1074 		Z_Free (svs.client_entities);
1075 	if (svs.demofile)
1076 		fclose (svs.demofile);
1077 	memset (&svs, 0, sizeof(svs));
1078 }
1079 
1080