Xonotic
cmd.qc
Go to the documentation of this file.
1 #include "cmd.qh"
2 
3 #include <common/command/_mod.qh>
4 #include <common/constants.qh>
6 #include <common/effects/all.qh>
8 #include <common/mapinfo.qh>
11 #include <common/monsters/_mod.qh>
15 #include <common/physics/player.qh>
16 #include <common/teams.qh>
17 #include <common/util.qh>
18 #include <common/vehicles/all.qh>
19 #include <lib/warpzone/common.qh>
20 #include <server/bot/api.qh>
22 #include <server/campaign.qh>
23 #include <server/chat.qh>
24 #include <server/cheats.qh>
25 #include <server/client.qh>
26 #include <server/clientkill.qh>
27 #include <server/command/common.qh>
29 #include <server/command/vote.qh>
30 #include <server/ipban.qh>
31 #include <server/mapvoting.qh>
32 #include <server/mutators/_mod.qh>
33 #include <server/player.qh>
34 #include <server/scores.qh>
35 #include <server/teamplay.qh>
36 #include <server/world.qh>
37 
38 // =========================================================
39 // Server side networked commands code, reworked by Samual
40 // Last updated: December 28th, 2011
41 // =========================================================
42 
44 {
45  entity store = IS_CLIENT(this) ? CS(this) : this; // unfortunately, we need to store these on the client initially
46 
47  if (!timeout_status) // not while paused
48  {
49  if (time <= (store.cmd_floodtime + autocvar_sv_clientcommand_antispam_time))
50  {
51  store.cmd_floodcount += 1;
52  if (store.cmd_floodcount > autocvar_sv_clientcommand_antispam_count) return false; // too much spam, halt
53  }
54  else
55  {
56  store.cmd_floodtime = time;
57  store.cmd_floodcount = 1;
58  }
59  }
60  return true; // continue, as we're not flooding yet
61 }
62 
63 
64 // =======================
65 // Command Sub-Functions
66 // =======================
67 
68 void ClientCommand_autoswitch(entity caller, int request, int argc)
69 {
70  switch (request)
71  {
73  {
74  if (argv(1) != "")
75  {
76  CS_CVAR(caller).cvar_cl_autoswitch = InterpretBoolean(argv(1));
77  sprint(caller, strcat("^1autoswitch is currently turned ", (CS_CVAR(caller).cvar_cl_autoswitch ? "on" : "off"), ".\n"));
78  return;
79  }
80  }
81 
82  default:
83  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
84  case CMD_REQUEST_USAGE:
85  {
86  sprint(caller, "\nUsage:^3 cmd autoswitch <selection>\n");
87  sprint(caller, " Where <selection> controls if autoswitch is on or off.\n");
88  return;
89  }
90  }
91 }
92 
93 void ClientCommand_clientversion(entity caller, int request, int argc) // internal command, used only by code
94 {
95  switch (request)
96  {
98  {
99  if (argv(1) != "")
100  {
101  if (IS_CLIENT(caller))
102  {
103  CS(caller).version = ((argv(1) == "$gameversion") ? 1 : stof(argv(1)));
104 
105  if (CS(caller).version < autocvar_gameversion_min || CS(caller).version > autocvar_gameversion_max)
106  {
107  CS(caller).version_mismatch = true;
108  ClientKill_TeamChange(caller, -2); // observe
109  }
111  {
112  // JoinBestTeam(caller, false, true);
113  }
114  else if (teamplay && !autocvar_sv_spectate && !(Player_GetForcedTeamIndex(caller) > 0))
115  {
116  TRANSMUTE(Observer, caller); // really?
117  stuffcmd(caller, "menu_showteamselect\n");
118  }
119  }
120 
121  return;
122  }
123  }
124 
125  default:
126  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
127  case CMD_REQUEST_USAGE:
128  {
129  sprint(caller, "\nUsage:^3 cmd clientversion <version>\n");
130  sprint(caller, " Where <version> is the game version reported by caller.\n");
131  return;
132  }
133  }
134 }
135 
136 void ClientCommand_mv_getpicture(entity caller, int request, int argc) // internal command, used only by code
137 {
138  switch (request)
139  {
140  case CMD_REQUEST_COMMAND:
141  {
142  if (argv(1) != "")
143  {
145 
146  return;
147  }
148  }
149 
150  default:
151  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
152  case CMD_REQUEST_USAGE:
153  {
154  sprint(caller, "\nUsage:^3 cmd mv_getpicture <mapid>\n");
155  sprint(caller, " Where <mapid> is the id number of the map to request an image of on the map vote selection menu.\n");
156  return;
157  }
158  }
159 }
160 
161 void ClientCommand_wpeditor(entity caller, int request, int argc)
162 {
163  switch (request)
164  {
165  case CMD_REQUEST_COMMAND:
166  {
168  {
169  sprint(caller, "ERROR: this command works only if the waypoint editor is on\n");
170  return;
171  }
172 
173  if (argv(1) != "")
174  {
175  if (argv(1) == "spawn")
176  {
177  string s = argv(2);
178  if (!IS_PLAYER(caller))
179  sprint(caller, "ERROR: this command works only if you are player\n");
180  else
181  waypoint_spawn_fromeditor(caller, (s == "crosshair"), (s == "jump"), (s == "crouch"), (s == "support"));
182  return;
183  }
184  else if (argv(1) == "remove")
185  {
186  if (!IS_PLAYER(caller))
187  sprint(caller, "ERROR: this command works only if you are player\n");
188  else
190  return;
191  }
192  else if (argv(1) == "hardwire")
193  {
194  string s = argv(2);
195  waypoint_start_hardwiredlink(caller, (s == "crosshair"));
196  return;
197  }
198  else if (argv(1) == "lock")
199  {
200  waypoint_lock(caller);
201  return;
202  }
203  else if (argv(1) == "unreachable")
204  {
205  if (!IS_PLAYER(caller))
206  sprint(caller, "ERROR: this command works only if you are player\n");
207  else
208  waypoint_unreachable(caller);
209  return;
210  }
211  else if (argv(1) == "saveall")
212  {
214  return;
215  }
216  else if (argv(1) == "relinkall")
217  {
219  return;
220  }
221  else if (argv(1) == "symaxis")
222  {
223  if (argv(2) == "set" || argv(2) == "get")
224  {
225  waypoint_getSymmetricalAxis_cmd(caller, (argv(2) == "set"), 3);
226  return;
227  }
228  }
229  else if (argv(1) == "symorigin")
230  {
231  if (argv(2) == "set" || argv(2) == "get")
232  {
233  waypoint_getSymmetricalOrigin_cmd(caller, (argv(2) == "set"), 3);
234  return;
235  }
236  }
237  }
238  }
239 
240  default:
241  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
242  case CMD_REQUEST_USAGE:
243  {
244  sprint(caller, "\nUsage:^3 cmd wpeditor <action>\n");
245  sprint(caller, " Where <action> can be:\n");
246  sprint(caller, " ^2spawn^7: spawns a waypoint at player's position\n");
247  sprint(caller, " ^2remove^7: removes player's nearest waypoint\n");
248  sprint(caller, " ^2unreachable^7: reveals waypoints and items unreachable from the current position and spawnpoints without a nearest waypoint\n");
249  sprint(caller, " ^2saveall^7: saves all waypoints and links to file\n");
250  sprint(caller, " ^2relinkall^7: relinks all waypoints as if they were respawned\n");
251  sprint(caller, " ^2spawn crosshair^7: spawns a waypoint at crosshair's position\n");
252  sprint(caller, " ^7 in general useful to create special and hardwired links with ease from existing waypoints\n");
253  sprint(caller, " ^7 in particular it's the only way to create custom jumppad waypoints (spawn another waypoint to create destination))\n");
254  sprint(caller, " ^2spawn jump^7: spawns a jump waypoint (place it at least 60 qu before jump start, spawn another waypoint to create destination)\n");
255  sprint(caller, " ^2spawn crouch^7: spawns a crouch waypoint (it links only to very close waypoints)\n");
256  sprint(caller, " ^2spawn support^7: spawns a support waypoint (spawn another waypoint to create destination from which all incoming links are removed)\n");
257  sprint(caller, " ^7 useful to replace links to problematic jumppad/teleport waypoints\n");
258  sprint(caller, " ^2hardwire^7: marks the nearest waypoint as origin of a new hardwired link (spawn another waypoint over an existing one to create destination)\n");
259  sprint(caller, " ^2hardwire crosshair^7: marks the waypoint at crosshair instead of the nearest waypoint\n");
260  sprint(caller, " ^2lock^7: locks link display of the aimed waypoint (unlocks if no waypoint is found at crosshair's position)\n");
261  sprint(caller, " ^2symorigin get|set\n");
262  sprint(caller, " ^2symorigin get|set <p1> <p2> ... <pX>\n");
263  sprint(caller, " ^2symaxis get|set <p1> <p2>\n");
264  sprint(caller, " ^7 where <p1> <p2> ... <pX> are positions (\"x y z\", z can be omitted) that you know are perfectly symmetrical"
265  " so you can determine origin/axis of symmetry of maps without ctf flags or where flags aren't perfectly symmetrical\n");
266  sprint(caller, " See ^5wpeditor_menu^7 for a selectable list of various commands and useful settings to edit waypoints.\n");
267  return;
268  }
269  }
270 }
271 
272 void ClientCommand_join(entity caller, int request)
273 {
274  switch (request)
275  {
276  case CMD_REQUEST_COMMAND:
277  {
278  if (!game_stopped && IS_CLIENT(caller) && !IS_PLAYER(caller))
279  {
280  if (joinAllowed(caller))
281  Join(caller);
282  else if(time < CS(caller).jointime + MIN_SPEC_TIME)
283  CS(caller).autojoin_checked = -1;
284  }
285 
286  return; // never fall through to usage
287  }
288 
289  default:
290  case CMD_REQUEST_USAGE:
291  {
292  sprint(caller, "\nUsage:^3 cmd join\n");
293  sprint(caller, " No arguments required.\n");
294  return;
295  }
296  }
297 }
298 
299 void ClientCommand_kill(entity caller, int request)
300 {
301  switch (request)
302  {
303  case CMD_REQUEST_COMMAND:
304  {
305  if(IS_SPEC(caller) || IS_OBSERVER(caller))
306  return; // no point warning about this, command does nothing
307 
308  if(GetResource(caller, RES_HEALTH) <= 0)
309  {
310  sprint(caller, "Can't die - you are already dead!\n");
311  return;
312  }
313 
314  ClientKill(caller);
315 
316  return; // never fall through to usage
317  }
318 
319  default:
320  case CMD_REQUEST_USAGE:
321  {
322  sprint(caller, "\nUsage:^3 cmd kill\n");
323  sprint(caller, " No arguments required.\n");
324  return;
325  }
326  }
327 }
328 
329 void ClientCommand_physics(entity caller, int request, int argc)
330 {
331  switch (request)
332  {
333  case CMD_REQUEST_COMMAND:
334  {
335  string command = strtolower(argv(1));
336 
337  if (!autocvar_g_physics_clientselect)
338  {
339  sprint(caller, "Client physics selection is currently disabled.\n");
340  return;
341  }
342 
343  if (command == "list" || command == "help")
344  {
345  sprint(caller, strcat("Available physics sets: \n\n", autocvar_g_physics_clientselect_options, " default\n"));
346  return;
347  }
348 
349  if (Physics_Valid(command) || command == "default")
350  {
351  stuffcmd(caller, strcat("\nseta cl_physics ", command, "\n"));
352  sprint(caller, strcat("^2Physics set successfully changed to ^3", command, "\n"));
353  return;
354  }
355  }
356 
357  default:
358  sprint(caller, strcat("Current physics set: ^3", CS_CVAR(caller).cvar_cl_physics, "\n"));
359  case CMD_REQUEST_USAGE:
360  {
361  sprint(caller, "\nUsage:^3 cmd physics <physics>\n");
362  sprint(caller, " See 'cmd physics list' for available physics sets.\n");
363  sprint(caller, " Argument 'default' resets to standard physics.\n");
364  return;
365  }
366  }
367 }
368 
369 void ClientCommand_ready(entity caller, int request)
370 {
371  switch (request)
372  {
373  case CMD_REQUEST_COMMAND:
374  {
375  if (IS_CLIENT(caller) && caller.last_ready < time - 3)
376  {
377  if (warmup_stage || g_race_qualifying == 2)
378  {
379  if (time < game_starttime) // game is already restarting
380  return;
381  if (caller.ready) // toggle
382  {
383  caller.ready = false;
384  if (IS_PLAYER(caller) || INGAME_JOINED(caller))
385  bprint(playername(caller.netname, caller.team, false), "^2 is ^1NOT^2 ready\n");
386  }
387  else
388  {
389  caller.ready = true;
390  if (IS_PLAYER(caller) || INGAME_JOINED(caller))
391  bprint(playername(caller.netname, caller.team, false), "^2 is ready\n");
392  }
393 
394  caller.last_ready = time;
395 
396  // cannot reset the game while a timeout is active!
397  if (!timeout_status) ReadyCount();
398  }
399  }
400  return; // never fall through to usage
401  }
402 
403  default:
404  case CMD_REQUEST_USAGE:
405  {
406  sprint(caller, "\nUsage:^3 cmd ready\n");
407  sprint(caller, " No arguments required.\n");
408  return;
409  }
410  }
411 }
412 
413 void ClientCommand_say(entity caller, int request, int argc, string command)
414 {
415  switch (request)
416  {
417  case CMD_REQUEST_COMMAND:
418  {
419  if (argc >= 2) Say(caller, false, NULL, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
420  return; // never fall through to usage
421  }
422 
423  default:
424  case CMD_REQUEST_USAGE:
425  {
426  sprint(caller, "\nUsage:^3 cmd say <message>\n");
427  sprint(caller, " Where <message> is the string of text to say.\n");
428  return;
429  }
430  }
431 }
432 
433 void ClientCommand_say_team(entity caller, int request, int argc, string command)
434 {
435  switch (request)
436  {
437  case CMD_REQUEST_COMMAND:
438  {
439  if (argc >= 2)
440  Say(caller, true, NULL, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
441  return; // never fall through to usage
442  }
443 
444  default:
445  case CMD_REQUEST_USAGE:
446  {
447  sprint(caller, "\nUsage:^3 cmd say_team <message>\n");
448  sprint(caller, " Where <message> is the string of text to say.\n");
449  return;
450  }
451  }
452 }
453 
455 void ClientCommand_selectteam(entity caller, int request, int argc)
456 {
457  switch (request)
458  {
459  case CMD_REQUEST_COMMAND:
460  {
461  if (argv(1) == "")
462  {
463  return;
464  }
465  if (!IS_CLIENT(caller))
466  {
467  return;
468  }
469  if (!teamplay)
470  {
471  sprint(caller, "^7selectteam can only be used in teamgames\n");
472  return;
473  }
474  if (Player_GetForcedTeamIndex(caller) > 0)
475  {
476  sprint(caller, "^7selectteam can not be used as your team is forced\n");
477  return;
478  }
479  if (lockteams)
480  {
481  sprint(caller, "^7The game has already begun, you must wait until the next map to be able to join a team.\n");
482  return;
483  }
484 
485  float team_num = Team_ColorToTeam(argv(1));
486  if (team_num == -1) // invalid
487  return;
488  if (caller.team == team_num && team_num && !IS_DEAD(caller))
489  {
490  sprint(caller, "^7You already are on that team.\n");
491  return;
492  }
494  {
495  sprint(caller, "^1You cannot change team, forbidden by the server.\n");
496  return;
497  }
499  {
500  entity balance = TeamBalance_CheckAllowedTeams(caller);
501  TeamBalance_GetTeamCounts(balance, caller);
502  if ((Team_IndexToBit(Team_TeamToIndex(team_num)) &
503  TeamBalance_FindBestTeams(balance, caller, false)) == 0)
504  {
505  Send_Notification(NOTIF_ONE, caller, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
506  TeamBalance_Destroy(balance);
507  return;
508  }
509  TeamBalance_Destroy(balance);
510  }
511  if (team_num)
512  ClientKill_TeamChange(caller, team_num);
513  else // auto
514  ClientKill_TeamChange(caller, -1);
515 
516  if (!IS_PLAYER(caller))
517  {
518  caller.team_selected = true; // avoids asking again for team selection on join
519  }
520  return;
521  }
522  default:
523  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
524  case CMD_REQUEST_USAGE:
525  {
526  sprint(caller, "\nUsage:^3 cmd selectteam <team>\n");
527  sprint(caller, " Where <team> is the prefered team to try and join.\n");
528  sprint(caller, " Full list of options here: red, blue, yellow, pink, auto.\n");
529  return;
530  }
531  }
532 }
533 
534 void ClientCommand_selfstuff(entity caller, int request, string command)
535 {
536  switch (request)
537  {
538  case CMD_REQUEST_COMMAND:
539  {
540  if (argv(1) != "")
541  {
542  stuffcmd(caller, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
543  return;
544  }
545  }
546 
547  default:
548  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
549  case CMD_REQUEST_USAGE:
550  {
551  sprint(caller, "\nUsage:^3 cmd selfstuff <command>\n");
552  sprint(caller, " Where <command> is the string to be stuffed to your client.\n");
553  return;
554  }
555  }
556 }
557 
558 void ClientCommand_sentcvar(entity caller, int request, int argc)
559 {
560  switch (request)
561  {
562  case CMD_REQUEST_COMMAND:
563  {
564  if (argc >= 3)
565  {
566  // NOTE: client-side settings do not exist on the server, this functionality has been deprecated
567  #if 0
568  if (argc == 2) // undefined cvar: use the default value on the server then
569  {
570  string s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
571  tokenize_console(s);
572  }
573  #endif
574 
575  GetCvars(caller, CS_CVAR(caller), 1);
576 
577  return;
578  }
579  }
580 
581  default:
582  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
583  case CMD_REQUEST_USAGE:
584  {
585  sprint(caller, "\nUsage:^3 cmd sentcvar <cvar> <arguments>\n");
586  sprint(caller, " Where <cvar> is the cvar plus arguments to send to the server.\n");
587  return;
588  }
589  }
590 }
591 
592 void ClientCommand_spectate(entity caller, int request)
593 {
594  switch (request)
595  {
596  case CMD_REQUEST_COMMAND:
597  {
598  if (!intermission_running && IS_CLIENT(caller))
599  {
600  if(argv(1) != "")
601  {
602  if(IS_SPEC(caller) || IS_OBSERVER(caller))
603  {
604  entity client = GetFilteredEntity(argv(1));
605  int spec_accepted = VerifyClientEntity(client, false, false);
606  if(spec_accepted > 0 && IS_PLAYER(client))
607  {
608  bool caller_is_observer = (IS_OBSERVER(caller));
609  Spectate(caller, client);
610  if (caller_is_observer)
611  TRANSMUTE(Spectator, caller);
612  }
613  else
614  sprint(caller, "Can't spectate ", argv(1), "^7\n");
615  }
616  else
617  sprint(caller, "cmd spectate <client> only works when you are spectator/observer\n");
618  return;
619  }
620 
621  int mutator_returnvalue = MUTATOR_CALLHOOK(ClientCommand_Spectate, caller);
622 
623  if (mutator_returnvalue == MUT_SPECCMD_RETURN) return;
624 
625  if ((IS_PLAYER(caller) || mutator_returnvalue == MUT_SPECCMD_FORCE))
626  if (autocvar_sv_spectate == 1)
627  ClientKill_TeamChange(caller, -2); // observe
628  }
629  return; // never fall through to usage
630  }
631 
632  default:
633  case CMD_REQUEST_USAGE:
634  {
635  sprint(caller, "\nUsage:^3 cmd spectate [<client>]\n");
636  sprint(caller, " Where <client> can be the player to spectate.\n");
637  return;
638  }
639  }
640 }
641 
642 void ClientCommand_suggestmap(entity caller, int request, int argc)
643 {
644  switch (request)
645  {
646  case CMD_REQUEST_COMMAND:
647  {
648  if (argv(1) != "")
649  {
650  sprint(caller, strcat(MapVote_Suggest(caller, argv(1)), "\n"));
651  return;
652  }
653  }
654 
655  default:
656  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
657  case CMD_REQUEST_USAGE:
658  {
659  sprint(caller, "\nUsage:^3 cmd suggestmap <map>\n");
660  sprint(caller, " Where <map> is the name of the map to suggest.\n");
661  return;
662  }
663  }
664 }
665 
666 void ClientCommand_tell(entity caller, int request, int argc, string command)
667 {
668  switch (request)
669  {
670  case CMD_REQUEST_COMMAND:
671  {
672  if (argc >= 3)
673  {
674  if(!IS_CLIENT(caller) && IS_REAL_CLIENT(caller)) // connecting
675  {
676  print_to(caller, "You can't ^2tell^7 a message while connecting.");
677  return;
678  }
679 
680  entity tell_to = GetIndexedEntity(argc, 1);
681  float tell_accepted = VerifyClientEntity(tell_to, true, false);
682 
683  if (tell_accepted > 0) // the target is a real client
684  {
685  if (tell_to != caller) // and we're allowed to send to them :D
686  {
687  // workaround for argv indexes indexing ascii chars instead of utf8 chars
688  // In this case when the player name contains utf8 chars
689  // the message gets partially trimmed in the beginning.
690  // Potentially this bug affects any substring call that uses
691  // argv_start_index and argv_end_index.
692 
693  string utf8_enable_save = cvar_string("utf8_enable");
694  cvar_set("utf8_enable", "0");
696  cvar_set("utf8_enable", utf8_enable_save);
697 
698  Say(caller, false, tell_to, msg, true);
699  return;
700  }
701  else { print_to(caller, "You can't ^2tell^7 a message to yourself."); return; }
702  }
703  else if (argv(1) == "#0")
704  {
705  trigger_magicear_processmessage_forallears(caller, -1, NULL, substring(command, argv_start_index(next_token), argv_end_index(-1) - argv_start_index(next_token)));
706  return;
707  }
708  else { print_to(caller, strcat("tell: ", GetClientErrorString(tell_accepted, argv(1)), ".")); return; }
709  }
710  }
711 
712  default:
713  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
714  case CMD_REQUEST_USAGE:
715  {
716  sprint(caller, "\nUsage:^3 cmd tell <client> <message>\n");
717  sprint(caller, " Where <client> is the entity number or name of the player to send <message> to.\n");
718  return;
719  }
720  }
721 }
722 
723 void ClientCommand_voice(entity caller, int request, int argc, string command)
724 {
725  switch (request)
726  {
727  case CMD_REQUEST_COMMAND:
728  {
729  if (argv(1) != "")
730  {
731  entity e = GetVoiceMessage(argv(1));
732  if (!e)
733  {
734  sprint(caller, sprintf("Invalid voice. Use one of: %s\n", allvoicesamples));
735  return;
736  }
737  if (IS_DEAD(caller))
738  {
739  // don't warn the caller when trying to taunt while dead, just don't play any sounds!
740  // we still allow them to see warnings if it's invalid, so a dead player can find out the sounds in peace
741  return;
742  }
743  if (IS_SPEC(caller) || IS_OBSERVER(caller))
744  {
745  // observers/spectators have no player model of their own to play taunts from
746  // again, allow them to see warnings
747  return;
748  }
749  string msg = "";
750  if (argc >= 3)
751  msg = substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2));
752  VoiceMessage(caller, e, msg);
753 
754  return;
755  }
756  }
757 
758  default:
759  sprint(caller, sprintf("Incorrect parameters for ^2%s^7\n", argv(0)));
760  case CMD_REQUEST_USAGE:
761  {
762  sprint(caller, "\nUsage:^3 cmd voice <voicetype> [<message>]\n");
763  sprint(caller, " <voicetype> is the type of voice message, it can be one of these:\n");
764  sprint(caller, sprintf(" %s\n", allvoicesamples));
765  sprint(caller, " and <message> is the text message to send.\n");
766  return;
767  }
768  }
769 }
770 
771 /* use this when creating a new command, making sure to place it in alphabetical order... also,
772 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
773 void ClientCommand_(entity caller, int request)
774 {
775  switch(request)
776  {
777  case CMD_REQUEST_COMMAND:
778  {
779 
780  return; // never fall through to usage
781  }
782 
783  default:
784  case CMD_REQUEST_USAGE:
785  {
786  sprint(caller, "\nUsage:^3 cmd \n");
787  sprint(caller, " No arguments required.\n");
788  return;
789  }
790  }
791 }
792 */
793 
794 
795 // =====================================
796 // Macro system for networked commands
797 // =====================================
798 
799 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
800 #define CLIENT_COMMANDS(ent, request, arguments, command) \
801  CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(ent, request, arguments), "Whether or not to switch automatically when getting a better weapon") \
802  CLIENT_COMMAND("clientversion", ClientCommand_clientversion(ent, request, arguments), "Release version of the game") \
803  CLIENT_COMMAND("join", ClientCommand_join(ent, request), "Become a player in the game") \
804  CLIENT_COMMAND("kill", ClientCommand_kill(ent, request), "Become a member of the dead") \
805  CLIENT_COMMAND("minigame", ClientCommand_minigame(ent, request, arguments, command), "Start a minigame") \
806  CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(ent, request, arguments), "Retrieve mapshot picture from the server") \
807  CLIENT_COMMAND("physics", ClientCommand_physics(ent, request, arguments), "Change physics set") \
808  CLIENT_COMMAND("ready", ClientCommand_ready(ent, request), "Qualify as ready to end warmup stage (or restart server if allowed)") \
809  CLIENT_COMMAND("say", ClientCommand_say(ent, request, arguments, command), "Print a message to chat to all players") \
810  CLIENT_COMMAND("say_team", ClientCommand_say_team(ent, request, arguments, command), "Print a message to chat to all team mates") \
811  CLIENT_COMMAND("selectteam", ClientCommand_selectteam(ent, request, arguments), "Attempt to choose a team to join into") \
812  CLIENT_COMMAND("selfstuff", ClientCommand_selfstuff(ent, request, command), "Stuffcmd a command to your own client") \
813  CLIENT_COMMAND("sentcvar", ClientCommand_sentcvar(ent, request, arguments), "New system for sending a client cvar to the server") \
814  CLIENT_COMMAND("spectate", ClientCommand_spectate(ent, request), "Become an observer") \
815  CLIENT_COMMAND("suggestmap", ClientCommand_suggestmap(ent, request, arguments), "Suggest a map to the mapvote at match end") \
816  CLIENT_COMMAND("tell", ClientCommand_tell(ent, request, arguments, command), "Send a message directly to a player") \
817  CLIENT_COMMAND("voice", ClientCommand_voice(ent, request, arguments, command), "Send voice message via sound") \
818  CLIENT_COMMAND("wpeditor", ClientCommand_wpeditor(ent, request, arguments), "Waypoint editor commands") \
819  /* nothing */
820 
822 {
823  #define CLIENT_COMMAND(name, function, description) \
824  { sprint(caller, " ^2", name, "^7: ", description, "\n"); }
825 
826  CLIENT_COMMANDS(NULL, 0, 0, "");
827 #undef CLIENT_COMMAND
828 }
829 
830 float ClientCommand_macro_command(int argc, entity caller, string command)
831 {
832  #define CLIENT_COMMAND(name, function, description) \
833  { if (name == strtolower(argv(0))) { function; return true; } }
834 
835  CLIENT_COMMANDS(caller, CMD_REQUEST_COMMAND, argc, command);
836 #undef CLIENT_COMMAND
837 
838  return false;
839 }
840 
841 float ClientCommand_macro_usage(int argc, entity caller)
842 {
843  #define CLIENT_COMMAND(name, function, description) \
844  { if (name == strtolower(argv(1))) { function; return true; } }
845 
846  CLIENT_COMMANDS(caller, CMD_REQUEST_USAGE, argc, "");
847 #undef CLIENT_COMMAND
848 
849  return false;
850 }
851 
853 {
854  #define CLIENT_COMMAND(name, function, description) \
855  { CMD_Write_Alias("qc_cmd_cmd", name, description); }
856 
857  CLIENT_COMMANDS(NULL, 0, 0, "");
858 #undef CLIENT_COMMAND
859 }
860 
861 // ======================================
862 // Main Function Called By Engine (cmd)
863 // ======================================
864 // If this function exists, server game code parses clientcommand before the engine code gets it.
865 
866 void SV_ParseClientCommand(entity this, string command)
867 {
868  // If invalid UTF-8, don't even parse it
869  string command2 = "";
870  float len = strlen(command);
871  float i;
872  for (i = 0; i < len; ++i)
873  command2 = strcat(command2, chr2str(str2chr(command, i)));
874  if (command != command2) return;
875 
876  // if we're banned, don't even parse the command
877  if (Ban_MaybeEnforceBanOnce(this)) return;
878 
879  int argc = tokenize_console(command);
880 
881  // Guide for working with argc arguments by example:
882  // argc: 1 - 2 - 3 - 4
883  // argv: 0 - 1 - 2 - 3
884  // cmd vote - master - login - password
885 
886  // for floodcheck
887  switch (strtolower(argv(0)))
888  {
889  // exempt commands which are not subject to floodcheck
890  case "begin": break; // handled by engine in host_cmd.c
891  case "download": break; // handled by engine in cl_parse.c
892  case "mv_getpicture": break; // handled by server in this file
893  case "wpeditor": break; // handled by server in this file
894  case "pause": break; // handled by engine in host_cmd.c
895  case "prespawn": break; // handled by engine in host_cmd.c
896  case "sentcvar": break; // handled by server in this file
897  case "spawn": break; // handled by engine in host_cmd.c
898  case "color": case "topcolor": case "bottomcolor": // handled by engine in host_cmd.c
899  if(!IS_CLIENT(this)) // on connection
900  {
901  // since gamecode doesn't have any calls earlier than this, do the connecting message here
902  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CONNECTING, this.netname);
903  }
904  if(teamplay)
905  return;
906  break;
907  case "c2s": Net_ClientCommand(this, command); return; // handled by net.qh
908 
909  default:
910  if (SV_ParseClientCommand_floodcheck(this)) break; // "true": continue, as we're not flooding yet
911  else return; // "false": not allowed to continue, halt // print("^1ERROR: ^7ANTISPAM CAUGHT: ", command, ".\n");
912  }
913 
914  /* NOTE: should this be disabled? It can be spammy perhaps, but hopefully it's okay for now */
915  if (argv(0) == "help")
916  {
917  if (argc == 1)
918  {
919  sprint(this, "\nClient networked commands:\n");
921 
922  sprint(this, "\nCommon networked commands:\n");
924 
925  sprint(this, "\nUsage:^3 cmd <command>^7, where possible commands are listed above.\n");
926  sprint(this, "For help about a specific command, type cmd help <command>\n");
927  return;
928  }
929  else if (CommonCommand_macro_usage(argc, this)) // Instead of trying to call a command, we're going to see detailed information about it
930  {
931  return;
932  }
933  else if (ClientCommand_macro_usage(argc, this)) // same, but for normal commands now
934  {
935  return;
936  }
937  }
938  else if (MUTATOR_CALLHOOK(SV_ParseClientCommand, this, strtolower(argv(0)), argc, command))
939  {
940  return; // handled by a mutator
941  }
942  else if (CheatCommand(this, argc))
943  {
944  return; // handled by server/cheats.qc
945  }
946  else if (CommonCommand_macro_command(argc, this, command))
947  {
948  return; // handled by server/command/common.qc
949  }
950  else if (ClientCommand_macro_command(argc, this, command)) // continue as usual and scan for normal commands
951  {
952  return; // handled by one of the above ClientCommand_* functions
953  }
954  else
955  {
956  clientcommand(this, command);
957  }
958 }
void ClientCommand_tell(entity caller, int request, int argc, string command)
Definition: cmd.qc:666
int Team_IndexToBit(int index)
Converts team index into bit value that is used in team bitmasks.
Definition: teams.qh:211
void ClientKill_TeamChange(entity this, float targetteam)
Definition: clientkill.qc:98
void ReadyCount()
Definition: vote.qc:498
entity GetIndexedEntity(int argc, float start_index)
Definition: common.qc:83
bool SV_ParseClientCommand_floodcheck(entity this)
Definition: cmd.qc:43
TRANSMUTE(Player, this)
bool team_selected
Definition: cmd.qc:454
float CommonCommand_macro_usage(int argc, entity caller)
Definition: common.qh:178
bool wasplayer
Definition: client.qh:69
int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol)
message "": do not say, just test flood control return value: 1 = accept 0 = reject -1 = fake accept ...
Definition: chat.qc:25
#define str2chr
Definition: dpextensions.qh:45
#define IS_CLIENT(v)
Definition: utils.qh:13
void ClientCommand_autoswitch(entity caller, int request, int argc)
Definition: cmd.qc:68
void waypoint_lock(entity pl)
Definition: waypoints.qc:268
const int CMD_REQUEST_USAGE
Definition: command.qh:4
void ClientCommand_macro_help(entity caller)
Definition: cmd.qc:821
void ClientCommand_selectteam(entity caller, int request, int argc)
Definition: cmd.qc:455
void ClientCommand_selfstuff(entity caller, int request, string command)
Definition: cmd.qc:534
void ClientKill(entity this)
Definition: clientkill.qc:208
int TeamBalance_FindBestTeams(entity balance, entity player, bool use_score)
Returns the bitmask of the teams that will make the game most balanced if the player joins any of the...
Definition: teamplay.qc:820
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition: teamplay.qc:627
entity() spawn
void CommonCommand_macro_help(entity caller)
Definition: common.qh:163
void ClientCommand_say_team(entity caller, int request, int argc, string command)
Definition: cmd.qc:433
ClientState CS(Client this)
Definition: state.qh:47
float ClientCommand_macro_command(int argc, entity caller, string command)
Definition: cmd.qc:830
#define CLIENT_COMMANDS(ent, request, arguments, command)
Definition: cmd.qc:800
#define IS_OBSERVER(v)
Definition: utils.qh:11
#define CS_CVAR(this)
Definition: state.qh:51
void ClientCommand_say(entity caller, int request, int argc, string command)
Definition: cmd.qc:413
float autocvar_gameversion_max
Definition: client.qh:48
float VerifyClientEntity(entity client, float must_be_real, float must_be_bots)
Definition: common.qc:47
bool warmup_stage
Definition: main.qh:103
void ClientCommand_voice(entity caller, int request, int argc, string command)
Definition: cmd.qc:723
void waypoint_spawn_fromeditor(entity pl, bool at_crosshair, bool is_jump_wp, bool is_crouch_wp, bool is_support_wp)
Definition: waypoints.qc:570
string netname
Definition: powerups.qc:20
bool Spectate(entity this, entity pl)
Definition: client.qc:1873
int autocvar_sv_clientcommand_antispam_count
Definition: cmd.qh:4
void print_to(entity to, string input)
Definition: common.qc:172
bool autocvar_g_changeteam_banned
Definition: teamplay.qh:5
void ClientCommand_macro_write_aliases(float fh)
Definition: cmd.qc:852
void ClientCommand_kill(entity caller, int request)
Definition: cmd.qc:299
bool g_race_qualifying
Definition: race.qh:12
entity GetFilteredEntity(string input)
Definition: common.qc:144
void ClientCommand_mv_getpicture(entity caller, int request, int argc)
Definition: cmd.qc:136
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
float autocvar_gameversion_min
Definition: client.qh:47
void waypoint_getSymmetricalAxis_cmd(entity caller, bool save, int arg_idx)
Definition: waypoints.qc:137
RES_HEALTH
Definition: ent_cs.qc:126
float next_token
Definition: common.qh:71
#define IS_SPEC(v)
Definition: utils.qh:10
#define argv_end_index
Definition: dpextensions.qh:30
bool waypointeditor_enabled
Definition: waypoints.qh:3
float ClientCommand_macro_usage(int argc, entity caller)
Definition: cmd.qc:841
bool autocvar_g_balance_teams_prevent_imbalance
Definition: teamplay.qh:9
void SV_ParseClientCommand(entity this, string command)
Definition: cmd.qc:866
void ClientCommand_sentcvar(entity caller, int request, int argc)
Definition: cmd.qc:558
int Player_GetForcedTeamIndex(entity player)
Returns the index of the forced team of the given player.
Definition: teamplay.qc:346
void ClientCommand_ready(entity caller, int request)
Definition: cmd.qc:369
void ClientCommand_join(entity caller, int request)
Definition: cmd.qc:272
#define argv_start_index
Definition: dpextensions.qh:27
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
void GetCvars(entity this, entity store, int f)
Definition: getreplies.qc:381
void TeamBalance_GetTeamCounts(entity balance, entity ignore)
Counts the number of players and various other information about each team.
Definition: teamplay.qc:681
void waypoint_saveall()
Definition: waypoints.qc:1760
bool joinAllowed(entity this)
Definition: client.qc:2108
void ClientCommand_spectate(entity caller, int request)
Definition: cmd.qc:592
#define NULL
Definition: post.qh:17
const int CMD_REQUEST_COMMAND
Definition: command.qh:3
void MapVote_SendPicture(entity to, int id)
Definition: mapvoting.qc:258
ERASEABLE float InterpretBoolean(string input)
Definition: bool.qh:13
int autocvar_sv_spectate
Definition: client.qh:54
void waypoint_getSymmetricalOrigin_cmd(entity caller, bool save, int arg_idx)
Definition: waypoints.qc:169
float jointime
Definition: client.qh:64
bool Ban_MaybeEnforceBanOnce(entity client)
Definition: ipban.qc:471
void waypoint_start_hardwiredlink(entity pl, bool at_crosshair)
Definition: waypoints.qc:534
float teamplay
Definition: progsdefs.qc:31
#define IS_DEAD(s)
Definition: utils.qh:26
void ClientCommand_physics(entity caller, int request, int argc)
Definition: cmd.qc:329
bool lockteams
Definition: teamplay.qh:13
void waypoint_unreachable(entity pl)
Definition: waypoints.qc:28
#define GetClientErrorString(clienterror, original_input)
Definition: common.qh:87
int Team_TeamToIndex(int team_num)
Converts team value into team index.
Definition: teams.qh:184
#define stuffcmd(cl,...)
Definition: progsdefs.qh:23
#define tokenize_console
Definition: dpextensions.qh:24
float Team_ColorToTeam(string team_color)
Definition: teams.qh:116
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
string allvoicesamples
Definition: globalsound.qh:159
string MapVote_Suggest(entity this, string m)
Definition: mapvoting.qc:119
void ClientCommand_suggestmap(entity caller, int request, int argc)
Definition: cmd.qc:642
#define INGAME_JOINED(it)
Definition: sv_rules.qh:21
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
bool autocvar_g_balance_teams
Definition: teamplay.qh:8
bool intermission_running
Definition: intermission.qh:9
bool autocvar_g_campaign
Definition: campaign.qh:6
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings...
Definition: teamplay.qc:487
const int MIN_SPEC_TIME
Definition: client.qh:399
float CheatCommand(entity this, int argc)
Definition: cheats.qc:285
entity GetVoiceMessage(string type)
Definition: globalsound.qc:196
void waypoint_schedulerelinkall()
Definition: waypoints.qc:1309
void ClientCommand_clientversion(entity caller, int request, int argc)
Definition: cmd.qc:93
float time
Definition: csprogsdefs.qc:16
#define chr2str
Definition: dpextensions.qh:48
float autocvar_sv_clientcommand_antispam_time
Definition: cmd.qh:3
void Join(entity this)
Definition: client.qc:1966
#define IS_PLAYER(v)
Definition: utils.qh:9
void ClientCommand_wpeditor(entity caller, int request, int argc)
Definition: cmd.qc:161
void waypoint_remove_fromeditor(entity pl)
Definition: waypoints.qc:840
float CommonCommand_macro_command(int argc, entity caller, string command)
Definition: common.qh:168