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