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