3 #include <server/defs.qh>
4 #include <server/miscfunctions.qh>
6 #include <common/command/_mod.qh>
11 #include "../bot/api.qh"
13 #include "../campaign.qh"
14 #include "../cheats.qh"
15 #include "../client.qh"
16 #include "../clientkill.qh"
17 #include "../player.qh"
18 #include "../ipban.qh"
19 #include "../mapvoting.qh"
20 #include "../scores.qh"
21 #include "../teamplay.qh"
23 #include <server/mutators/_mod.qh>
24 #include <common/gamemodes/_mod.qh>
27 #include <common/vehicles/all.qh>
30 #include <common/constants.qh>
31 #include <common/deathtypes/all.qh>
32 #include <common/effects/all.qh>
33 #include <common/mapinfo.qh>
34 #include <common/notifications/all.qh>
35 #include <common/physics/player.qh>
36 #include <common/teams.qh>
37 #include <common/util.qh>
38 #include <common/mapobjects/triggers.qh>
40 #include <common/minigames/sv_minigames.qh>
42 #include <common/monsters/_mod.qh>
43 #include <common/monsters/sv_spawn.qh>
44 #include <common/monsters/sv_monsters.qh>
46 #include <lib/warpzone/common.qh>
48 // =========================================================
49 // Server side networked commands code, reworked by Samual
50 // Last updated: December 28th, 2011
51 // =========================================================
53 bool SV_ParseClientCommand_floodcheck(entity this)
55 entity store = IS_CLIENT(this) ? CS(this) : this; // unfortunately, we need to store these on the client initially
57 if (!timeout_status) // not while paused
59 if (time <= (store.cmd_floodtime + autocvar_sv_clientcommand_antispam_time))
61 store.cmd_floodcount += 1;
62 if (store.cmd_floodcount > autocvar_sv_clientcommand_antispam_count) return false; // too much spam, halt
66 store.cmd_floodtime = time;
67 store.cmd_floodcount = 1;
70 return true; // continue, as we're not flooding yet
74 // =======================
75 // Command Sub-Functions
76 // =======================
78 void ClientCommand_autoswitch(entity caller, int request, int argc)
82 case CMD_REQUEST_COMMAND:
86 CS(caller).autoswitch = InterpretBoolean(argv(1));
87 sprint(caller, strcat("^1autoswitch is currently turned ", (CS(caller).autoswitch ? "on" : "off"), ".\n"));
93 sprint(caller, "Incorrect parameters for ^2autoswitch^7\n");
94 case CMD_REQUEST_USAGE:
96 sprint(caller, "\nUsage:^3 cmd autoswitch selection\n");
97 sprint(caller, " Where 'selection' controls if autoswitch is on or off.\n");
103 void ClientCommand_clientversion(entity caller, int request, int argc) // internal command, used only by code
107 case CMD_REQUEST_COMMAND:
111 if (IS_CLIENT(caller))
113 CS(caller).version = ((argv(1) == "$gameversion") ? 1 : stof(argv(1)));
115 if (CS(caller).version < autocvar_gameversion_min || CS(caller).version > autocvar_gameversion_max)
117 CS(caller).version_mismatch = true;
118 ClientKill_TeamChange(caller, -2); // observe
120 else if (autocvar_g_campaign || autocvar_g_balance_teams)
122 // JoinBestTeam(caller, false, true);
124 else if (teamplay && !autocvar_sv_spectate && !(Player_GetForcedTeamIndex(caller) > 0))
126 TRANSMUTE(Observer, caller); // really?
127 stuffcmd(caller, "menu_showteamselect\n");
136 sprint(caller, "Incorrect parameters for ^2clientversion^7\n");
137 case CMD_REQUEST_USAGE:
139 sprint(caller, "\nUsage:^3 cmd clientversion version\n");
140 sprint(caller, " Where 'version' is the game version reported by caller.\n");
146 void ClientCommand_mv_getpicture(entity caller, int request, int argc) // internal command, used only by code
150 case CMD_REQUEST_COMMAND:
154 if (intermission_running) MapVote_SendPicture(caller, stof(argv(1)));
161 sprint(caller, "Incorrect parameters for ^2mv_getpicture^7\n");
162 case CMD_REQUEST_USAGE:
164 sprint(caller, "\nUsage:^3 cmd mv_getpicture mapid\n");
165 sprint(caller, " Where 'mapid' is the id number of the map to request an image of on the map vote selection menu.\n");
171 void ClientCommand_wpeditor(entity caller, int request, int argc)
175 case CMD_REQUEST_COMMAND:
177 if (!autocvar_g_waypointeditor)
179 sprint(caller, "ERROR: this command works only if the waypoint editor is on\n");
185 if (argv(1) == "spawn")
188 if (!IS_PLAYER(caller))
189 sprint(caller, "ERROR: this command works only if you are player\n");
191 waypoint_spawn_fromeditor(caller, (s == "crosshair"), (s == "jump"), (s == "crouch"), (s == "support"));
194 else if (argv(1) == "remove")
196 if (!IS_PLAYER(caller))
197 sprint(caller, "ERROR: this command works only if you are player\n");
199 waypoint_remove_fromeditor(caller);
202 else if (argv(1) == "hardwire")
205 waypoint_start_hardwiredlink(caller, (s == "crosshair"));
208 else if (argv(1) == "lock")
210 waypoint_lock(caller);
213 else if (argv(1) == "unreachable")
215 if (!IS_PLAYER(caller))
216 sprint(caller, "ERROR: this command works only if you are player\n");
218 waypoint_unreachable(caller);
221 else if (argv(1) == "saveall")
226 else if (argv(1) == "relinkall")
228 waypoint_schedulerelinkall();
231 else if (argv(1) == "symaxis")
233 if (argv(2) == "set" || argv(2) == "get")
235 waypoint_getSymmetricalAxis_cmd(caller, (argv(2) == "set"), 3);
239 else if (argv(1) == "symorigin")
241 if (argv(2) == "set" || argv(2) == "get")
243 waypoint_getSymmetricalOrigin_cmd(caller, (argv(2) == "set"), 3);
251 sprint(caller, "Incorrect parameters for ^2wpeditor^7\n");
252 case CMD_REQUEST_USAGE:
254 sprint(caller, "\nUsage:^3 cmd wpeditor action\n");
255 sprint(caller, " Where 'action' can be:\n");
256 sprint(caller, " ^5spawn^7: spawns a waypoint at player's position\n");
257 sprint(caller, " ^5remove^7: remove player's nearest waypoint\n");
258 sprint(caller, " ^5unreachable^7: useful to reveal waypoints and items unreachable from the current position and spawnpoints without a nearest waypoint\n");
259 sprint(caller, " ^5saveall^7: saves all waypoints and links to file\n");
260 sprint(caller, " ^5relinkall^7: relink all waypoints as if they were respawned\n");
261 sprint(caller, " ^5spawn crosshair^7: spawns a waypoint at crosshair's position (in general useful to create special and hardwired links with ease from existing waypoints, in particular it's the only way to create custom jumppad waypoints (spawn another waypoint to create destination))\n");
262 sprint(caller, " ^5spawn jump^7: spawns a jump waypoint (spawn another waypoint to create destination)\n");
263 sprint(caller, " ^5spawn crouch^7: spawns a crouch waypoint\n");
264 sprint(caller, " ^5spawn support^7: spawns a support waypoint (spawn another waypoint to create destination from which all incoming links are removed), useful to replace links to preblematic jumppad/teleport waypoints\n");
265 sprint(caller, " ^5hardwire^7: marks the nearest waypoint as origin of a new hardwired link (spawn another waypoint over an existing one to create destination)\n");
266 sprint(caller, " ^5hardwire crosshair^7: marks the waypoint at crosshair instead of the nearest waypoint\n");
267 sprint(caller, " ^5lock^7: locks link display of the aimed waypoint (unlocks if no waypoint is found at crosshair's position)\n");
268 sprint(caller, " ^5symorigin get|set\n");
269 sprint(caller, " ^5symorigin get|set p1 p2 ... pX\n");
270 sprint(caller, " ^5symaxis get|set p1 p2\n");
271 sprint(caller, " ^7 where p1 p2 ... pX are positions (\"x y z\", z can be omitted) that you know are perfectly symmetrical");
272 sprint(caller, " ^7 so you can determine origin/axis of symmetry of maps without ctf flags or where flags aren't perfectly symmetrical\n");
273 sprint(caller, " See 'wpeditor_menu' for a selectable list of various commands and useful settings to edit waypoints.\n");
279 void ClientCommand_join(entity caller, int request)
283 case CMD_REQUEST_COMMAND:
286 if (IS_CLIENT(caller) && !IS_PLAYER(caller))
287 if (joinAllowed(caller))
290 return; // never fall through to usage
294 case CMD_REQUEST_USAGE:
296 sprint(caller, "\nUsage:^3 cmd join\n");
297 sprint(caller, " No arguments required.\n");
303 void ClientCommand_kill(entity caller, int request)
307 case CMD_REQUEST_COMMAND:
309 if(IS_SPEC(caller) || IS_OBSERVER(caller))
310 return; // no point warning about this, command does nothing
312 if(GetResource(caller, RES_HEALTH) <= 0)
314 sprint(caller, "Can't die - you are already dead!\n");
320 return; // never fall through to usage
324 case CMD_REQUEST_USAGE:
326 sprint(caller, "\nUsage:^3 cmd kill\n");
327 sprint(caller, " No arguments required.\n");
333 void ClientCommand_physics(entity caller, int request, int argc)
337 case CMD_REQUEST_COMMAND:
339 string command = strtolower(argv(1));
341 if (!autocvar_g_physics_clientselect)
343 sprint(caller, "Client physics selection is currently disabled.\n");
347 if (command == "list" || command == "help")
349 sprint(caller, strcat("Available physics sets: \n\n", autocvar_g_physics_clientselect_options, " default\n"));
353 if (Physics_Valid(command) || command == "default")
355 stuffcmd(caller, strcat("\nseta cl_physics ", command, "\nsendcvar cl_physics\n"));
356 sprint(caller, strcat("^2Physics set successfully changed to ^3", command, "\n"));
362 sprint(caller, strcat("Current physics set: ^3", CS(caller).cvar_cl_physics, "\n"));
363 case CMD_REQUEST_USAGE:
365 sprint(caller, "\nUsage:^3 cmd physics <physics>\n");
366 sprint(caller, " See 'cmd physics list' for available physics sets.\n");
367 sprint(caller, " Argument 'default' resets to standard physics.\n");
373 void ClientCommand_ready(entity caller, int request) // todo: anti-spam for toggling readyness
377 case CMD_REQUEST_COMMAND:
379 if (IS_CLIENT(caller))
381 if (warmup_stage || sv_ready_restart || g_race_qualifying == 2)
383 if (!readyrestart_happened || sv_ready_restart_repeatable)
385 if (time < game_starttime) // game is already restarting
387 if (caller.ready) // toggle
389 caller.ready = false;
390 if(IS_PLAYER(caller) || caller.caplayer == 1)
391 bprint(playername(caller, false), "^2 is ^1NOT^2 ready\n");
396 if(IS_PLAYER(caller) || caller.caplayer == 1)
397 bprint(playername(caller, false), "^2 is ready\n");
400 // cannot reset the game while a timeout is active!
401 if (!timeout_status) ReadyCount();
405 sprint(caller, "^1Game has already been restarted\n");
409 return; // never fall through to usage
413 case CMD_REQUEST_USAGE:
415 sprint(caller, "\nUsage:^3 cmd ready\n");
416 sprint(caller, " No arguments required.\n");
422 void ClientCommand_say(entity caller, int request, int argc, string command)
426 case CMD_REQUEST_COMMAND:
428 if (argc >= 2) Say(caller, false, NULL, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
429 return; // never fall through to usage
433 case CMD_REQUEST_USAGE:
435 sprint(caller, "\nUsage:^3 cmd say <message>\n");
436 sprint(caller, " Where 'message' is the string of text to say.\n");
442 void ClientCommand_say_team(entity caller, int request, int argc, string command)
446 case CMD_REQUEST_COMMAND:
449 Say(caller, true, NULL, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
450 return; // never fall through to usage
454 case CMD_REQUEST_USAGE:
456 sprint(caller, "\nUsage:^3 cmd say_team <message>\n");
457 sprint(caller, " Where 'message' is the string of text to say.\n");
464 void ClientCommand_selectteam(entity caller, int request, int argc)
468 case CMD_REQUEST_COMMAND:
474 if (!IS_CLIENT(caller))
480 sprint(caller, "^7selectteam can only be used in teamgames\n");
483 if (Player_GetForcedTeamIndex(caller) > 0)
485 sprint(caller, "^7selectteam can not be used as your team is forced\n");
490 sprint(caller, "^7The game has already begun, you must wait until the next map to be able to join a team.\n");
498 selection = NUM_TEAM_1;
503 selection = NUM_TEAM_2;
508 selection = NUM_TEAM_3;
513 selection = NUM_TEAM_4;
526 if (caller.team == selection && selection != -1 && !IS_DEAD(caller))
528 sprint(caller, "^7You already are on that team.\n");
531 if (CS(caller).wasplayer && autocvar_g_changeteam_banned)
533 sprint(caller, "^1You cannot change team, forbidden by the server.\n");
536 if ((selection != -1) && autocvar_g_balance_teams &&
537 autocvar_g_balance_teams_prevent_imbalance)
539 entity balance = TeamBalance_CheckAllowedTeams(caller);
540 TeamBalance_GetTeamCounts(balance, caller);
541 if ((Team_IndexToBit(Team_TeamToIndex(selection)) &
542 TeamBalance_FindBestTeams(balance, caller, false)) == 0)
544 Send_Notification(NOTIF_ONE, caller, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
545 TeamBalance_Destroy(balance);
548 TeamBalance_Destroy(balance);
550 ClientKill_TeamChange(caller, selection);
551 if (!IS_PLAYER(caller))
553 caller.team_selected = true; // avoids asking again for team selection on join
558 sprint(caller, "Incorrect parameters for ^2selectteam^7\n");
559 case CMD_REQUEST_USAGE:
561 sprint(caller, "\nUsage:^3 cmd selectteam team\n");
562 sprint(caller, " Where 'team' is the prefered team to try and join.\n");
563 sprint(caller, " Full list of options here: \"red, blue, yellow, pink, auto\"\n");
569 void ClientCommand_selfstuff(entity caller, int request, string command)
573 case CMD_REQUEST_COMMAND:
577 stuffcmd(caller, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
583 sprint(caller, "Incorrect parameters for ^2selfstuff^7\n");
584 case CMD_REQUEST_USAGE:
586 sprint(caller, "\nUsage:^3 cmd selfstuff <command>\n");
587 sprint(caller, " Where 'command' is the string to be stuffed to your client.\n");
593 void ClientCommand_sentcvar(entity caller, int request, int argc, string command)
597 case CMD_REQUEST_COMMAND:
604 if (argc == 2) // undefined cvar: use the default value on the server then
606 s = strcat(substring(command, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
610 GetCvars(caller, CS(caller), 1);
617 sprint(caller, "Incorrect parameters for ^2sentcvar^7\n");
618 case CMD_REQUEST_USAGE:
620 sprint(caller, "\nUsage:^3 cmd sentcvar <cvar>\n");
621 sprint(caller, " Where 'cvar' is the cvar plus arguments to send to the server.\n");
627 void ClientCommand_spectate(entity caller, int request)
631 case CMD_REQUEST_COMMAND:
633 if (!intermission_running && IS_CLIENT(caller))
635 if((IS_SPEC(caller) || IS_OBSERVER(caller)) && argv(1) != "")
637 entity client = GetFilteredEntity(argv(1));
638 int spec_accepted = VerifyClientEntity(client, false, false);
639 if(spec_accepted > 0 && IS_PLAYER(client))
641 if(Spectate(caller, client))
642 return; // fall back to regular handling
646 int mutator_returnvalue = MUTATOR_CALLHOOK(ClientCommand_Spectate, caller);
648 if (mutator_returnvalue == MUT_SPECCMD_RETURN) return;
650 if ((IS_PLAYER(caller) || mutator_returnvalue == MUT_SPECCMD_FORCE))
651 if (autocvar_sv_spectate == 1)
652 ClientKill_TeamChange(caller, -2); // observe
654 return; // never fall through to usage
658 case CMD_REQUEST_USAGE:
660 sprint(caller, "\nUsage:^3 cmd spectate <client>\n");
661 sprint(caller, " Where 'client' can be the player to spectate.\n");
667 void ClientCommand_suggestmap(entity caller, int request, int argc)
671 case CMD_REQUEST_COMMAND:
675 sprint(caller, strcat(MapVote_Suggest(caller, argv(1)), "\n"));
681 sprint(caller, "Incorrect parameters for ^2suggestmap^7\n");
682 case CMD_REQUEST_USAGE:
684 sprint(caller, "\nUsage:^3 cmd suggestmap map\n");
685 sprint(caller, " Where 'map' is the name of the map to suggest.\n");
691 void ClientCommand_tell(entity caller, int request, int argc, string command)
695 case CMD_REQUEST_COMMAND:
699 if(!IS_CLIENT(caller) && IS_REAL_CLIENT(caller)) // connecting
701 print_to(caller, "You can't ^2tell^7 a message while connecting.");
705 entity tell_to = GetIndexedEntity(argc, 1);
706 float tell_accepted = VerifyClientEntity(tell_to, true, false);
708 if (tell_accepted > 0) // the target is a real client
710 if (tell_to != caller) // and we're allowed to send to them :D
712 // workaround for argv indexes indexing ascii chars instead of utf8 chars
713 // In this case when the player name contains utf8 chars
714 // the message gets partially trimmed in the beginning.
715 // Potentially this bug affects any substring call that uses
716 // argv_start_index and argv_end_index.
718 string utf8_enable_save = cvar_string("utf8_enable");
719 cvar_set("utf8_enable", "0");
720 string msg = substring(command, argv_start_index(next_token), argv_end_index(-1) - argv_start_index(next_token));
721 cvar_set("utf8_enable", utf8_enable_save);
723 Say(caller, false, tell_to, msg, true);
726 else { print_to(caller, "You can't ^2tell^7 a message to yourself."); return; }
728 else if (argv(1) == "#0")
730 trigger_magicear_processmessage_forallears(caller, -1, NULL, substring(command, argv_start_index(next_token), argv_end_index(-1) - argv_start_index(next_token)));
733 else { print_to(caller, strcat("tell: ", GetClientErrorString(tell_accepted, argv(1)), ".")); return; }
738 sprint(caller, "Incorrect parameters for ^2tell^7\n");
739 case CMD_REQUEST_USAGE:
741 sprint(caller, "\nUsage:^3 cmd tell client <message>\n");
742 sprint(caller, " Where 'client' is the entity number or name of the player to send 'message' to.\n");
748 void ClientCommand_voice(entity caller, int request, int argc, string command)
752 case CMD_REQUEST_COMMAND:
756 entity e = GetVoiceMessage(argv(1));
759 sprint(caller, sprintf("Invalid voice. Use one of: %s\n", allvoicesamples));
764 msg = substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2));
765 VoiceMessage(caller, e, msg);
772 sprint(caller, "Incorrect parameters for ^2voice^7\n");
773 case CMD_REQUEST_USAGE:
775 sprint(caller, "\nUsage:^3 cmd voice messagetype <soundname>\n");
776 sprint(caller, " 'messagetype' is the type of broadcast to do, like team only or such,\n");
777 sprint(caller, " and 'soundname' is the string/filename of the sound/voice message to play.\n");
783 /* use this when creating a new command, making sure to place it in alphabetical order... also,
784 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
785 void ClientCommand_(entity caller, int request)
789 case CMD_REQUEST_COMMAND:
792 return; // never fall through to usage
796 case CMD_REQUEST_USAGE:
798 sprint(caller, "\nUsage:^3 cmd \n");
799 sprint(caller, " No arguments required.\n");
807 // =====================================
808 // Macro system for networked commands
809 // =====================================
811 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
812 #define CLIENT_COMMANDS(ent, request, arguments, command) \
813 CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(ent, request, arguments), "Whether or not to switch automatically when getting a better weapon") \
814 CLIENT_COMMAND("clientversion", ClientCommand_clientversion(ent, request, arguments), "Release version of the game") \
815 CLIENT_COMMAND("join", ClientCommand_join(ent, request), "Become a player in the game") \
816 CLIENT_COMMAND("kill", ClientCommand_kill(ent, request), "Become a member of the dead") \
817 CLIENT_COMMAND("minigame", ClientCommand_minigame(ent, request, arguments, command), "Start a minigame") \
818 CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(ent, request, arguments), "Retrieve mapshot picture from the server") \
819 CLIENT_COMMAND("physics", ClientCommand_physics(ent, request, arguments), "Change physics set") \
820 CLIENT_COMMAND("ready", ClientCommand_ready(ent, request), "Qualify as ready to end warmup stage (or restart server if allowed)") \
821 CLIENT_COMMAND("say", ClientCommand_say(ent, request, arguments, command), "Print a message to chat to all players") \
822 CLIENT_COMMAND("say_team", ClientCommand_say_team(ent, request, arguments, command), "Print a message to chat to all team mates") \
823 CLIENT_COMMAND("selectteam", ClientCommand_selectteam(ent, request, arguments), "Attempt to choose a team to join into") \
824 CLIENT_COMMAND("selfstuff", ClientCommand_selfstuff(ent, request, command), "Stuffcmd a command to your own client") \
825 CLIENT_COMMAND("sentcvar", ClientCommand_sentcvar(ent, request, arguments, command), "New system for sending a client cvar to the server") \
826 CLIENT_COMMAND("spectate", ClientCommand_spectate(ent, request), "Become an observer") \
827 CLIENT_COMMAND("suggestmap", ClientCommand_suggestmap(ent, request, arguments), "Suggest a map to the mapvote at match end") \
828 CLIENT_COMMAND("tell", ClientCommand_tell(ent, request, arguments, command), "Send a message directly to a player") \
829 CLIENT_COMMAND("voice", ClientCommand_voice(ent, request, arguments, command), "Send voice message via sound") \
830 CLIENT_COMMAND("wpeditor", ClientCommand_wpeditor(ent, request, arguments), "Waypoint editor commands") \
833 void ClientCommand_macro_help(entity caller)
835 #define CLIENT_COMMAND(name, function, description) \
836 { sprint(caller, " ^2", name, "^7: ", description, "\n"); }
838 CLIENT_COMMANDS(NULL, 0, 0, "");
839 #undef CLIENT_COMMAND
842 float ClientCommand_macro_command(int argc, entity caller, string command)
844 #define CLIENT_COMMAND(name, function, description) \
845 { if (name == strtolower(argv(0))) { function; return true; } }
847 CLIENT_COMMANDS(caller, CMD_REQUEST_COMMAND, argc, command);
848 #undef CLIENT_COMMAND
853 float ClientCommand_macro_usage(int argc, entity caller)
855 #define CLIENT_COMMAND(name, function, description) \
856 { if (name == strtolower(argv(1))) { function; return true; } }
858 CLIENT_COMMANDS(caller, CMD_REQUEST_USAGE, argc, "");
859 #undef CLIENT_COMMAND
864 void ClientCommand_macro_write_aliases(float fh)
866 #define CLIENT_COMMAND(name, function, description) \
867 { CMD_Write_Alias("qc_cmd_cmd", name, description); }
869 CLIENT_COMMANDS(NULL, 0, 0, "");
870 #undef CLIENT_COMMAND
873 // ======================================
874 // Main Function Called By Engine (cmd)
875 // ======================================
876 // If this function exists, server game code parses clientcommand before the engine code gets it.
878 void SV_ParseClientCommand(entity this, string command)
880 // If invalid UTF-8, don't even parse it
881 string command2 = "";
882 float len = strlen(command);
884 for (i = 0; i < len; ++i)
885 command2 = strcat(command2, chr2str(str2chr(command, i)));
886 if (command != command2) return;
888 // if we're banned, don't even parse the command
889 if (Ban_MaybeEnforceBanOnce(this)) return;
891 int argc = tokenize_console(command);
893 // Guide for working with argc arguments by example:
894 // argc: 1 - 2 - 3 - 4
895 // argv: 0 - 1 - 2 - 3
896 // cmd vote - master - login - password
899 switch (strtolower(argv(0)))
901 // exempt commands which are not subject to floodcheck
902 case "begin": break; // handled by engine in host_cmd.c
903 case "download": break; // handled by engine in cl_parse.c
904 case "mv_getpicture": break; // handled by server in this file
905 case "wpeditor": break; // handled by server in this file
906 case "pause": break; // handled by engine in host_cmd.c
907 case "prespawn": break; // handled by engine in host_cmd.c
908 case "sentcvar": break; // handled by server in this file
909 case "spawn": break; // handled by engine in host_cmd.c
910 case "c2s": Net_ClientCommand(this, command); return; // handled by net.qh
913 if (SV_ParseClientCommand_floodcheck(this)) break; // "true": continue, as we're not flooding yet
914 else return; // "false": not allowed to continue, halt // print("^1ERROR: ^7ANTISPAM CAUGHT: ", command, ".\n");
917 /* NOTE: should this be disabled? It can be spammy perhaps, but hopefully it's okay for now */
918 if (argv(0) == "help")
922 sprint(this, "\nClient networked commands:\n");
923 ClientCommand_macro_help(this);
925 sprint(this, "\nCommon networked commands:\n");
926 CommonCommand_macro_help(this);
928 sprint(this, "\nUsage:^3 cmd COMMAND...^7, where possible commands are listed above.\n");
929 sprint(this, "For help about a specific command, type cmd help COMMAND\n");
932 else if (CommonCommand_macro_usage(argc, this)) // Instead of trying to call a command, we're going to see detailed information about it
936 else if (ClientCommand_macro_usage(argc, this)) // same, but for normal commands now
941 else if (MUTATOR_CALLHOOK(SV_ParseClientCommand, this, strtolower(argv(0)), argc, command))
943 return; // handled by a mutator
945 else if (CheatCommand(this, argc))
947 return; // handled by server/cheats.qc
949 else if (CommonCommand_macro_command(argc, this, command))
951 return; // handled by server/command/common.qc
953 else if (ClientCommand_macro_command(argc, this, command)) // continue as usual and scan for normal commands
955 return; // handled by one of the above ClientCommand_* functions
959 clientcommand(this, command);