]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/gamecommand.qc
Remove the old help request system
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / gamecommand.qc
1 // =====================================================
2 //  Server side game commands code, reworked by Samual
3 //  Last updated: November 6th, 2011
4 // =====================================================
5
6 #define GC_REQUEST_COMMAND 1
7 #define GC_REQUEST_USAGE 2
8
9 float RadarMap_Make(float argc);
10
11 string GotoMap(string m);
12
13 void race_deleteTime(string map, float pos);
14
15
16 // ============================
17 //  Misc. Supporting Functions
18 // ============================
19
20 //  used by GameCommand_make_mapinfo()
21 void make_mapinfo_Think()
22 {
23         if(MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1))
24         {
25                 print("Done rebuiling mapinfos.\n");
26                 MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
27                 remove(self);
28         }
29         else
30         {
31                 self.think = make_mapinfo_Think;
32                 self.nextthink = time;
33         }
34 }
35
36 //  used by GameCommand_extendmatchtime() and GameCommand_reducematchtime()
37 void changematchtime(float delta, float mi, float ma)
38 {
39         float cur;
40         float new;
41         float lim;
42
43         if(delta == 0)
44                 return;
45         if(autocvar_timelimit < 0)
46                 return;
47
48         if(mi <= 10)
49                 mi = 10; // at least ten sec in the future
50         cur = time - game_starttime;
51         if(cur > 0)
52                 mi += cur; // from current time!
53
54         lim = autocvar_timelimit * 60;
55
56         if(delta > 0)
57         {
58                 if(lim == 0)
59                         return; // cannot increase any further
60                 else if(lim < ma)
61                         new = min(ma, lim + delta);
62                 else // already above maximum: FAIL
63                         return;
64         }
65         else
66         {
67                 if(lim == 0) // infinite: try reducing to max, if we are allowed to
68                         new = max(mi, ma);
69                 else if(lim > mi) // above minimum: decrease
70                         new = max(mi, lim + delta);
71                 else // already below minimum: FAIL
72                         return;
73         }
74
75         cvar_set("timelimit", ftos(new / 60));
76 }
77
78 //  used by GameCommand_modelbug() // TODO: is this even needed?
79 float g_clientmodel_genericsendentity (entity to, float sf);
80 void modelbug_make_svqc();
81 void modelbug_make_csqc()
82 {
83         Net_LinkEntity(self, TRUE, 0, g_clientmodel_genericsendentity);
84         self.think = modelbug_make_svqc;
85         self.nextthink = time + 1;
86         setorigin(self, self.origin - '0 0 8');
87 }
88 void modelbug_make_svqc()
89 {
90         self.SendEntity = func_null;
91         self.think = modelbug_make_csqc;
92         self.nextthink = time + 1;
93         setorigin(self, self.origin + '0 0 8');
94 }
95 void modelbug()
96 {
97         entity e;
98         e = spawn();
99         setorigin(e, nextent(world).origin);
100         precache_model("models_portal.md3");
101         setmodel(e, "models/portal.md3");
102         e.think = modelbug_make_svqc;
103         e.nextthink = time + 1;
104 }
105
106
107 // =======================
108 //  Command Sub-Functions
109 // =======================
110
111 void GameCommand_adminmsg(float request, float argc)
112 {
113         switch(request)
114         {
115                 case GC_REQUEST_COMMAND:
116                 {
117                         entity client;
118                         float entno = stof(argv(1)); 
119                         float n, i;
120                         string s;
121                         
122                         if(argc >= 3 && argc <= 4) {
123                                 if((entno < 0) | (entno > maxclients)) {
124                                         print("Player ", argv(1), " doesn't exist\n");
125                                         return;
126                                 }
127                                 n = 0;
128                                 for(i = (entno ? entno : 1); i <= (entno ? entno : maxclients); ++i)
129                                 {
130                                         client = edict_num(i);
131                                         if(client.flags & FL_CLIENT)
132                                         {
133                                                 if(argc == 4)
134                                                 {
135                                                         // make the string console safe
136                                                         s = argv(2);
137                                                         s = strreplace("\n", "", s);
138                                                         s = strreplace("\\", "\\\\", s);
139                                                         s = strreplace("$", "$$", s);
140                                                         s = strreplace("\"", "\\\"", s);
141                                                         stuffcmd(client, sprintf("\ninfobar %f \"%s\"\n", stof(argv(3)), s));
142                                                 }
143                                                 else
144                                                 {
145                                                         centerprint(client, strcat("^3", admin_name(), ":\n\n^7", argv(2)));
146                                                         sprint(client, strcat("\{1}\{13}^3", admin_name(), "^7: ", argv(2), "\n"));
147                                                 }
148                                                 dprint("Message sent to ", client.netname, "\n");
149                                                 ++n;
150                                         }
151                                 }
152                                 if(!n) { print(strcat("Client (", argv(1) ,") not found.\n")); } 
153                                 return;
154                         }
155                 }
156                 
157                 default:
158                         print("Incorrect parameters for ^2adminmsg^7\n");
159                 case GC_REQUEST_USAGE:
160                 {
161                         print("\nUsage:^3 sv_cmd adminmsg clientnumber \"message\" [infobartime]\n");
162                         print("  If infobartime is provided, the message will be sent to infobar.\n");
163                         print("  Otherwise, it will just be sent as a centerprint message.\n");
164                         print("Examples: adminmsg 4 \"this infomessage will last for ten seconds\" 10\n");
165                         print("          adminmsg 2 \"this message will be a centerprint\"\n");
166                         return;
167                 }
168         }
169 }
170
171 void GameCommand_allready(float request)
172 {
173         switch(request)
174         {
175                 case GC_REQUEST_COMMAND:
176                 {
177                         ReadyRestart();
178                         return;
179                 }
180                         
181                 default:
182                 case GC_REQUEST_USAGE:
183                 {
184                         print("\nUsage:^3 sv_cmd allready\n");
185                         print("  No arguments required.\n");
186                         return;
187                 }
188         }
189 }
190
191 void GameCommand_allspec(float request, float argc)
192 {       
193         switch(request)
194         {
195                 case GC_REQUEST_COMMAND:
196                 {
197                         entity client;
198                         string reason = argv(1);
199                         float i;
200                         
201                         FOR_EACH_PLAYER(client)
202                         {
203                                 self = client;
204                                 PutObserverInServer();
205                                 ++i;
206                         }
207                         if(i) { bprint(strcat("Successfully forced all (", ftos(i), ") players to spectate", (reason ? strcat(" for reason: '", reason, "'") : ""), ".\n")); }
208                         else { print("No players found to spectate.\n"); }
209                         return;
210                 }
211                         
212                 default:
213                 case GC_REQUEST_USAGE:
214                 {
215                         print("\nUsage:^3 sv_cmd allspec [reason]\n");
216                         print("  Where 'reason' is an optional argument for explanation of allspec command.\n");
217                         print("See also: ^2moveplayer^7\n");
218                         return;
219                 }
220         }
221 }
222
223 void GameCommand_anticheat(float request, float argc)
224 {
225         switch(request)
226         {
227                 case GC_REQUEST_COMMAND:
228                 {
229                         entity client;
230                         float entno = stof(argv(1)); 
231                         
232                         if((entno < 1) | (entno > maxclients)) {
233                                 print("Player ", argv(1), " doesn't exist\n");
234                                 return;
235                         }
236                         client = edict_num(entno);
237                         if(clienttype(client) != CLIENTTYPE_REAL || clienttype(client) != CLIENTTYPE_BOT) {
238                                 print("Player ", client.netname, " is not active\n");
239                                 return;
240                         }
241                         self = client;
242                         anticheat_report();
243                         return;
244                 }
245                         
246                 default:
247                         print("Incorrect parameters for ^2anticheat^7\n");
248                 case GC_REQUEST_USAGE:
249                 {
250                         print("\nUsage:^3 sv_cmd anticheat clientnumber\n");
251                         print("  where 'clientnumber' is player entity number.\n");
252                         return;
253                 }
254         }
255 }
256
257 void GameCommand_bbox(float request)
258 {
259         switch(request)
260         {
261                 case GC_REQUEST_COMMAND:
262                 {
263                         print("Original size: ", ftos(world.absmin_x), " ", ftos(world.absmin_y), " ", ftos(world.absmin_z));
264                         print(" ", ftos(world.absmax_x), " ", ftos(world.absmax_y), " ", ftos(world.absmax_z), "\n");
265                         print("Currently set size: ", ftos(world.mins_x), " ", ftos(world.mins_y), " ", ftos(world.mins_z));
266                         print(" ", ftos(world.maxs_x), " ", ftos(world.maxs_y), " ", ftos(world.maxs_z), "\n");
267                         print("Solid bounding box size:");
268
269                         tracebox('1 0 0' * world.absmin_x,
270                                                         '0 1 0' * world.absmin_y + '0 0 1' * world.absmin_z,
271                                                         '0 1 0' * world.absmax_y + '0 0 1' * world.absmax_z,
272                                                         '1 0 0' * world.absmax_x,
273                                         MOVE_WORLDONLY,
274                                         world);
275                         if(trace_startsolid)
276                                 print(" ", ftos(world.absmin_x));
277                         else
278                                 print(" ", ftos(trace_endpos_x));
279
280                         tracebox('0 1 0' * world.absmin_y,
281                                                         '1 0 0' * world.absmin_x + '0 0 1' * world.absmin_z,
282                                                         '1 0 0' * world.absmax_x + '0 0 1' * world.absmax_z,
283                                                         '0 1 0' * world.absmax_y,
284                                         MOVE_WORLDONLY,
285                                         world);
286                         if(trace_startsolid)
287                                 print(" ", ftos(world.absmin_y));
288                         else
289                                 print(" ", ftos(trace_endpos_y));
290
291                         tracebox('0 0 1' * world.absmin_z,
292                                                         '1 0 0' * world.absmin_x + '0 1 0' * world.absmin_y,
293                                                         '1 0 0' * world.absmax_x + '0 1 0' * world.absmax_y,
294                                                         '0 0 1' * world.absmax_z,
295                                         MOVE_WORLDONLY,
296                                         world);
297                         if(trace_startsolid)
298                                 print(" ", ftos(world.absmin_z));
299                         else
300                                 print(" ", ftos(trace_endpos_z));
301
302                         tracebox('1 0 0' * world.absmax_x,
303                                                         '0 1 0' * world.absmin_y + '0 0 1' * world.absmin_z,
304                                                         '0 1 0' * world.absmax_y + '0 0 1' * world.absmax_z,
305                                                         '1 0 0' * world.absmin_x,
306                                         MOVE_WORLDONLY,
307                                         world);
308                         if(trace_startsolid)
309                                 print(" ", ftos(world.absmax_x));
310                         else
311                                 print(" ", ftos(trace_endpos_x));
312
313                         tracebox('0 1 0' * world.absmax_y,
314                                                         '1 0 0' * world.absmin_x + '0 0 1' * world.absmin_z,
315                                                         '1 0 0' * world.absmax_x + '0 0 1' * world.absmax_z,
316                                                         '0 1 0' * world.absmin_y,
317                                         MOVE_WORLDONLY,
318                                         world);
319                         if(trace_startsolid)
320                                 print(" ", ftos(world.absmax_y));
321                         else
322                                 print(" ", ftos(trace_endpos_y));
323
324                         tracebox('0 0 1' * world.absmax_z,
325                                                         '1 0 0' * world.absmin_x + '0 1 0' * world.absmin_y,
326                                                         '1 0 0' * world.absmax_x + '0 1 0' * world.absmax_y,
327                                                         '0 0 1' * world.absmin_z,
328                                         MOVE_WORLDONLY,
329                                         world);
330                         if(trace_startsolid)
331                                 print(" ", ftos(world.absmax_z));
332                         else
333                                 print(" ", ftos(trace_endpos_z));
334                                 
335                         print("\n");
336                         return;
337                 }
338                         
339                 default:
340                 case GC_REQUEST_USAGE:
341                 {
342                         print("\nUsage:^3 sv_cmd bbox\n");
343                         print("  No arguments required.\n");
344                         print("See also: ^2gettaginfo^7\n");
345                         return;
346                 }
347         }
348 }
349
350 void GameCommand_bot_cmd(float request, float argc) // what a mess... old old code.
351 {
352         switch(request)
353         {
354                 case GC_REQUEST_COMMAND:
355                 {
356                         entity bot;
357                         
358                         if(argv(1) == "reset")
359                         {
360                                 bot_resetqueues();
361                                 return;
362                         }
363                         else if(argv(1) == "load" && argc == 3)
364                         {
365                                 float fh, i;
366                                 string s;
367                                 fh = fopen(argv(2), FILE_READ);
368                                 if(fh < 0)
369                                 {
370                                         print("cannot open the file\n");
371                                         return;
372                                 }
373
374                                 i = 0;
375                                 while((s = fgets(fh)))
376                                 {
377                                         argc = tokenize_console(s);
378
379                                         if(argc >= 3 && argv(0) == "sv_cmd" && argv(1) == "bot_cmd")
380                                         {
381                                                 if(argv(2) == "reset")
382                                                 {
383                                                         bot_resetqueues();
384                                                 }
385                                                 else if(argv(2) == "setbots")
386                                                 {
387                                                         cvar_settemp("minplayers", "0");
388                                                         cvar_settemp("bot_number", argv(3));
389                                                         if(!bot_fixcount())
390                                                                 print("Sorry, could not set requested bot count\n");
391                                                 }
392                                                 else
393                                                 {
394                                                         // let's start at token 2 so we can skip sv_cmd bot_cmd
395                                                         bot = find_bot_by_number(stof(argv(2)));
396                                                         if(bot == world)
397                                                                 bot = find_bot_by_name(argv(2));
398                                                         if(bot)
399                                                                 bot_queuecommand(bot, strcat(argv(3), " ", argv(4)));
400                                                 }
401                                         }
402                                         else
403                                                 localcmd(strcat(s, "\n"));
404
405                                         ++i;
406                                 }
407                                 print(ftos(i), " commands read\n");
408                                 fclose(fh);
409                                 return;
410                         }
411                         else if(argv(1) == "help")
412                         {
413                                 if(argv(2))
414                                         bot_cmdhelp(argv(2));
415                                 else
416                                         bot_list_commands();
417                                 return;
418                         }
419                         else if(argc >= 3) // this comes last
420                         {
421                                 bot = find_bot_by_number(stof(argv(1)));
422                                 if(bot == world)
423                                         bot = find_bot_by_name(argv(1));
424                                 if(bot)
425                                 {
426                                         print(strcat("Command '", strcat(argv(2), " ", argv(3)), "' sent to bot ", bot.netname, "\n"));
427                                         bot_queuecommand(bot, strcat(argv(2), " ", argv(3)));
428                                         return;
429                                 }
430                                 else
431                                         print(strcat("Error: Can't find bot with the name or id '", argv(1),"' - Did you mistype the command?\n")); // don't return so that usage is shown
432                         }
433                 }
434                         
435                 default:
436                         print("Incorrect parameters for ^2bot_cmd^7\n");
437                 case GC_REQUEST_USAGE:
438                 {
439                         print("\nUsage:^3 sv_cmd bot_cmd client command [argument]\n");
440                         print("  'client' can be either the name or entity id of the bot\n");
441                         print("  For full list of commands, see bot_cmd help [command].\n");
442                         print("Examples: bot_cmd <id> cc \"say something\"\n");
443                         print("          bot_cmd <id> presskey jump\n");
444                         return;
445                 }
446         }
447 }
448
449 void GameCommand_cointoss(float request, float argc)
450 {
451         switch(request)
452         {
453                 case GC_REQUEST_COMMAND:
454                 {
455                         entity client;
456                         string result1 = (argv(2) ? strcat("^7", argv(1), "^3!\n") : "^1HEADS^3!\n");
457                         string result2 = (argv(2) ? strcat("^7", argv(2), "^3!\n") : "^4TAILS^3!\n");
458                         string choice = ((random() > 0.5) ? result1 : result2);
459                         
460                         FOR_EACH_CLIENT(client)
461                                 centerprint(client, strcat("^3Throwing coin... Result: ", choice));
462                         bprint(strcat("^3Throwing coin... Result: ", choice));
463                         return;
464                 }
465                 
466                 default:
467                 case GC_REQUEST_USAGE:
468                 {
469                         print("\nUsage:^3 sv_cmd cointoss [result1 result2]\n");
470                         print("  Where 'result1' and 'result2' are user created options.\n");
471                         return;
472                 }
473         }
474 }
475
476 void GameCommand_cvar_changes(float request)
477 {
478         switch(request)
479         {
480                 case GC_REQUEST_COMMAND:
481                 {
482                         print(cvar_changes);
483                         return;
484                 }
485                         
486                 default:
487                 case GC_REQUEST_USAGE:
488                 {
489                         print("\nUsage:^3 sv_cmd cvar_changes\n");
490                         print("  No arguments required.\n");
491                         print("See also: ^2cvar_purechanges^7\n");
492                         return;
493                 }
494         }
495 }
496
497 void GameCommand_cvar_purechanges(float request)
498 {
499         switch(request)
500         {
501                 case GC_REQUEST_COMMAND:
502                 {
503                         print(cvar_purechanges);
504                         return;
505                 }
506                         
507                 default:
508                 case GC_REQUEST_USAGE:
509                 {
510                         print("\nUsage:^3 sv_cmd cvar_purechanges\n");
511                         print("  No arguments required.\n");
512                         print("See also: ^2cvar_changes^7\n");
513                         return;
514                 }
515         }
516 }
517
518 void GameCommand_database(float request, float argc)
519 {
520         switch(request)
521         {
522                 case GC_REQUEST_COMMAND:
523                 {
524                         if(argc == 3)
525                         {
526                                 if(argv(1) == "save")
527                                 {
528                                         db_save(ServerProgsDB, argv(2));
529                                         print(strcat("Copied serverprogs database to '", argv(2), "' in the data directory.\n"));
530                                         return;
531                                 }
532                                 else if(argv(1) == "dump")
533                                 {
534                                         db_dump(ServerProgsDB, argv(2));
535                                         print("DB dumped.\n"); // wtf does this do?
536                                         return;
537                                 }
538                                 else if(argv(1) == "load")
539                                 {
540                                         db_close(ServerProgsDB);
541                                         ServerProgsDB = db_load(argv(2));
542                                         print(strcat("Loaded '", argv(2), "' as new serverprogs database.\n"));
543                                         return;
544                                 }
545                         }
546                 }
547                         
548                 default:
549                         print("Incorrect parameters for ^2database^7\n");
550                 case GC_REQUEST_USAGE:
551                 {
552                         print("\nUsage:^3 sv_cmd database action filename\n");
553                         print("  Where 'action' is the command to complete,\n");
554                         print("  and 'filename' is what it acts upon.\n");
555                         print("  Full list of commands here: \"save, dump, load.\"\n");
556                         return;
557                 }
558         }
559 }
560
561 void GameCommand_defer_clear(float request, float argc)
562 {       
563         switch(request)
564         {
565                 case GC_REQUEST_COMMAND:
566                 {
567                         entity client;
568                         float entno = stof(argv(1));
569                         
570                         if(argc == 2)
571                         {
572                                 // player_id is out of range
573                                 if((entno < 1) | (entno > maxclients)) {
574                                         print("Player ", argv(1), " doesn't exist\n");
575                                         return;
576                                 }
577                                 client = edict_num(entno);
578                                 if not(client.flags & FL_CLIENT) {
579                                         print("Player ", argv(1), " doesn't exist\n");
580                                         return;
581                                 }
582                                 if(clienttype(client) == CLIENTTYPE_BOT) {
583                                         print("Player ", argv(1), " (", client.netname, ") is a bot\n");
584                                         return;
585                                 }
586                                 stuffcmd(client, "defer clear\n");
587                                 print("defer clear stuffed to ", argv(1), " (", client.netname, ")\n");
588                                 return;
589                         }
590                 }
591                 
592                 default:
593                         print("Incorrect parameters for ^2defer_clear^7\n");
594                 case GC_REQUEST_USAGE:
595                 {
596                         print("\nUsage:^3 sv_cmd defer_clear clientnumber\n");
597                         print("  where 'clientnumber' is player entity number.\n");
598                         print("See also: ^2defer_clear_all^7\n");
599                         return;
600                 }
601         }
602 }
603
604 void GameCommand_defer_clear_all(float request)
605 {       
606         switch(request)
607         {
608                 case GC_REQUEST_COMMAND:
609                 {
610                         entity client;
611                         float i;
612                         float argc;
613                         
614                         FOR_EACH_CLIENT(client)
615                         {
616                                 argc = tokenize_console(strcat("defer_clear ", ftos(num_for_edict(client))));
617                                 GameCommand_defer_clear(GC_REQUEST_COMMAND, argc);      
618                                 ++i;
619                         }
620                         if(i) { bprint(strcat("Successfully stuffed defer clear to all clients (", ftos(i), ")\n")); } // should a message be added if no players were found? 
621                         return;
622                 }
623                 
624                 default:
625                 case GC_REQUEST_USAGE:
626                 {
627                         print("\nUsage:^3 sv_cmd defer_clear_all\n");
628                         print("  No arguments required.\n");
629                         print("See also: ^2defer_clear^7\n");
630                         return;
631                 }
632         }
633 }
634
635 void GameCommand_delrec(float request, float argc) // UNTESTED // perhaps merge later with records and printstats and such?
636 {
637         switch(request)
638         {
639                 case GC_REQUEST_COMMAND:
640                 {
641                         if(argv(1))
642                         {
643                                 if(argv(2))
644                                         race_deleteTime(argv(2), stof(argv(1)));
645                                 else
646                                         race_deleteTime(GetMapname(), stof(argv(1)));
647                                 return;
648                         }
649                 }       
650                 
651                 default:
652                         print("Incorrect parameters for ^2delrec^7\n");
653                 case GC_REQUEST_USAGE:
654                 {
655                         print("\nUsage:^3 sv_cmd delrec ranking [map]\n");
656                         print("  'ranking' is which ranking level to clear up to, \n");
657                         print("  it will clear all records up to nth place.\n");
658                         print("  if 'map' is not provided it will use current map.\n");
659                         return;
660                 }
661         }
662 }
663
664 void GameCommand_effectindexdump(float request)
665 {
666         switch(request)
667         {
668                 case GC_REQUEST_COMMAND:
669                 {
670                         float fh, d;
671                         string s;
672                         
673                         d = db_create();
674                         print("begin of effects list\n");
675                         db_put(d, "TE_GUNSHOT", "1"); print("effect TE_GUNSHOT is ", ftos(particleeffectnum("TE_GUNSHOT")), "\n");
676                         db_put(d, "TE_GUNSHOTQUAD", "1"); print("effect TE_GUNSHOTQUAD is ", ftos(particleeffectnum("TE_GUNSHOTQUAD")), "\n");
677                         db_put(d, "TE_SPIKE", "1"); print("effect TE_SPIKE is ", ftos(particleeffectnum("TE_SPIKE")), "\n");
678                         db_put(d, "TE_SPIKEQUAD", "1"); print("effect TE_SPIKEQUAD is ", ftos(particleeffectnum("TE_SPIKEQUAD")), "\n");
679                         db_put(d, "TE_SUPERSPIKE", "1"); print("effect TE_SUPERSPIKE is ", ftos(particleeffectnum("TE_SUPERSPIKE")), "\n");
680                         db_put(d, "TE_SUPERSPIKEQUAD", "1"); print("effect TE_SUPERSPIKEQUAD is ", ftos(particleeffectnum("TE_SUPERSPIKEQUAD")), "\n");
681                         db_put(d, "TE_WIZSPIKE", "1"); print("effect TE_WIZSPIKE is ", ftos(particleeffectnum("TE_WIZSPIKE")), "\n");
682                         db_put(d, "TE_KNIGHTSPIKE", "1"); print("effect TE_KNIGHTSPIKE is ", ftos(particleeffectnum("TE_KNIGHTSPIKE")), "\n");
683                         db_put(d, "TE_EXPLOSION", "1"); print("effect TE_EXPLOSION is ", ftos(particleeffectnum("TE_EXPLOSION")), "\n");
684                         db_put(d, "TE_EXPLOSIONQUAD", "1"); print("effect TE_EXPLOSIONQUAD is ", ftos(particleeffectnum("TE_EXPLOSIONQUAD")), "\n");
685                         db_put(d, "TE_TAREXPLOSION", "1"); print("effect TE_TAREXPLOSION is ", ftos(particleeffectnum("TE_TAREXPLOSION")), "\n");
686                         db_put(d, "TE_TELEPORT", "1"); print("effect TE_TELEPORT is ", ftos(particleeffectnum("TE_TELEPORT")), "\n");
687                         db_put(d, "TE_LAVASPLASH", "1"); print("effect TE_LAVASPLASH is ", ftos(particleeffectnum("TE_LAVASPLASH")), "\n");
688                         db_put(d, "TE_SMALLFLASH", "1"); print("effect TE_SMALLFLASH is ", ftos(particleeffectnum("TE_SMALLFLASH")), "\n");
689                         db_put(d, "TE_FLAMEJET", "1"); print("effect TE_FLAMEJET is ", ftos(particleeffectnum("TE_FLAMEJET")), "\n");
690                         db_put(d, "EF_FLAME", "1"); print("effect EF_FLAME is ", ftos(particleeffectnum("EF_FLAME")), "\n");
691                         db_put(d, "TE_BLOOD", "1"); print("effect TE_BLOOD is ", ftos(particleeffectnum("TE_BLOOD")), "\n");
692                         db_put(d, "TE_SPARK", "1"); print("effect TE_SPARK is ", ftos(particleeffectnum("TE_SPARK")), "\n");
693                         db_put(d, "TE_PLASMABURN", "1"); print("effect TE_PLASMABURN is ", ftos(particleeffectnum("TE_PLASMABURN")), "\n");
694                         db_put(d, "TE_TEI_G3", "1"); print("effect TE_TEI_G3 is ", ftos(particleeffectnum("TE_TEI_G3")), "\n");
695                         db_put(d, "TE_TEI_SMOKE", "1"); print("effect TE_TEI_SMOKE is ", ftos(particleeffectnum("TE_TEI_SMOKE")), "\n");
696                         db_put(d, "TE_TEI_BIGEXPLOSION", "1"); print("effect TE_TEI_BIGEXPLOSION is ", ftos(particleeffectnum("TE_TEI_BIGEXPLOSION")), "\n");
697                         db_put(d, "TE_TEI_PLASMAHIT", "1"); print("effect TE_TEI_PLASMAHIT is ", ftos(particleeffectnum("TE_TEI_PLASMAHIT")), "\n");
698                         db_put(d, "EF_STARDUST", "1"); print("effect EF_STARDUST is ", ftos(particleeffectnum("EF_STARDUST")), "\n");
699                         db_put(d, "TR_ROCKET", "1"); print("effect TR_ROCKET is ", ftos(particleeffectnum("TR_ROCKET")), "\n");
700                         db_put(d, "TR_GRENADE", "1"); print("effect TR_GRENADE is ", ftos(particleeffectnum("TR_GRENADE")), "\n");
701                         db_put(d, "TR_BLOOD", "1"); print("effect TR_BLOOD is ", ftos(particleeffectnum("TR_BLOOD")), "\n");
702                         db_put(d, "TR_WIZSPIKE", "1"); print("effect TR_WIZSPIKE is ", ftos(particleeffectnum("TR_WIZSPIKE")), "\n");
703                         db_put(d, "TR_SLIGHTBLOOD", "1"); print("effect TR_SLIGHTBLOOD is ", ftos(particleeffectnum("TR_SLIGHTBLOOD")), "\n");
704                         db_put(d, "TR_KNIGHTSPIKE", "1"); print("effect TR_KNIGHTSPIKE is ", ftos(particleeffectnum("TR_KNIGHTSPIKE")), "\n");
705                         db_put(d, "TR_VORESPIKE", "1"); print("effect TR_VORESPIKE is ", ftos(particleeffectnum("TR_VORESPIKE")), "\n");
706                         db_put(d, "TR_NEHAHRASMOKE", "1"); print("effect TR_NEHAHRASMOKE is ", ftos(particleeffectnum("TR_NEHAHRASMOKE")), "\n");
707                         db_put(d, "TR_NEXUIZPLASMA", "1"); print("effect TR_NEXUIZPLASMA is ", ftos(particleeffectnum("TR_NEXUIZPLASMA")), "\n");
708                         db_put(d, "TR_GLOWTRAIL", "1"); print("effect TR_GLOWTRAIL is ", ftos(particleeffectnum("TR_GLOWTRAIL")), "\n");
709                         db_put(d, "TR_SEEKER", "1"); print("effect TR_SEEKER is ", ftos(particleeffectnum("TR_SEEKER")), "\n");
710                         db_put(d, "SVC_PARTICLE", "1"); print("effect SVC_PARTICLE is ", ftos(particleeffectnum("SVC_PARTICLE")), "\n");
711
712                         fh = fopen("effectinfo.txt", FILE_READ);
713                         while((s = fgets(fh)))
714                         {
715                                 tokenize_console(s);
716                                 if(argv(0) == "effect")
717                                 {
718                                         if(db_get(d, argv(1)) != "1")
719                                         {
720                                                 if(particleeffectnum(argv(1)) >= 0)
721                                                         print("effect ", argv(1), " is ", ftos(particleeffectnum(argv(1))), "\n");
722                                                 db_put(d, argv(1), "1");
723                                         }
724                                 }
725                         }
726                         print("end of effects list\n");
727
728                         db_close(d);
729                         return;
730                 }
731                         
732                 default:
733                 case GC_REQUEST_USAGE:
734                 {
735                         print("\nUsage:^3 sv_cmd effectindexdump\n");
736                         print("  No arguments required.\n");
737                         return;
738                 }
739         }
740 }
741
742 void GameCommand_extendmatchtime(float request)
743 {
744         switch(request)
745         {
746                 case GC_REQUEST_COMMAND:
747                 {
748                         changematchtime(autocvar_timelimit_increment* 60, autocvar_timelimit_min*60, autocvar_timelimit_max*60);
749                         return;
750                 }
751                         
752                 default:
753                 case GC_REQUEST_USAGE:
754                 {
755                         print("\nUsage:^3 sv_cmd extendmatchtime\n");
756                         print("  No arguments required.\n");
757                         print("See also: ^2reducematchtime^7\n");
758                         return;
759                 }
760         }
761 }
762
763 void GameCommand_find(float request, float argc)
764 {       
765         switch(request)
766         {
767                 case GC_REQUEST_COMMAND:
768                 {
769                         entity client;
770                         
771                         for(client = world; (client = find(client, classname, argv(1))); )
772                                 print(etos(client), "\n");
773                                 
774                         return;
775                 }
776                         
777                 default:
778                         print("Incorrect parameters for ^2find^7\n");
779                 case GC_REQUEST_USAGE:
780                 {
781                         print("\nUsage:^3 sv_cmd find classname\n");
782                         print("  Where 'classname' is the classname to search for.\n");
783                         return;
784                 }
785         }
786 }
787
788 void GameCommand_gametype(float request, float argc)
789 {       
790         switch(request)
791         {
792                 case GC_REQUEST_COMMAND:
793                 {
794                         string s = argv(1);
795                         float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype();
796                         
797                         if(t)
798                         {
799                                 MapInfo_SwitchGameType(t);
800                                 MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
801                                 if(MapInfo_count > 0)
802                                         bprint("Game type successfully switched to ", s, "\n");
803                                 else
804                                 {
805                                         bprint("Cannot use this game type: no map for it found\n");
806                                         MapInfo_SwitchGameType(tsave);
807                                         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
808                                 }
809                         }
810                         else
811                                 bprint("Game type switch to ", s, " failed: this type does not exist!\n");
812                         return;
813                 }
814                         
815                 default:
816                         print("Incorrect parameters for ^2gametype^7\n");
817                 case GC_REQUEST_USAGE:
818                 {
819                         print("\nUsage:^3 sv_cmd gametype mode\n");
820                         print("  Where 'mode' is the gametype mode to switch to.\n");
821                         print("See also: ^2gotomap^7\n");
822                         return;
823                 }
824         }
825 }
826
827 void GameCommand_gettaginfo(float request, float argc) // UNTESTED // todo: finish usage description for it (but, must first learn this shit)
828 {       
829         switch(request)
830         {
831                 case GC_REQUEST_COMMAND:
832                 {
833                         entity tmp_entity;
834                         float i;
835                         vector v;
836                         
837                         if(argc >= 4)
838                         {
839                                 tmp_entity = spawn();
840                                 if(argv(1) == "w")
841                                         setmodel(tmp_entity, (nextent(world)).weaponentity.model);
842                                 else
843                                 {
844                                         precache_model(argv(1));
845                                         setmodel(tmp_entity, argv(1));
846                                 }
847                                 tmp_entity.frame = stof(argv(2));
848                                 if(substring(argv(3), 0, 1) == "#")
849                                         i = stof(substring(argv(3), 1, -1));
850                                 else
851                                         i = gettagindex(tmp_entity, argv(3));
852                                 if(i)
853                                 {
854                                         v = gettaginfo(tmp_entity, i);
855                                         print("model ", tmp_entity.model, " frame ", ftos(tmp_entity.frame), " tag ", gettaginfo_name);
856                                         print(" index ", ftos(i), " parent ", ftos(gettaginfo_parent), "\n");
857                                         print(" vector = ", ftos(v_x), " ", ftos(v_y), " ", ftos(v_z), "\n");
858                                         print(" offset = ", ftos(gettaginfo_offset_x), " ", ftos(gettaginfo_offset_y), " ", ftos(gettaginfo_offset_z), "\n");
859                                         print(" forward = ", ftos(gettaginfo_forward_x), " ", ftos(gettaginfo_forward_y), " ", ftos(gettaginfo_forward_z), "\n");
860                                         print(" right = ", ftos(gettaginfo_right_x), " ", ftos(gettaginfo_right_y), " ", ftos(gettaginfo_right_z), "\n");
861                                         print(" up = ", ftos(gettaginfo_up_x), " ", ftos(gettaginfo_up_y), " ", ftos(gettaginfo_up_z), "\n");
862                                         if(argc >= 6)
863                                         {
864                                                 v_y = -v_y;
865                                                 localcmd(strcat(argv(4), vtos(v), argv(5), "\n"));
866                                         }
867                                 }
868                                 else
869                                         print("bone not found\n");
870                                         
871                                 remove(tmp_entity);
872                                 return;
873                         }
874                 }
875                         
876                 default:
877                         print("Incorrect parameters for ^2gettaginfo^7\n");
878                 case GC_REQUEST_USAGE:
879                 {
880                         print("\nUsage:^3 sv_cmd gettaginfo\n");
881                         print("  FIXME: Arguments currently unknown\n");
882                         print("See also: ^2bbox^7\n");
883                         return;
884                 }
885         }
886 }
887
888 void GameCommand_gotomap(float request, float argc)
889 {
890         switch(request)
891         {
892                 case GC_REQUEST_COMMAND:
893                 {
894                         if(argc == 2)
895                         {
896                                 print(GotoMap(argv(1)), "\n");
897                                 return;
898                         }
899                 }
900                         
901                 default:
902                         print("Incorrect parameters for ^2gotomap^7\n");
903                 case GC_REQUEST_USAGE:
904                 {
905                         print("\nUsage:^3 sv_cmd gotomap map\n");
906                         print("  Where 'map' is the *.bsp file to change to.\n");
907                         print("See also: ^2gametype^7\n");
908                         return;
909                 }
910         }
911 }
912
913 void GameCommand_ladder(float request)
914 {
915         switch(request)
916         {
917                 case GC_REQUEST_COMMAND:
918                 {
919                         print(ladder_reply);
920                         return;
921                 }
922                         
923                 default:
924                 case GC_REQUEST_USAGE:
925                 {
926                         print("\nUsage:^3 sv_cmd ladder\n");
927                         print("  No arguments required.\n");
928                         return;
929                 }
930         }
931 }
932
933 void GameCommand_lockteams(float request)
934 {
935         switch(request)
936         {
937                 case GC_REQUEST_COMMAND:
938                 {
939                         if(teamplay)
940                         {
941                                 lockteams = 1;
942                                 bprint("^1The teams are now locked.\n");
943                         }
944                         else
945                                 bprint("That command can only be used in a team-based gamemode.\n");
946                         return;
947                 }
948                         
949                 default:
950                 case GC_REQUEST_USAGE:
951                 {
952                         print("\nUsage:^3 sv_cmd lockteams\n");
953                         print("  No arguments required.\n");
954                         print("See also: ^2unlockteams^7\n");
955                         return;
956                 }
957         }
958 }
959
960 void GameCommand_make_mapinfo(float request) // UNTESTED
961 {
962         switch(request)
963         {
964                 case GC_REQUEST_COMMAND:
965                 { 
966                         entity tmp_entity;
967                         
968                         tmp_entity = spawn();
969                         tmp_entity.classname = "make_mapinfo";
970                         tmp_entity.think = make_mapinfo_Think;
971                         tmp_entity.nextthink = time; // this sucks... todo: re-write this -- Use initializeentity later
972                         MapInfo_Enumerate();
973                         return;
974                 }
975                         
976                 default:
977                 case GC_REQUEST_USAGE:
978                 {
979                         print("\nUsage:^3 sv_cmd make_mapinfo\n");
980                         print("  No arguments required.\n");
981                         return;
982                 }
983         }
984 }
985
986 void GameCommand_modelbug(float request) // UNTESTED // is this even needed anymore? 
987 {
988         switch(request)
989         {
990                 case GC_REQUEST_COMMAND:
991                 {
992                         modelbug();
993                         return;
994                 }
995                         
996                 default:
997                 case GC_REQUEST_USAGE:
998                 {
999                         print("\nUsage:^3 sv_cmd modelbug\n");
1000                         print("  No arguments required.\n");
1001                         return;
1002                 }
1003         }
1004 }
1005
1006 void GameCommand_moveplayer(float request, float argc)
1007 {
1008         switch(request)
1009         {
1010                 case GC_REQUEST_COMMAND:
1011                 {
1012                         entity client;
1013         
1014                         string targets = strreplace(",", " ", argv(1));
1015                         string original_targets = strreplace(" ", ", ", targets);
1016                         string destination = argv(2);
1017                         string notify = argv(3);
1018                         
1019                         string successful, t;
1020                         
1021                         // lets see if the target(s) even actually exist.
1022                         if((targets) && (destination))
1023                         { 
1024                                 for(;targets;)
1025                                 {
1026                                         t = car(targets); targets = cdr(targets);
1027
1028                                         // Check to see if the player is a valid target
1029                                         if((GetFilteredNumber(t) < 1) || (GetFilteredNumber(t) > maxclients)) // player_id is out of range
1030                                         {
1031                                                 print("Player ", ftos(GetFilteredNumber(t)), " doesn't exist (out of range)", (targets ? ", skipping to next player.\n" : ".\n"));
1032                                                 continue; 
1033                                         }
1034                                         client = edict_num(GetFilteredNumber(t));
1035                                         if not(client.flags & FL_CLIENT) // player entity is not a client
1036                                         {
1037                                                 print("Player ", ftos(GetFilteredNumber(t)), " doesn't exist (not a client)", (targets ? ", skipping to next player.\n" : ".\n"));
1038                                                 continue;
1039                                         }
1040                                         
1041                                         // Where are we putting this player?
1042                                         if(destination == "spec" || destination == "spectator") 
1043                                         {
1044                                                 if(client.classname != "spectator" && client.classname != "observer")
1045                                                 {
1046                                                         self = client;
1047                                                         PutObserverInServer();
1048                                                         
1049                                                         successful = strcat(successful, (successful ? ", " : ""), client.netname);
1050                                                 }
1051                                                 else
1052                                                 {
1053                                                         print("Player ", ftos(GetFilteredNumber(t)), " (", client.netname, ") is already spectating.\n");
1054                                                 }
1055                                                 continue;
1056                                         }
1057                                         else
1058                                         {
1059                                                 if(client.classname != "spectator" && client.classname != "observer")
1060                                                 {
1061                                                         if(teamplay)
1062                                                         {
1063                                                                 // set up
1064                                                                 float team_color;
1065                                                                 float save = client.team_forced;
1066                                                                 client.team_forced = 0;
1067
1068                                                                 // find the team to move the player to
1069                                                                 team_color = ColourToNumber(destination);
1070                                                                 if(team_color == client.team) // already on the destination team
1071                                                                 {
1072                                                                         // keep the forcing undone
1073                                                                         print("Player ", ftos(GetFilteredNumber(t)), " (", client.netname, ") is already on the ", ColoredTeamName(client.team), (targets ? ", skipping to next player.\n" : ".\n"));
1074                                                                         continue;
1075                                                                 } 
1076                                                                 else if(team_color == 0)  // auto team
1077                                                                 {
1078                                                                         team_color = NumberToTeamNumber(FindSmallestTeam(client, FALSE));
1079                                                                 }
1080                                                                 else
1081                                                                 {
1082                                                                         CheckAllowedTeams(client);
1083                                                                 }
1084                                                                 client.team_forced = save;
1085                                                                 
1086                                                                 // Check to see if the destination team is even available
1087                                                                 switch(team_color) 
1088                                                                 {
1089                                                                         case COLOR_TEAM1: if(c1 == -1) { print("Sorry, can't move player to red team if it doesn't exist.\n"); return; } break;
1090                                                                         case COLOR_TEAM2: if(c2 == -1) { print("Sorry, can't move player to blue team if it doesn't exist.\n"); return; } break;
1091                                                                         case COLOR_TEAM3: if(c3 == -1) { print("Sorry, can't move player to yellow team if it doesn't exist.\n"); return; } break;
1092                                                                         case COLOR_TEAM4: if(c4 == -1) { print("Sorry, can't move player to pink team if it doesn't exist.\n"); return; } break;
1093                                                                         
1094                                                                         default: print("Sorry, can't move player here if team ", destination, " doesn't exist.\n"); return;
1095                                                                 }
1096                                                                 
1097                                                                 // If so, lets continue and finally move the player
1098                                                                 client.team_forced = 0;
1099                                                                 MoveToTeam(client, team_color, 6, stof(notify));
1100                                                                 successful = strcat(successful, (successful ? ", " : ""), client.netname);
1101                                                                 print("Player ", ftos(GetFilteredNumber(t)), " (", client.netname, ") has been moved to the ", ColoredTeamName(team_color), ".\n");
1102                                                                 continue;
1103                                                         }
1104                                                         else
1105                                                         {
1106                                                                 print("Can't change teams when currently not playing a team game.\n");
1107                                                                 return;
1108                                                         }
1109                                                 }
1110                                                 else
1111                                                 {
1112                                                         print("Can't change teams if the player isn't in the game.\n"); // well technically we could, but should we allow that? :P 
1113                                                         return;
1114                                                 }
1115                                         }
1116                                 }
1117                                 
1118                                 if(successful)
1119                                         print("Successfully moved players ", successful, " to destination ", destination, ".\n");
1120                                 else
1121                                         print("No players given (", original_targets, ") are able to move.\n");
1122                                         
1123                                 return; // still correct parameters so return to avoid usage print
1124                         }
1125                 }
1126                         
1127                 default:
1128                         print("Incorrect parameters for ^2moveplayer^7\n");
1129                 case GC_REQUEST_USAGE:
1130                 {
1131                         print("\nUsage:^3 sv_cmd moveplayer clientnumbers destination [notify]\n");
1132                         print("  'clientnumbers' is a list (separated by commas) of player entity ID's\n");
1133                         print("  'destination' is what to send the player to, be it team or spectating\n");
1134                         print("  Full list of destinations here: \"spec, spectator, red, blue, yellow, pink, auto.\"\n");
1135                         print("  'notify' is whether or not to send messages notifying of the move. Detail below.\n");
1136                         print("    0 (00) automove centerprint, admin message; 1 (01) automove centerprint, no admin message\n");
1137                         print("    2 (10) no centerprint, admin message; 3 (11) no centerprint, no admin message\n");
1138                         print("Examples: moveplayer 1,3,5 red 3\n");
1139                         print("          moveplayer 2 spec \n");
1140                         print("See also: ^2allspec^7\n");
1141                         return;
1142                 }
1143         }
1144 }
1145
1146 void GameCommand_nospectators(float request)
1147 {
1148         switch(request)
1149         {
1150                 case GC_REQUEST_COMMAND:
1151                 {
1152                         blockSpectators = 1;
1153                         entity plr;
1154                         FOR_EACH_CLIENT(plr) //give every spectator <g_maxplayers_spectator_blocktime> seconds time to become a player
1155                         {
1156                                 if(plr.classname == "spectator" || plr.classname == "observer")
1157                                 {
1158                                         plr.spectatortime = time;
1159                                         sprint(plr, strcat("^7You have to become a player within the next ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds, otherwise you will be kicked, because spectators aren't allowed at this time!\n"));
1160                                 }
1161                         }
1162                         bprint(strcat("^7All spectators will be automatically kicked when not joining the game after ", ftos(autocvar_g_maxplayers_spectator_blocktime), " seconds!\n"));
1163                         return;
1164                 }
1165                         
1166                 default:
1167                 case GC_REQUEST_USAGE:
1168                 {
1169                         print("\nUsage:^3 sv_cmd nospectators\n");
1170                         print("  No arguments required.\n");
1171                         return;
1172                 }
1173         }
1174 }
1175
1176 void GameCommand_onslaught_updatelinks(float request) // UNTESTED // should this be here? Perhaps some mutatorhook call instead....
1177 {
1178         switch(request)
1179         {
1180                 case GC_REQUEST_COMMAND:
1181                 {
1182                         onslaught_updatelinks();
1183                         print("ONS links updated\n");
1184                         return;
1185                 }
1186                         
1187                 default:
1188                 case GC_REQUEST_USAGE:
1189                 {
1190                         print("\nUsage:^3 sv_cmd onslaught_updatelinks\n");
1191                         print("  No arguments required.\n");
1192                         return;
1193                 }
1194         }
1195 }
1196
1197 void GameCommand_playerdemo(float request, float argc) // UNTESTED // TODO: change the if statements for sub arguments to switches
1198 {       
1199         switch(request)
1200         {
1201                 case GC_REQUEST_COMMAND:
1202                 {
1203                         entity client;
1204                         float i, n, entno;
1205                         string s;
1206                         
1207                         if(argv(1) == "read")
1208                         {
1209                                 // TODO: Create a general command for looking this up, save a lot of space everywhere in this file
1210                                 entno = stof(argv(2));
1211                                 if((entno < 1) | (entno > maxclients)) {
1212                                         print("Player ", argv(2), " doesn't exist\n");
1213                                         return;
1214                                 }
1215                                 client = edict_num(entno);
1216                                 if(clienttype(client) != CLIENTTYPE_BOT) {
1217                                         print("Player ", client.netname, " is not a bot\n");
1218                                         return;
1219                                 }
1220                                 self = client;
1221                                 playerdemo_open_read(argv(3));
1222                                 return;
1223                         }
1224                         else if(argv(1) == "write")
1225                         {
1226                                 entno = stof(argv(2));
1227                                 if((entno < 1) | (entno > maxclients)) {
1228                                         print("Player ", argv(2), " doesn't exist\n");
1229                                         return;
1230                                 }
1231                                 client = edict_num(entno);
1232                                 self = client;
1233                                 playerdemo_open_write(argv(3));
1234                                 return;
1235                         }
1236                         else if(argv(1) == "auto_read_and_write")
1237                         {
1238                                 s = argv(2);
1239                                 n = stof(argv(3));
1240                                 cvar_set("bot_number", ftos(n));
1241                                 localcmd("wait; wait; wait\n");
1242                                 for(i = 0; i < n; ++i)
1243                                         localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", s, ftos(i+1), "\n");
1244                                 localcmd("sv_cmd playerdemo write 1 ", ftos(n+1), "\n");
1245                                 return;
1246                         }
1247                         else if(argv(1) == "auto_read")
1248                         {
1249                                 s = argv(2);
1250                                 n = stof(argv(3));
1251                                 cvar_set("bot_number", ftos(n));
1252                                 localcmd("wait; wait; wait\n");
1253                                 for(i = 0; i < n; ++i)
1254                                         localcmd("sv_cmd playerdemo read ", ftos(i+2), " ", s, ftos(i+1), "\n");
1255                                 return;
1256                         }
1257                         return;
1258                 }
1259                         
1260                 default:
1261                         print("Incorrect parameters for ^2radarmap^7\n");
1262                 case GC_REQUEST_USAGE:
1263                 {
1264                         print("\nUsage:^3 sv_cmd \n");
1265                         print("  FIXME: Arguments currently unknown\n");
1266                         return;
1267                 }
1268         }
1269 }
1270
1271 void GameCommand_printstats(float request)
1272 {
1273         switch(request)
1274         {
1275                 case GC_REQUEST_COMMAND:
1276                 {
1277                         DumpStats(FALSE);
1278                         print("stats dumped.\n");
1279                         return;
1280                 }
1281                         
1282                 default:
1283                 case GC_REQUEST_USAGE:
1284                 {
1285                         print("\nUsage:^3 sv_cmd printstats\n");
1286                         print("  No arguments required.\n");
1287                         return;
1288                 }
1289         }
1290 }
1291
1292 void GameCommand_radarmap(float request, float argc)
1293 {
1294         switch(request)
1295         {
1296                 case GC_REQUEST_COMMAND:
1297                 {
1298                         if(RadarMap_Make(argc))
1299                                 return;
1300                 }
1301                         
1302                 default:
1303                         print("Incorrect parameters for ^2radarmap^7\n");
1304                 case GC_REQUEST_USAGE:
1305                 {
1306                         print("\nUsage:^3 sv_cmd radarmap [--force] [--loop] [--quit] [--block | --trace | --sample | --lineblock] [--sharpen N] [--res W H] [--qual Q]\n");
1307                         print("  The quality factor Q is roughly proportional to the time taken.\n");
1308                         print("  trace supports no quality factor; its result should look like --block with infinite quality factor.\n");
1309                         return;
1310                 }
1311         }
1312 }
1313
1314 void GameCommand_rankings(float request) // this is OLD.... jeez.
1315 {
1316         switch(request)
1317         {
1318                 case GC_REQUEST_COMMAND:
1319                 {
1320                         strunzone(rankings_reply);
1321                         rankings_reply = strzone(getrankings());
1322                         print(rankings_reply);
1323                         return;
1324                 }
1325                         
1326                 default:
1327                 case GC_REQUEST_USAGE:
1328                 {
1329                         print("\nUsage:^3 sv_cmd rankings\n");
1330                         print("  No arguments required.\n");
1331                         return;
1332                 }
1333         }
1334 }
1335
1336 void GameCommand_records(float request)
1337 {
1338         switch(request)
1339         {
1340                 case GC_REQUEST_COMMAND:
1341                 {
1342                         float i;
1343                         
1344                         for (i = 0; i < 10; ++i)
1345                                 print(records_reply[i]);
1346                                 
1347                         return;
1348                 }
1349                         
1350                 default:
1351                 case GC_REQUEST_USAGE:
1352                 {
1353                         print("\nUsage:^3 sv_cmd records\n");
1354                         print("  No arguments required.\n");
1355                         return;
1356                 }
1357         }
1358 }
1359
1360 void GameCommand_reducematchtime(float request)
1361 {
1362         switch(request)
1363         {
1364                 case GC_REQUEST_COMMAND:
1365                 {
1366                         changematchtime(autocvar_timelimit_decrement*-60, autocvar_timelimit_min*60, autocvar_timelimit_max*60);
1367                         return;
1368                 }
1369                         
1370                 default:
1371                 case GC_REQUEST_USAGE:
1372                 {
1373                         print("\nUsage:^3 sv_cmd reducematchtime\n");
1374                         print("  No arguments required.\n");
1375                         print("See also: ^2extendmatchtime^7\n");
1376                         return;
1377                 }
1378         }
1379 }
1380
1381 void GameCommand_setbots(float request, float argc)
1382 {
1383         switch(request)
1384         {
1385                 case GC_REQUEST_COMMAND:
1386                 {
1387                         if(argc >= 3 && argv(1) == "setbots")
1388                         {
1389                                 cvar_settemp("minplayers", "0");
1390                                 cvar_settemp("bot_number", argv(2));
1391                                 bot_fixcount();
1392                                 return;
1393                         }
1394                 }
1395                         
1396                 default:
1397                 case GC_REQUEST_USAGE:
1398                 {
1399                         print("\nUsage:^3 sv_cmd setbots botnumber\n");
1400                         print("  Where 'botnumber' is the amount of bots to set bot_number cvar to.\n");
1401                         print("See also: ^2bot_cmd^7\n");
1402                         return;
1403                 }
1404         }
1405 }
1406
1407 void GameCommand_stuffto(float request, float argc)
1408 {
1409         // This... is a fairly dangerous and powerful command... - It allows any arguments to be sent to a client via rcon.
1410         // Because of this, it is disabled by default and must be enabled by the server owner when doing compilation. That way,
1411         // we can be certain they understand the risks of it... So to enable, compile server with -DSTUFFTO_ENABLED argument.
1412         
1413         #ifdef STUFFTO_ENABLED
1414         switch(request)
1415         {
1416                 case GC_REQUEST_COMMAND:
1417                 {
1418                         entity client;
1419                         float entno;
1420                         
1421                         if(argc == 3)
1422                         {
1423                                 entno = stof(argv(1));
1424                                 client = world;
1425                                 if(entno <= maxclients)
1426                                         client = edict_num(entno);
1427                                 if(client.flags & FL_CLIENT)
1428                                 {
1429                                         stuffcmd(client, strcat("\n", argv(2), "\n"));
1430                                         print(strcat("Command: \"", argv(2), "\" sent to ", client.netname, " (", argv(1) ,").\n"));
1431                                 }
1432                                 else
1433                                         print(strcat("Client (", argv(1) ,") not found.\n"));
1434                                 
1435                                 return;
1436                         }
1437                 }
1438                         
1439                 default:
1440                         print("Incorrect parameters for ^2stuffto^7\n");
1441                 case GC_REQUEST_USAGE:
1442                 {
1443                         print("\nUsage:^3 sv_cmd stuffto clientnumber command\n");
1444                         print("  FIXME: Arguments currently unknown\n");
1445                         return;
1446                 }
1447         }
1448         #else
1449         if(request)
1450         {
1451                 print("stuffto command is not enabled on this server.\n");
1452                 return;
1453         }
1454         #endif
1455 }
1456
1457 void GameCommand_teamstatus(float request)
1458 {
1459         switch(request)
1460         {
1461                 case GC_REQUEST_COMMAND:
1462                 {
1463                         Score_NicePrint(world);
1464                         return;
1465                 }
1466                         
1467                 default:
1468                 case GC_REQUEST_USAGE:
1469                 {
1470                         print("\nUsage:^3 sv_cmd teamstatus\n");
1471                         print("  No arguments required.\n");
1472                         return;
1473                 }
1474         }
1475 }
1476
1477 void GameCommand_time(float request)
1478 {
1479         switch(request)
1480         {
1481                 case GC_REQUEST_COMMAND:
1482                 {
1483                         print("time = ", ftos(time), "\n");
1484                         print("frame start = ", ftos(gettime(GETTIME_FRAMESTART)), "\n");
1485                         print("realtime = ", ftos(gettime(GETTIME_REALTIME)), "\n");
1486                         print("hires = ", ftos(gettime(GETTIME_HIRES)), "\n");
1487                         print("uptime = ", ftos(gettime(GETTIME_UPTIME)), "\n");
1488                         print("localtime = ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n"); // FIXME: Why is strftime broken? is engine problem, I think.
1489                         print("gmtime = ", strftime(FALSE, "%a %b %e %H:%M:%S %Z %Y"), "\n");
1490                         return;
1491                 }
1492                         
1493                 default:
1494                 case GC_REQUEST_USAGE:
1495                 {
1496                         print("\nUsage:^3 sv_cmd time\n");
1497                         print("  No arguments required.\n");
1498                         return;
1499                 }
1500         }
1501 }
1502
1503 void GameCommand_trace(float request, float argc)
1504 {                                               
1505         switch(request)
1506         {
1507                 case GC_REQUEST_COMMAND:
1508                 {
1509                         // TODO: Clean up all of these variables and merge the code below to use only a few
1510                         entity e;
1511                         vector org, delta, start, end, p, q, q0, pos, vv, dv;
1512                         float i, f, safe, unsafe, dq, dqf;
1513         
1514                         switch(argv(1))
1515                         {
1516                                 case "debug":
1517                                         print("TEST CASE. If this returns the runaway loop counter error, possibly everything is oaky.\n");
1518                                         for(;;)
1519                                         {
1520                                                 org = world.mins;
1521                                                 delta = world.maxs - world.mins;
1522
1523                                                 start_x = org_x + random() * delta_x;
1524                                                 start_y = org_y + random() * delta_y;
1525                                                 start_z = org_z + random() * delta_z;
1526
1527                                                 end_x = org_x + random() * delta_x;
1528                                                 end_y = org_y + random() * delta_y;
1529                                                 end_z = org_z + random() * delta_z;
1530
1531                                                 start = stov(vtos(start));
1532                                                 end = stov(vtos(end));
1533
1534                                                 tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
1535                                                 if(!trace_startsolid)
1536                                                 {
1537                                                         p = trace_endpos;
1538                                                         tracebox(p, PL_MIN, PL_MAX, p, MOVE_NOMONSTERS, world);
1539                                                         if(trace_startsolid || trace_fraction == 1)
1540                                                         {
1541                                                                 rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid
1542                                                                 tracebox(start, PL_MIN, PL_MAX, end, MOVE_NOMONSTERS, world);
1543                                                                 tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
1544
1545                                                                 if(trace_startsolid)
1546                                                                 {
1547                                                                         // how much do we need to back off?
1548                                                                         safe = 1;
1549                                                                         unsafe = 0;
1550                                                                         for(;;)
1551                                                                         {
1552                                                                                 pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5);
1553                                                                                 tracebox(pos, PL_MIN, PL_MAX, pos, MOVE_NOMONSTERS, world);
1554                                                                                 if(trace_startsolid)
1555                                                                                 {
1556                                                                                         if((safe + unsafe) * 0.5 == unsafe)
1557                                                                                                 break;
1558                                                                                         unsafe = (safe + unsafe) * 0.5;
1559                                                                                 }
1560                                                                                 else
1561                                                                                 {
1562                                                                                         if((safe + unsafe) * 0.5 == safe)
1563                                                                                                 break;
1564                                                                                         safe = (safe + unsafe) * 0.5;
1565                                                                                 }
1566                                                                         }
1567
1568                                                                         print("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu\n");
1569                                                                         print("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n");
1570
1571                                                                         tracebox(p, PL_MIN + '0.1 0.1 0.1', PL_MAX - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, world);
1572                                                                         if(trace_startsolid)
1573                                                                                 print("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1574                                                                         else
1575                                                                                 print("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1576                                                                         break;
1577                                                                 }
1578
1579                                                                 q0 = p;
1580                                                                 dq = 0;
1581                                                                 dqf = 1;
1582                                                                 for(;;)
1583                                                                 {
1584                                                                         q = p + normalize(end - p) * (dq + dqf);
1585                                                                         if(q == q0)
1586                                                                                 break;
1587                                                                         tracebox(p, PL_MIN, PL_MAX, q, MOVE_NOMONSTERS, world);
1588                                                                         if(trace_startsolid)
1589                                                                                 error("THIS ONE cannot happen");
1590                                                                         if(trace_fraction > 0)
1591                                                                                 dq += dqf * trace_fraction;
1592                                                                         dqf *= 0.5;
1593                                                                         q0 = q;
1594                                                                 }
1595                                                                 if(dq > 0)
1596                                                                 {
1597                                                                         print("trace_endpos still before solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n");
1598                                                                         print("could go ", ftos(dq), " units further to ", vtos(q), "\n");
1599                                                                         break;
1600                                                                 }
1601                                                         }
1602                                                 }
1603                                         }
1604                                         return;
1605                                         
1606                                 case "debug2":
1607                                         e = nextent(world);
1608                                         tracebox(e.origin + '0 0 32', e.mins, e.maxs, e.origin + '0 0 -1024', MOVE_NORMAL, e);
1609                                         vv = trace_endpos;
1610                                         if(trace_fraction == 1)
1611                                         {
1612                                                 print("not above ground, aborting\n");
1613                                                 return;
1614                                         }
1615                                         f = 0;
1616                                         for(i = 0; i < 100000; ++i)
1617                                         {
1618                                                 dv = randomvec();
1619                                                 if(dv_z > 0)
1620                                                         dv = -1 * dv;
1621                                                 tracebox(vv, e.mins, e.maxs, vv + dv, MOVE_NORMAL, e);
1622                                                 if(trace_startsolid)
1623                                                         print("bug 1\n");
1624                                                 if(trace_fraction == 1)
1625                                                 if(dv_z < f)
1626                                                 {
1627                                                         print("bug 2: ", ftos(dv_x), " ", ftos(dv_y), " ", ftos(dv_z));
1628                                                         print(" (", ftos(asin(dv_z / vlen(dv)) * 180 / M_PI), " degrees)\n");
1629                                                         f = dv_z;
1630                                                 }
1631                                         }
1632                                         print("highest possible dist: ", ftos(f), "\n");
1633                                         return;
1634                                 
1635                                 case "walk":
1636                                         if(argc == 3)
1637                                         {
1638                                                 e = nextent(world);
1639                                                 if(tracewalk(e, stov(argv(1)), e.mins, e.maxs, stov(argv(2)), MOVE_NORMAL))
1640                                                         print("can walk\n");
1641                                                 else
1642                                                         print("cannot walk\n");
1643                                                 return;
1644                                         }
1645                                                 
1646                                 case "showline":
1647                                         if(argc == 3)
1648                                         {
1649                                                 vv = stov(argv(1));
1650                                                 dv = stov(argv(2));
1651                                                 traceline(vv, dv, MOVE_NORMAL, world);
1652                                                 trailparticles(world, particleeffectnum("TR_NEXUIZPLASMA"), vv, trace_endpos);
1653                                                 trailparticles(world, particleeffectnum("TR_CRYLINKPLASMA"), trace_endpos, dv);
1654                                                 return;
1655                                         }
1656                                 // no default case, just go straight to "invalid arguments"
1657                         }
1658                 }
1659                         
1660                 default:
1661                 case GC_REQUEST_USAGE:
1662                 {
1663                         print("\nUsage:^3 sv_cmd trace command [arguments]\n");
1664                         print("  FIXME: Arguments currently unknown\n");
1665                         return;
1666                 }
1667         }
1668 }
1669
1670 void GameCommand_unlockteams(float request)
1671 {
1672         switch(request)
1673         {
1674                 case GC_REQUEST_COMMAND:
1675                 {
1676                         if(teamplay)
1677                         {
1678                                 lockteams = 0;
1679                                 bprint("^1The teams are now unlocked.\n");
1680                         }
1681                         else
1682                                 bprint("That command can only be used in a team-based gamemode.\n");
1683                         return;
1684                 }
1685                         
1686                 default:
1687                 case GC_REQUEST_USAGE:
1688                 {
1689                         print("\nUsage:^3 sv_cmd unlockteams\n");
1690                         print("  No arguments required.\n");
1691                         print("See also: ^2lockteams^7\n");
1692                         return;
1693                 }
1694         }
1695 }
1696
1697 void GameCommand_warp(float request, float argc)
1698 {
1699         switch (request)
1700         {
1701                 case GC_REQUEST_COMMAND:
1702                 {
1703                         if(autocvar_g_campaign)
1704                         {
1705                                 
1706                                 if(argc >= 2)
1707                                 {
1708                                         CampaignLevelWarp(stof(argv(1)));
1709                                         print("Successfully warped to campaign level ", stof(argv(1)), ".\n");
1710                                 }       
1711                                 else
1712                                 {
1713                                         CampaignLevelWarp(-1);
1714                                         print("Successfully warped to next campaign level.\n");
1715                                 }
1716                         }
1717                         else
1718                                 print("Not in campaign, can't level warp\n");
1719                         return;
1720                 }
1721                 
1722                 default:
1723                 case GC_REQUEST_USAGE:
1724                 {
1725                         print("\nUsage:^3 sv_cmd level\n");
1726                         print("  'level' is the level to change campaign mode to.\n");
1727                         return;
1728                 }
1729         }
1730 }
1731
1732 /*
1733 void GameCommand_(float request)
1734 {
1735         switch(request)
1736         {
1737                 case GC_REQUEST_COMMAND:
1738                 {
1739                         
1740                         return;
1741                 }
1742                         
1743                 default:
1744                 case GC_REQUEST_USAGE:
1745                 {
1746                         sprint(self, "\nUsage:^3 cmd \n");
1747                         sprint(self, "  No arguments required.\n");
1748                         return;
1749                 }
1750         }
1751 }
1752 */
1753
1754
1755 // ===========================
1756 //  Macro system for commands
1757 // ===========================
1758
1759 // For now, the list has to be split up due to the suckage called fteqcc which limits macros to only 1024 characters
1760 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1761 #define SERVER_COMMANDS_1(request,arguments) \
1762         SERVER_COMMAND("adminmsg", GameCommand_adminmsg(request, arguments), "Send an admin message to a client directly") \
1763         SERVER_COMMAND("allready", GameCommand_allready(request), "Restart the server and reset the players") \
1764         SERVER_COMMAND("allspec", GameCommand_allspec(request, arguments), "Force all players to spectate") \
1765         SERVER_COMMAND("anticheat", GameCommand_anticheat(request, arguments), "Create an anticheat report for a client") \
1766         SERVER_COMMAND("bbox", GameCommand_bbox(request), "Print detailed information about world size") \
1767         SERVER_COMMAND("bot_cmd", GameCommand_bot_cmd(request, arguments), "Control and send commands to bots") \
1768         SERVER_COMMAND("cointoss", GameCommand_cointoss(request, arguments), "Flip a virtual coin and give random result") \
1769         SERVER_COMMAND("cvar_changes", GameCommand_cvar_changes(request), "Prints a list of all changed server cvars") \
1770         /* nothing */
1771         
1772 #define SERVER_COMMANDS_2(request,arguments) \
1773         SERVER_COMMAND("cvar_purechanges", GameCommand_cvar_purechanges(request), "Prints a list of all changed gameplay cvars") \
1774         SERVER_COMMAND("database", GameCommand_database(request, arguments), "Extra controls of the serverprogs database") \
1775         SERVER_COMMAND("defer_clear", GameCommand_defer_clear(request, arguments), "Clear all queued defer commands for a specific client") \
1776         SERVER_COMMAND("defer_clear_all", GameCommand_defer_clear_all(request), "Clear all queued defer commands for all clients") \
1777         SERVER_COMMAND("delrec", GameCommand_delrec(request, arguments), "Delete race time record for a map") \
1778         SERVER_COMMAND("effectindexdump", GameCommand_effectindexdump(request), "Dump list of effects from code and effectinfo.txt") \
1779         SERVER_COMMAND("extendmatchtime", GameCommand_extendmatchtime(request), "Increase the timelimit value incrementally") \
1780                 /* nothing */
1781
1782 #define SERVER_COMMANDS_3(request,arguments) \
1783         SERVER_COMMAND("find", GameCommand_find(request, arguments), "Search through entities for matching classname") \
1784         SERVER_COMMAND("gametype", GameCommand_gametype(request, arguments), "Simple command to change the active gametype") \
1785         SERVER_COMMAND("gettaginfo", GameCommand_gettaginfo(request, arguments), "Get specific information about a weapon model") \
1786         SERVER_COMMAND("gotomap", GameCommand_gotomap(request, arguments), "Simple command to switch to another map") \
1787         SERVER_COMMAND("ladder", GameCommand_ladder(request), "Get information about top players if supported") \
1788         SERVER_COMMAND("lockteams", GameCommand_lockteams(request), "Disable the ability for players to switch or enter teams") \
1789         SERVER_COMMAND("make_mapinfo", GameCommand_make_mapinfo(request), "Automatically rebuild mapinfo files") \
1790         SERVER_COMMAND("modelbug", GameCommand_modelbug(request), "TODO foobar") \
1791         /* nothing */
1792
1793 #define SERVER_COMMANDS_4(request,arguments) \
1794         SERVER_COMMAND("moveplayer", GameCommand_moveplayer(request, arguments), "Change the team/status of a player") \
1795         SERVER_COMMAND("nospectators", GameCommand_nospectators(request), "Automatically remove spectators from a match") \
1796         SERVER_COMMAND("onslaught_updatelinks", GameCommand_onslaught_updatelinks(request), "Refresh link status for onslaught") \
1797         SERVER_COMMAND("playerdemo", GameCommand_playerdemo(request, arguments), "Control the ability to save demos of players") \
1798         SERVER_COMMAND("printstats", GameCommand_printstats(request), "TODO foobar") \
1799         SERVER_COMMAND("radarmap", GameCommand_radarmap(request, arguments), "Generate a radar image of the map") \
1800         SERVER_COMMAND("rankings", GameCommand_rankings(request), "Print information about rankings") \
1801         SERVER_COMMAND("records", GameCommand_records(request), "List top 10 records for the current map") \
1802         /* nothing */
1803
1804 #define SERVER_COMMANDS_5(request,arguments) \
1805         SERVER_COMMAND("reducematchtime", GameCommand_reducematchtime(request), "Decrease the timelimit value incrementally") \
1806         SERVER_COMMAND("setbots", GameCommand_setbots(request, arguments), "Adjust how many bots are in the match") \
1807         SERVER_COMMAND("stuffto", GameCommand_stuffto(request, arguments), "Send a command to be executed on a client") \
1808         SERVER_COMMAND("teamstatus", GameCommand_teamstatus(request), "Show information about player and team scores") \
1809         SERVER_COMMAND("time", GameCommand_time(request), "Print different formats/readouts of time") \
1810         SERVER_COMMAND("trace", GameCommand_trace(request, arguments), "Various debugging tools with tracing") \
1811         SERVER_COMMAND("unlockteams", GameCommand_unlockteams(request), "Enable the ability for players to switch or enter teams") \
1812         SERVER_COMMAND("warp", GameCommand_warp(request, arguments), "Choose different level in campaign") \
1813         /* nothing */
1814
1815 void GameCommand_macro_help()
1816 {
1817         #define SERVER_COMMAND(name,function,description) \
1818                 { print("  ^2", name, "^7: ", description, "\n"); }
1819                 
1820         SERVER_COMMANDS_1(0, 0)
1821         SERVER_COMMANDS_2(0, 0)
1822         SERVER_COMMANDS_3(0, 0)
1823         SERVER_COMMANDS_4(0, 0)
1824         SERVER_COMMANDS_5(0, 0)
1825         #undef SERVER_COMMAND
1826         
1827         return;
1828 }
1829
1830 float GameCommand_macro_command(float argc)
1831 {
1832         #define SERVER_COMMAND(name,function,description) \
1833                 { if(name == strtolower(argv(0))) { function; return TRUE; } }
1834                 
1835         SERVER_COMMANDS_1(GC_REQUEST_COMMAND, argc)
1836         SERVER_COMMANDS_2(GC_REQUEST_COMMAND, argc)
1837         SERVER_COMMANDS_3(GC_REQUEST_COMMAND, argc)
1838         SERVER_COMMANDS_4(GC_REQUEST_COMMAND, argc)
1839         SERVER_COMMANDS_5(GC_REQUEST_COMMAND, argc)
1840         #undef SERVER_COMMAND
1841         
1842         return FALSE;
1843 }
1844
1845 float GameCommand_macro_usage(float argc)
1846 {
1847         #define SERVER_COMMAND(name,function,description) \
1848                 { if(name == strtolower(argv(1))) { function; return TRUE; } }
1849                 
1850         SERVER_COMMANDS_1(GC_REQUEST_USAGE, argc)
1851         SERVER_COMMANDS_2(GC_REQUEST_USAGE, argc)
1852         SERVER_COMMANDS_3(GC_REQUEST_USAGE, argc)
1853         SERVER_COMMANDS_4(GC_REQUEST_USAGE, argc)
1854         SERVER_COMMANDS_5(GC_REQUEST_USAGE, argc)
1855         #undef SERVER_COMMAND
1856         
1857         return FALSE;
1858 }
1859         
1860
1861 // =========================================
1862 //  Main Function Called By Engine (sv_cmd)
1863 // =========================================
1864 // If this function exists, game code handles gamecommand instead of the engine code.
1865
1866 void GameCommand(string command)
1867 {
1868         float argc = tokenize_console(command);
1869
1870         if(strtolower(argv(0)) == "help") 
1871         {
1872                 if(argc == 1) 
1873                 {
1874                         print("\nUsage:^3 sv_cmd COMMAND...^7, where possible commands are:\n");
1875                         GameCommand_macro_help();
1876                         GameCommand_Vote("help", world);
1877                         GameCommand_Ban("help");
1878                         GameCommand_Generic("help");
1879                         print("For help about specific commands, type sv_cmd help COMMAND\n");
1880                         return;
1881                 } 
1882                 else if(GameCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it
1883                 {
1884                         return;
1885                 }
1886         } 
1887         else if(GameCommand_Vote(command, world)) 
1888         {
1889                 return; // handled by server/vote.qc 
1890         }
1891         else if(GameCommand_Ban(command)) 
1892         {
1893                 return; // handled by server/ipban.qc
1894         }
1895         else if(GameCommand_Generic(command)) 
1896         {
1897                 return; // handled by common/gamecommand.qc
1898         }
1899         else if(GameCommand_macro_command(argc)) // continue as usual and scan for normal commands
1900         {
1901                 return; // handled by one of the above GameCommand_* functions
1902         }
1903         
1904         // nothing above caught the command, must be invalid
1905         print("Invalid command. For a list of supported commands, try sv_cmd help.\n");
1906 }