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