]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/command/cmd.qc
Now make everything else compile too
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / command / cmd.qc
1 // =========================================================
2 //  Server side networked commands code, reworked by Samual
3 //  Last updated: December 11th, 2011
4 // =========================================================
5
6 // move any necessary sprint statements to "print_to"
7
8 // ============================
9 //  Misc. Supporting Functions
10 // ============================
11
12 float SV_ParseClientCommand_floodcheck()
13 {
14         if (timeoutStatus != 2) // if the game is not paused... but wait, doesn't that mean it could be dos'd by pausing it? eh? (old code)
15         {
16                 if(time <= (self.cmd_floodtime + autocvar_sv_clientcommand_antispam_time))
17                 {
18                         self.cmd_floodcount += 1;
19                         if(self.cmd_floodcount > autocvar_sv_clientcommand_antispam_count) { return FALSE; } // too much spam, halt
20                 }
21                 else
22                 {
23                         self.cmd_floodtime = time;
24                         self.cmd_floodcount = 1;
25                 }
26         }
27         return TRUE; // continue, as we're not flooding yet
28 }
29
30
31 // =======================
32 //  Command Sub-Functions
33 // =======================
34
35 void ClientCommand_autoswitch(float request, float argc)
36 {
37         switch(request)
38         {
39                 case CC_REQUEST_COMMAND:
40                 {
41                         self.autoswitch = ("0" != argv(1));
42                         sprint(self, strcat("^1autoswitch is currently turned ", (self.autoswitch ? "on" : "off"), ".\n"));
43                         return; // never fall through to usage
44                 }
45                         
46                 default:
47                 case CC_REQUEST_USAGE:
48                 {
49                         sprint(self, "\nUsage:^3 cmd autoswitch selection\n");
50                         sprint(self, "  Where 'selection' is 1 or 0 for on or off.\n"); 
51                         return;
52                 }
53         }
54 }
55
56 void ClientCommand_checkfail(float request, string command) // used only by client side code
57 {
58         switch(request)
59         {
60                 case CC_REQUEST_COMMAND:
61                 {
62                         print(sprintf("CHECKFAIL: %s (%s) epically failed check %s\n", self.netname, self.netaddress, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))));
63                         self.checkfail = 1;
64                         return; // never fall through to usage
65                 }
66                         
67                 default:
68                 case CC_REQUEST_USAGE:
69                 {
70                         sprint(self, "\nUsage:^3 cmd checkfail message\n");
71                         sprint(self, "  Where 'message' is the message reported by client about the fail.\n");
72                         return;
73                 }
74         }
75 }
76
77 void ClientCommand_clientversion(float request, float argc) // used only by client side code
78 {
79         switch(request)
80         {
81                 case CC_REQUEST_COMMAND:
82                 {
83                         if(self.flags & FL_CLIENT)
84                         {
85                                 self.version = ((argv(1) == "$gameversion") ? 1 : stof(argv(1)));
86                                 
87                                 if(self.version < autocvar_gameversion_min || self.version > autocvar_gameversion_max)
88                                 {
89                                         self.version_mismatch = 1;
90                                         ClientKill_TeamChange(-2); // observe
91                                 } 
92                                 else if(autocvar_g_campaign || autocvar_g_balance_teams || autocvar_g_balance_teams_force) 
93                                 {
94                                         //JoinBestTeam(self, FALSE, TRUE);
95                                 } 
96                                 else if(teamplay && !autocvar_sv_spectate && !(self.team_forced > 0)) 
97                                 {
98                                         self.classname = "observer"; // really?
99                                         stuffcmd(self, "menu_showteamselect\n");
100                                 }
101                         }
102                         return; // never fall through to usage
103                 }
104                         
105                 default:
106                 case CC_REQUEST_USAGE:
107                 {
108                         sprint(self, "\nUsage:^3 cmd clientversion version\n");
109                         sprint(self, "  Where 'version' is the game version reported by self.\n");
110                         return;
111                 }
112         }
113 }
114
115 void ClientCommand_cvar_changes(float request)
116 {
117         switch(request)
118         {
119                 case CC_REQUEST_COMMAND:
120                 {
121                         sprint(self, cvar_changes);
122                         return; // never fall through to usage
123                 }
124                         
125                 default:
126                 case CC_REQUEST_USAGE:
127                 {
128                         sprint(self, "\nUsage:^3 sv_cmd cvar_changes\n");
129                         sprint(self, "  No arguments required.\n");
130                         //sprint(self, "See also: ^2cvar_purechanges^7\n");
131                         return;
132                 }
133         }
134 }
135
136 void ClientCommand_cvar_purechanges(float request)
137 {
138         switch(request)
139         {
140                 case CC_REQUEST_COMMAND:
141                 {
142                         sprint(self, cvar_purechanges);
143                         return; // never fall through to usage
144                 }
145                         
146                 default:
147                 case CC_REQUEST_USAGE:
148                 {
149                         sprint(self, "\nUsage:^3 sv_cmd cvar_purechanges\n");
150                         sprint(self, "  No arguments required.\n");
151                         //sprint(self, "See also: ^2cvar_changes^7\n");
152                         return;
153                 }
154         }
155 }
156
157 void ClientCommand_getmapvotepic(float request, float argc)
158 {
159         switch(request)
160         {
161                 case CC_REQUEST_COMMAND:
162                 {
163                         if(intermission_running)                                
164                                 MapVote_SendPicture(stof(argv(1)));
165
166                         return; // never fall through to usage
167                 }
168                         
169                 default:
170                 case CC_REQUEST_USAGE:
171                 {
172                         sprint(self, "\nUsage:^3 cmd getmapvotepic mapid\n");
173                         sprint(self, "  Where 'mapid' is the id number of the map to request an image of on the map vote selection menu.\n");
174                         return;
175                 }
176         }
177 }
178
179 void ClientCommand_info(float request, float argc)
180 {       
181         switch(request)
182         {
183                 case CC_REQUEST_COMMAND:
184                 {
185                         string command;
186                         
187                         command = builtin_cvar_string(strcat("sv_info_", argv(1))); 
188                         if(command)
189                                 wordwrap_sprint(command, 1111); // why 1111?
190                         else
191                                 sprint(self, "ERROR: unsupported info command\n");
192                                 
193                         return; // never fall through to usage
194                 }
195                         
196                 default:
197                 case CC_REQUEST_USAGE:
198                 {
199                         sprint(self, "\nUsage:^3 cmd info request\n");
200                         sprint(self, "  Where 'request' is the suffixed string appended onto the request for cvar.\n");
201                         return;
202                 }
203         }
204 }
205
206 void ClientCommand_join(float request)
207 {
208         switch(request)
209         {
210                 case CC_REQUEST_COMMAND:
211                 {
212                         if(self.flags & FL_CLIENT)
213                         {
214                                 if(self.classname != "player" && !lockteams && !g_arena)
215                                 {
216                                         if(nJoinAllowed(1)) 
217                                         {
218                                                 if(g_ca) { self.caplayer = 1; }
219                                                 if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
220                                                 
221                                                 self.classname = "player";
222                                                 PlayerScore_Clear(self);
223                                                 bprint ("^4", self.netname, "^4 is playing now\n");
224                                                 PutClientInServer();
225                                         }
226                                         else 
227                                         {
228                                                 //player may not join because of g_maxplayers is set
229                                                 centerprint(self, PREVENT_JOIN_TEXT);
230                                         }
231                                 }
232                         }
233                         return; // never fall through to usage
234                 }
235                         
236                 default:
237                 case CC_REQUEST_USAGE:
238                 {
239                         sprint(self, "\nUsage:^3 cmd join\n");
240                         sprint(self, "  No arguments required.\n");
241                         return;
242                 }
243         }
244 }
245
246 void ClientCommand_ladder(float request)
247 {
248         switch(request)
249         {
250                 case CC_REQUEST_COMMAND:
251                 {
252                         sprint(self, ladder_reply);
253                         return; // never fall through to usage
254                 }
255                         
256                 default:
257                 case CC_REQUEST_USAGE:
258                 {
259                         sprint(self, "\nUsage:^3 cmd ladder\n");
260                         sprint(self, "  No arguments required.\n");
261                         return;
262                 }
263         }
264 }
265
266 void ClientCommand_lsmaps(float request)
267 {
268         switch(request)
269         {
270                 case CC_REQUEST_COMMAND:
271                 {
272                         sprint(self, lsmaps_reply);
273                         return; // never fall through to usage
274                 }
275                         
276                 default:
277                 case CC_REQUEST_USAGE:
278                 {
279                         sprint(self, "\nUsage:^3 cmd lsmaps\n");
280                         sprint(self, "  No arguments required.\n");
281                         return;
282                 }
283         }
284 }
285
286 void ClientCommand_lsnewmaps(float request)
287 {
288         switch(request)
289         {
290                 case CC_REQUEST_COMMAND:
291                 {
292                         sprint(self, lsnewmaps_reply);
293                         return; // never fall through to usage
294                 }
295                         
296                 default:
297                 case CC_REQUEST_USAGE:
298                 {
299                         sprint(self, "\nUsage:^3 cmd lsnewmaps\n");
300                         sprint(self, "  No arguments required.\n");
301                         return;
302                 }
303         }
304 }
305
306 void ClientCommand_maplist(float request)
307 {
308         switch(request)
309         {
310                 case CC_REQUEST_COMMAND:
311                 {
312                         sprint(self, maplist_reply);
313                         return; // never fall through to usage
314                 }
315                         
316                 default:
317                 case CC_REQUEST_USAGE:
318                 {
319                         sprint(self, "\nUsage:^3 cmd maplist\n");
320                         sprint(self, "  No arguments required.\n");
321                         return;
322                 }
323         }
324 }
325
326 void ClientCommand_rankings(float request)
327 {
328         switch(request)
329         {
330                 case CC_REQUEST_COMMAND:
331                 {
332                         sprint(self, rankings_reply);
333                         return; // never fall through to usage
334                 }
335                         
336                 default:
337                 case CC_REQUEST_USAGE:
338                 {
339                         sprint(self, "\nUsage:^3 cmd rankings\n");
340                         sprint(self, "  No arguments required.\n");
341                         return;
342                 }
343         }
344 }
345
346 void ClientCommand_ready(float request) 
347 {
348         switch(request)
349         {
350                 case CC_REQUEST_COMMAND:
351                 {
352                         if(self.flags & FL_CLIENT)
353                         {
354                                 if(inWarmupStage || autocvar_sv_ready_restart || g_race_qualifying == 2)
355                                 {
356                                         if(!readyrestart_happened || autocvar_sv_ready_restart_repeatable)
357                                         {
358                                                 if (self.ready) // toggle
359                                                 {
360                                                         self.ready = FALSE;
361                                                         bprint(self.netname, "^2 is ^1NOT^2 ready\n");
362                                                 }
363                                                 else
364                                                 {
365                                                         self.ready = TRUE;
366                                                         bprint(self.netname, "^2 is ready\n");
367                                                 }
368
369                                                 // cannot reset the game while a timeout is active!
370                                                 if(!timeoutStatus)
371                                                         ReadyCount();
372                                         } else {
373                                                 sprint(self, "^1Game has already been restarted\n");
374                                         }
375                                 }
376                         }
377                         return; // never fall through to usage
378                 }
379                         
380                 default:
381                 case CC_REQUEST_USAGE:
382                 {
383                         sprint(self, "\nUsage:^3 cmd ready\n");
384                         sprint(self, "  No arguments required.\n");
385                         return;
386                 }
387         }
388 }
389
390 void ClientCommand_records(float request) // TODO: Isn't this flooding with the sprint messages? Old code, but perhaps bad?
391 {       
392         switch(request)
393         {
394                 case CC_REQUEST_COMMAND:
395                 {
396                         float i;
397                         
398                         for(i = 0; i < 10; ++i)
399                                 sprint(self, records_reply[i]);
400                                 
401                         return; // never fall through to usage
402                 }
403                         
404                 default:
405                 case CC_REQUEST_USAGE:
406                 {
407                         sprint(self, "\nUsage:^3 cmd records\n");
408                         sprint(self, "  No arguments required.\n");
409                         return;
410                 }
411         }
412 }
413
414 void ClientCommand_reportcvar(float request, float argc, string command) // TODO: confirm this works
415 {       
416         switch(request)
417         {
418                 case CC_REQUEST_COMMAND:
419                 {
420                         float tokens;
421                         string s;
422                         
423                         if(substring(argv(2), 0, 1) == "$") // undefined cvar: use the default value on the server then
424                         {
425                                 s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
426                                 tokens = tokenize_console(s);
427                         }
428                         GetCvars(1);
429                         return; // never fall through to usage
430                 }
431                         
432                 default:
433                 case CC_REQUEST_USAGE:
434                 {
435                         sprint(self, "\nUsage:^3 cmd reportcvar <cvar>\n");
436                         sprint(self, "  Where 'cvar' is the cvar plus arguments to send to the server.\n");
437                         return;
438                 }
439         }
440 }
441
442 void ClientCommand_say(float request, float argc, string command)
443 {
444         switch(request)
445         {
446                 case CC_REQUEST_COMMAND:
447                 {
448                         if(argc >= 2) { Say(self, FALSE, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); }
449                         return; // never fall through to usage
450                 }
451                         
452                 default:
453                 case CC_REQUEST_USAGE:
454                 {
455                         sprint(self, "\nUsage:^3 cmd say <message>\n");
456                         sprint(self, "  Where 'message' is the string of text to say.\n");
457                         return;
458                 }
459         }
460 }
461
462 void ClientCommand_say_team(float request, float argc, string command)
463 {
464         switch(request)
465         {
466                 case CC_REQUEST_COMMAND:
467                 {
468                         if(argc >= 2) { Say(self, TRUE, world, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); }
469                         return; // never fall through to usage
470                 }
471                         
472                 default:
473                 case CC_REQUEST_USAGE:
474                 {
475                         sprint(self, "\nUsage:^3 cmd say_team <message>\n");
476                         sprint(self, "  Where 'message' is the string of text to say.\n");
477                         return;
478                 }
479         }
480 }
481
482 void ClientCommand_selectteam(float request, float argc) // TODO: Update the messages for this command
483 {
484         switch(request)
485         {
486                 case CC_REQUEST_COMMAND:
487                 {
488                         float selection;
489                         
490                         if (self.flags & FL_CLIENT)
491                         {
492                                 if(teamplay)
493                                         if not(self.team_forced > 0) 
494                                                 if not(lockteams) 
495                                                 {
496                                                         switch(argv(1))
497                                                         {
498                                                                 case "red": selection = COLOR_TEAM1; break;
499                                                                 case "blue": selection = COLOR_TEAM2; break;
500                                                                 case "yellow": selection = COLOR_TEAM3; break;
501                                                                 case "pink": selection = COLOR_TEAM4; break;
502                                                                 case "auto": selection = (-1); break;
503                                                                 
504                                                                 default: break;
505                                                         }
506                                                         
507                                                         if(selection)
508                                                         {
509                                                                 if(self.team == selection && self.deadflag == DEAD_NO)
510                                                                         sprint(self, "^7You already are on that team.\n");
511                                                                 else if(self.wasplayer && autocvar_g_changeteam_banned)
512                                                                         sprint(self, "^1You cannot change team, forbidden by the server.\n");
513                                                                 else
514                                                                         ClientKill_TeamChange(selection);
515                                                         }
516                                                 }
517                                                 else
518                                                         sprint(self, "^7The game has already begun, you must wait until the next map to be able to join a team.\n");
519                                         else
520                                                 sprint(self, "^7selectteam can not be used as your team is forced\n");
521                                 else
522                                         sprint(self, "^7selectteam can only be used in teamgames\n");
523                         }
524                         return; // never fall through to usage
525                 }
526
527                 default:
528                 case CC_REQUEST_USAGE:
529                 {
530                         //sprint(self, strcat( "selectteam none/red/blue/yellow/pink/auto - \"", argv(1), "\" not recognised\n" ) );
531                         sprint(self, "\nUsage:^3 cmd selectteam team\n");
532                         sprint(self, "  Where 'team' is the prefered team to try and join.\n");
533                         sprint(self, "  Full list of options here: \"red, blue, yellow, pink, auto\"\n");
534                         return;
535                 }
536         }
537 }
538
539 void ClientCommand_selfstuff(float request, string command)
540 {
541         switch(request)
542         {
543                 case CC_REQUEST_COMMAND:
544                 {
545                         stuffcmd(self, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
546                         return; // never fall through to usage
547                 }
548                         
549                 default:
550                 case CC_REQUEST_USAGE:
551                 {
552                         sprint(self, "\nUsage:^3 cmd selfstuff command\n");
553                         sprint(self, "  Where 'command' is the string to be stuffed to your client.\n");
554                         return;
555                 }
556         }
557 }
558
559 void ClientCommand_sentcvar(float request, float argc, string command)
560 {
561         switch(request)
562         {
563                 case CC_REQUEST_COMMAND:
564                 {
565                         float tokens;
566                         string s;
567                         
568                         if(argc == 2) // undefined cvar: use the default value on the server then
569                         {
570                                 s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
571                                 tokens = tokenize_console(s);
572                         }
573                         GetCvars(1);
574                         return; // never fall through to usage
575                 }
576                         
577                 default:
578                 case CC_REQUEST_USAGE:
579                 {
580                         sprint(self, "\nUsage:^3 cmd sentcvar <cvar>\n");
581                         sprint(self, "  Where 'cvar' is the cvar plus arguments to send to the server.\n");
582                         return;
583                 }
584         }
585 }
586
587 void ClientCommand_spectate(float request)
588 {
589         switch(request)
590         {
591                 case CC_REQUEST_COMMAND:
592                 {
593                         if(self.flags & FL_CLIENT)
594                         {
595                                 if(g_arena) { return; } 
596                                 if(g_lms)
597                                 {
598                                         if(self.lms_spectate_warning)
599                                         {
600                                                 // mark player as spectator
601                                                 PlayerScore_Add(self, SP_LMS_RANK, 666 - PlayerScore_Add(self, SP_LMS_RANK, 0));
602                                         }
603                                         else
604                                         {
605                                                 self.lms_spectate_warning = 1;
606                                                 sprint(self, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n");
607                                                 return;
608                                         }
609                                 }
610                                 
611                                 if(self.classname == "player" && autocvar_sv_spectate == 1) 
612                                         ClientKill_TeamChange(-2); // observe
613                                 
614                                 // in CA, allow a dead player to move to spectatators (without that, caplayer!=0 will be moved back to the player list)
615                                 // note: if arena game mode is ever done properly, this needs to be removed.
616                                 if(g_ca && self.caplayer && (self.classname == "spectator" || self.classname == "observer"))
617                                 {
618                                         sprint(self, "WARNING: you will spectate in the next round.\n");
619                                         self.caplayer = 0;
620                                 }
621                         }
622                         return; // never fall through to usage
623                 }
624                         
625                 default:
626                 case CC_REQUEST_USAGE:
627                 {
628                         sprint(self, "\nUsage:^3 cmd spectate\n");
629                         sprint(self, "  No arguments required.\n");
630                         return;
631                 }
632         }
633 }
634
635 void ClientCommand_suggestmap(float request, float argc)
636 {
637         switch(request)
638         {
639                 case CC_REQUEST_COMMAND:
640                 {
641                         sprint(self, strcat(MapVote_Suggest(argv(1)), "\n"));
642                         return; // never fall through to usage
643                 }
644                         
645                 default:
646                 case CC_REQUEST_USAGE:
647                 {
648                         sprint(self, "\nUsage:^3 cmd suggestmap map\n");
649                         sprint(self, "  Where 'map' is the name of the map to suggest.\n");
650                         return;
651                 }
652         }
653 }
654
655 void ClientCommand_teamstatus(float request)
656 {
657         switch(request)
658         {
659                 case CC_REQUEST_COMMAND:
660                 {
661                         Score_NicePrint(self);
662                         return; // never fall through to usage
663                 }
664                         
665                 default:
666                 case CC_REQUEST_USAGE:
667                 {
668                         sprint(self, "\nUsage:^3 cmd teamstatus\n");
669                         sprint(self, "  No arguments required.\n");
670                         return;
671                 }
672         }
673 }
674
675 void ClientCommand_tell(float request, float argc, string command)
676 {
677         switch(request)
678         {
679                 case CC_REQUEST_COMMAND:
680                 {
681                         entity e = GetCommandPlayerSlotTargetFromTokenizedCommand(argc, 1);
682                         
683                         if(e && argc > ParseCommandPlayerSlotTarget_firsttoken)
684                         {
685                                 Say(self, FALSE, e, substring(command, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)), TRUE);
686                         }
687                         else
688                         {
689                                 if(argc > ParseCommandPlayerSlotTarget_firsttoken)
690                                         trigger_magicear_processmessage_forallears(self, -1, world, substring(command, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)));
691                         }
692                         return; // never fall through to usage
693                 }
694                         
695                 default:
696                 case CC_REQUEST_USAGE:
697                 {
698                         sprint(self, "\nUsage:^3 cmd tell playerid <message>\n");
699                         sprint(self, "  Where 'playerid' is the entity number of the player to send the 'message' to.\n");
700                         return;
701                 }
702         }
703 }
704
705 void ClientCommand_timein(float request)
706 {
707         switch(request)
708         {
709                 case CC_REQUEST_COMMAND:
710                 {
711                         if(self.flags & FL_CLIENT)
712                         {
713                                 if(autocvar_sv_timeout)
714                                 {
715                                         if (!timeoutStatus)
716                                                 return sprint(self, "^7Error: There is no active timeout which could be aborted!\n");
717                                         if (self != timeoutInitiator)
718                                                 return sprint(self, "^7Error: You may not abort the active timeout. Only the player who called it can do that!\n");
719                                                 
720                                         if (timeoutStatus == 1) 
721                                         {
722                                                 remainingTimeoutTime = timeoutStatus = 0;
723                                                 timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
724                                                 bprint(strcat("^7The timeout was aborted by ", self.netname, " !\n"));
725                                         }
726                                         else if (timeoutStatus == 2) 
727                                         {
728                                                 //only shorten the remainingTimeoutTime if it makes sense
729                                                 if( remainingTimeoutTime > (autocvar_sv_timeout_resumetime + 1) ) 
730                                                 {
731                                                         bprint(strcat("^1Attention: ^7", self.netname, " resumed the game! Prepare for battle!\n"));
732                                                         remainingTimeoutTime = autocvar_sv_timeout_resumetime;
733                                                         timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
734                                                 }
735                                                 else
736                                                         sprint(self, "^7Error: Your resumegame call was discarded!\n");
737                                         }
738                                 }
739                         }
740                         return; // never fall through to usage
741                 }
742                         
743                 default:
744                 case CC_REQUEST_USAGE:
745                 {
746                         sprint(self, "\nUsage:^3 cmd timein\n");
747                         sprint(self, "  No arguments required.\n");
748                         return;
749                 }
750         }
751 }
752
753 void ClientCommand_timeout(float request) // DEAR GOD THIS COMMAND IS TERRIBLE.
754 {
755         switch(request)
756         {
757                 case CC_REQUEST_COMMAND:
758                 {
759                         if(self.flags & FL_CLIENT)
760                         {
761                                 if(autocvar_sv_timeout) 
762                                 {
763                                         if(self.classname == "player") 
764                                         {
765                                                 if(votecalled)
766                                                         sprint(self, "^7Error: you can not call a timeout while a vote is active!\n");
767                                                 else
768                                                 {
769                                                         if (inWarmupStage && !g_warmup_allow_timeout)
770                                                                 return sprint(self, "^7Error: You can not call a timeout in warmup-stage!\n");
771                                                         if (time < game_starttime )
772                                                                 return sprint(self, "^7Error: You can not call a timeout while the map is being restarted!\n");
773                                                                 
774                                                         if (timeoutStatus != 2) {
775                                                                 //if the map uses a timelimit make sure that timeout cannot be called right before the map ends
776                                                                 if (autocvar_timelimit) {
777                                                                         //a timelimit was used
778                                                                         float myTl;
779                                                                         myTl = autocvar_timelimit;
780
781                                                                         float lastPossibleTimeout;
782                                                                         lastPossibleTimeout = (myTl*60) - autocvar_sv_timeout_leadtime - 1;
783
784                                                                         if (lastPossibleTimeout < time - game_starttime)
785                                                                                 return sprint(self, "^7Error: It is too late to call a timeout now!\n");
786                                                                 }
787                                                         }
788                                                         
789                                                         //player may not call a timeout if he has no calls left
790                                                         if (self.allowedTimeouts < 1)
791                                                                 return sprint(self, "^7Error: You already used all your timeout calls for this map!\n");
792                                                                 
793                                                                 
794                                                         //now all required checks are passed
795                                                         self.allowedTimeouts -= 1;
796                                                         bprint(self.netname, " ^7called a timeout (", ftos(self.allowedTimeouts), " timeouts left)!\n"); //write a bprint who started the timeout (and how many he has left)
797                                                         remainingTimeoutTime = autocvar_sv_timeout_length;
798                                                         remainingLeadTime = autocvar_sv_timeout_leadtime;
799                                                         timeoutInitiator = self;
800                                                         if (timeoutStatus == 0) { //if another timeout was already active, don't change its status (which was 1 or 2) to 1, only change it to 1 if no timeout was active yet
801                                                                 timeoutStatus = 1;
802                                                                 //create the timeout indicator which centerprints the information to all players and takes care of pausing/unpausing
803                                                                 timeoutHandler = spawn();
804                                                                 timeoutHandler.think = timeoutHandler_Think;
805                                                         }
806                                                         timeoutHandler.nextthink = time; //always let the entity think asap
807
808                                                         //inform all connected clients about the timeout call
809                                                         Announce("timeoutcalled");
810                                                 }
811                                         }
812                                         else
813                                                 sprint(self, "^7Error: only players can call a timeout!\n");
814                                 }
815                         }
816                         return; // never fall through to usage
817                 }
818                         
819                 default:
820                 case CC_REQUEST_USAGE:
821                 {
822                         sprint(self, "\nUsage:^3 cmd timeout\n");
823                         sprint(self, "  No arguments required.\n");
824                         return;
825                 }
826         }
827 }
828
829 void ClientCommand_voice(float request, float argc, string command)
830 {
831         switch(request)
832         {
833                 case CC_REQUEST_COMMAND:
834                 {
835                         if(argc >= 3)
836                                 VoiceMessage(argv(1), substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
837                         else
838                                 VoiceMessage(argv(1), "");
839                         return; // never fall through to usage
840                 }
841                         
842                 default:
843                 case CC_REQUEST_USAGE:
844                 {
845                         sprint(self, "\nUsage:^3 cmd voice\n");
846                         sprint(self, "  FIXME ARGUMENTS UNKNOWN.\n");
847                         return;
848                 }
849         }
850 }
851
852 void ClientCommand_who(float request)
853 {
854         switch(request)
855         {
856                 case CC_REQUEST_COMMAND:
857                 {
858                         float total_listed_players, tmp_hours, tmp_minutes, tmp_seconds;
859                         entity tmp_player;
860                         //string tmp_player_name;
861                         
862                         sprint(self, strcat("List of client information", (autocvar_sv_status_privacy ? " (some data is hidden for privacy)" : string_null), ":\n"));
863                         sprint(self, sprintf(" %-4s %-20s %-5s %-3s %-9s %-16s %s\n", "ent", "nickname", "ping", "pl", "time", "ip", "crypto_id"));
864                         
865                         FOR_EACH_CLIENT(tmp_player)
866                         {
867                                 //tmp_player_name = strlimitedlen(tmp_player.netname, TRUE, 20);
868                                 
869                                 tmp_hours = tmp_minutes = tmp_seconds = 0;
870                                 
871                                 tmp_seconds = (time - tmp_player.jointime);
872                                 tmp_minutes = (tmp_seconds / 60);
873                                 
874                                 if(tmp_minutes)
875                                 {
876                                         tmp_seconds -= (tmp_minutes * 60);
877                                         tmp_hours = (tmp_minutes / 60);
878                                         
879                                         if(tmp_hours) { tmp_minutes -= (tmp_hours * 60); }
880                                 }
881                                 
882                                 sprint(self, sprintf(" %-4s %-20s %-5d %-3d %-9s %-16s %s\n", 
883                                         strcat("#", ftos(num_for_edict(tmp_player))), 
884                                         tmp_player.netname, //strcat(tmp_player_name, sprintf("%*s", (20 - strlen(strdecolorize(tmp_player_name))), "")),
885                                         tmp_player.ping, tmp_player.ping_packetloss, 
886                                         sprintf("%02d:%02d:%02d", tmp_hours, tmp_minutes, tmp_seconds),
887                                         (autocvar_sv_status_privacy ? "hidden" : tmp_player.netaddress),
888                                         (autocvar_sv_status_privacy ? "hidden" : tmp_player.crypto_idfp)));
889                                         
890                                 ++total_listed_players;
891                         }
892                         
893                         sprint(self, strcat("Finished listing ", ftos(total_listed_players), " client(s). \n"));
894                         
895                         return; // never fall through to usage
896                 }
897                         
898                 default:
899                 case CC_REQUEST_USAGE:
900                 {
901                         sprint(self, "\nUsage:^3 cmd who\n");
902                         sprint(self, "  No arguments required.\n");
903                         return;
904                 }
905         }
906 }
907
908 /* use this when creating a new command, making sure to place it in alphabetical order.
909 void ClientCommand_(float request)
910 {
911         switch(request)
912         {
913                 case CC_REQUEST_COMMAND:
914                 {
915                         
916                         return; // never fall through to usage
917                 }
918                         
919                 default:
920                 case CC_REQUEST_USAGE:
921                 {
922                         sprint(self, "\nUsage:^3 cmd \n");
923                         sprint(self, "  No arguments required.\n");
924                         return;
925                 }
926         }
927 }
928 */
929
930
931 // =====================================
932 //  Macro system for networked commands
933 // =====================================
934
935 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
936 #define CLIENT_COMMANDS(request,arguments,command) \
937         CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(request, arguments), "Whether or not to switch automatically when getting a better weapon") \
938         CLIENT_COMMAND("checkfail", ClientCommand_checkfail(request, command), "Report if a client-side check failed") \
939         CLIENT_COMMAND("clientversion", ClientCommand_clientversion(request, arguments), "Release version of the game") \
940         CLIENT_COMMAND("cvar_changes", ClientCommand_cvar_changes(request), "Prints a list of all changed server cvars") \
941         CLIENT_COMMAND("cvar_purechanges", ClientCommand_cvar_purechanges(request), "Prints a list of all changed gameplay cvars") \
942         CLIENT_COMMAND("getmapvotepic", ClientCommand_getmapvotepic(request, arguments), "Retrieve mapshot picture from the server") \
943         CLIENT_COMMAND("info", ClientCommand_info(request, arguments), "Request for unique server information set up by admin") \
944         CLIENT_COMMAND("join", ClientCommand_join(request), "Become a player in the game") \
945         CLIENT_COMMAND("ladder", ClientCommand_ladder(request), "Get information about top players if supported") \
946         CLIENT_COMMAND("lsmaps", ClientCommand_lsmaps(request), "List maps which can be used with the current game mode") \
947         CLIENT_COMMAND("lsnewmaps", ClientCommand_lsnewmaps(request), "List maps which TODO") \
948         CLIENT_COMMAND("maplist", ClientCommand_maplist(request), "Full server maplist reply") \
949         CLIENT_COMMAND("rankings", ClientCommand_rankings(request), "Print information about rankings") \
950         CLIENT_COMMAND("ready", ClientCommand_ready(request), "Qualify as ready to end warmup stage (or restart server if allowed)") \
951         CLIENT_COMMAND("records", ClientCommand_records(request), "List top 10 records for the current map") \
952         CLIENT_COMMAND("reportcvar", ClientCommand_reportcvar(request, arguments, command), "Old system for sending a client cvar to the server") \
953         CLIENT_COMMAND("say", ClientCommand_say(request, arguments, command), "Print a message to chat to all players") \
954         CLIENT_COMMAND("say_team", ClientCommand_say_team(request, arguments, command), "Print a message to chat to all team mates") \
955         CLIENT_COMMAND("selectteam", ClientCommand_selectteam(request, arguments), "Attempt to choose a team to join into") \
956         CLIENT_COMMAND("selfstuff", ClientCommand_selfstuff(request, command), "Stuffcmd a command to your own client") \
957         CLIENT_COMMAND("sentcvar", ClientCommand_sentcvar(request, arguments, command), "New system for sending a client cvar to the server") \
958         CLIENT_COMMAND("spectate", ClientCommand_spectate(request), "Become an observer") \
959         CLIENT_COMMAND("suggestmap", ClientCommand_suggestmap(request, arguments), "Suggest a map to the mapvote at match end") \
960         CLIENT_COMMAND("teamstatus", ClientCommand_teamstatus(request), "Print detailed score information for all players") \
961         CLIENT_COMMAND("tell", ClientCommand_tell(request, arguments, command), "Send a message directly to a player") \
962         CLIENT_COMMAND("timein", ClientCommand_timein(request), "Resume the game from being paused with a timeout") \
963         CLIENT_COMMAND("timeout", ClientCommand_timeout(request), "Call a timeout which pauses the game for certain amount of time unless unpaused") \
964         CLIENT_COMMAND("voice", ClientCommand_voice(request, arguments, command), "Send voice message via sound") \
965         CLIENT_COMMAND("vote", VoteCommand(request, self, arguments, command), "Request an action to be voted upon by players") \
966         CLIENT_COMMAND("who", ClientCommand_who(request), "Display detailed client information about all players") \
967         /* nothing */
968         
969 void ClientCommand_macro_help()
970 {
971         #define CLIENT_COMMAND(name,function,description) \
972                 { print("  ^2", name, "^7: ", description, "\n"); }
973                 
974         CLIENT_COMMANDS(0, 0, "")
975         #undef CLIENT_COMMAND
976         
977         return;
978 }
979
980 float ClientCommand_macro_command(float argc, string command)
981 {
982         #define CLIENT_COMMAND(name,function,description) \
983                 { if(name == strtolower(argv(0))) { function; return TRUE; } }
984                 
985         CLIENT_COMMANDS(CC_REQUEST_COMMAND, argc, command)
986         #undef CLIENT_COMMAND
987         
988         return FALSE;
989 }
990
991 float ClientCommand_macro_usage(float argc, string command)
992 {
993         #define CLIENT_COMMAND(name,function,description) \
994                 { if(name == strtolower(argv(1))) { function; return TRUE; } }
995                 
996         CLIENT_COMMANDS(CC_REQUEST_USAGE, argc, command)
997         #undef CLIENT_COMMAND
998         
999         return FALSE;
1000 }
1001
1002
1003 // ======================================
1004 //  Main Function Called By Engine (cmd)
1005 // ======================================
1006 // If this function exists, server game code parses clientcommand before the engine code gets it.
1007
1008 void SV_ParseClientCommand(string command)
1009 {
1010         float argc = tokenize_console(command);
1011         
1012         // for floodcheck
1013         switch(strtolower(argv(0)))
1014         {
1015                 // exempt commands which are not subject to floodcheck
1016                 case "begin": break; // handled by engine in host_cmd.c
1017                 case "getmapvotepic": break; // handled by server in this file
1018                 case "pause": break; // handled by engine in host_cmd.c
1019                 case "prespawn": break; // handled by engine in host_cmd.c
1020                 case "reportcvar": break; // handled by server in this file
1021                 case "sentcvar": break; // handled by server in this file
1022                 case "spawn": break; // handled by engine in host_cmd.c
1023                 
1024                 default: 
1025                         if(SV_ParseClientCommand_floodcheck())
1026                                 break; // "TRUE": continue, as we're not flooding yet
1027                         else
1028                                 return; // "FALSE": not allowed to continue, halt
1029         }
1030         
1031         /* NOTE: totally disabled for now, however the functionality and descriptions are there if we ever want it.
1032         if(argv(0) == "help") 
1033         {
1034                 if(argc == 1) 
1035                 {
1036                         sprint(self, "\nUsage:^3 cmd COMMAND...^7, where possible commands are:\n");
1037                         ClientCommand_macro_help;
1038                         sprint(self, "For help about specific commands, type cmd help COMMAND\n");
1039                         return;
1040                 } 
1041                 else if(ClientCommand_macro_usage(argc, command)) // Instead of trying to call a command, we're going to see detailed information about it
1042                 {
1043                         return;
1044                 }
1045         } 
1046         else*/ if(MUTATOR_CALLHOOK(SV_ParseClientCommand))
1047         {
1048                 return; // handled by a mutator
1049         }
1050         else if(CheatCommand(argc)) 
1051         {
1052                 return; // handled by server/cheats.qc
1053         }
1054         else if(ClientCommand_macro_command(argc, command)) // continue as usual and scan for normal commands
1055         {
1056                 return; // handled by one of the above GameCommand_* functions
1057         }
1058         else
1059                 clientcommand(self, command);
1060 }