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
23qboolean	sv_allow_cheats;
24
25int fp_messages=4, fp_persecond=4, fp_secondsdead=10;
26char fp_msg[255] = { 0 };
27extern cvar_t cl_warncmd;
28	extern		redirect_t	sv_redirected;
29
30
31/*
32===============================================================================
33
34OPERATOR CONSOLE ONLY COMMANDS
35
36These commands can only be entered from stdin or by a remote operator datagram
37===============================================================================
38*/
39
40/*
41====================
42SV_SetMaster_f
43
44Make a master server current
45====================
46*/
47void SV_SetMaster_f (void)
48{
49	char	data[2];
50	int		i;
51
52	memset (&master_adr, 0, sizeof(master_adr));
53
54	for (i=1 ; i<Cmd_Argc() ; i++)
55	{
56		if (!strcmp(Cmd_Argv(i), "none") || !NET_StringToAdr (Cmd_Argv(i), &master_adr[i-1]))
57		{
58			Con_Printf ("Setting nomaster mode.\n");
59			return;
60		}
61		if (master_adr[i-1].port == 0)
62			master_adr[i-1].port = BigShort (27000);
63
64		Con_Printf ("Master server at %s\n", NET_AdrToString (master_adr[i-1]));
65
66		Con_Printf ("Sending a ping.\n");
67
68		data[0] = A2A_PING;
69		data[1] = 0;
70		NET_SendPacket (2, data, master_adr[i-1]);
71	}
72
73	svs.last_heartbeat = -99999;
74}
75
76
77/*
78==================
79SV_Quit_f
80==================
81*/
82void SV_Quit_f (void)
83{
84	SV_FinalMessage ("server shutdown\n");
85	Con_Printf ("Shutting down.\n");
86	SV_Shutdown ();
87	Sys_Quit ();
88}
89
90/*
91============
92SV_Logfile_f
93============
94*/
95void SV_Logfile_f (void)
96{
97	char	name[MAX_OSPATH];
98
99	if (sv_logfile)
100	{
101		Con_Printf ("File logging off.\n");
102		fclose (sv_logfile);
103		sv_logfile = NULL;
104		return;
105	}
106
107	sprintf (name, "%s/qconsole.log", com_gamedir);
108	Con_Printf ("Logging text to %s.\n", name);
109	sv_logfile = fopen (name, "w");
110	if (!sv_logfile)
111		Con_Printf ("failed.\n");
112}
113
114
115/*
116============
117SV_Fraglogfile_f
118============
119*/
120void SV_Fraglogfile_f (void)
121{
122	char	name[MAX_OSPATH];
123	int		i;
124
125	if (sv_fraglogfile)
126	{
127		Con_Printf ("Frag file logging off.\n");
128		fclose (sv_fraglogfile);
129		sv_fraglogfile = NULL;
130		return;
131	}
132
133	// find an unused name
134	for (i=0 ; i<1000 ; i++)
135	{
136		sprintf (name, "%s/frag_%i.log", com_gamedir, i);
137		sv_fraglogfile = fopen (name, "r");
138		if (!sv_fraglogfile)
139		{	// can't read it, so create this one
140			sv_fraglogfile = fopen (name, "w");
141			if (!sv_fraglogfile)
142				i=1000;	// give error
143			break;
144		}
145		fclose (sv_fraglogfile);
146	}
147	if (i==1000)
148	{
149		Con_Printf ("Can't open any logfiles.\n");
150		sv_fraglogfile = NULL;
151		return;
152	}
153
154	Con_Printf ("Logging frags to %s.\n", name);
155}
156
157
158/*
159==================
160SV_SetPlayer
161
162Sets host_client and sv_player to the player with idnum Cmd_Argv(1)
163==================
164*/
165qboolean SV_SetPlayer (void)
166{
167	client_t	*cl;
168	int			i;
169	int			idnum;
170
171	idnum = atoi(Cmd_Argv(1));
172
173	for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
174	{
175		if (!cl->state)
176			continue;
177		if (cl->userid == idnum)
178		{
179			host_client = cl;
180			sv_player = host_client->edict;
181			return true;
182		}
183	}
184	Con_Printf ("Userid %i is not on the server\n", idnum);
185	return false;
186}
187
188
189/*
190==================
191SV_God_f
192
193Sets client to godmode
194==================
195*/
196void SV_God_f (void)
197{
198	if (!sv_allow_cheats)
199	{
200		Con_Printf ("You must run the server with -cheats to enable this command.\n");
201		return;
202	}
203
204	if (!SV_SetPlayer ())
205		return;
206
207	sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE;
208	if (!((int)sv_player->v.flags & FL_GODMODE) )
209		SV_ClientPrintf (host_client, PRINT_HIGH, "godmode OFF\n");
210	else
211		SV_ClientPrintf (host_client, PRINT_HIGH, "godmode ON\n");
212}
213
214
215void SV_Noclip_f (void)
216{
217	if (!sv_allow_cheats)
218	{
219		Con_Printf ("You must run the server with -cheats to enable this command.\n");
220		return;
221	}
222
223	if (!SV_SetPlayer ())
224		return;
225
226	if (sv_player->v.movetype != MOVETYPE_NOCLIP)
227	{
228		sv_player->v.movetype = MOVETYPE_NOCLIP;
229		SV_ClientPrintf (host_client, PRINT_HIGH, "noclip ON\n");
230	}
231	else
232	{
233		sv_player->v.movetype = MOVETYPE_WALK;
234		SV_ClientPrintf (host_client, PRINT_HIGH, "noclip OFF\n");
235	}
236}
237
238
239/*
240==================
241SV_Give_f
242==================
243*/
244void SV_Give_f (void)
245{
246	char	*t;
247	int		v;
248
249	if (!sv_allow_cheats)
250	{
251		Con_Printf ("You must run the server with -cheats to enable this command.\n");
252		return;
253	}
254
255	if (!SV_SetPlayer ())
256		return;
257
258	t = Cmd_Argv(2);
259	v = atoi (Cmd_Argv(3));
260
261	switch (t[0])
262	{
263	case '2':
264	case '3':
265	case '4':
266	case '5':
267	case '6':
268	case '7':
269	case '8':
270	case '9':
271		sv_player->v.items = (int)sv_player->v.items | IT_SHOTGUN<< (t[0] - '2');
272		break;
273
274	case 's':
275		sv_player->v.ammo_shells = v;
276		break;
277	case 'n':
278		sv_player->v.ammo_nails = v;
279		break;
280	case 'r':
281		sv_player->v.ammo_rockets = v;
282		break;
283	case 'h':
284		sv_player->v.health = v;
285		break;
286	case 'c':
287		sv_player->v.ammo_cells = v;
288		break;
289	}
290}
291
292
293/*
294======================
295SV_Map_f
296
297handle a
298map <mapname>
299command from the console or progs.
300======================
301*/
302void SV_Map_f (void)
303{
304	char	level[MAX_QPATH];
305	char	expanded[MAX_QPATH];
306	FILE	*f;
307
308	if (Cmd_Argc() != 2)
309	{
310		Con_Printf ("map <levelname> : continue game on a new level\n");
311		return;
312	}
313	strcpy (level, Cmd_Argv(1));
314
315#if 0
316	if (!strcmp (level, "e1m8"))
317	{	// QuakeWorld can't go to e1m8
318		SV_BroadcastPrintf (PRINT_HIGH, "can't go to low grav level in QuakeWorld...\n");
319		strcpy (level, "e1m5");
320	}
321#endif
322
323	// check to make sure the level exists
324	sprintf (expanded, "maps/%s.bsp", level);
325	COM_FOpenFile (expanded, &f);
326	if (!f)
327	{
328		Con_Printf ("Can't find %s\n", expanded);
329		return;
330	}
331	fclose (f);
332
333	SV_BroadcastCommand ("changing\n");
334	SV_SendMessagesToAll ();
335
336	SV_SpawnServer (level);
337
338	SV_BroadcastCommand ("reconnect\n");
339}
340
341
342/*
343==================
344SV_Kick_f
345
346Kick a user off of the server
347==================
348*/
349void SV_Kick_f (void)
350{
351	int			i;
352	client_t	*cl;
353	int			uid;
354
355	uid = atoi(Cmd_Argv(1));
356
357	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
358	{
359		if (!cl->state)
360			continue;
361		if (cl->userid == uid)
362		{
363			SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name);
364			// print directly, because the dropped client won't get the
365			// SV_BroadcastPrintf message
366			SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game\n");
367			SV_DropClient (cl);
368			return;
369		}
370	}
371
372	Con_Printf ("Couldn't find user number %i\n", uid);
373}
374
375
376/*
377================
378SV_Status_f
379================
380*/
381void SV_Status_f (void)
382{
383	int			i, j, l;
384	client_t	*cl;
385	float		cpu, avg, pak;
386	char		*s;
387
388
389	cpu = (svs.stats.latched_active+svs.stats.latched_idle);
390	if (cpu)
391		cpu = 100*svs.stats.latched_active/cpu;
392	avg = 1000*svs.stats.latched_active / STATFRAMES;
393	pak = (float)svs.stats.latched_packets/ STATFRAMES;
394
395	Con_Printf ("net address      : %s\n",NET_AdrToString (net_local_adr));
396	Con_Printf ("cpu utilization  : %3i%%\n",(int)cpu);
397	Con_Printf ("avg response time: %i ms\n",(int)avg);
398	Con_Printf ("packets/frame    : %5.2f (%d)\n", pak, num_prstr);
399
400// min fps lat drp
401	if (sv_redirected != RD_NONE) {
402		// most remote clients are 40 columns
403		//           0123456789012345678901234567890123456789
404		Con_Printf ("name               userid frags\n");
405        Con_Printf ("  address          rate ping drop\n");
406		Con_Printf ("  ---------------- ---- ---- -----\n");
407		for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
408		{
409			if (!cl->state)
410				continue;
411
412			Con_Printf ("%-16.16s  ", cl->name);
413
414			Con_Printf ("%6i %5i", cl->userid, (int)cl->edict->v.frags);
415			if (cl->spectator)
416				Con_Printf(" (s)\n");
417			else
418				Con_Printf("\n");
419
420			s = NET_BaseAdrToString ( cl->netchan.remote_address);
421			Con_Printf ("  %-16.16s", s);
422			if (cl->state == cs_connected)
423			{
424				Con_Printf ("CONNECTING\n");
425				continue;
426			}
427			if (cl->state == cs_zombie)
428			{
429				Con_Printf ("ZOMBIE\n");
430				continue;
431			}
432			Con_Printf ("%4i %4i %5.2f\n"
433				, (int)(1000*cl->netchan.frame_rate)
434				, (int)SV_CalcPing (cl)
435				, 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence);
436		}
437	} else {
438		Con_Printf ("frags userid address         name            rate ping drop  qport\n");
439		Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- -----\n");
440		for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
441		{
442			if (!cl->state)
443				continue;
444			Con_Printf ("%5i %6i ", (int)cl->edict->v.frags,  cl->userid);
445
446			s = NET_BaseAdrToString ( cl->netchan.remote_address);
447			Con_Printf ("%s", s);
448			l = 16 - strlen(s);
449			for (j=0 ; j<l ; j++)
450				Con_Printf (" ");
451
452			Con_Printf ("%s", cl->name);
453			l = 16 - strlen(cl->name);
454			for (j=0 ; j<l ; j++)
455				Con_Printf (" ");
456			if (cl->state == cs_connected)
457			{
458				Con_Printf ("CONNECTING\n");
459				continue;
460			}
461			if (cl->state == cs_zombie)
462			{
463				Con_Printf ("ZOMBIE\n");
464				continue;
465			}
466			Con_Printf ("%4i %4i %3.1f %4i"
467				, (int)(1000*cl->netchan.frame_rate)
468				, (int)SV_CalcPing (cl)
469				, 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence
470				, cl->netchan.qport);
471			if (cl->spectator)
472				Con_Printf(" (s)\n");
473			else
474				Con_Printf("\n");
475
476
477		}
478	}
479	Con_Printf ("\n");
480}
481
482/*
483==================
484SV_ConSay_f
485==================
486*/
487void SV_ConSay_f(void)
488{
489	client_t *client;
490	int		j;
491	char	*p;
492	char	text[1024];
493
494	if (Cmd_Argc () < 2)
495		return;
496
497	Q_strcpy (text, "console: ");
498	p = Cmd_Args();
499
500	if (*p == '"')
501	{
502		p++;
503		p[Q_strlen(p)-1] = 0;
504	}
505
506	Q_strcat(text, p);
507
508	for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++)
509	{
510		if (client->state != cs_spawned)
511			continue;
512		SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text);
513	}
514}
515
516
517/*
518==================
519SV_Heartbeat_f
520==================
521*/
522void SV_Heartbeat_f (void)
523{
524	svs.last_heartbeat = -9999;
525}
526
527void SV_SendServerInfoChange(char *key, char *value)
528{
529	if (!sv.state)
530		return;
531
532	MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo);
533	MSG_WriteString (&sv.reliable_datagram, key);
534	MSG_WriteString (&sv.reliable_datagram, value);
535}
536
537/*
538===========
539SV_Serverinfo_f
540
541  Examine or change the serverinfo string
542===========
543*/
544char *CopyString(char *s);
545void SV_Serverinfo_f (void)
546{
547	cvar_t	*var;
548
549	if (Cmd_Argc() == 1)
550	{
551		Con_Printf ("Server info settings:\n");
552		Info_Print (svs.info);
553		return;
554	}
555
556	if (Cmd_Argc() != 3)
557	{
558		Con_Printf ("usage: serverinfo [ <key> <value> ]\n");
559		return;
560	}
561
562	if (Cmd_Argv(1)[0] == '*')
563	{
564		Con_Printf ("Star variables cannot be changed.\n");
565		return;
566	}
567	Info_SetValueForKey (svs.info, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING);
568
569	// if this is a cvar, change it too
570	var = Cvar_FindVar (Cmd_Argv(1));
571	if (var)
572	{
573		Z_Free (var->string);	// free the old value string
574		var->string = CopyString (Cmd_Argv(2));
575		var->value = Q_atof (var->string);
576	}
577
578	SV_SendServerInfoChange(Cmd_Argv(1), Cmd_Argv(2));
579}
580
581
582/*
583===========
584SV_Serverinfo_f
585
586  Examine or change the serverinfo string
587===========
588*/
589char *CopyString(char *s);
590void SV_Localinfo_f (void)
591{
592	if (Cmd_Argc() == 1)
593	{
594		Con_Printf ("Local info settings:\n");
595		Info_Print (localinfo);
596		return;
597	}
598
599	if (Cmd_Argc() != 3)
600	{
601		Con_Printf ("usage: localinfo [ <key> <value> ]\n");
602		return;
603	}
604
605	if (Cmd_Argv(1)[0] == '*')
606	{
607		Con_Printf ("Star variables cannot be changed.\n");
608		return;
609	}
610	Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING);
611}
612
613
614/*
615===========
616SV_User_f
617
618Examine a users info strings
619===========
620*/
621void SV_User_f (void)
622{
623	if (Cmd_Argc() != 2)
624	{
625		Con_Printf ("Usage: info <userid>\n");
626		return;
627	}
628
629	if (!SV_SetPlayer ())
630		return;
631
632	Info_Print (host_client->userinfo);
633}
634
635/*
636================
637SV_Gamedir
638
639Sets the fake *gamedir to a different directory.
640================
641*/
642void SV_Gamedir (void)
643{
644	char			*dir;
645
646	if (Cmd_Argc() == 1)
647	{
648		Con_Printf ("Current *gamedir: %s\n", Info_ValueForKey (svs.info, "*gamedir"));
649		return;
650	}
651
652	if (Cmd_Argc() != 2)
653	{
654		Con_Printf ("Usage: sv_gamedir <newgamedir>\n");
655		return;
656	}
657
658	dir = Cmd_Argv(1);
659
660	if (strstr(dir, "..") || strstr(dir, "/")
661		|| strstr(dir, "\\") || strstr(dir, ":") )
662	{
663		Con_Printf ("*Gamedir should be a single filename, not a path\n");
664		return;
665	}
666
667	Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
668}
669
670/*
671================
672SV_Floodport_f
673
674Sets the gamedir and path to a different directory.
675================
676*/
677
678void SV_Floodprot_f (void)
679{
680	int arg1, arg2, arg3;
681
682	if (Cmd_Argc() == 1)
683	{
684		if (fp_messages) {
685			Con_Printf ("Current floodprot settings: \nAfter %d msgs per %d seconds, silence for %d seconds\n",
686				fp_messages, fp_persecond, fp_secondsdead);
687			return;
688		} else
689			Con_Printf ("No floodprots enabled.\n");
690	}
691
692	if (Cmd_Argc() != 4)
693	{
694		Con_Printf ("Usage: floodprot <# of messages> <per # of seconds> <seconds to silence>\n");
695		Con_Printf ("Use floodprotmsg to set a custom message to say to the flooder.\n");
696		return;
697	}
698
699	arg1 = atoi(Cmd_Argv(1));
700	arg2 = atoi(Cmd_Argv(2));
701	arg3 = atoi(Cmd_Argv(3));
702
703	if (arg1<=0 || arg2 <= 0 || arg3<=0) {
704		Con_Printf ("All values must be positive numbers\n");
705		return;
706	}
707
708	if (arg1 > 10) {
709		Con_Printf ("Can only track up to 10 messages.\n");
710		return;
711	}
712
713	fp_messages	= arg1;
714	fp_persecond = arg2;
715	fp_secondsdead = arg3;
716}
717
718void SV_Floodprotmsg_f (void)
719{
720	if (Cmd_Argc() == 1) {
721		Con_Printf("Current msg: %s\n", fp_msg);
722		return;
723	} else if (Cmd_Argc() != 2) {
724		Con_Printf("Usage: floodprotmsg \"<message>\"\n");
725		return;
726	}
727	sprintf(fp_msg, "%s", Cmd_Argv(1));
728}
729
730/*
731================
732SV_Gamedir_f
733
734Sets the gamedir and path to a different directory.
735================
736*/
737char	gamedirfile[MAX_OSPATH];
738void SV_Gamedir_f (void)
739{
740	char			*dir;
741
742	if (Cmd_Argc() == 1)
743	{
744		Con_Printf ("Current gamedir: %s\n", com_gamedir);
745		return;
746	}
747
748	if (Cmd_Argc() != 2)
749	{
750		Con_Printf ("Usage: gamedir <newdir>\n");
751		return;
752	}
753
754	dir = Cmd_Argv(1);
755
756	if (strstr(dir, "..") || strstr(dir, "/")
757		|| strstr(dir, "\\") || strstr(dir, ":") )
758	{
759		Con_Printf ("Gamedir should be a single filename, not a path\n");
760		return;
761	}
762
763	COM_Gamedir (dir);
764	Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
765}
766
767/*
768================
769SV_Snap
770================
771*/
772void SV_Snap (int uid)
773{
774	client_t *cl;
775	char		pcxname[80];
776	char		checkname[MAX_OSPATH];
777	int			i;
778
779	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
780	{
781		if (!cl->state)
782			continue;
783		if (cl->userid == uid)
784			break;
785	}
786	if (i >= MAX_CLIENTS) {
787		Con_Printf ("userid not found\n");
788		return;
789	}
790
791	sprintf(pcxname, "%d-00.pcx", uid);
792
793	sprintf(checkname, "%s/snap", gamedirfile);
794	Sys_mkdir(gamedirfile);
795	Sys_mkdir(checkname);
796
797	for (i=0 ; i<=99 ; i++)
798	{
799		pcxname[strlen(pcxname) - 6] = i/10 + '0';
800		pcxname[strlen(pcxname) - 5] = i%10 + '0';
801		sprintf (checkname, "%s/snap/%s", gamedirfile, pcxname);
802		if (Sys_FileTime(checkname) == -1)
803			break;	// file doesn't exist
804	}
805	if (i==100)
806	{
807		Con_Printf ("Snap: Couldn't create a file, clean some out.\n");
808		return;
809	}
810	strcpy(cl->uploadfn, checkname);
811
812	memcpy(&cl->snap_from, &net_from, sizeof(net_from));
813	if (sv_redirected != RD_NONE)
814		cl->remote_snap = true;
815	else
816		cl->remote_snap = false;
817
818	ClientReliableWrite_Begin (cl, svc_stufftext, 24);
819	ClientReliableWrite_String (cl, "cmd snap");
820	Con_Printf ("Requesting snap from user %d...\n", uid);
821}
822
823/*
824================
825SV_Snap_f
826================
827*/
828void SV_Snap_f (void)
829{
830	int			uid;
831
832	if (Cmd_Argc() != 2)
833	{
834		Con_Printf ("Usage:  snap <userid>\n");
835		return;
836	}
837
838	uid = atoi(Cmd_Argv(1));
839
840	SV_Snap(uid);
841}
842
843/*
844================
845SV_Snap
846================
847*/
848void SV_SnapAll_f (void)
849{
850	client_t *cl;
851	int			i;
852
853	for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
854	{
855		if (cl->state < cs_connected || cl->spectator)
856			continue;
857		SV_Snap(cl->userid);
858	}
859}
860
861/*
862==================
863SV_InitOperatorCommands
864==================
865*/
866void SV_InitOperatorCommands (void)
867{
868	if (COM_CheckParm ("-cheats"))
869	{
870		sv_allow_cheats = true;
871		Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING);
872	}
873
874	Cmd_AddCommand ("logfile", SV_Logfile_f);
875	Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f);
876
877	Cmd_AddCommand ("snap", SV_Snap_f);
878	Cmd_AddCommand ("snapall", SV_SnapAll_f);
879	Cmd_AddCommand ("kick", SV_Kick_f);
880	Cmd_AddCommand ("status", SV_Status_f);
881
882	Cmd_AddCommand ("map", SV_Map_f);
883	Cmd_AddCommand ("setmaster", SV_SetMaster_f);
884
885	Cmd_AddCommand ("say", SV_ConSay_f);
886	Cmd_AddCommand ("heartbeat", SV_Heartbeat_f);
887	Cmd_AddCommand ("quit", SV_Quit_f);
888	Cmd_AddCommand ("god", SV_God_f);
889	Cmd_AddCommand ("give", SV_Give_f);
890	Cmd_AddCommand ("noclip", SV_Noclip_f);
891	Cmd_AddCommand ("serverinfo", SV_Serverinfo_f);
892	Cmd_AddCommand ("localinfo", SV_Localinfo_f);
893	Cmd_AddCommand ("user", SV_User_f);
894	Cmd_AddCommand ("gamedir", SV_Gamedir_f);
895	Cmd_AddCommand ("sv_gamedir", SV_Gamedir);
896	Cmd_AddCommand ("floodprot", SV_Floodprot_f);
897	Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f);
898
899	cl_warncmd.value = 1;
900}
901