]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/clientcommands.qc
Make it compile
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / clientcommands.qc
1 // =======================================================
2 //  Server side client commands code, reworked by Samual
3 //  Last updated: July 24th, 2011
4 // =======================================================
5
6 #define CC_REQUEST_HELP 1
7 #define CC_REQUEST_COMMAND 2
8 #define CC_REQUEST_USAGE 3
9
10 entity nagger;
11
12 .float cmd_floodtime;
13 .float cmd_floodcount;
14 .float checkfail;
15
16 float readyrestart_happened;
17 float readycount;
18
19 string MapVote_Suggest(string m);
20
21 void ReadyCount();
22
23
24 // ============================
25 //  Misc. Supporting Functions
26 // ============================
27
28 float SV_ParseClientCommand_floodcheck()
29 {
30         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)
31         {
32                 if(time == self.cmd_floodtime) // todo: add buffer time as well, ONLY one second is a short amount of time for someone to be spamming. 
33                 {
34                         self.cmd_floodcount += 1;
35                         if(self.cmd_floodcount > 8) // todo: replace constant 8 with a cvar for the server to control
36                                 return FALSE; // too much spam, halt
37                 }
38                 else
39                 {
40                         self.cmd_floodtime = time;
41                         self.cmd_floodcount = 1;
42                 }
43         }
44         return TRUE; // continue, as we're not flooding yet
45 }
46
47 float Nagger_SendEntity(entity to, float sendflags)
48 {
49         float nags, i, f, b;
50         entity e;
51         WriteByte(MSG_ENTITY, ENT_CLIENT_NAGGER);
52
53         nags = 0;
54         if(readycount)
55         {
56                 nags |= 1;
57                 if(to.ready == 0)
58                         nags |= 2;
59         }
60         if(votecalled)
61         {
62                 nags |= 4;
63                 if(to.vote_vote == 0)
64                         nags |= 8;
65         }
66         if(inWarmupStage)
67                 nags |= 16;
68
69         if(sendflags & 128)
70                 nags |= 128;
71
72         if(!(nags & 4)) // no vote called? send no string
73                 nags &~= 128;
74
75         WriteByte(MSG_ENTITY, nags);
76
77         if(nags & 128)
78         {
79                 WriteString(MSG_ENTITY, votecalledvote_display);
80         }
81
82         if(nags & 1)
83         {
84                 for(i = 1; i <= maxclients; i += 8)
85                 {
86                         for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
87                                 if(clienttype(e) != CLIENTTYPE_REAL || e.ready)
88                                         f |= b;
89                         WriteByte(MSG_ENTITY, f);
90                 }
91         }
92
93         return TRUE;
94 }
95 void Nagger_Init()
96 {
97         Net_LinkEntity(nagger = spawn(), FALSE, 0, Nagger_SendEntity);
98 }
99 void Nagger_VoteChanged()
100 {
101         if(nagger)
102                 nagger.SendFlags |= 128;
103 }
104 void Nagger_VoteCountChanged()
105 {
106         if(nagger)
107                 nagger.SendFlags |= 1;
108 }
109 void Nagger_ReadyCounted()
110 {
111         if(nagger)
112                 nagger.SendFlags |= 1;
113 }
114
115 void ReadyRestartForce()
116 {
117         local entity e;
118
119         bprint("^1Server is restarting...\n");
120
121         VoteReset();
122
123         // clear overtime
124         if (checkrules_overtimesadded > 0 && g_race_qualifying != 2) {
125                 //we have to decrease timelimit to its original value again!!
126                 float newTL;
127                 newTL = autocvar_timelimit;
128                 newTL -= checkrules_overtimesadded * autocvar_timelimit_overtime;
129                 cvar_set("timelimit", ftos(newTL));
130         }
131
132         checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
133
134
135         readyrestart_happened = 1;
136         game_starttime = time;
137         if(!g_ca && !g_arena)
138                 game_starttime += RESTART_COUNTDOWN;
139         restart_mapalreadyrestarted = 0; //reset this var, needed when cvar sv_ready_restart_repeatable is in use
140
141         inWarmupStage = 0; //once the game is restarted the game is in match stage
142
143         //reset the .ready status of all players (also spectators)
144         FOR_EACH_CLIENTSLOT(e)
145                 e.ready = 0;
146         readycount = 0;
147         Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
148
149         if(autocvar_teamplay_lockonrestart && teamplay) {
150                 lockteams = 1;
151                 bprint("^1The teams are now locked.\n");
152         }
153
154         //initiate the restart-countdown-announcer entity
155         if(autocvar_sv_ready_restart_after_countdown && !g_ca && !g_arena)
156         {
157                 restartTimer = spawn();
158                 restartTimer.think = restartTimer_Think;
159                 restartTimer.nextthink = game_starttime;
160         }
161
162         //after a restart every players number of allowed timeouts gets reset, too
163         if(autocvar_sv_timeout)
164         {
165                 FOR_EACH_REALPLAYER(e)
166                         e.allowedTimeouts = autocvar_sv_timeout_number;
167         }
168
169         //reset map immediately if this cvar is not set
170         if (!autocvar_sv_ready_restart_after_countdown)
171                 reset_map(TRUE);
172
173         if(autocvar_sv_eventlog)
174                 GameLogEcho(":restart");
175 }
176
177 void ReadyRestart()
178 {
179         // no arena, assault support yet...
180         if(g_arena | g_assault | gameover | intermission_running | race_completing)
181                 localcmd("restart\n");
182         else
183                 localcmd("\nsv_hook_gamerestart\n");
184
185         ReadyRestartForce();
186
187         // reset ALL scores, but only do that at the beginning
188         //of the countdown if sv_ready_restart_after_countdown is off!
189         //Otherwise scores could be manipulated during the countdown!
190         if (!autocvar_sv_ready_restart_after_countdown)
191                 Score_ClearAll();
192 }
193
194 /**
195  * Counts how many players are ready. If not enough players are ready, the function
196  * does nothing. If all players are ready, the timelimit will be extended and the
197  * restart_countdown variable is set to allow other functions like PlayerPostThink
198  * to detect that the countdown is now active. If the cvar sv_ready_restart_after_countdown
199  * is not set the map will be resetted.
200  *
201  * Function is called after the server receives a 'ready' sign from a player.
202  */
203 void ReadyCount()
204 {
205         local entity e;
206         local float r, p;
207
208         r = p = 0;
209
210         FOR_EACH_REALPLAYER(e)
211         {
212                 p += 1;
213                 if(e.ready)
214                         r += 1;
215         }
216
217         readycount = r;
218
219         Nagger_ReadyCounted();
220
221         if(r) // at least one is ready
222         if(r == p) // and, everyone is ready
223                 ReadyRestart();
224 }
225
226 /**
227  * Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown
228  * is set)
229  */
230 void restartTimer_Think() {
231         restart_mapalreadyrestarted = 1;
232         reset_map(TRUE);
233         Score_ClearAll();
234         remove(self);
235         return;
236 }
237
238 /**
239  * Checks whether the player who calls the timeout is allowed to do so.
240  * If so, it initializes the timeout countdown. It also checks whether another
241  * timeout was already running at this time and reacts correspondingly.
242  *
243  * affected globals/fields: .allowedTimeouts, remainingTimeoutTime, remainingLeadTime,
244  *                          timeoutInitiator, timeoutStatus, timeoutHandler
245  *
246  * This function is called when a player issues the calltimeout command.
247  */
248 void evaluateTimeout() {
249         if (inWarmupStage && !g_warmup_allow_timeout)
250                 return sprint(self, "^7Error: You can not call a timeout in warmup-stage!\n");
251         if (time < game_starttime )
252                 return sprint(self, "^7Error: You can not call a timeout while the map is being restarted!\n");
253         if (timeoutStatus != 2) {
254                 //if the map uses a timelimit make sure that timeout cannot be called right before the map ends
255                 if (autocvar_timelimit) {
256                         //a timelimit was used
257                         local float myTl;
258                         myTl = autocvar_timelimit;
259
260                         local float lastPossibleTimeout;
261                         lastPossibleTimeout = (myTl*60) - autocvar_sv_timeout_leadtime - 1;
262
263                         if (lastPossibleTimeout < time - game_starttime)
264                                 return sprint(self, "^7Error: It is too late to call a timeout now!\n");
265                 }
266         }
267         //player may not call a timeout if he has no calls left
268         if (self.allowedTimeouts < 1)
269                 return sprint(self, "^7Error: You already used all your timeout calls for this map!\n");
270         //now all required checks are passed
271         self.allowedTimeouts -= 1;
272         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)
273         remainingTimeoutTime = autocvar_sv_timeout_length;
274         remainingLeadTime = autocvar_sv_timeout_leadtime;
275         timeoutInitiator = self;
276         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
277                 timeoutStatus = 1;
278                 //create the timeout indicator which centerprints the information to all players and takes care of pausing/unpausing
279                 timeoutHandler = spawn();
280                 timeoutHandler.think = timeoutHandler_Think;
281         }
282         timeoutHandler.nextthink = time; //always let the entity think asap
283
284         //inform all connected clients about the timeout call
285         Announce("timeoutcalled");
286 }
287
288 /**
289  * Checks whether a player is allowed to resume the game. If he is allowed to do it,
290  * and the lead time for the timeout is still active, this countdown just will be aborted (the
291  * game will never be paused). Otherwise the remainingTimeoutTime will be set to the corresponding
292  * value of the cvar sv_timeout_resumetime.
293  *
294  * This function is called when a player issues the resumegame command.
295  */
296 void evaluateTimein() {
297         if (!timeoutStatus)
298                 return sprint(self, "^7Error: There is no active timeout which could be aborted!\n");
299         if (self != timeoutInitiator)
300                 return sprint(self, "^7Error: You may not abort the active timeout. Only the player who called it can do that!\n");
301         if (timeoutStatus == 1) {
302                 remainingTimeoutTime = timeoutStatus = 0;
303                 timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
304                 bprint(strcat("^7The timeout was aborted by ", self.netname, " !\n"));
305         }
306         else if (timeoutStatus == 2) {
307                 //only shorten the remainingTimeoutTime if it makes sense
308                 if( remainingTimeoutTime > (autocvar_sv_timeout_resumetime + 1) ) {
309                         bprint(strcat("^1Attention: ^7", self.netname, " resumed the game! Prepare for battle!\n"));
310                         remainingTimeoutTime = autocvar_sv_timeout_resumetime;
311                         timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
312                 }
313                 else
314                         sprint(self, "^7Error: Your resumegame call was discarded!\n");
315
316         }
317 }
318
319
320 // =======================
321 //  Command Sub-Functions
322 // =======================
323
324 void ClientCommand_autoswitch(float request, entity client, float argc)
325 {
326         switch(request)
327         {
328                 case CC_REQUEST_HELP:
329                         sprint(client, "  ^2autoswitch^7: Whether or not to switch automatically when getting a better weapon\n");
330                         return;
331                         
332                 case CC_REQUEST_COMMAND:
333                         client.autoswitch = ("0" != argv(1));
334                         sprint(client, strcat("^1autoswitch is currently turned ", (client.autoswitch ? "on" : "off"), ".\n"));
335                         return; // never fall through to usage
336                         
337                 default:
338                 case CC_REQUEST_USAGE:
339                         sprint(client, "\nUsage:^3 cmd autoswitch selection\n");
340                         sprint(client, "  Where 'selection' is 1 or 0 for on or off.\n"); 
341                         return;
342         }
343 }
344
345 void ClientCommand_checkfail(float request, entity client, string command) // used only by client side code
346 {
347         switch(request)
348         {
349                 case CC_REQUEST_HELP:
350                         sprint(client, "  ^2checkfail^7: Report if a client-side check failed\n");
351                         return;
352                         
353                 case CC_REQUEST_COMMAND:
354                         print(sprintf("CHECKFAIL: %s (%s) epically failed check %s\n", client.netname, client.netaddress, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))));
355                         client.checkfail = 1;
356                         return; // never fall through to usage
357                         
358                 default:
359                 case CC_REQUEST_USAGE:
360                         sprint(client, "\nUsage:^3 cmd checkfail message\n");
361                         sprint(client, "  Where 'message' is the message reported by client about the fail.\n");
362                         return;
363         }
364 }
365
366 void ClientCommand_clientversion(float request, entity client, float argc) // used only by client side code
367 {
368         switch(request)
369         {
370                 case CC_REQUEST_HELP:
371                         sprint(client, "  ^2clientversion^7: Release version of the game\n");
372                         return;
373                         
374                 case CC_REQUEST_COMMAND:
375                         if(client.flags & FL_CLIENT)
376                         {
377                                 client.version = ((argv(1) == "$gameversion") ? 1 : stof(argv(1)));
378                                 
379                                 if(client.version < autocvar_gameversion_min || client.version > autocvar_gameversion_max)
380                                 {
381                                         client.version_mismatch = 1;
382                                         ClientKill_TeamChange(-2); // observe
383                                 } 
384                                 else if(autocvar_g_campaign || autocvar_g_balance_teams || autocvar_g_balance_teams_force) 
385                                 {
386                                         //JoinBestTeam(self, FALSE, TRUE);
387                                 } 
388                                 else if(teamplay && !autocvar_sv_spectate && !(client.team_forced > 0)) 
389                                 {
390                                         client.classname = "observer"; // really?
391                                         stuffcmd(client, "menu_showteamselect\n");
392                                 }
393                         }
394                         return; // never fall through to usage
395                         
396                 default:
397                 case CC_REQUEST_USAGE:
398                         sprint(client, "\nUsage:^3 cmd clientversion version\n");
399                         sprint(client, "  Where 'version' is the game version reported by client.\n");
400                         return;
401         }
402 }
403
404 void ClientCommand_cvar_changes(float request, entity client)
405 {
406         switch(request)
407         {
408                 case CC_REQUEST_HELP:
409                         sprint(client, "  ^2cvar_changes^7: Prints a list of all changed server cvars\n");
410                         return;
411                         
412                 case CC_REQUEST_COMMAND:
413                         sprint(client, cvar_changes);
414                         return; // never fall through to usage
415                         
416                 default:
417                 case CC_REQUEST_USAGE:
418                         sprint(client, "\nUsage:^3 sv_cmd cvar_changes\n");
419                         sprint(client, "  No arguments required.\n");
420                         //sprint(client, "See also: ^2cvar_purechanges^7\n");
421                         return;
422         }
423 }
424
425 void ClientCommand_cvar_purechanges(float request, entity client)
426 {
427         switch(request)
428         {
429                 case CC_REQUEST_HELP:
430                         sprint(client, "  ^2cvar_purechanges^7: Prints a list of all changed gameplay cvars\n");
431                         return;
432                         
433                 case CC_REQUEST_COMMAND:
434                         sprint(client, cvar_purechanges);
435                         return; // never fall through to usage
436                         
437                 default:
438                 case CC_REQUEST_USAGE:
439                         sprint(client, "\nUsage:^3 sv_cmd cvar_purechanges\n");
440                         sprint(client, "  No arguments required.\n");
441                         //sprint(client, "See also: ^2cvar_changes^7\n");
442                         return;
443         }
444 }
445
446 void ClientCommand_info(float request, entity client, float argc)
447 {
448         string command;
449         
450         switch(request)
451         {
452                 case CC_REQUEST_HELP:
453                         sprint(client, "  ^2info^7: Request for unique server information set up by admin\n");
454                         return;
455                         
456                 case CC_REQUEST_COMMAND:
457                         command = cvar_string_builtin(strcat("sv_info_", argv(1))); 
458                         if(command)
459                                 wordwrap_sprint(command, 1111); // why 1111?
460                         else
461                                 sprint(client, "ERROR: unsupported info command\n");
462                         return; // never fall through to usage
463                         
464                 default:
465                 case CC_REQUEST_USAGE:
466                         sprint(client, "\nUsage:^3 cmd info request\n");
467                         sprint(client, "  Where 'request' is the suffixed string appended onto the request for cvar.\n");
468                         return;
469         }
470 }
471
472 void ClientCommand_join(float request, entity client)
473 {
474         entity oldself;
475
476         switch(request)
477         {
478                 case CC_REQUEST_HELP:
479                         sprint(client, "  ^2join^7: Become a player in the game\n");
480                         return;
481                         
482                 case CC_REQUEST_COMMAND:
483                         if(client.flags & FL_CLIENT)
484                         {
485                                 if(client.classname != "player" && !lockteams && !g_arena)
486                                 {
487                                         if(nJoinAllowed(1)) 
488                                         {
489                                                 oldself = self;
490                                                 self = client;
491                                                 if(g_ca) { client.caplayer = 1; }
492                                                 if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
493                                                 
494                                                 client.classname = "player";
495                                                 PlayerScore_Clear(client);
496                                                 bprint ("^4", client.netname, "^4 is playing now\n");
497                                                 PutClientInServer();
498                                                 self = oldself;
499                                         }
500                                         else 
501                                         {
502                                                 //player may not join because of g_maxplayers is set
503                                                 centerprint_atprio(client, CENTERPRIO_MAPVOTE, PREVENT_JOIN_TEXT);
504                                         }
505                                 }
506                         }
507                         return; // never fall through to usage
508                         
509                 default:
510                 case CC_REQUEST_USAGE:
511                         sprint(client, "\nUsage:^3 cmd join\n");
512                         sprint(client, "  No arguments required.\n");
513                         return;
514         }
515 }
516
517 void ClientCommand_ladder(float request, entity client)
518 {
519         switch(request)
520         {
521                 case CC_REQUEST_HELP:
522                         sprint(client, "  ^2ladder^7: Get information about top players if supported\n");
523                         return;
524                         
525                 case CC_REQUEST_COMMAND:
526                         sprint(client, ladder_reply);
527                         return; // never fall through to usage
528                         
529                 default:
530                 case CC_REQUEST_USAGE:
531                         sprint(client, "\nUsage:^3 cmd ladder\n");
532                         sprint(client, "  No arguments required.\n");
533                         return;
534         }
535 }
536
537 void ClientCommand_lsmaps(float request, entity client)
538 {
539         switch(request)
540         {
541                 case CC_REQUEST_HELP:
542                         sprint(client, "  ^2lsmaps^7: List maps which can be used with the current game mode\n");
543                         return;
544                         
545                 case CC_REQUEST_COMMAND:
546                         sprint(client, lsmaps_reply);
547                         return; // never fall through to usage
548                         
549                 default:
550                 case CC_REQUEST_USAGE:
551                         sprint(client, "\nUsage:^3 cmd lsmaps\n");
552                         sprint(client, "  No arguments required.\n");
553                         return;
554         }
555 }
556
557 void ClientCommand_lsnewmaps(float request, entity client)
558 {
559         switch(request)
560         {
561                 case CC_REQUEST_HELP:
562                         sprint(client, "  ^2lsnewmaps^7: List maps which TODO\n");
563                         return;
564                         
565                 case CC_REQUEST_COMMAND:
566                         sprint(client, lsnewmaps_reply);
567                         return; // never fall through to usage
568                         
569                 default:
570                 case CC_REQUEST_USAGE:
571                         sprint(client, "\nUsage:^3 cmd lsnewmaps\n");
572                         sprint(client, "  No arguments required.\n");
573                         return;
574         }
575 }
576
577 void ClientCommand_maplist(float request, entity client)
578 {
579         switch(request)
580         {
581                 case CC_REQUEST_HELP:
582                         sprint(client, "  ^2maplist^7: Full server maplist reply\n");
583                         return;
584                         
585                 case CC_REQUEST_COMMAND:
586                         sprint(client, maplist_reply);
587                         return; // never fall through to usage
588                         
589                 default:
590                 case CC_REQUEST_USAGE:
591                         sprint(client, "\nUsage:^3 cmd maplist\n");
592                         sprint(client, "  No arguments required.\n");
593                         return;
594         }
595 }
596
597 void ClientCommand_rankings(float request, entity client)
598 {
599         switch(request)
600         {
601                 case CC_REQUEST_HELP:
602                         sprint(client, "  ^2rankings^7: Print information about rankings\n");
603                         return;
604                         
605                 case CC_REQUEST_COMMAND:
606                         sprint(client, rankings_reply);
607                         return; // never fall through to usage
608                         
609                 default:
610                 case CC_REQUEST_USAGE:
611                         sprint(client, "\nUsage:^3 cmd rankings\n");
612                         sprint(client, "  No arguments required.\n");
613                         return;
614         }
615 }
616
617 void ClientCommand_ready(float request, entity client) // TODO: reimplement how this works
618 {
619         switch(request)
620         {
621                 case CC_REQUEST_HELP:
622                         sprint(client, "  ^2ready^7: Qualify as ready to end warmup stage (or restart server if allowed)\n");
623                         return;
624                         
625                 case CC_REQUEST_COMMAND:
626                         if(client.flags & FL_CLIENT)
627                         {
628                                 if(inWarmupStage || autocvar_sv_ready_restart || g_race_qualifying == 2)
629                                 {
630                                         if(!readyrestart_happened || autocvar_sv_ready_restart_repeatable)
631                                         {
632                                                 if (client.ready) // toggle
633                                                 {
634                                                         client.ready = FALSE;
635                                                         bprint(client.netname, "^2 is ^1NOT^2 ready\n");
636                                                 }
637                                                 else
638                                                 {
639                                                         client.ready = TRUE;
640                                                         bprint(client.netname, "^2 is ready\n");
641                                                 }
642
643                                                 // cannot reset the game while a timeout is active!
644                                                 if(!timeoutStatus)
645                                                         ReadyCount();
646                                         } else {
647                                                 sprint(client, "^1Game has already been restarted\n");
648                                         }
649                                 }
650                         }
651                         return; // never fall through to usage
652                         
653                 default:
654                 case CC_REQUEST_USAGE:
655                         sprint(client, "\nUsage:^3 cmd ready\n");
656                         sprint(client, "  No arguments required.\n");
657                         return;
658         }
659 }
660
661 void ClientCommand_records(float request, entity client)
662 {
663         float i;
664         
665         switch(request)
666         {
667                 case CC_REQUEST_HELP:
668                         sprint(client, "  ^2records^7: List top 10 records for the current map\n");
669                         return;
670                         
671                 case CC_REQUEST_COMMAND:
672                         for(i = 0; i < 10; ++i)
673                                 sprint(client, records_reply[i]);
674                         return; // never fall through to usage
675                         
676                 default:
677                 case CC_REQUEST_USAGE:
678                         sprint(client, "\nUsage:^3 cmd records\n");
679                         sprint(client, "  No arguments required.\n");
680                         return;
681         }
682 }
683
684 void ClientCommand_reportcvar(float request, entity client, string command)
685 {
686         float tokens;
687         string s;
688         
689         switch(request)
690         {
691                 case CC_REQUEST_HELP:
692                         sprint(client, "  ^2reportcvar^7: Old system for sending a client cvar to the server\n");
693                         return;
694                         
695                 case CC_REQUEST_COMMAND:
696                         if(substring(argv(2), 0, 1) == "$") // undefined cvar: use the default value on the server then
697                         {
698                                 s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
699                                 tokens = tokenize_console(s);
700                         }
701                         GetCvars(1);
702                         return;
703                         
704                 default:
705                 case CC_REQUEST_USAGE:
706                         sprint(client, "\nUsage:^3 cmd reportcvar\n");
707                         sprint(client, "  No arguments required.\n");
708                         return;
709         }
710 }
711 /*
712 void ClientCommand_(float request, entity client)
713 {
714         switch(request)
715         {
716                 case CC_REQUEST_HELP:
717                         sprint(client, "  ^2blah^7: foobar\n");
718                         return;
719                         
720                 case CC_REQUEST_COMMAND:
721                         
722                         return;
723                         
724                 default:
725                 case CC_REQUEST_USAGE:
726                         sprint(client, "\nUsage:^3 cmd \n");
727                         sprint(client, "  No arguments required.\n");
728                         return;
729         }
730 }
731
732 void ClientCommand_(float request, entity client)
733 {
734         switch(request)
735         {
736                 case CC_REQUEST_HELP:
737                         sprint(client, "  ^2blah^7: foobar\n");
738                         return;
739                         
740                 case CC_REQUEST_COMMAND:
741                         
742                         return;
743                         
744                 default:
745                 case CC_REQUEST_USAGE:
746                         sprint(client, "\nUsage:^3 cmd \n");
747                         sprint(client, "  No arguments required.\n");
748                         return;
749         }
750 }
751 */
752
753
754 // ======================================
755 //  Main Function Called By Engine (cmd)
756 // ======================================
757 // If this function exists, server game code parses clientcommand before the engine code gets it.
758
759 void SV_ParseClientCommand(string command)
760 {
761         float search_request_type;
762         float argc = tokenize_console(command);
763         
764         // for floodcheck
765         switch(strtolower(argv(0)))
766         {
767                 // exempt commands which are not subject to floodcheck
768                 case "begin": break; // handled by engine in host_cmd.c
769                 case "pause": break; // handled by engine in host_cmd.c
770                 case "prespawn": break; // handled by engine in host_cmd.c
771                 case "reportcvar": break; // handled by server in this file
772                 case "sentcvar": break; // handled by server in this file
773                 case "spawn": break; // handled by engine in host_cmd.c
774                 
775                 default: 
776                         if(SV_ParseClientCommand_floodcheck())
777                                 break; // "TRUE": continue, as we're not flooding yet
778                         else
779                                 return; // "FALSE": not allowed to continue, halt
780         }
781         
782         /* NOTE: totally disabled for now, however the functionality and descriptions are there if we ever want it.
783         if(argv(0) == "help") 
784         {
785                 if(argc == 1) 
786                 {
787                         sprint(self, "\nUsage:^3 cmd COMMAND...^7, where possible commands are:\n");
788                         ClientCommand_autoswitch(CC_REQUEST_HELP, self, 0);
789                         ClientCommand_checkfail(CC_REQUEST_HELP, self, "");
790                         ClientCommand_clientversion(CC_REQUEST_HELP, self, 0);
791                         ClientCommand_cvar_changes(CC_REQUEST_HELP, self);
792                         ClientCommand_cvar_purechanges(CC_REQUEST_HELP, self);
793                         ClientCommand_info(CC_REQUEST_HELP, self, 0);
794                         ClientCommand_join(CC_REQUEST_HELP, self); 
795                         ClientCommand_ladder(CC_REQUEST_HELP, self);
796                         ClientCommand_lsmaps(CC_REQUEST_HELP, self);
797                         ClientCommand_lsnewmaps(CC_REQUEST_HELP, self);
798                         ClientCommand_maplist(CC_REQUEST_HELP, self);
799                         ClientCommand_rankings(CC_REQUEST_HELP, self);
800                         ClientCommand_ready(CC_REQUEST_HELP, self);
801                         ClientCommand_records(CC_REQUEST_HELP, self);
802                         sprint(self, "For help about specific commands, type cmd help COMMAND\n");
803                         return;
804                 } 
805                 else
806                         search_request_type = CC_REQUEST_USAGE; // Instead of trying to call a command, we're going to see detailed information about it
807         } 
808         else*/ if(GameCommand_Vote(command, self)) 
809         {
810                 return; // handled by server/vote.qc 
811         }
812         else if(GameCommand_MapVote(argv(0))) 
813         {
814                 return; // handled by server/g_world.qc
815         }
816         else if(CheatCommand(argc)) 
817         {
818                 return; // handled by server/cheats.qc
819         }
820         else
821                 search_request_type = CC_REQUEST_COMMAND; // continue as usual and scan for normal commands
822         
823         switch(strtolower((search_request_type == CC_REQUEST_USAGE) ? argv(1) : argv(0)))
824         {
825                 // Do not hard code aliases for these, instead create them in defaultXonotic.cfg
826                 // also: keep in alphabetical order, please ;)
827                 
828                 case "autoswitch": ClientCommand_autoswitch(search_request_type, self, argc); break;
829                 case "checkfail": ClientCommand_checkfail(search_request_type, self, command); break;
830                 case "clientversion": ClientCommand_clientversion(search_request_type, self, argc); break;
831                 case "cvar_changes": ClientCommand_cvar_changes(search_request_type, self); break;
832                 case "cvar_purechanges": ClientCommand_cvar_purechanges(search_request_type, self); break;
833                 case "info": ClientCommand_info(search_request_type, self, argc); break;
834                 case "join": ClientCommand_join(search_request_type, self); break;
835                 case "ladder": ClientCommand_ladder(search_request_type, self); break;
836                 case "lsmaps": ClientCommand_lsmaps(search_request_type, self); break;
837                 case "lsnewmaps": ClientCommand_lsnewmaps(search_request_type, self); break;
838                 case "maplist": ClientCommand_maplist(search_request_type, self); break;
839                 case "rankings": ClientCommand_rankings(search_request_type, self); break;
840                 case "ready": ClientCommand_ready(search_request_type, self); break;
841                 case "records": ClientCommand_records(search_request_type, self); break;
842                 
843                 default:
844                         clientcommand(self, command); //print("Invalid command. For a list of supported commands, try cmd help.\n");
845         }
846 }