1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20
21#include "qwsvdef.h"
22
23quakeparms_t host_parms;
24
25qboolean	host_initialized;		// true if into command execution (compatability)
26
27double		host_frametime;
28double		realtime;				// without any filtering or bounding
29
30int			host_hunklevel;
31
32netadr_t	master_adr[MAX_MASTERS];	// address of group servers
33
34client_t	*host_client;			// current client
35
36cvar_t	sv_mintic = {"sv_mintic","0.03"};	// bound the size of the
37cvar_t	sv_maxtic = {"sv_maxtic","0.1"};	// physics time tic
38
39cvar_t	developer = {"developer","0"};		// show extra messages
40
41cvar_t	timeout = {"timeout","65"};		// seconds without any message
42cvar_t	zombietime = {"zombietime", "2"};	// seconds to sink messages
43											// after disconnect
44
45cvar_t	rcon_password = {"rcon_password", ""};	// password for remote server commands
46cvar_t	password = {"password", ""};	// password for entering the game
47cvar_t	spectator_password = {"spectator_password", ""};	// password for entering as a sepctator
48
49cvar_t	allow_download = {"allow_download", "1"};
50cvar_t	allow_download_skins = {"allow_download_skins", "1"};
51cvar_t	allow_download_models = {"allow_download_models", "1"};
52cvar_t	allow_download_sounds = {"allow_download_sounds", "1"};
53cvar_t	allow_download_maps = {"allow_download_maps", "1"};
54
55cvar_t sv_highchars = {"sv_highchars", "1"};
56
57cvar_t sv_phs = {"sv_phs", "1"};
58
59cvar_t pausable	= {"pausable", "1"};
60
61
62//
63// game rules mirrored in svs.info
64//
65cvar_t	fraglimit = {"fraglimit","0",false,true};
66cvar_t	timelimit = {"timelimit","0",false,true};
67cvar_t	teamplay = {"teamplay","0",false,true};
68cvar_t	samelevel = {"samelevel","0", false, true};
69cvar_t	maxclients = {"maxclients","8", false, true};
70cvar_t	maxspectators = {"maxspectators","8", false, true};
71cvar_t	deathmatch = {"deathmatch","1", false, true};			// 0, 1, or 2
72cvar_t	spawn = {"spawn","0", false, true};
73cvar_t	watervis = {"watervis", "0", false, true};
74
75cvar_t	hostname = {"hostname","unnamed", false, true};
76
77FILE	*sv_logfile;
78FILE	*sv_fraglogfile;
79
80void SV_AcceptClient (netadr_t adr, int userid, char *userinfo);
81void Master_Shutdown (void);
82
83//============================================================================
84
85qboolean ServerPaused(void)
86{
87	return sv.paused;
88}
89
90/*
91================
92SV_Shutdown
93
94Quake calls this before calling Sys_Quit or Sys_Error
95================
96*/
97void SV_Shutdown (void)
98{
99	Master_Shutdown ();
100	if (sv_logfile)
101	{
102		fclose (sv_logfile);
103		sv_logfile = NULL;
104	}
105	if (sv_fraglogfile)
106	{
107		fclose (sv_fraglogfile);
108		sv_logfile = NULL;
109	}
110	NET_Shutdown ();
111}
112
113/*
114================
115SV_Error
116
117Sends a datagram to all the clients informing them of the server crash,
118then exits
119================
120*/
121void SV_Error (char *error, ...)
122{
123	va_list		argptr;
124	static	char		string[1024];
125	static	qboolean inerror = false;
126
127	if (inerror)
128		Sys_Error ("SV_Error: recursively entered (%s)", string);
129
130	inerror = true;
131
132	va_start (argptr,error);
133	vsprintf (string,error,argptr);
134	va_end (argptr);
135
136	Con_Printf ("SV_Error: %s\n",string);
137
138	SV_FinalMessage (va("server crashed: %s\n", string));
139
140	SV_Shutdown ();
141
142	Sys_Error ("SV_Error: %s\n",string);
143}
144
145/*
146==================
147SV_FinalMessage
148
149Used by SV_Error and SV_Quit_f to send a final message to all connected
150clients before the server goes down.  The messages are sent immediately,
151not just stuck on the outgoing message list, because the server is going
152to totally exit after returning from this function.
153==================
154*/
155void SV_FinalMessage (char *message)
156{
157	int			i;
158	client_t	*cl;
159
160	SZ_Clear (&net_message);
161	MSG_WriteByte (&net_message, svc_print);
162	MSG_WriteByte (&net_message, PRINT_HIGH);
163	MSG_WriteString (&net_message, message);
164	MSG_WriteByte (&net_message, svc_disconnect);
165
166	for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++)
167		if (cl->state >= cs_spawned)
168			Netchan_Transmit (&cl->netchan, net_message.cursize
169			, net_message.data);
170}
171
172
173
174/*
175=====================
176SV_DropClient
177
178Called when the player is totally leaving the server, either willingly
179or unwillingly.  This is NOT called if the entire server is quiting
180or crashing.
181=====================
182*/
183void SV_DropClient (client_t *drop)
184{
185	// add the disconnect
186	MSG_WriteByte (&drop->netchan.message, svc_disconnect);
187
188	if (drop->state == cs_spawned)
189		if (!drop->spectator)
190		{
191			// call the prog function for removing a client
192			// this will set the body to a dead frame, among other things
193			pr_global_struct->self = EDICT_TO_PROG(drop->edict);
194			PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
195		}
196		else if (SpectatorDisconnect)
197		{
198			// call the prog function for removing a client
199			// this will set the body to a dead frame, among other things
200			pr_global_struct->self = EDICT_TO_PROG(drop->edict);
201			PR_ExecuteProgram (SpectatorDisconnect);
202		}
203
204	if (drop->spectator)
205		Con_Printf ("Spectator %s removed\n",drop->name);
206	else
207		Con_Printf ("Client %s removed\n",drop->name);
208
209	if (drop->download)
210	{
211		fclose (drop->download);
212		drop->download = NULL;
213	}
214	if (drop->upload)
215	{
216		fclose (drop->upload);
217		drop->upload = NULL;
218	}
219	*drop->uploadfn = 0;
220
221	drop->state = cs_zombie;		// become free in a few seconds
222	drop->connection_started = realtime;	// for zombie timeout
223
224	drop->old_frags = 0;
225	drop->edict->v.frags = 0;
226	drop->name[0] = 0;
227	memset (drop->userinfo, 0, sizeof(drop->userinfo));
228
229// send notification to all remaining clients
230	SV_FullClientUpdate (drop, &sv.reliable_datagram);
231}
232
233
234//====================================================================
235
236/*
237===================
238SV_CalcPing
239
240===================
241*/
242int SV_CalcPing (client_t *cl)
243{
244	float		ping;
245	int			i;
246	int			count;
247	register	client_frame_t *frame;
248
249	ping = 0;
250	count = 0;
251	for (frame = cl->frames, i=0 ; i<UPDATE_BACKUP ; i++, frame++)
252	{
253		if (frame->ping_time > 0)
254		{
255			ping += frame->ping_time;
256			count++;
257		}
258	}
259	if (!count)
260		return 9999;
261	ping /= count;
262
263	return ping*1000;
264}
265
266/*
267===================
268SV_FullClientUpdate
269
270Writes all update values to a sizebuf
271===================
272*/
273void SV_FullClientUpdate (client_t *client, sizebuf_t *buf)
274{
275	int		i;
276	char	info[MAX_INFO_STRING];
277
278	i = client - svs.clients;
279
280//Sys_Printf("SV_FullClientUpdate:  Updated frags for client %d\n", i);
281
282	MSG_WriteByte (buf, svc_updatefrags);
283	MSG_WriteByte (buf, i);
284	MSG_WriteShort (buf, client->old_frags);
285
286	MSG_WriteByte (buf, svc_updateping);
287	MSG_WriteByte (buf, i);
288	MSG_WriteShort (buf, SV_CalcPing (client));
289
290	MSG_WriteByte (buf, svc_updatepl);
291	MSG_WriteByte (buf, i);
292	MSG_WriteByte (buf, client->lossage);
293
294	MSG_WriteByte (buf, svc_updateentertime);
295	MSG_WriteByte (buf, i);
296	MSG_WriteFloat (buf, realtime - client->connection_started);
297
298	strcpy (info, client->userinfo);
299	Info_RemovePrefixedKeys (info, '_');	// server passwords, etc
300
301	MSG_WriteByte (buf, svc_updateuserinfo);
302	MSG_WriteByte (buf, i);
303	MSG_WriteLong (buf, client->userid);
304	MSG_WriteString (buf, info);
305}
306
307/*
308===================
309SV_FullClientUpdateToClient
310
311Writes all update values to a client's reliable stream
312===================
313*/
314void SV_FullClientUpdateToClient (client_t *client, client_t *cl)
315{
316	ClientReliableCheckBlock(cl, 24 + strlen(client->userinfo));
317	if (cl->num_backbuf) {
318		SV_FullClientUpdate (client, &cl->backbuf);
319		ClientReliable_FinishWrite(cl);
320	} else
321		SV_FullClientUpdate (client, &cl->netchan.message);
322}
323
324
325/*
326==============================================================================
327
328CONNECTIONLESS COMMANDS
329
330==============================================================================
331*/
332
333/*
334================
335SVC_Status
336
337Responds with all the info that qplug or qspy can see
338This message can be up to around 5k with worst case string lengths.
339================
340*/
341void SVC_Status (void)
342{
343	int		i;
344	client_t	*cl;
345	int		ping;
346	int		top, bottom;
347
348	Cmd_TokenizeString ("status");
349	SV_BeginRedirect (RD_PACKET);
350	Con_Printf ("%s\n", svs.info);
351	for (i=0 ; i<MAX_CLIENTS ; i++)
352	{
353		cl = &svs.clients[i];
354		if ((cl->state == cs_connected || cl->state == cs_spawned ) && !cl->spectator)
355		{
356			top = atoi(Info_ValueForKey (cl->userinfo, "topcolor"));
357			bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor"));
358			top = (top < 0) ? 0 : ((top > 13) ? 13 : top);
359			bottom = (bottom < 0) ? 0 : ((bottom > 13) ? 13 : bottom);
360			ping = SV_CalcPing (cl);
361			Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid,
362				cl->old_frags, (int)(realtime - cl->connection_started)/60,
363				ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom);
364		}
365	}
366	SV_EndRedirect ();
367}
368
369/*
370===================
371SV_CheckLog
372
373===================
374*/
375#define	LOG_HIGHWATER	4096
376#define	LOG_FLUSH		10*60
377void SV_CheckLog (void)
378{
379	sizebuf_t	*sz;
380
381	sz = &svs.log[svs.logsequence&1];
382
383	// bump sequence if allmost full, or ten minutes have passed and
384	// there is something still sitting there
385	if (sz->cursize > LOG_HIGHWATER
386	|| (realtime - svs.logtime > LOG_FLUSH && sz->cursize) )
387	{
388		// swap buffers and bump sequence
389		svs.logtime = realtime;
390		svs.logsequence++;
391		sz = &svs.log[svs.logsequence&1];
392		sz->cursize = 0;
393		Con_Printf ("beginning fraglog sequence %i\n", svs.logsequence);
394	}
395
396}
397
398/*
399================
400SVC_Log
401
402Responds with all the logged frags for ranking programs.
403If a sequence number is passed as a parameter and it is
404the same as the current sequence, an A2A_NACK will be returned
405instead of the data.
406================
407*/
408void SVC_Log (void)
409{
410	int		seq;
411	char	data[MAX_DATAGRAM+64];
412
413	if (Cmd_Argc() == 2)
414		seq = atoi(Cmd_Argv(1));
415	else
416		seq = -1;
417
418	if (seq == svs.logsequence-1 || !sv_fraglogfile)
419	{	// they allready have this data, or we aren't logging frags
420		data[0] = A2A_NACK;
421		NET_SendPacket (1, data, net_from);
422		return;
423	}
424
425	Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(net_from));
426
427	sprintf (data, "stdlog %i\n", svs.logsequence-1);
428	strcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)]);
429
430	NET_SendPacket (strlen(data)+1, data, net_from);
431}
432
433/*
434================
435SVC_Ping
436
437Just responds with an acknowledgement
438================
439*/
440void SVC_Ping (void)
441{
442	char	data;
443
444	data = A2A_ACK;
445
446	NET_SendPacket (1, &data, net_from);
447}
448
449/*
450=================
451SVC_GetChallenge
452
453Returns a challenge number that can be used
454in a subsequent client_connect command.
455We do this to prevent denial of service attacks that
456flood the server with invalid connection IPs.  With a
457challenge, they must give a valid IP address.
458=================
459*/
460void SVC_GetChallenge (void)
461{
462	int		i;
463	int		oldest;
464	int		oldestTime;
465
466	oldest = 0;
467	oldestTime = 0x7fffffff;
468
469	// see if we already have a challenge for this ip
470	for (i = 0 ; i < MAX_CHALLENGES ; i++)
471	{
472		if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
473			break;
474		if (svs.challenges[i].time < oldestTime)
475		{
476			oldestTime = svs.challenges[i].time;
477			oldest = i;
478		}
479	}
480
481	if (i == MAX_CHALLENGES)
482	{
483		// overwrite the oldest
484		svs.challenges[oldest].challenge = (rand() << 16) ^ rand();
485		svs.challenges[oldest].adr = net_from;
486		svs.challenges[oldest].time = realtime;
487		i = oldest;
488	}
489
490	// send it back
491	Netchan_OutOfBandPrint (net_from, "%c%i", S2C_CHALLENGE,
492			svs.challenges[i].challenge);
493}
494
495/*
496==================
497SVC_DirectConnect
498
499A connection request that did not come from the master
500==================
501*/
502void SVC_DirectConnect (void)
503{
504	char		userinfo[1024];
505	static		int	userid;
506	netadr_t	adr;
507	int			i;
508	client_t	*cl, *newcl;
509	client_t	temp;
510	edict_t		*ent;
511	int			edictnum;
512	char		*s;
513	int			clients, spectators;
514	qboolean	spectator;
515	int			qport;
516	int			version;
517	int			challenge;
518
519	version = atoi(Cmd_Argv(1));
520	if (version != PROTOCOL_VERSION)
521	{
522		Netchan_OutOfBandPrint (net_from, "%c\nServer is version %4.2f.\n", A2C_PRINT, VERSION);
523		Con_Printf ("* rejected connect from version %i\n", version);
524		return;
525	}
526
527	qport = atoi(Cmd_Argv(2));
528
529	challenge = atoi(Cmd_Argv(3));
530
531	// note an extra byte is needed to replace spectator key
532	strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-2);
533	userinfo[sizeof(userinfo) - 2] = 0;
534
535	// see if the challenge is valid
536	for (i=0 ; i<MAX_CHALLENGES ; i++)
537	{
538		if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
539		{
540			if (challenge == svs.challenges[i].challenge)
541				break;		// good
542			Netchan_OutOfBandPrint (net_from, "%c\nBad challenge.\n", A2C_PRINT);
543			return;
544		}
545	}
546	if (i == MAX_CHALLENGES)
547	{
548		Netchan_OutOfBandPrint (net_from, "%c\nNo challenge for address.\n", A2C_PRINT);
549		return;
550	}
551
552	// check for password or spectator_password
553	s = Info_ValueForKey (userinfo, "spectator");
554	if (s[0] && strcmp(s, "0"))
555	{
556		if (spectator_password.string[0] &&
557			stricmp(spectator_password.string, "none") &&
558			strcmp(spectator_password.string, s) )
559		{	// failed
560			Con_Printf ("%s:spectator password failed\n", NET_AdrToString (net_from));
561			Netchan_OutOfBandPrint (net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT);
562			return;
563		}
564		Info_RemoveKey (userinfo, "spectator"); // remove passwd
565		Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING);
566		spectator = true;
567	}
568	else
569	{
570		s = Info_ValueForKey (userinfo, "password");
571		if (password.string[0] &&
572			stricmp(password.string, "none") &&
573			strcmp(password.string, s) )
574		{
575			Con_Printf ("%s:password failed\n", NET_AdrToString (net_from));
576			Netchan_OutOfBandPrint (net_from, "%c\nserver requires a password\n\n", A2C_PRINT);
577			return;
578		}
579		spectator = false;
580		Info_RemoveKey (userinfo, "password"); // remove passwd
581	}
582
583	adr = net_from;
584	userid++;	// so every client gets a unique id
585
586	newcl = &temp;
587	memset (newcl, 0, sizeof(client_t));
588
589	newcl->userid = userid;
590
591	// works properly
592	if (!sv_highchars.value) {
593		byte *p, *q;
594
595		for (p = (byte *)newcl->userinfo, q = (byte *)userinfo;
596			*q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++)
597			if (*q > 31 && *q <= 127)
598				*p++ = *q;
599	} else
600		strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1);
601
602	// if there is allready a slot for this ip, drop it
603	for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
604	{
605		if (cl->state == cs_free)
606			continue;
607		if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
608			&& ( cl->netchan.qport == qport
609			|| adr.port == cl->netchan.remote_address.port ))
610		{
611			if (cl->state == cs_connected) {
612				Con_Printf("%s:dup connect\n", NET_AdrToString (adr));
613				userid--;
614				return;
615			}
616
617			Con_Printf ("%s:reconnect\n", NET_AdrToString (adr));
618			SV_DropClient (cl);
619			break;
620		}
621	}
622
623	// count up the clients and spectators
624	clients = 0;
625	spectators = 0;
626	for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
627	{
628		if (cl->state == cs_free)
629			continue;
630		if (cl->spectator)
631			spectators++;
632		else
633			clients++;
634	}
635
636	// if at server limits, refuse connection
637	if ( maxclients.value > MAX_CLIENTS )
638		Cvar_SetValue ("maxclients", MAX_CLIENTS);
639	if (maxspectators.value > MAX_CLIENTS)
640		Cvar_SetValue ("maxspectators", MAX_CLIENTS);
641	if (maxspectators.value + maxclients.value > MAX_CLIENTS)
642		Cvar_SetValue ("maxspectators", MAX_CLIENTS - maxspectators.value + maxclients.value);
643	if ( (spectator && spectators >= (int)maxspectators.value)
644		|| (!spectator && clients >= (int)maxclients.value) )
645	{
646		Con_Printf ("%s:full connect\n", NET_AdrToString (adr));
647		Netchan_OutOfBandPrint (adr, "%c\nserver is full\n\n", A2C_PRINT);
648		return;
649	}
650
651	// find a client slot
652	newcl = NULL;
653	for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
654	{
655		if (cl->state == cs_free)
656		{
657			newcl = cl;
658			break;
659		}
660	}
661	if (!newcl)
662	{
663		Con_Printf ("WARNING: miscounted available clients\n");
664		return;
665	}
666
667
668	// build a new connection
669	// accept the new client
670	// this is the only place a client_t is ever initialized
671	*newcl = temp;
672
673	Netchan_OutOfBandPrint (adr, "%c", S2C_CONNECTION );
674
675	edictnum = (newcl-svs.clients)+1;
676
677	Netchan_Setup (&newcl->netchan , adr, qport);
678
679	newcl->state = cs_connected;
680
681	newcl->datagram.allowoverflow = true;
682	newcl->datagram.data = newcl->datagram_buf;
683	newcl->datagram.maxsize = sizeof(newcl->datagram_buf);
684
685	// spectator mode can ONLY be set at join time
686	newcl->spectator = spectator;
687
688	ent = EDICT_NUM(edictnum);
689	newcl->edict = ent;
690
691	// parse some info from the info strings
692	SV_ExtractFromUserinfo (newcl);
693
694	// JACK: Init the floodprot stuff.
695	for (i=0; i<10; i++)
696		newcl->whensaid[i] = 0.0;
697	newcl->whensaidhead = 0;
698	newcl->lockedtill = 0;
699
700	// call the progs to get default spawn parms for the new client
701	PR_ExecuteProgram (pr_global_struct->SetNewParms);
702	for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
703		newcl->spawn_parms[i] = (&pr_global_struct->parm1)[i];
704
705	if (newcl->spectator)
706		Con_Printf ("Spectator %s connected\n", newcl->name);
707	else
708		Con_DPrintf ("Client %s connected\n", newcl->name);
709	newcl->sendinfo = true;
710}
711
712int Rcon_Validate (void)
713{
714	if (!strlen (rcon_password.string))
715		return 0;
716
717	if (strcmp (Cmd_Argv(1), rcon_password.string) )
718		return 0;
719
720	return 1;
721}
722
723/*
724===============
725SVC_RemoteCommand
726
727A client issued an rcon command.
728Shift down the remaining args
729Redirect all printfs
730===============
731*/
732void SVC_RemoteCommand (void)
733{
734	int		i;
735	char	remaining[1024];
736
737
738	if (!Rcon_Validate ()) {
739		Con_Printf ("Bad rcon from %s:\n%s\n"
740			, NET_AdrToString (net_from), net_message.data+4);
741
742		SV_BeginRedirect (RD_PACKET);
743
744		Con_Printf ("Bad rcon_password.\n");
745
746	} else {
747
748		Con_Printf ("Rcon from %s:\n%s\n"
749			, NET_AdrToString (net_from), net_message.data+4);
750
751		SV_BeginRedirect (RD_PACKET);
752
753		remaining[0] = 0;
754
755		for (i=2 ; i<Cmd_Argc() ; i++)
756		{
757			strcat (remaining, Cmd_Argv(i) );
758			strcat (remaining, " ");
759		}
760
761		Cmd_ExecuteString (remaining);
762
763	}
764
765	SV_EndRedirect ();
766}
767
768
769/*
770=================
771SV_ConnectionlessPacket
772
773A connectionless packet has four leading 0xff
774characters to distinguish it from a game channel.
775Clients that are in the game can still send
776connectionless packets.
777=================
778*/
779void SV_ConnectionlessPacket (void)
780{
781	char	*s;
782	char	*c;
783
784	MSG_BeginReading ();
785	MSG_ReadLong ();		// skip the -1 marker
786
787	s = MSG_ReadStringLine ();
788
789	Cmd_TokenizeString (s);
790
791	c = Cmd_Argv(0);
792
793	if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) )
794	{
795		SVC_Ping ();
796		return;
797	}
798	if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') )
799	{
800		Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (net_from));
801		return;
802	}
803	else if (!strcmp(c,"status"))
804	{
805		SVC_Status ();
806		return;
807	}
808	else if (!strcmp(c,"log"))
809	{
810		SVC_Log ();
811		return;
812	}
813	else if (!strcmp(c,"connect"))
814	{
815		SVC_DirectConnect ();
816		return;
817	}
818	else if (!strcmp(c,"getchallenge"))
819	{
820		SVC_GetChallenge ();
821		return;
822	}
823	else if (!strcmp(c, "rcon"))
824		SVC_RemoteCommand ();
825	else
826		Con_Printf ("bad connectionless packet from %s:\n%s\n"
827		, NET_AdrToString (net_from), s);
828}
829
830/*
831==============================================================================
832
833PACKET FILTERING
834
835
836You can add or remove addresses from the filter list with:
837
838addip <ip>
839removeip <ip>
840
841The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
842
843Removeip will only remove an address specified exactly the same way.  You cannot addip a subnet, then removeip a single host.
844
845listip
846Prints the current list of filters.
847
848writeip
849Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date.  The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.
850
851filterban <0 or 1>
852
853If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game.  This is the default setting.
854
855If 0, then only addresses matching the list will be allowed.  This lets you easily set up a private game, or a game that only allows players from your local network.
856
857
858==============================================================================
859*/
860
861
862typedef struct
863{
864	unsigned	mask;
865	unsigned	compare;
866} ipfilter_t;
867
868#define	MAX_IPFILTERS	1024
869
870ipfilter_t	ipfilters[MAX_IPFILTERS];
871int			numipfilters;
872
873cvar_t	filterban = {"filterban", "1"};
874
875/*
876=================
877StringToFilter
878=================
879*/
880qboolean StringToFilter (char *s, ipfilter_t *f)
881{
882	char	num[128];
883	int		i, j;
884	byte	b[4];
885	byte	m[4];
886
887	for (i=0 ; i<4 ; i++)
888	{
889		b[i] = 0;
890		m[i] = 0;
891	}
892
893	for (i=0 ; i<4 ; i++)
894	{
895		if (*s < '0' || *s > '9')
896		{
897			Con_Printf ("Bad filter address: %s\n", s);
898			return false;
899		}
900
901		j = 0;
902		while (*s >= '0' && *s <= '9')
903		{
904			num[j++] = *s++;
905		}
906		num[j] = 0;
907		b[i] = atoi(num);
908		if (b[i] != 0)
909			m[i] = 255;
910
911		if (!*s)
912			break;
913		s++;
914	}
915
916	f->mask = *(unsigned *)m;
917	f->compare = *(unsigned *)b;
918
919	return true;
920}
921
922/*
923=================
924SV_AddIP_f
925=================
926*/
927void SV_AddIP_f (void)
928{
929	int		i;
930
931	for (i=0 ; i<numipfilters ; i++)
932		if (ipfilters[i].compare == 0xffffffff)
933			break;		// free spot
934	if (i == numipfilters)
935	{
936		if (numipfilters == MAX_IPFILTERS)
937		{
938			Con_Printf ("IP filter list is full\n");
939			return;
940		}
941		numipfilters++;
942	}
943
944	if (!StringToFilter (Cmd_Argv(1), &ipfilters[i]))
945		ipfilters[i].compare = 0xffffffff;
946}
947
948/*
949=================
950SV_RemoveIP_f
951=================
952*/
953void SV_RemoveIP_f (void)
954{
955	ipfilter_t	f;
956	int			i, j;
957
958	if (!StringToFilter (Cmd_Argv(1), &f))
959		return;
960	for (i=0 ; i<numipfilters ; i++)
961		if (ipfilters[i].mask == f.mask
962		&& ipfilters[i].compare == f.compare)
963		{
964			for (j=i+1 ; j<numipfilters ; j++)
965				ipfilters[j-1] = ipfilters[j];
966			numipfilters--;
967			Con_Printf ("Removed.\n");
968			return;
969		}
970	Con_Printf ("Didn't find %s.\n", Cmd_Argv(1));
971}
972
973/*
974=================
975SV_ListIP_f
976=================
977*/
978void SV_ListIP_f (void)
979{
980	int		i;
981	byte	b[4];
982
983	Con_Printf ("Filter list:\n");
984	for (i=0 ; i<numipfilters ; i++)
985	{
986		*(unsigned *)b = ipfilters[i].compare;
987		Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
988	}
989}
990
991/*
992=================
993SV_WriteIP_f
994=================
995*/
996void SV_WriteIP_f (void)
997{
998	FILE	*f;
999	char	name[MAX_OSPATH];
1000	byte	b[4];
1001	int		i;
1002
1003	sprintf (name, "%s/listip.cfg", com_gamedir);
1004
1005	Con_Printf ("Writing %s.\n", name);
1006
1007	f = fopen (name, "wb");
1008	if (!f)
1009	{
1010		Con_Printf ("Couldn't open %s\n", name);
1011		return;
1012	}
1013
1014	for (i=0 ; i<numipfilters ; i++)
1015	{
1016		*(unsigned *)b = ipfilters[i].compare;
1017		fprintf (f, "addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
1018	}
1019
1020	fclose (f);
1021}
1022
1023/*
1024=================
1025SV_SendBan
1026=================
1027*/
1028void SV_SendBan (void)
1029{
1030	char		data[128];
1031
1032	data[0] = data[1] = data[2] = data[3] = 0xff;
1033	data[4] = A2C_PRINT;
1034	data[5] = 0;
1035	strcat (data, "\nbanned.\n");
1036
1037	NET_SendPacket (strlen(data), data, net_from);
1038}
1039
1040/*
1041=================
1042SV_FilterPacket
1043=================
1044*/
1045qboolean SV_FilterPacket (void)
1046{
1047	int		i;
1048	unsigned	in;
1049
1050	in = *(unsigned *)net_from.ip;
1051
1052	for (i=0 ; i<numipfilters ; i++)
1053		if ( (in & ipfilters[i].mask) == ipfilters[i].compare)
1054			return filterban.value;
1055
1056	return !filterban.value;
1057}
1058
1059//============================================================================
1060
1061/*
1062=================
1063SV_ReadPackets
1064=================
1065*/
1066void SV_ReadPackets (void)
1067{
1068	int			i;
1069	client_t	*cl;
1070	qboolean	good;
1071	int			qport;
1072
1073	good = false;
1074	while (NET_GetPacket ())
1075	{
1076		if (SV_FilterPacket ())
1077		{
1078			SV_SendBan ();	// tell them we aren't listening...
1079			continue;
1080		}
1081
1082		// check for connectionless packet (0xffffffff) first
1083		if (*(int *)net_message.data == -1)
1084		{
1085			SV_ConnectionlessPacket ();
1086			continue;
1087		}
1088
1089		// read the qport out of the message so we can fix up
1090		// stupid address translating routers
1091		MSG_BeginReading ();
1092		MSG_ReadLong ();		// sequence number
1093		MSG_ReadLong ();		// sequence number
1094		qport = MSG_ReadShort () & 0xffff;
1095
1096		// check for packets from connected clients
1097		for (i=0, cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
1098		{
1099			if (cl->state == cs_free)
1100				continue;
1101			if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
1102				continue;
1103			if (cl->netchan.qport != qport)
1104				continue;
1105			if (cl->netchan.remote_address.port != net_from.port)
1106			{
1107				Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n");
1108				cl->netchan.remote_address.port = net_from.port;
1109			}
1110			if (Netchan_Process(&cl->netchan))
1111			{	// this is a valid, sequenced packet, so process it
1112				svs.stats.packets++;
1113				good = true;
1114				cl->send_message = true;	// reply at end of frame
1115				if (cl->state != cs_zombie)
1116					SV_ExecuteClientMessage (cl);
1117			}
1118			break;
1119		}
1120
1121		if (i != MAX_CLIENTS)
1122			continue;
1123
1124		// packet is not from a known client
1125		//	Con_Printf ("%s:sequenced packet without connection\n"
1126		// ,NET_AdrToString(net_from));
1127	}
1128}
1129
1130/*
1131==================
1132SV_CheckTimeouts
1133
1134If a packet has not been received from a client in timeout.value
1135seconds, drop the conneciton.
1136
1137When a client is normally dropped, the client_t goes into a zombie state
1138for a few seconds to make sure any final reliable message gets resent
1139if necessary
1140==================
1141*/
1142void SV_CheckTimeouts (void)
1143{
1144	int		i;
1145	client_t	*cl;
1146	float	droptime;
1147	int	nclients;
1148
1149	droptime = realtime - timeout.value;
1150	nclients = 0;
1151
1152	for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
1153	{
1154		if (cl->state == cs_connected || cl->state == cs_spawned) {
1155			if (!cl->spectator)
1156				nclients++;
1157			if (cl->netchan.last_received < droptime) {
1158				SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
1159				SV_DropClient (cl);
1160				cl->state = cs_free;	// don't bother with zombie state
1161			}
1162		}
1163		if (cl->state == cs_zombie &&
1164			realtime - cl->connection_started > zombietime.value)
1165		{
1166			cl->state = cs_free;	// can now be reused
1167		}
1168	}
1169	if (sv.paused && !nclients) {
1170		// nobody left, unpause the server
1171		SV_TogglePause("Pause released since no players are left.\n");
1172	}
1173}
1174
1175/*
1176===================
1177SV_GetConsoleCommands
1178
1179Add them exactly as if they had been typed at the console
1180===================
1181*/
1182void SV_GetConsoleCommands (void)
1183{
1184	char	*cmd;
1185
1186	while (1)
1187	{
1188		cmd = Sys_ConsoleInput ();
1189		if (!cmd)
1190			break;
1191		Cbuf_AddText (cmd);
1192	}
1193}
1194
1195/*
1196===================
1197SV_CheckVars
1198
1199===================
1200*/
1201void SV_CheckVars (void)
1202{
1203	static char *pw, *spw;
1204	int			v;
1205
1206	if (password.string == pw && spectator_password.string == spw)
1207		return;
1208	pw = password.string;
1209	spw = spectator_password.string;
1210
1211	v = 0;
1212	if (pw && pw[0] && strcmp(pw, "none"))
1213		v |= 1;
1214	if (spw && spw[0] && strcmp(spw, "none"))
1215		v |= 2;
1216
1217	Con_Printf ("Updated needpass.\n");
1218	if (!v)
1219		Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING);
1220	else
1221		Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING);
1222}
1223
1224/*
1225==================
1226SV_Frame
1227
1228==================
1229*/
1230void SV_Frame (float time)
1231{
1232	static double	start, end;
1233
1234	start = Sys_DoubleTime ();
1235	svs.stats.idle += start - end;
1236
1237// keep the random time dependent
1238	rand ();
1239
1240// decide the simulation time
1241	if (!sv.paused) {
1242		realtime += time;
1243		sv.time += time;
1244	}
1245
1246// check timeouts
1247	SV_CheckTimeouts ();
1248
1249// toggle the log buffer if full
1250	SV_CheckLog ();
1251
1252// move autonomous things around if enough time has passed
1253	if (!sv.paused)
1254		SV_Physics ();
1255
1256// get packets
1257	SV_ReadPackets ();
1258
1259// check for commands typed to the host
1260	SV_GetConsoleCommands ();
1261
1262// process console commands
1263	Cbuf_Execute ();
1264
1265	SV_CheckVars ();
1266
1267// send messages back to the clients that had packets read this frame
1268	SV_SendClientMessages ();
1269
1270// send a heartbeat to the master if needed
1271	Master_Heartbeat ();
1272
1273// collect timing statistics
1274	end = Sys_DoubleTime ();
1275	svs.stats.active += end-start;
1276	if (++svs.stats.count == STATFRAMES)
1277	{
1278		svs.stats.latched_active = svs.stats.active;
1279		svs.stats.latched_idle = svs.stats.idle;
1280		svs.stats.latched_packets = svs.stats.packets;
1281		svs.stats.active = 0;
1282		svs.stats.idle = 0;
1283		svs.stats.packets = 0;
1284		svs.stats.count = 0;
1285	}
1286}
1287
1288/*
1289===============
1290SV_InitLocal
1291===============
1292*/
1293void SV_InitLocal (void)
1294{
1295	int		i;
1296	extern	cvar_t	sv_maxvelocity;
1297	extern	cvar_t	sv_gravity;
1298	extern	cvar_t	sv_aim;
1299	extern	cvar_t	sv_stopspeed;
1300	extern	cvar_t	sv_spectatormaxspeed;
1301	extern	cvar_t	sv_accelerate;
1302	extern	cvar_t	sv_airaccelerate;
1303	extern	cvar_t	sv_wateraccelerate;
1304	extern	cvar_t	sv_friction;
1305	extern	cvar_t	sv_waterfriction;
1306
1307	SV_InitOperatorCommands	();
1308	SV_UserInit ();
1309
1310	Cvar_RegisterVariable (&rcon_password);
1311	Cvar_RegisterVariable (&password);
1312	Cvar_RegisterVariable (&spectator_password);
1313
1314	Cvar_RegisterVariable (&sv_mintic);
1315	Cvar_RegisterVariable (&sv_maxtic);
1316
1317	Cvar_RegisterVariable (&fraglimit);
1318	Cvar_RegisterVariable (&timelimit);
1319	Cvar_RegisterVariable (&teamplay);
1320	Cvar_RegisterVariable (&samelevel);
1321	Cvar_RegisterVariable (&maxclients);
1322	Cvar_RegisterVariable (&maxspectators);
1323	Cvar_RegisterVariable (&hostname);
1324	Cvar_RegisterVariable (&deathmatch);
1325	Cvar_RegisterVariable (&spawn);
1326	Cvar_RegisterVariable (&watervis);
1327
1328	Cvar_RegisterVariable (&developer);
1329
1330	Cvar_RegisterVariable (&timeout);
1331	Cvar_RegisterVariable (&zombietime);
1332
1333	Cvar_RegisterVariable (&sv_maxvelocity);
1334	Cvar_RegisterVariable (&sv_gravity);
1335	Cvar_RegisterVariable (&sv_stopspeed);
1336	Cvar_RegisterVariable (&sv_maxspeed);
1337	Cvar_RegisterVariable (&sv_spectatormaxspeed);
1338	Cvar_RegisterVariable (&sv_accelerate);
1339	Cvar_RegisterVariable (&sv_airaccelerate);
1340	Cvar_RegisterVariable (&sv_wateraccelerate);
1341	Cvar_RegisterVariable (&sv_friction);
1342	Cvar_RegisterVariable (&sv_waterfriction);
1343
1344	Cvar_RegisterVariable (&sv_aim);
1345
1346	Cvar_RegisterVariable (&filterban);
1347
1348	Cvar_RegisterVariable (&allow_download);
1349	Cvar_RegisterVariable (&allow_download_skins);
1350	Cvar_RegisterVariable (&allow_download_models);
1351	Cvar_RegisterVariable (&allow_download_sounds);
1352	Cvar_RegisterVariable (&allow_download_maps);
1353
1354	Cvar_RegisterVariable (&sv_highchars);
1355
1356	Cvar_RegisterVariable (&sv_phs);
1357
1358	Cvar_RegisterVariable (&pausable);
1359
1360	Cmd_AddCommand ("addip", SV_AddIP_f);
1361	Cmd_AddCommand ("removeip", SV_RemoveIP_f);
1362	Cmd_AddCommand ("listip", SV_ListIP_f);
1363	Cmd_AddCommand ("writeip", SV_WriteIP_f);
1364
1365	for (i=0 ; i<MAX_MODELS ; i++)
1366		sprintf (localmodels[i], "*%i", i);
1367
1368	Info_SetValueForStarKey (svs.info, "*version", va("%4.2f", VERSION), MAX_SERVERINFO_STRING);
1369
1370	// init fraglog stuff
1371	svs.logsequence = 1;
1372	svs.logtime = realtime;
1373	svs.log[0].data = svs.log_buf[0];
1374	svs.log[0].maxsize = sizeof(svs.log_buf[0]);
1375	svs.log[0].cursize = 0;
1376	svs.log[0].allowoverflow = true;
1377	svs.log[1].data = svs.log_buf[1];
1378	svs.log[1].maxsize = sizeof(svs.log_buf[1]);
1379	svs.log[1].cursize = 0;
1380	svs.log[1].allowoverflow = true;
1381}
1382
1383
1384//============================================================================
1385
1386/*
1387================
1388Master_Heartbeat
1389
1390Send a message to the master every few minutes to
1391let it know we are alive, and log information
1392================
1393*/
1394#define	HEARTBEAT_SECONDS	300
1395void Master_Heartbeat (void)
1396{
1397	char		string[2048];
1398	int			active;
1399	int			i;
1400
1401	if (realtime - svs.last_heartbeat < HEARTBEAT_SECONDS)
1402		return;		// not time to send yet
1403
1404	svs.last_heartbeat = realtime;
1405
1406	//
1407	// count active users
1408	//
1409	active = 0;
1410	for (i=0 ; i<MAX_CLIENTS ; i++)
1411		if (svs.clients[i].state == cs_connected ||
1412		svs.clients[i].state == cs_spawned )
1413			active++;
1414
1415	svs.heartbeat_sequence++;
1416	sprintf (string, "%c\n%i\n%i\n", S2M_HEARTBEAT,
1417		svs.heartbeat_sequence, active);
1418
1419
1420	// send to group master
1421	for (i=0 ; i<MAX_MASTERS ; i++)
1422		if (master_adr[i].port)
1423		{
1424			Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
1425			NET_SendPacket (strlen(string), string, master_adr[i]);
1426		}
1427}
1428
1429/*
1430=================
1431Master_Shutdown
1432
1433Informs all masters that this server is going down
1434=================
1435*/
1436void Master_Shutdown (void)
1437{
1438	char		string[2048];
1439	int			i;
1440
1441	sprintf (string, "%c\n", S2M_SHUTDOWN);
1442
1443	// send to group master
1444	for (i=0 ; i<MAX_MASTERS ; i++)
1445		if (master_adr[i].port)
1446		{
1447			Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
1448			NET_SendPacket (strlen(string), string, master_adr[i]);
1449		}
1450}
1451
1452/*
1453=================
1454SV_ExtractFromUserinfo
1455
1456Pull specific info from a newly changed userinfo string
1457into a more C freindly form.
1458=================
1459*/
1460void SV_ExtractFromUserinfo (client_t *cl)
1461{
1462	char	*val, *p, *q;
1463	int		i;
1464	client_t	*client;
1465	int		dupc = 1;
1466	char	newname[80];
1467
1468
1469	// name for C code
1470	val = Info_ValueForKey (cl->userinfo, "name");
1471
1472	// trim user name
1473	strncpy(newname, val, sizeof(newname) - 1);
1474	newname[sizeof(newname) - 1] = 0;
1475
1476	for (p = newname; (*p == ' ' || *p == '\r' || *p == '\n') && *p; p++)
1477		;
1478
1479	if (p != newname && !*p) {
1480		//white space only
1481		strcpy(newname, "unnamed");
1482		p = newname;
1483	}
1484
1485	if (p != newname && *p) {
1486		for (q = newname; *p; *q++ = *p++)
1487			;
1488		*q = 0;
1489	}
1490	for (p = newname + strlen(newname) - 1; p != newname && (*p == ' ' || *p == '\r' || *p == '\n') ; p--)
1491		;
1492	p[1] = 0;
1493
1494	if (strcmp(val, newname)) {
1495		Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING);
1496		val = Info_ValueForKey (cl->userinfo, "name");
1497	}
1498
1499	if (!val[0] || !stricmp(val, "console")) {
1500		Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING);
1501		val = Info_ValueForKey (cl->userinfo, "name");
1502	}
1503
1504	// check to see if another user by the same name exists
1505	while (1) {
1506		for (i=0, client = svs.clients ; i<MAX_CLIENTS ; i++, client++) {
1507			if (client->state != cs_spawned || client == cl)
1508				continue;
1509			if (!stricmp(client->name, val))
1510				break;
1511		}
1512		if (i != MAX_CLIENTS) { // dup name
1513			if (strlen(val) > sizeof(cl->name) - 1)
1514				val[sizeof(cl->name) - 4] = 0;
1515			p = val;
1516
1517			if (val[0] == '(')
1518				if (val[2] == ')')
1519					p = val + 3;
1520				else if (val[3] == ')')
1521					p = val + 4;
1522
1523			sprintf(newname, "(%d)%-.40s", dupc++, p);
1524			Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING);
1525			val = Info_ValueForKey (cl->userinfo, "name");
1526		} else
1527			break;
1528	}
1529
1530	if (strncmp(val, cl->name, strlen(cl->name))) {
1531		if (!sv.paused) {
1532			if (!cl->lastnametime || realtime - cl->lastnametime > 5) {
1533				cl->lastnamecount = 0;
1534				cl->lastnametime = realtime;
1535			} else if (cl->lastnamecount++ > 4) {
1536				SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked for name spam\n", cl->name);
1537				SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game for name spamming\n");
1538				SV_DropClient (cl);
1539				return;
1540			}
1541		}
1542
1543		if (cl->state >= cs_spawned && !cl->spectator)
1544			SV_BroadcastPrintf (PRINT_HIGH, "%s changed name to %s\n", cl->name, val);
1545	}
1546
1547
1548	strncpy (cl->name, val, sizeof(cl->name)-1);
1549
1550	// rate command
1551	val = Info_ValueForKey (cl->userinfo, "rate");
1552	if (strlen(val))
1553	{
1554		i = atoi(val);
1555		if (i < 500)
1556			i = 500;
1557		if (i > 10000)
1558			i = 10000;
1559		cl->netchan.rate = 1.0/i;
1560	}
1561
1562	// msg command
1563	val = Info_ValueForKey (cl->userinfo, "msg");
1564	if (strlen(val))
1565	{
1566		cl->messagelevel = atoi(val);
1567	}
1568
1569}
1570
1571
1572//============================================================================
1573
1574/*
1575====================
1576SV_InitNet
1577====================
1578*/
1579void SV_InitNet (void)
1580{
1581	int	port;
1582	int	p;
1583
1584	port = PORT_SERVER;
1585	p = COM_CheckParm ("-port");
1586	if (p && p < com_argc)
1587	{
1588		port = atoi(com_argv[p+1]);
1589		Con_Printf ("Port: %i\n", port);
1590	}
1591	NET_Init (port);
1592
1593	Netchan_Init ();
1594
1595	// heartbeats will allways be sent to the id master
1596	svs.last_heartbeat = -99999;		// send immediately
1597//	NET_StringToAdr ("192.246.40.70:27000", &idmaster_adr);
1598}
1599
1600
1601/*
1602====================
1603SV_Init
1604====================
1605*/
1606void SV_Init (quakeparms_t *parms)
1607{
1608	COM_InitArgv (parms->argc, parms->argv);
1609	COM_AddParm ("-game");
1610	COM_AddParm ("qw");
1611
1612	if (COM_CheckParm ("-minmemory"))
1613		parms->memsize = MINIMUM_MEMORY;
1614
1615	host_parms = *parms;
1616
1617	if (parms->memsize < MINIMUM_MEMORY)
1618		SV_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000);
1619
1620	Memory_Init (parms->membase, parms->memsize);
1621	Cbuf_Init ();
1622	Cmd_Init ();
1623
1624	COM_Init ();
1625
1626	PR_Init ();
1627	Mod_Init ();
1628
1629	SV_InitNet ();
1630
1631	SV_InitLocal ();
1632	Sys_Init ();
1633	Pmove_Init ();
1634
1635	Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
1636	host_hunklevel = Hunk_LowMark ();
1637
1638	Cbuf_InsertText ("exec server.cfg\n");
1639
1640	host_initialized = true;
1641
1642	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
1643	Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
1644
1645	Con_Printf ("\nServer Version %4.2f (Build %04d)\n\n", VERSION, build_number());
1646
1647	Con_Printf ("======== QuakeWorld Initialized ========\n");
1648
1649// process command line arguments
1650	Cmd_StuffCmds_f ();
1651	Cbuf_Execute ();
1652
1653// if a map wasn't specified on the command line, spawn start.map
1654	if (sv.state == ss_dead)
1655		Cmd_ExecuteString ("map start");
1656	if (sv.state == ss_dead)
1657		SV_Error ("Couldn't spawn a server");
1658}
1659