Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
sv_cmd.qc
Go to the documentation of this file.
1#include "sv_cmd.qh"
2
3#include <common/constants.qh>
6#include <common/mapinfo.qh>
11#include <common/playerstats.qh>
12#include <common/stats.qh>
13#include <common/teams.qh>
14#include <common/util.qh>
15#include <server/anticheat.qh>
16#include <server/bot/api.qh>
17#include <server/campaign.qh>
18#include <server/client.qh>
21#include <server/command/cmd.qh>
26#include <server/ipban.qh>
28#include <server/player.qh>
30#include <server/teamplay.qh>
31#include <server/world.qh>
32
33// used by GameCommand_make_mapinfo()
35{
37 {
38 LOG_INFO("Done rebuiling mapinfos.");
40 delete(this);
41 }
42 else
43 {
45 this.nextthink = time;
46 }
47}
48
49// used by GameCommand_extendmatchtime() and GameCommand_reducematchtime()
50void changematchtime(float delta, float mi, float ma)
51{
52 float update;
53
54 if (delta == 0)
55 return;
56 if (autocvar_timelimit < 0)
57 return;
58
59 if (mi <= 10)
60 mi = 10; // at least ten sec in the future
61 float cur = time - game_starttime;
62 if (cur > 0)
63 mi += cur; // from current time!
64
65 float lim = autocvar_timelimit * 60;
66
67 if (delta > 0)
68 {
69 if (lim == 0)
70 return; // cannot increase any further
71 else if (lim < ma)
72 update = min(ma, lim + delta);
73 else // already above maximum: FAIL
74 return;
75 }
76 else
77 {
78 if (lim == 0) // infinite: try reducing to max, if we are allowed to
79 update = max(mi, ma);
80 else if (lim > mi) // above minimum: decrease
81 update = max(mi, lim + delta);
82 else // already below minimum: FAIL
83 return;
84 }
85
86 cvar_set("timelimit", ftos(update / 60));
87}
88
89
90// =======================
91// Command Sub-Functions
92// =======================
93
94void GameCommand_adminmsg(int request, int argc)
95{
96 switch (request)
97 {
99 {
101 {
102 LOG_HELPF("This command works only when the server is running.");
103 return;
104 }
105 entity client;
106 float accepted;
107
108 string targets = strreplace(",", " ", argv(1));
109 string original_targets = strreplace(" ", ", ", targets);
110 string admin_message = argv(2);
111 float infobartime = stof(argv(3));
112
113 string successful, t;
114 successful = string_null;
115
116 if ((targets) && (admin_message))
117 {
118 for ( ; targets; )
119 {
120 t = car(targets);
121 targets = cdr(targets);
122
123 // Check to see if the player is a valid target
124 client = GetFilteredEntity(t);
125 accepted = VerifyClientEntity(client, true, false);
126
127 if (accepted <= 0)
128 {
129 LOG_INFO("adminmsg: ", GetClientErrorString(accepted, t), (targets ? ", skipping to next player.\n" : "."));
130 continue;
131 }
132
133 // send the centerprint/console print or infomessage
134 if (infobartime)
135 stuffcmd(client, sprintf("\ninfobar %f \"%s\"\n", infobartime, MakeConsoleSafe(admin_message)));
136 else
137 {
138 centerprint(client, strcat("^3", GetCallerName(NULL), ":\n^7", admin_message));
139 sprint(client, strcat("\{1}\{13}^3", GetCallerName(NULL), "^7: ", admin_message, "\n"));
140 }
141
142 successful = strcat(successful, (successful ? ", " : ""), playername(client.netname, client.team, false));
143 LOG_TRACE("Message sent to ", playername(client.netname, client.team, false));
144 continue;
145 }
146
147 if (successful)
148 bprint("Successfully sent message '", admin_message, "' to ", successful, ".\n");
149 else
150 LOG_INFO("No players given (", original_targets, ") could receive the message.");
151
152 return;
153 }
154 }
155
156 default:
157 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
159 {
160 LOG_HELP("Usage:^3 sv_cmd adminmsg <clients> \"<message>\" [<infobartime>]");
161 LOG_HELP(" <clients> is a list (separated by commas) of player entity ID's or nicknames");
162 LOG_HELP(" If <infobartime> is provided, the message will be sent to infobar.");
163 LOG_HELP(" Otherwise, it will just be sent as a centerprint message.");
164 LOG_HELP("Examples: adminmsg 2,4 \"this infomessage will last for ten seconds\" 10");
165 LOG_HELP(" adminmsg 2,5 \"this message will be a centerprint\"");
166 return;
167 }
168 }
169}
170
171void GameCommand_allready(int request)
172{
173 switch (request)
174 {
176 {
178 {
179 LOG_HELPF("This command works only when the server is running.");
180 return;
181 }
182 if (warmup_stage)
183 ReadyRestart(true);
184 else
185 LOG_INFO("Not in warmup.");
186
187 return;
188 }
189
190 default:
192 {
193 LOG_HELP("Usage:^3 sv_cmd allready");
194 LOG_HELP(" No arguments required.");
195 return;
196 }
197 }
198}
199
200void GameCommand_allspec(int request, int argc)
201{
202 switch (request)
203 {
205 {
207 {
208 LOG_HELPF("This command works only when the server is running.");
209 return;
210 }
211 string reason = argv(1);
212 int n = 0;
214 PutObserverInServer(it, true, true);
215 ++n;
216 });
217 if (n)
218 bprint(strcat("Successfully forced all (", ftos(n), ") players to spectate",
219 (reason ? strcat(" for reason: '", reason, "'") : ""), ".\n"));
220 else
221 LOG_INFO("No players found to spectate.");
222 return;
223 }
224
225 default:
227 {
228 LOG_HELP("Usage:^3 sv_cmd allspec [<reason>]");
229 LOG_HELP(" Where <reason> is an optional argument for explanation of allspec command.");
230 LOG_HELP("See also: ^2moveplayer, shuffleteams^7");
231 return;
232 }
233 }
234}
235
236void GameCommand_anticheat(int request, int argc)
237{
238 switch (request)
239 {
241 {
243 {
244 LOG_HELPF("This command works only when the server is running.");
245 return;
246 }
247 entity client = GetIndexedEntity(argc, 1);
248 float accepted = VerifyClientEntity(client, false, false);
249
250 if (accepted > 0)
251 {
253 return;
254 }
255 LOG_INFO("anticheat: ", GetClientErrorString(accepted, argv(1)), ".");
256 }
257
258 default:
259 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
261 {
262 LOG_HELP("Usage:^3 sv_cmd anticheat <client>");
263 LOG_HELP(" <client> is the entity number or name of the player.");
264 return;
265 }
266 }
267}
268
269/*
270 * Handler for balanceteams requests
271 * Need to construct the plain/text body xonstat is expecting, it looks like this.
272 * Some of these aren't needed for the balance endpoint logic, but need to be included to satisfy the api contract, in that place substituting dummy values.
273 * see https://gitlab.com/xonotic/xonstat-go/-/blob/master/pkg/submission/rawsubmission.go
274 */
275void balanceteams_handler(entity fh, entity p, float status)
276{
277 switch(status)
278 {
280 {
281 url_fputs(fh, "V 9\n");
282 #ifdef WATERMARK
283 url_fputs(fh, sprintf("R %s\n", WATERMARK));
284 #endif
285 url_fputs(fh, sprintf("G %s\n", PlayerStats_GetGametype()));
286 url_fputs(fh, sprintf("O %s\n", modname));
287 url_fputs(fh, sprintf("M %s\n", GetMapname()));
288 url_fputs(fh, sprintf("I %s\n", matchid));
289 url_fputs(fh, sprintf("S %s\n", cvar_string("hostname")));
290 url_fputs(fh, sprintf("C %d\n", cvar_purechanges_count));
291 url_fputs(fh, sprintf("U %d\n", cvar("port")));
292 url_fputs(fh, sprintf("D %f\n", max(0, time - game_starttime)));
293 // Add Q entries: this tells the XonStat API how many teams there are so that it doesn't guess based on the current team assignments.
294 // For example if there are teams Red: {1,2} , Blue: {3,4,5} , Yellow: {} and no Q entries are present then XonStat thinks only 2 teams exist.
295 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
296 url_fputs(fh, sprintf("Q team#%d\n", Team_IndexToTeam(i)));
297 // fetch all the player info, construct the plain text body required for the xonstat /balance endpoint (each P entry)
298 FOREACH_CLIENT(!IS_BOT_CLIENT(it) && (IS_PLAYER(it) || INGAME(it)), {
299 url_fputs(fh, sprintf("P %s\n", it.playerstats_id));
300 url_fputs(fh, sprintf("i %d\n", it.playerid));
301 url_fputs(fh, sprintf("n %s\n", it.netname));
302 url_fputs(fh, sprintf("t %d\n", it.team));
303 // we need to account for warmup, if players call balanceteams during warmup where people are messing around, you could get inaccurate skill ratings and worse balance
304 // the 'e' entries in the body can be placed in any order, so handling the warmup logic for scoreboard-score and alivetime here.
305 if (warmup_stage)
306 {
307 // The API is expecting a positive value for scoreboard-score or fails validation, so setting it to 1 since alivetime can be safely set to 0
308 // alivetime and scoreboard-score are used to calculate score per second which influences the skill rating
309 url_fputs(fh, "e scoreboard-score 1\n");
310 url_fputs(fh, "e alivetime 0\n");
311 }
312 else
313 {
314 url_fputs(fh, sprintf("e scoreboard-score %g\n", PlayerScore_Get(it, SP_SCORE)));
316 alivetime += max(0, time - it.alivetime_start);
317 url_fputs(fh, sprintf("e alivetime %g\n", alivetime));
318 }
319 // this is required to satisfy the API contract, but is not used
320 url_fputs(fh, "e acc-blaster-cnt-fired 1\n");
321 url_fputs(fh, "e matches 1\n");
322 url_fputs(fh, "e scoreboardvalid 1\n");
323 url_fputs(fh, "e joins 1\n");
324
325 });
326 url_fputs(fh, "\n"); // need this \n at the end or else validation will fail
327 url_fclose(fh);
328 break;
329 }
330
332 {
333 string response_data = "";
334 string team_rows = "";
335 string player_name = string_null;
336 int player_team = 0;
337 int current_player_id = 0;
338 for (string s; (s = url_fgets(fh)); )
339 {
340 if (response_data != "")
341 {
342 response_data = strcat(response_data, "\n", s);
343 // if the current line starts with i, we need to track it, as the upcoming t entry will tell us which team player i needs to be assigned to
344 if (strstrofs(s, "i ", 0) == 0)
345 current_player_id = stof(substring(s, 2, -1));
346 else if (strstrofs(s, "n ", 0) == 0)
347 player_name = substring(s, 2, -1);
348 else if (strstrofs(s, "t ", 0) == 0)
349 {
350 player_team = stoi(substring(s, 2, -1));
351 if (Team_IsValidTeam(player_team))
352 {
353 FOREACH_CLIENT(it.playerid == current_player_id,
354 MoveToTeam(it, Team_TeamToIndex(player_team), 6);
355 );
356 }
357 }
358 else if (strstrofs(s, "e skill ", 0) == 0 && player_name && Team_IsValidTeam(player_team))
359 {
360 team_rows = strcat(team_rows, Static_Team_ColorName_Upper(player_team), ": ", player_name,
361 ", ", substring(s, 8, -1), "\n");
362 }
363
364 }
365 else
366 response_data = s;
367 }
368 bprint("Team assignments and skill ratings:\n", team_rows);
369 url_fclose(fh);
370 break;
371 }
372 case URL_READY_CLOSED:
373 {
374 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TEAMS_BALANCED);
375 break;
376 }
377 case URL_READY_ERROR:
378 default:
379 {
380 bprint("Failed to balanceteams teams: status code from XonStat API is ", ftos(status), "\n");
381 break;
382 }
383 }
384}
385
387{
388 if (!teamplay)
389 {
390 bprint("Can't balance teams when currently not playing a team game.\n");
391 return;
392 }
393 // We want all players in the game, even if they are dead and waiting to respawn (for example in clan arena), but not bots.
394 int active_player_count = 0;
395 FOREACH_CLIENT(!IS_BOT_CLIENT(it) && (IS_PLAYER(it) || INGAME(it)), {
396 ++active_player_count;
397 });
398 if (active_player_count <= 1)
399 {
400 bprint("You need more than 1 player to balance the teams.\n");
401 return;
402 }
403 if (active_player_count > 128)
404 {
405 bprint("Balancing is only supported for max 128 players, you currently have %d players.\n", active_player_count);
406 return;
407 }
408 if (AVAILABLE_TEAMS > 4)
409 {
410 bprint("XonStat does not support balancing with more than 4 teams.\n");
411 return;
412 }
413 // To justify calling the XonStat API, we need `AVAILABLE_TEAMS + 1` players
414 if (active_player_count <= AVAILABLE_TEAMS)
415 {
416 entity switch_players[NUM_TEAMS - 1];
417 bool team_not_empty[NUM_TEAMS];
418 for (int i = 0; i < AVAILABLE_TEAMS; ++i)
419 team_not_empty[i] = false;
420 int switch_count = 0;
421
422 // Find doubling-up players
423 FOREACH_CLIENT(!IS_BOT_CLIENT(it) && (IS_PLAYER(it) || INGAME(it)),
424 {
425 if (team_not_empty[Team_TeamToIndex(it.team) - 1])
426 switch_players[switch_count++] = it;
427 else
428 team_not_empty[Team_TeamToIndex(it.team) - 1] = true;
429 });
430
431 // Move doubling-up players
432 int team_i = 0;
433 for (int i = 0; i < switch_count; ++i)
434 {
435 while (team_not_empty[team_i]) // find an empty team
436 ++team_i;
437 MoveToTeam(switch_players[i], team_i + 1, 6);
438 }
439 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TEAMS_BALANCED);
440 return;
441 }
442 bprint("Attempting to balance teams by skill...\n");
443 string balance_uri_with_query_params = sprintf("%s?scorefactor=%d&maxDifference=%d&stability=%d&sigmaRange=%.2f",
449 url_single_fopen(balance_uri_with_query_params, FILE_WRITE, balanceteams_handler, NULL);
450}
451
452void GameCommand_balanceteams(int request, int argc)
453{
454 switch (request)
455 {
457 {
459 LOG_HELPF("This command works only when the server is running.");
461 bprint("Players will be balanced by skill when this round is over.\n");
462 else
463 balanceteams();
464 return;
465 }
466 default:
468 {
469 LOG_HELP("Usage:^3 sv_cmd balanceteams");
470 LOG_HELP(" No arguments required.");
471 LOG_HELP(" Balances teams by skill.");
472 return;
473 }
474 }
475}
476
477void GameCommand_bbox(int request)
478{
479 switch (request)
480 {
482 {
483 vector size_min = '0 0 0';
484 vector size_max = '0 0 0';
485 tracebox('1 0 0' * world.absmin.x,
486 '0 1 0' * world.absmin.y + '0 0 1' * world.absmin.z,
487 '0 1 0' * world.absmax.y + '0 0 1' * world.absmax.z,
488 '1 0 0' * world.absmax.x,
490 NULL);
491 size_min.x = (trace_startsolid) ? world.absmin.x : trace_endpos.x;
492
493 tracebox('0 1 0' * world.absmin.y,
494 '1 0 0' * world.absmin.x + '0 0 1' * world.absmin.z,
495 '1 0 0' * world.absmax.x + '0 0 1' * world.absmax.z,
496 '0 1 0' * world.absmax.y,
498 NULL);
499 size_min.y = (trace_startsolid) ? world.absmin.y : trace_endpos.y;
500
501 tracebox('0 0 1' * world.absmin.z,
502 '1 0 0' * world.absmin.x + '0 1 0' * world.absmin.y,
503 '1 0 0' * world.absmax.x + '0 1 0' * world.absmax.y,
504 '0 0 1' * world.absmax.z,
506 NULL);
507 size_min.z = (trace_startsolid) ? world.absmin.z : trace_endpos.z;
508
509 tracebox('1 0 0' * world.absmax.x,
510 '0 1 0' * world.absmin.y + '0 0 1' * world.absmin.z,
511 '0 1 0' * world.absmax.y + '0 0 1' * world.absmax.z,
512 '1 0 0' * world.absmin.x,
514 NULL);
515 size_max.x = (trace_startsolid) ? world.absmax.x : trace_endpos.x;
516
517 tracebox('0 1 0' * world.absmax.y,
518 '1 0 0' * world.absmin.x + '0 0 1' * world.absmin.z,
519 '1 0 0' * world.absmax.x + '0 0 1' * world.absmax.z,
520 '0 1 0' * world.absmin.y,
522 NULL);
523 size_max.y = (trace_startsolid) ? world.absmax.y : trace_endpos.y;
524
525 tracebox('0 0 1' * world.absmax.z,
526 '1 0 0' * world.absmin.x + '0 1 0' * world.absmin.y,
527 '1 0 0' * world.absmax.x + '0 1 0' * world.absmax.y,
528 '0 0 1' * world.absmin.z,
530 NULL);
531 size_max.z = (trace_startsolid) ? world.absmax.z : trace_endpos.z;
532
533 LOG_INFOF("Original size: %v %v", world.absmin, world.absmax);
534 LOG_INFOF("Currently set size: %v %v", world.mins, world.maxs);
535 LOG_INFOF("Solid bounding box size: %v %v", size_min, size_max);
536 return;
537 }
538
539 default:
541 {
542 LOG_HELP("Usage:^3 sv_cmd bbox");
543 LOG_HELP(" No arguments required.");
544 LOG_HELP("See also: ^2gettaginfo, trace^7");
545 return;
546 }
547 }
548}
549
550void GameCommand_bot_cmd(int request, int argc, string command)
551{
552 switch (request)
553 {
555 {
557 {
558 LOG_HELPF("This command works only when the server is running.");
559 return;
560 }
561 entity bot;
562
563 if (argv(1) == "reset")
564 {
566 return;
567 }
568 else if (argv(1) == "setbots")
569 {
570 cvar_settemp("bot_vs_human", "0");
571 cvar_settemp("minplayers", "0");
572 cvar_settemp("minplayers_per_team", "0");
573 cvar_settemp("bot_number", "0");
574 bot_fixcount(false); // Kill all bots.
575 cvar_settemp("bot_number", argv(2));
576 if (!bot_fixcount(true))
577 LOG_INFO("Sorry, could not set requested bot count");
578 return;
579 }
580 else if (argv(1) == "load" && argc == 3)
581 {
582 float fh, i;
583 string s;
584 fh = fopen(argv(2), FILE_READ);
585 if (fh < 0)
586 {
587 LOG_INFO("cannot open the file");
588 return;
589 }
590
591 i = 0;
592 while ((s = fgets(fh)))
593 {
594 argc = tokenize_console(s);
595
596 if (argc >= 3 && argv(0) == "sv_cmd" && argv(1) == "bot_cmd")
597 {
598 if (argv(2) == "reset")
600 else if (argv(2) == "setbots")
601 {
602 cvar_settemp("bot_vs_human", "0");
603 cvar_settemp("minplayers", "0");
604 cvar_settemp("minplayers_per_team", "0");
605 cvar_settemp("bot_number", "0");
606 bot_fixcount(false); // Kill all bots.
607 cvar_settemp("bot_number", argv(3));
608 if (!bot_fixcount(true))
609 LOG_INFO("Sorry, could not set requested bot count");
610 }
611 else
612 {
613 if (argv(2) == "*" || argv(2) == "all")
616 });
617 else
618 {
619 bot = find_bot_by_number(stof(argv(2)));
620 if (bot == NULL)
621 bot = find_bot_by_name(argv(2));
622 if (bot)
624 }
625 }
626 }
627 else
628 localcmd(strcat(s, "\n"));
629
630 ++i;
631 }
632 LOG_INFO(ftos(i), " commands read");
633 fclose(fh);
634 return;
635 }
636 else if (argv(1) == "help")
637 {
638 if (argv(2))
639 bot_cmdhelp(argv(2));
640 else
642 return;
643 }
644 else if (argc >= 3) // this comes last
645 {
646 if (argv(1) == "*" || argv(1) == "all")
647 {
648 int bot_num = 0;
650 bot_queuecommand(it, substring(command, argv_start_index(2), -1));
651 ++bot_num;
652 });
653 if (bot_num)
654 LOG_INFO("Command '", substring(command, argv_start_index(2), -1), "' sent to all bots (", ftos(bot_num), ")");
655 return;
656 }
657 else
658 {
659 bot = find_bot_by_number(stof(argv(1)));
660 if (bot == NULL)
661 bot = find_bot_by_name(argv(1));
662 if (bot)
663 {
664 LOG_INFO("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname);
665 bot_queuecommand(bot, substring(command, argv_start_index(2), -1));
666 return;
667 }
668 else
669 LOG_INFO("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?"); // don't return so that usage is shown
670 }
671 }
672 }
673
674 default:
675 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
677 {
678 LOG_HELP("Usage:^3 sv_cmd bot_cmd <client> <command> [<arguments>]");
679 LOG_HELP(" <client> can be either the name of the bot or a progressive number (not the entity number!)");
680 LOG_HELP(" can also be '*' or 'all' to allow sending the command to all the bots");
681 LOG_HELP(" For full list of commands, see bot_cmd help [<command>].");
682 LOG_HELP("Examples: sv_cmd bot_cmd 1 cc say something");
683 LOG_HELP(" sv_cmd bot_cmd 1 presskey jump");
684 LOG_HELP(" sv_cmd bot_cmd * pause");
685 return;
686 }
687 }
688}
689
690void GameCommand_cointoss(int request, int argc)
691{
692 switch (request)
693 {
695 {
697 {
698 LOG_HELPF("This command works only when the server is running.");
699 return;
700 }
701 string result1 = (argv(2) ? strcat("^7", argv(1)) : "^1HEADS");
702 string result2 = (argv(2) ? strcat("^7", argv(2)) : "^4TAILS");
703 string choice = ((random() > 0.5) ? result1 : result2);
704
705 Send_Notification(NOTIF_ALL, NULL, MSG_MULTI, MULTI_COINTOSS, choice);
706 return;
707 }
708
709 default:
711 {
712 LOG_HELP("Usage:^3 sv_cmd cointoss [<result1> <result2>]");
713 LOG_HELP(" Where <result1> and <result2> are user created options.");
714 return;
715 }
716 }
717}
718
719void GameCommand_database(int request, int argc)
720{
721 switch (request)
722 {
724 {
725 if (argc == 3)
726 {
727 if (argv(1) == "save")
728 {
730 LOG_INFO("Copied serverprogs database to '", argv(2), "' in the data directory.");
731 return;
732 }
733 else if (argv(1) == "dump")
734 {
736 LOG_INFO("DB dumped."); // wtf does this do?
737 return;
738 }
739 else if (argv(1) == "load")
740 {
743 LOG_INFO("Loaded '", argv(2), "' as new serverprogs database.");
744 return;
745 }
746 }
747 }
748
749 default:
750 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
752 {
753 LOG_HELP("Usage:^3 sv_cmd database <action> <filename>");
754 LOG_HELP(" Where <action> is the command to complete,");
755 LOG_HELP(" and <filename> is what it acts upon.");
756 LOG_HELP(" Full list of commands here: save, dump, load.");
757 return;
758 }
759 }
760}
761
762void GameCommand_defer_clear(int request, int argc)
763{
764 switch (request)
765 {
767 {
769 {
770 LOG_HELPF("This command works only when the server is running.");
771 return;
772 }
773 entity client;
774 float accepted;
775
776 if (argc >= 2)
777 {
778 client = GetIndexedEntity(argc, 1);
779 accepted = VerifyClientEntity(client, true, false);
780
781 if (accepted > 0)
782 {
783 stuffcmd(client, "defer clear\n");
784 LOG_INFO("defer clear stuffed to ", playername(client.netname, client.team, false));
785 }
786 else
787 LOG_INFO("defer_clear: ", GetClientErrorString(accepted, argv(1)), ".");
788
789 return;
790 }
791 }
792
793 default:
794 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
796 {
797 LOG_HELP("Usage:^3 sv_cmd defer_clear <client>");
798 LOG_HELP(" <client> is the entity number or name of the player.");
799 LOG_HELP("See also: ^2defer_clear_all^7");
800 return;
801 }
802 }
803}
804
806{
807 switch (request)
808 {
810 {
812 {
813 LOG_HELPF("This command works only when the server is running.");
814 return;
815 }
816 int n = 0;
817 int argc;
818
819 FOREACH_CLIENT(true, {
820 argc = tokenize_console(strcat("defer_clear ", ftos(etof(it))));
822 ++n;
823 });
824 if (n)
825 LOG_INFO("Successfully stuffed defer clear to all clients (", ftos(n), ")"); // should a message be added if no players were found?
826 return;
827 }
828
829 default:
831 {
832 LOG_HELP("Usage:^3 sv_cmd defer_clear_all");
833 LOG_HELP(" No arguments required.");
834 LOG_HELP("See also: ^2defer_clear^7");
835 return;
836 }
837 }
838}
839
840void GameCommand_delrec(int request, int argc) // perhaps merge later with records and printstats and such?
841{
842 switch (request)
843 {
845 {
846 if (argv(1))
847 {
848 if (argv(2))
849 race_deleteTime(argv(2), stof(argv(1)));
850 else
852 return;
853 }
854 }
855
856 default:
857 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
859 {
860 LOG_HELP("Usage:^3 sv_cmd delrec <ranking> [<map>]");
861 LOG_HELP(" <ranking> is which ranking level to clear up to, ");
862 LOG_HELP(" it will clear all records up to nth place.");
863 LOG_HELP(" if <map> is not provided it will use current map.");
864 return;
865 }
866 }
867}
868
869void print_Effect_Index(int d, string effect_name)
870{
871 // this is inside a function to avoid expanding it on compilation everytime
872 LOG_INFO("effect ", effect_name, " is ", ftos(_particleeffectnum(effect_name)), "\n");
873 db_put(d, effect_name, "1");
874}
875
877{
878 switch (request)
879 {
881 {
882 float fh, d;
883 string s;
884
885 d = db_create();
886 LOG_INFO("begin of effects list");
887
888 print_Effect_Index(d, "TE_GUNSHOT");
889 print_Effect_Index(d, "TE_GUNSHOTQUAD");
890 print_Effect_Index(d, "TE_SPIKE");
891 print_Effect_Index(d, "TE_SPIKEQUAD");
892 print_Effect_Index(d, "TE_SUPERSPIKE");
893 print_Effect_Index(d, "TE_SUPERSPIKEQUAD");
894 print_Effect_Index(d, "TE_WIZSPIKE");
895 print_Effect_Index(d, "TE_KNIGHTSPIKE");
896 print_Effect_Index(d, "TE_EXPLOSION");
897 print_Effect_Index(d, "TE_EXPLOSIONQUAD");
898 print_Effect_Index(d, "TE_TAREXPLOSION");
899 print_Effect_Index(d, "TE_TELEPORT");
900 print_Effect_Index(d, "TE_LAVASPLASH");
901 print_Effect_Index(d, "TE_SMALLFLASH");
902 print_Effect_Index(d, "TE_FLAMEJET");
903 print_Effect_Index(d, "EF_FLAME");
904 print_Effect_Index(d, "TE_BLOOD");
905 print_Effect_Index(d, "TE_SPARK");
906 print_Effect_Index(d, "TE_PLASMABURN");
907 print_Effect_Index(d, "TE_TEI_G3");
908 print_Effect_Index(d, "TE_TEI_SMOKE");
909 print_Effect_Index(d, "TE_TEI_BIGEXPLOSION");
910 print_Effect_Index(d, "TE_TEI_PLASMAHIT");
911 print_Effect_Index(d, "EF_STARDUST");
912 print_Effect_Index(d, "TR_ROCKET");
913 print_Effect_Index(d, "TR_GRENADE");
914 print_Effect_Index(d, "TR_BLOOD");
915 print_Effect_Index(d, "TR_WIZSPIKE");
916 print_Effect_Index(d, "TR_SLIGHTBLOOD");
917 print_Effect_Index(d, "TR_KNIGHTSPIKE");
918 print_Effect_Index(d, "TR_VORESPIKE");
919 print_Effect_Index(d, "TR_NEHAHRASMOKE");
920 print_Effect_Index(d, "TR_NEXUIZPLASMA");
921 print_Effect_Index(d, "TR_GLOWTRAIL");
922 print_Effect_Index(d, "TR_SEEKER");
923 print_Effect_Index(d, "SVC_PARTICLE");
924
925 fh = fopen("effectinfo.txt", FILE_READ);
926 while ((s = fgets(fh)))
927 {
929 if (argv(0) == "effect")
930 {
931 if (db_get(d, argv(1)) != "1")
932 {
933 int i = _particleeffectnum(argv(1));
934 if (i >= 0)
935 LOG_INFO("effect ", argv(1), " is ", ftos(i));
936 db_put(d, argv(1), "1");
937 }
938 }
939 }
940 LOG_INFO("end of effects list");
941
942 db_close(d);
943 return;
944 }
945
946 default:
948 {
949 LOG_HELP("Usage:^3 sv_cmd effectindexdump");
950 LOG_HELP(" No arguments required.");
951 return;
952 }
953 }
954}
955
957{
958 switch (request)
959 {
961 {
963 {
964 LOG_HELPF("This command works only when the server is running.");
965 return;
966 }
968 return;
969 }
970
971 default:
973 {
974 LOG_HELP("Usage:^3 sv_cmd extendmatchtime");
975 LOG_HELP(" No arguments required.");
976 LOG_HELP("See also: ^2reducematchtime^7");
977 return;
978 }
979 }
980}
981
982void GameCommand_gametype(int request, int argc)
983{
984 switch (request)
985 {
987 {
989 {
990 LOG_HELPF("This command works only when the server is running.");
991 return;
992 }
993 if (argv(1) != "")
994 {
995 string s = argv(1);
996 Gametype t = MapInfo_Type_FromString(s, false, false);
997
998 if (t)
999 {
1000 // don't execute gametype hooks because they can change active
1001 // gametype rules if executed during the game
1002 GameTypeVote_SetGametype(t, "", false);
1003 }
1004 else
1005 bprint("Failed to switch to ", s, ": this gametype does not exist!\n");
1006
1007 return;
1008 }
1009 }
1010
1011 default:
1012 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1013 case CMD_REQUEST_USAGE:
1014 {
1015 LOG_HELP("Usage:^3 sv_cmd gametype <mode>");
1016 LOG_HELP(" Where <mode> is the gametype mode to switch to.");
1017 LOG_HELP("See also: ^2gotomap^7");
1018 return;
1019 }
1020 }
1021}
1022
1023void GameCommand_gettaginfo(int request, int argc)
1024{
1025 switch (request)
1026 {
1028 {
1029 if (argc >= 4)
1030 {
1031 entity tmp_entity = spawn();
1032 if (argv(1) == "w")
1033 {
1034 .entity weaponentity = weaponentities[0];
1035 _setmodel(tmp_entity, (nextent(NULL)).(weaponentity).model);
1036 }
1037 else
1038 {
1039 precache_model(argv(1));
1040 _setmodel(tmp_entity, argv(1));
1041 }
1042 tmp_entity.frame = stof(argv(2));
1043 float i;
1044 if (substring(argv(3), 0, 1) == "#")
1045 i = stof(substring(argv(3), 1, -1));
1046 else
1047 i = gettagindex(tmp_entity, argv(3));
1048 if (i)
1049 {
1050 vector v = gettaginfo(tmp_entity, i);
1051 LOG_HELPF("model %s frame %s tag %s index %s parent %s",
1052 tmp_entity.model, ftos(tmp_entity.frame), gettaginfo_name, ftos(i), ftos(gettaginfo_parent)
1053 );
1054 LOG_HELPF(" vector = %s %s %s", ftos(v.x), ftos(v.y), ftos(v.z));
1057 LOG_HELPF(" right = %s %s %s", ftos(gettaginfo_right.x), ftos(gettaginfo_right.y), ftos(gettaginfo_right.z));
1058 LOG_HELPF(" up = %s %s %s", ftos(gettaginfo_up.x), ftos(gettaginfo_up.y), ftos(gettaginfo_up.z));
1059 if (argc >= 6)
1060 {
1061 v.y = -v.y;
1062 localcmd(strcat(argv(4), vtos(v), argv(5), "\n"));
1063 }
1064 }
1065 else
1066 LOG_INFO("bone not found");
1067
1068 delete(tmp_entity);
1069 return;
1070 }
1071 }
1072
1073 default:
1074 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1075 case CMD_REQUEST_USAGE:
1076 {
1077 LOG_HELP("Usage:^3 sv_cmd gettaginfo <model> <frame> <index> [<command1>] [<command2>]");
1078 LOG_HELP("See also: ^2bbox, trace^7");
1079 return;
1080 }
1081 }
1082}
1083
1084void GameCommand_animbench(int request, int argc)
1085{
1086 switch (request)
1087 {
1089 {
1090 if (argc >= 4)
1091 {
1092 entity tmp_entity = spawn();
1093 if (argv(1) == "w")
1094 {
1095 .entity weaponentity = weaponentities[0];
1096 _setmodel(tmp_entity, (nextent(NULL)).(weaponentity).model);
1097 }
1098 else
1099 {
1100 precache_model(argv(1));
1101 _setmodel(tmp_entity, argv(1));
1102 }
1103 float f1 = stof(argv(2));
1104 float f2 = stof(argv(3));
1105 float t0;
1106 float t1 = 0;
1107 float t2 = 0;
1108 float n = 0;
1109
1110 while (t1 + t2 < 1)
1111 {
1112 tmp_entity.frame = f1;
1113 t0 = gettime(GETTIME_HIRES);
1114 getsurfacepoint(tmp_entity, 0, 0);
1115 t1 += gettime(GETTIME_HIRES) - t0;
1116 tmp_entity.frame = f2;
1117 t0 = gettime(GETTIME_HIRES);
1118 getsurfacepoint(tmp_entity, 0, 0);
1119 t2 += gettime(GETTIME_HIRES) - t0;
1120 ++n;
1121 }
1122 LOG_INFO("model ", tmp_entity.model, " frame ", ftos(f1), " animtime ", ftos(n / t1), "/s");
1123 LOG_INFO("model ", tmp_entity.model, " frame ", ftos(f2), " animtime ", ftos(n / t2), "/s");
1124
1125 delete(tmp_entity);
1126 return;
1127 }
1128 }
1129
1130 default:
1131 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1132 case CMD_REQUEST_USAGE:
1133 {
1134 LOG_HELP("Usage:^3 sv_cmd animbench <model> <frame1> <frame2>");
1135 LOG_HELP("See also: ^2bbox, trace^7");
1136 return;
1137 }
1138 }
1139}
1140
1141void GameCommand_gotomap(int request, int argc)
1142{
1143 switch (request)
1144 {
1146 {
1147 if (!world_initialized)
1148 {
1149 LOG_HELPF("This command works only when the server is running.");
1150 return;
1151 }
1152 if (argv(1))
1153 {
1154 LOG_INFO(GotoMap(argv(1)));
1155 return;
1156 }
1157 }
1158
1159 default:
1160 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1161 case CMD_REQUEST_USAGE:
1162 {
1163 LOG_HELP("Usage:^3 sv_cmd gotomap <map>");
1164 LOG_HELP(" Where <map> is the *.bsp file to change to.");
1165 LOG_HELP("See also: ^2gametype^7");
1166 return;
1167 }
1168 }
1169}
1170
1171void GameCommand_lockteams(int request)
1172{
1173 switch (request)
1174 {
1176 {
1177 if (!world_initialized)
1178 {
1179 LOG_HELPF("This command works only when the server is running.");
1180 return;
1181 }
1182 if (teamplay)
1183 {
1184 lockteams = 1;
1185 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TEAMS_LOCKED);
1186 // nobody can join or change teams now so remove everyone from the queue
1187 FOREACH_CLIENT(it.wants_join, it.wants_join = it.team_selected = 0);
1188 }
1189 else
1190 bprint("lockteams command can only be used in a team-based gametype.\n");
1191 return;
1192 }
1193
1194 default:
1195 case CMD_REQUEST_USAGE:
1196 {
1197 LOG_HELP("Usage:^3 sv_cmd lockteams");
1198 LOG_HELP(" No arguments required.");
1199 LOG_HELP("See also: ^2unlockteams^7");
1200 return;
1201 }
1202 }
1203}
1204
1206{
1207 switch (request)
1208 {
1210 {
1211 entity tmp_entity = new(make_mapinfo);
1212 setthink(tmp_entity, make_mapinfo_Think);
1213 tmp_entity.nextthink = time;
1215 return;
1216 }
1217
1218 default:
1219 case CMD_REQUEST_USAGE:
1220 {
1221 LOG_HELP("Usage:^3 sv_cmd make_mapinfo");
1222 LOG_HELP(" No arguments required.");
1223 LOG_HELP("See also: ^2radarmap^7");
1224 return;
1225 }
1226 }
1227}
1228
1229void GameCommand_moveplayer(int request, int argc)
1230{
1231 switch (request)
1232 {
1234 {
1235 if (!world_initialized)
1236 {
1237 LOG_HELPF("This command works only when the server is running.");
1238 return;
1239 }
1240 float accepted;
1241 entity client;
1242
1243 string targets = strreplace(",", " ", argv(1));
1244 string original_targets = strreplace(" ", ", ", targets);
1245 string destination = argv(2);
1246 if (destination == "spec")
1247 destination = "spectator";
1248
1249 if ((targets) && (destination))
1250 {
1251 string successful = string_null;
1252 string t;
1253 for ( ; targets; )
1254 {
1255 t = car(targets);
1256 targets = cdr(targets);
1257
1258 // Check to see if the player is a valid target
1259 client = GetFilteredEntity(t);
1260 accepted = VerifyClientEntity(client, false, false);
1261 string client_num_str = ftos(etof(client));
1262
1263 if (accepted <= 0)
1264 LOG_INFO("moveplayer: ", GetClientErrorString(accepted, t), ".");
1265 else if (destination == "spectator")
1266 {
1267 string pl_name = playername(client.netname, client.team, false);
1268 if (!(IS_SPEC(client) || IS_OBSERVER(client)) || INGAME(client))
1269 {
1270 PutObserverInServer(client, true, true);
1271
1272 successful = strcat(successful, (successful ? ", " : ""), pl_name);
1273 }
1274 else
1275 LOG_INFO("Player #", client_num_str, " (", pl_name, ") is already spectating.");
1276 }
1277 else
1278 {
1279 if (!teamplay)
1280 {
1281 LOG_INFO("Can't change teams when currently not playing a team game.");
1282 return;
1283 }
1284
1285 string pl_name = playername(client.netname, client.team, false);
1286 if (IS_SPEC(client) || IS_OBSERVER(client))
1287 {
1288 // well technically we could, but should we allow that? :P
1289 LOG_INFO("Player #", client_num_str, " (", pl_name, ") is not in the game.");
1290 continue;
1291 }
1292
1293 // find the team to move the player to
1294 int team_num = Team_ColorToTeam(destination);
1295 entity balance;
1296 if (team_num == client.team) // already on the destination team
1297 {
1298 LOG_INFO("Player #", client_num_str, " (", pl_name, ") is already on the ",
1299 Team_ColoredFullName(team_num), ".");
1300 continue;
1301 }
1302 else
1303 {
1304 balance = TeamBalance_CheckAllowedTeams(client);
1305 if (team_num == 0) // auto team
1306 team_num = Team_IndexToTeam(TeamBalance_FindBestTeam(balance, client, false));
1307 }
1308
1309 // Check to see if the destination team is even available
1310 int team_id = Team_TeamToIndex(team_num);
1311 if (team_id == -1)
1312 {
1313 LOG_INFO("Can't move player to ", destination, " team because it doesn't exist.");
1314 TeamBalance_Destroy(balance);
1315 return;
1316 }
1317 if (!(teamplay_bitmask & Team_TeamToBit(team_num)))
1318 {
1319 LOG_INFO("Can't move player to ", destination, " team because it isn't available.");
1320 TeamBalance_Destroy(balance);
1321 return;
1322 }
1323 if (!TeamBalance_IsTeamAllowed(balance, team_id))
1324 {
1325 LOG_INFO("Player #", client_num_str, " (", pl_name, ") is not allowed to join the ",
1326 Team_ColoredFullName(team_num), ".");
1327 TeamBalance_Destroy(balance);
1328 continue;
1329 }
1330 TeamBalance_Destroy(balance);
1331
1332 // If so, lets continue and finally move the player
1333 if (MoveToTeam(client, team_id, 6))
1334 {
1335 successful = strcat(successful, (successful ? ", " : ""), pl_name);
1336 LOG_INFO("Player #", client_num_str, " (", pl_name, ") has been moved to the ",
1337 Team_ColoredFullName(team_num), ".");
1338 }
1339 else
1340 LOG_INFO("Unable to move player #", client_num_str, " (", pl_name, ")");
1341 }
1342 } // loop end
1343
1344 if (successful)
1345 bprint("Successfully moved players ", successful, " to destination ", destination, ".\n");
1346 else
1347 LOG_INFO("No players given (", original_targets, ") are able to move.");
1348
1349 return; // still correct parameters so return to avoid usage print
1350 }
1351 }
1352
1353 default:
1354 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1355 case CMD_REQUEST_USAGE:
1356 {
1357 LOG_HELP("Usage:^3 sv_cmd moveplayer <clients> <destination>");
1358 LOG_HELP(" <clients> is a list (separated by commas) of player entity ID's or nicknames");
1359 LOG_HELP(" <destination> is what to send the player to, be it team or spectating");
1360 LOG_HELP(" Full list of destinations here: spec, spectator, red, blue, yellow, pink, auto.");
1361 LOG_HELP("Examples: sv_cmd moveplayer 1,3,5 red");
1362 LOG_HELP(" sv_cmd moveplayer 2 spec");
1363 LOG_HELP("See also: ^2allspec, shuffleteams^7");
1364 return;
1365 }
1366 }
1367}
1368
1369void GameCommand_nextmap(int request, int argc)
1370{
1371 switch (request)
1372 {
1374 {
1375 if (argc >= 2) // nextmap with mapname arg
1376 {
1377 if (argc > 2)
1378 LOG_HELP("Warning: nextmap only takes 1 argument");
1379
1380 string map = ValidateMap(argv(1), NULL);
1381 if (map != "" && map != get_nextmap())
1382 Set_NextMap(map);
1383 }
1384 else // nextmap without args
1385 {
1386 if (get_nextmap() != "")
1388 else
1389 LOG_HELP("none");
1390 }
1391 return; // never fall through to usage
1392 }
1393
1394 default:
1395 case CMD_REQUEST_USAGE:
1396 {
1397 LOG_HELP("Usage:^3 sv_cmd nextmap [<mapname>]");
1398 LOG_HELP(" Without arguments it prints current nextmap if one is set");
1399 LOG_HELP(" With arguments it attempts to set nextmap");
1400 return;
1401 }
1402 }
1403}
1404
1406{
1407 switch (request)
1408 {
1410 {
1411 if (!world_initialized)
1412 {
1413 LOG_HELPF("This command works only when the server is running.");
1414 return;
1415 }
1416 cvar_settemp("sv_spectate", "0");
1417 // if sv_spectate was enabled PlayerFrame() sends SPECTATE_WARNING notifications
1418 return;
1419 }
1420
1421 default:
1422 case CMD_REQUEST_USAGE:
1423 {
1424 LOG_HELP("Usage:^3 sv_cmd nospectators");
1425 LOG_HELP(" No arguments required.");
1426 LOG_HELP("Temporarily disables the ^3sv_spectate ^7cvar.");
1427 return;
1428 }
1429 }
1430}
1431
1432void GameCommand_printstats(int request)
1433{
1434 switch (request)
1435 {
1437 {
1438 if (!world_initialized)
1439 {
1440 LOG_HELPF("This command works only when the server is running.");
1441 return;
1442 }
1443 DumpStats(false);
1444 LOG_INFO("stats dumped.");
1445 return;
1446 }
1447
1448 default:
1449 case CMD_REQUEST_USAGE:
1450 {
1451 LOG_HELP("Usage:^3 sv_cmd printstats");
1452 LOG_HELP(" No arguments required.");
1453 return;
1454 }
1455 }
1456}
1457
1458void GameCommand_radarmap(int request, int argc)
1459{
1460 switch (request)
1461 {
1463 {
1464 if (RadarMap_Make(argc))
1465 return;
1466 }
1467
1468 default:
1469 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1470 case CMD_REQUEST_USAGE:
1471 {
1472 LOG_HELP("Usage:^3 sv_cmd radarmap [--force] [--loop] [--quit] [--block | --trace | --sample | --lineblock] [--sharpen N] [--res W H] [--qual Q]");
1473 LOG_HELP(" The quality factor Q is roughly proportional to the time taken.");
1474 LOG_HELP(" trace supports no quality factor; its result should look like --block with infinite quality factor.");
1475 LOG_HELP("See also: ^2make_mapinfo^7");
1476 return;
1477 }
1478 }
1479}
1480
1482{
1483 switch (request)
1484 {
1486 {
1487 if (!world_initialized)
1488 {
1489 LOG_HELPF("This command works only when the server is running.");
1490 return;
1491 }
1493 return;
1494 }
1495
1496 default:
1497 case CMD_REQUEST_USAGE:
1498 {
1499 LOG_HELP("Usage:^3 sv_cmd reducematchtime");
1500 LOG_HELP(" No arguments required.");
1501 LOG_HELP("See also: ^2extendmatchtime^7");
1502 return;
1503 }
1504 }
1505}
1506
1507void GameCommand_setbots(int request, int argc)
1508{
1509 switch (request)
1510 {
1512 {
1513 if (argc >= 2)
1514 {
1515 cvar_settemp("minplayers", "0");
1516 cvar_settemp("minplayers_per_team", "0");
1517 cvar_settemp("bot_number", argv(1));
1518 bot_fixcount(true);
1519 return;
1520 }
1521 }
1522
1523 default:
1524 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1525 case CMD_REQUEST_USAGE:
1526 {
1527 LOG_HELP("Usage:^3 sv_cmd setbots <botnumber>");
1528 LOG_HELP(" Where <botnumber> is the amount of bots to set bot_number cvar to.");
1529 LOG_HELP("See also: ^2bot_cmd^7");
1530 return;
1531 }
1532 }
1533}
1534
1536{
1537 if (!teamplay)
1538 {
1539 LOG_INFO("Can't shuffle teams when currently not playing a team game.");
1540 return;
1541 }
1542
1543 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
1544 if (Player_HasRealForcedTeam(it)) {
1545 // we could theoretically assign forced players to their teams
1546 // and shuffle the rest to fill the empty spots but in practise
1547 // either all players or none are gonna have forced teams
1548 LOG_INFO("Can't shuffle teams because at least one player has a forced team.");
1549 return;
1550 }
1551 });
1552
1553 int number_of_teams = 0;
1555 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1556 if (TeamBalance_IsTeamAllowed(balance, i))
1557 number_of_teams = max(i, number_of_teams);
1558 TeamBalance_Destroy(balance);
1559
1560 int team_index = 0;
1562 int target_team_index = team_index + 1;
1563 if (Entity_GetTeamIndex(it) != target_team_index)
1564 MoveToTeam(it, target_team_index, 6);
1565 team_index = (team_index + 1) % number_of_teams;
1566 });
1567
1568 bprint("Successfully shuffled the players around randomly.\n");
1569}
1570
1572{
1573 switch (request)
1574 {
1576 {
1577 if (!world_initialized)
1578 {
1579 LOG_HELPF("This command works only when the server is running.");
1580 return;
1581 }
1583 {
1584 bprint("Players will be shuffled when this round is over.\n");
1586 }
1587 else
1588 shuffleteams();
1589 return;
1590 }
1591
1592 default:
1593 case CMD_REQUEST_USAGE:
1594 {
1595 LOG_HELP("Usage:^3 sv_cmd shuffleteams");
1596 LOG_HELP(" No arguments required.");
1597 LOG_HELP("See also: ^2moveplayer, allspec^7");
1598 return;
1599 }
1600 }
1601}
1602
1603void GameCommand_resetmatch(int request)
1604{
1605 switch (request)
1606 {
1608 {
1609 if (!world_initialized)
1610 {
1611 LOG_HELPF("This command works only when the server is running.");
1612 return;
1613 }
1614 ReadyRestart(false);
1615 return;
1616 }
1617
1618 default:
1619 case CMD_REQUEST_USAGE:
1620 {
1621 LOG_HELP("Usage:^3 sv_cmd resetmatch");
1622 LOG_HELP(" No arguments required.");
1623 return;
1624 }
1625 }
1626}
1627
1628void GameCommand_stuffto(int request, int argc)
1629{
1630 // This... is a fairly dangerous and powerful command... - It allows any arguments to be sent to a client via rcon.
1631 // Because of this, it is disabled by default and must be enabled by the server owner when doing compilation. That way,
1632 // we can be certain they understand the risks of it... So to enable, compile server with -DSTUFFTO_ENABLED argument.
1633
1634#ifdef STUFFTO_ENABLED
1635 switch (request)
1636 {
1638 {
1639 if (!world_initialized)
1640 {
1641 LOG_HELPF("This command works only when the server is running.");
1642 return;
1643 }
1644 if (argv(2))
1645 {
1646 entity client = GetIndexedEntity(argc, 1);
1647 float accepted = VerifyClientEntity(client, true, false);
1648
1649 if (accepted > 0)
1650 {
1651 stuffcmd(client, strcat("\n", argv(next_token), "\n"));
1652 LOG_INFO("Command: \"", argv(next_token), "\" sent to ", GetCallerName(client), " (", argv(1), ").");
1653 }
1654 else
1655 LOG_INFO("stuffto: ", GetClientErrorString(accepted, argv(1)), ".");
1656
1657 return;
1658 }
1659 }
1660
1661 default:
1662 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1663 case CMD_REQUEST_USAGE:
1664 {
1665 LOG_HELP("Usage:^3 sv_cmd stuffto <client> \"<command>\"");
1666 LOG_HELP(" <client> is the entity number or name of the player,");
1667 LOG_HELP(" and <command> is the command to be sent to that player.");
1668 return;
1669 }
1670 }
1671#else
1672 if (request)
1673 {
1674 LOG_HELP("stuffto command is not enabled on this server.");
1675 return;
1676 }
1677#endif
1678}
1679
1680void GameCommand_trace(int request, int argc)
1681{
1682 switch (request)
1683 {
1685 {
1686 entity e;
1687 vector org, delta, start, end, p, q, q0, pos, vv, dv;
1688 float i, f, safe, unsafe, dq, dqf;
1689
1690 switch (argv(1))
1691 {
1692 case "debug":
1693 {
1694 float hitcount = 0;
1695 LOG_INFO("TEST CASE. If this returns the runaway loop counter error, possibly everything is oaky.");
1696 float worst_endpos_bug = 0;
1697 for ( ; ; )
1698 {
1699 org = world.mins;
1700 delta = world.maxs - world.mins;
1701
1702 start.x = org.x + random() * delta.x;
1703 start.y = org.y + random() * delta.y;
1704 start.z = org.z + random() * delta.z;
1705
1706 end.x = org.x + random() * delta.x;
1707 end.y = org.y + random() * delta.y;
1708 end.z = org.z + random() * delta.z;
1709
1710 start = stov(vtos(start));
1711 end = stov(vtos(end));
1712
1713 tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL);
1714 if (!trace_startsolid && trace_fraction < 1)
1715 {
1716 p = trace_endpos;
1717 tracebox(p, PL_MIN_CONST, PL_MAX_CONST, p, MOVE_NOMONSTERS, NULL);
1718 if (trace_startsolid)
1719 {
1720 rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid
1721 tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL);
1722
1723 // how much do we need to back off?
1724 safe = 1;
1725 unsafe = 0;
1726 for ( ; ; )
1727 {
1728 pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5);
1729 tracebox(pos, PL_MIN_CONST, PL_MAX_CONST, pos, MOVE_NOMONSTERS, NULL);
1730 if (trace_startsolid)
1731 {
1732 if ((safe + unsafe) * 0.5 == unsafe)
1733 break;
1734 unsafe = (safe + unsafe) * 0.5;
1735 }
1736 else
1737 {
1738 if ((safe + unsafe) * 0.5 == safe)
1739 break;
1740 safe = (safe + unsafe) * 0.5;
1741 }
1742 }
1743
1744 LOG_INFO("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu");
1745 LOG_INFO("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu");
1746
1747 tracebox(p, PL_MIN_CONST + '0.1 0.1 0.1', PL_MAX_CONST - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, NULL);
1748 if (trace_startsolid)
1749 LOG_INFO("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p));
1750 else
1751 LOG_INFO("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p));
1752 if (++hitcount >= 10)
1753 break;
1754 }
1755 else
1756 {
1757 q0 = p;
1758 dq = 0;
1759 dqf = 1;
1760 for ( ; ; )
1761 {
1762 q = p + normalize(end - p) * (dq + dqf);
1763 if (q == q0)
1764 break;
1765 tracebox(p, PL_MIN_CONST, PL_MAX_CONST, q, MOVE_NOMONSTERS, NULL);
1766 if (trace_startsolid)
1767 error("THIS ONE cannot happen");
1768 if (trace_fraction > 0)
1769 dq += dqf * trace_fraction;
1770 dqf *= 0.5;
1771 q0 = q;
1772 }
1773 if (dq > worst_endpos_bug)
1774 {
1775 worst_endpos_bug = dq;
1776 LOG_INFO("trace_endpos still before solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p));
1777 LOG_INFO("could go ", ftos(dq), " units further to ", vtos(q));
1778 if (++hitcount >= 10)
1779 break;
1780 }
1781 }
1782 }
1783 }
1784 return;
1785 }
1786
1787 case "debug2":
1788 {
1789 e = nextent(NULL);
1790 tracebox(e.origin + '0 0 32', e.mins, e.maxs, e.origin + '0 0 -1024', MOVE_NORMAL, e);
1791 vv = trace_endpos;
1792 if (trace_fraction == 1)
1793 {
1794 LOG_INFO("not above ground, aborting");
1795 return;
1796 }
1797 f = 0;
1798 for (i = 0; i < 100000; ++i)
1799 {
1800 dv = randomvec();
1801 if (dv.z > 0)
1802 dv = -1 * dv;
1803 tracebox(vv, e.mins, e.maxs, vv + dv, MOVE_NORMAL, e);
1804 if (trace_startsolid)
1805 LOG_INFO("bug 1");
1806 if (trace_fraction == 1)
1807 {
1808 if (dv.z < f)
1809 {
1810 LOG_INFO("bug 2: ", ftos(dv.x), " ", ftos(dv.y), " ", ftos(dv.z));
1811 LOG_INFO(" (", ftos(asin(dv.z / vlen(dv)) * 180 / M_PI), " degrees)");
1812 f = dv.z;
1813 }
1814 }
1815 }
1816 LOG_INFO("highest possible dist: ", ftos(f));
1817 return;
1818 }
1819
1820 case "walk":
1821 {
1822 if (argc == 4 || argc == 5)
1823 {
1824 e = nextent(NULL);
1825 int dphitcontentsmask_save = e.dphitcontentsmask;
1827 if (tracewalk(e, stov(argv(2)), e.mins, e.maxs, stov(argv(3)), stof(argv(4)), MOVE_NORMAL))
1828 LOG_INFO("can walk");
1829 else
1830 LOG_INFO("cannot walk");
1831 e.dphitcontentsmask = dphitcontentsmask_save;
1832 return;
1833 }
1834 }
1835
1836 case "showline":
1837 {
1838 if (argc == 4)
1839 {
1840 vv = stov(argv(2));
1841 dv = stov(argv(3));
1842 traceline(vv, dv, MOVE_NORMAL, NULL);
1843 __trailparticles(NULL, particleeffectnum(EFFECT_TR_NEXUIZPLASMA), vv, trace_endpos);
1844 __trailparticles(NULL, particleeffectnum(EFFECT_TR_CRYLINKPLASMA), trace_endpos, dv);
1845 return;
1846 }
1847 }
1848
1849 // no default case, just go straight to invalid
1850 }
1851 }
1852
1853 default:
1854 LOG_HELPF("Incorrect parameters for ^2%s^7", argv(0));
1855 case CMD_REQUEST_USAGE:
1856 {
1857 LOG_HELP("Usage:^3 sv_cmd trace <command> [<startpos> <endpos>] [<endpos_height>]");
1858 LOG_HELP(" Where <startpos> and <endpos> are parameters for the 'walk' and 'showline' commands,");
1859 LOG_HELP(" <endpos_height> is an optional parameter for the 'walk' command,");
1860 LOG_HELP(" Full list of commands here: debug, debug2, walk, showline.");
1861 LOG_HELP("See also: ^2bbox, gettaginfo^7");
1862 return;
1863 }
1864 }
1865}
1866
1868{
1869 switch (request)
1870 {
1872 {
1873 if (!world_initialized)
1874 {
1875 LOG_HELPF("This command works only when the server is running.");
1876 return;
1877 }
1878 if (teamplay)
1879 {
1880 lockteams = 0;
1881 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_TEAMS_UNLOCKED);
1882 }
1883 else
1884 bprint("unlockteams command can only be used in a team-based gametype.\n");
1885 return;
1886 }
1887
1888 default:
1889 case CMD_REQUEST_USAGE:
1890 {
1891 LOG_HELP("Usage:^3 sv_cmd unlockteams");
1892 LOG_HELP(" No arguments required.");
1893 LOG_HELP("See also: ^2lockteams^7");
1894 return;
1895 }
1896 }
1897}
1898
1899void GameCommand_warp(int request, int argc)
1900{
1901 switch (request)
1902 {
1904 {
1905 if (!world_initialized)
1906 {
1907 LOG_HELPF("This command works only when the server is running.");
1908 return;
1909 }
1911 {
1912 if (argc >= 2)
1913 {
1915 LOG_INFO("Successfully warped to campaign level ", argv(1), ".");
1916 }
1917 else
1918 {
1920 LOG_INFO("Successfully warped to next campaign level.");
1921 }
1922 }
1923 else
1924 LOG_INFO("Not in campaign, can't level warp");
1925 return;
1926 }
1927
1928 default:
1929 case CMD_REQUEST_USAGE:
1930 {
1931 LOG_HELP("Usage:^3 sv_cmd warp [<level>]");
1932 LOG_HELP(" <level> is the level to change campaign mode to.");
1933 LOG_HELP(" if <level> is not provided it will change to the next level.");
1934 return;
1935 }
1936 }
1937}
1938
1939/* use this when creating a new command, making sure to place it in alphabetical order... also,
1940** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
1941void GameCommand_(int request)
1942{
1943 switch(request)
1944 {
1945 case CMD_REQUEST_COMMAND:
1946 {
1947 if (!world_initialized)
1948 {
1949 LOG_HELPF("This command works only when the server is running.");
1950 return;
1951 }
1952
1953 return;
1954 }
1955
1956 default:
1957 case CMD_REQUEST_USAGE:
1958 {
1959 LOG_HELP("Usage:^3 sv_cmd ");
1960 LOG_HELP(" No arguments required.");
1961 return;
1962 }
1963 }
1964}
1965*/
1966
1967
1968// ==================================
1969// Macro system for server commands
1970// ==================================
1971
1972// Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
1973SERVER_COMMAND(adminmsg, "Send an admin message to a client directly") { GameCommand_adminmsg(request, arguments); }
1974SERVER_COMMAND(allready, "Ends warmup and starts the match") { GameCommand_allready(request); }
1975SERVER_COMMAND(allspec, "Force all players to spectate") { GameCommand_allspec(request, arguments); }
1976SERVER_COMMAND(anticheat, "Create an anticheat report for a client") { GameCommand_anticheat(request, arguments); }
1977SERVER_COMMAND(animbench, "Benchmark model animation (LAGS)") { GameCommand_animbench(request, arguments); }
1978SERVER_COMMAND(balanceteams, "Automatically balance teams by skill") { GameCommand_balanceteams(request, arguments); }
1979SERVER_COMMAND(bbox, "Print detailed information about world size") { GameCommand_bbox(request); }
1980SERVER_COMMAND(bot_cmd, "Control and send commands to bots") { GameCommand_bot_cmd(request, arguments, command); }
1981SERVER_COMMAND(cointoss, "Flip a virtual coin and give random result") { GameCommand_cointoss(request, arguments); }
1982SERVER_COMMAND(database, "Extra controls of the serverprogs database") { GameCommand_database(request, arguments); }
1983SERVER_COMMAND(defer_clear, "Clear all queued defer commands for a specific client") { GameCommand_defer_clear(request, arguments); }
1984SERVER_COMMAND(defer_clear_all, "Clear all queued defer commands for all clients") { GameCommand_defer_clear_all(request); }
1985SERVER_COMMAND(delrec, "Delete race time record for a map") { GameCommand_delrec(request, arguments); }
1986SERVER_COMMAND(effectindexdump, "Dump list of effects from code and effectinfo.txt") { GameCommand_effectindexdump(request); }
1987SERVER_COMMAND(extendmatchtime, "Increase the timelimit value incrementally") { GameCommand_extendmatchtime(request); }
1988SERVER_COMMAND(gametype, "Simple command to change the active gametype") { GameCommand_gametype(request, arguments); }
1989SERVER_COMMAND(gettaginfo, "Get specific information about a weapon model") { GameCommand_gettaginfo(request, arguments); }
1990SERVER_COMMAND(gotomap, "Simple command to switch to another map") { GameCommand_gotomap(request, arguments); }
1991SERVER_COMMAND(lockteams, "Disable the ability for players to switch or enter teams") { GameCommand_lockteams(request); }
1992SERVER_COMMAND(make_mapinfo, "Automatically rebuild mapinfo files") { GameCommand_make_mapinfo(request); }
1993SERVER_COMMAND(moveplayer, "Change the team/status of a player") { GameCommand_moveplayer(request, arguments); }
1994SERVER_COMMAND(nextmap, "Set/Query the next map") { GameCommand_nextmap(request, arguments); }
1995SERVER_COMMAND(nospectators, "Automatically remove spectators from a match") { GameCommand_nospectators(request); }
1996SERVER_COMMAND(printstats, "Dump eventlog player stats and other score information") { GameCommand_printstats(request); }
1997SERVER_COMMAND(radarmap, "Generate a radar image of the map") { GameCommand_radarmap(request, arguments); }
1998SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); }
1999SERVER_COMMAND(resetmatch, "Soft restart the game without changing teams; goes back to warmup if enabled") { GameCommand_resetmatch(request); }
2000SERVER_COMMAND(setbots, "Adjust how many bots are in the match") { GameCommand_setbots(request, arguments); }
2001SERVER_COMMAND(shuffleteams, "Randomly move players to different teams") { GameCommand_shuffleteams(request); }
2002SERVER_COMMAND(stuffto, "Send a command to be executed on a client") { GameCommand_stuffto(request, arguments); }
2003SERVER_COMMAND(trace, "Various debugging tools with tracing") { GameCommand_trace(request, arguments); }
2004SERVER_COMMAND(unlockteams, "Enable the ability for players to switch or enter teams") { GameCommand_unlockteams(request); }
2005SERVER_COMMAND(warp, "Choose different level in campaign") { GameCommand_warp(request, arguments); }
2006
2008{
2009 FOREACH(SERVER_COMMANDS, true, { LOG_HELPF(" ^2%s^7: %s", it.m_name, it.m_description); });
2010}
2011
2012float GameCommand_macro_command(int argc, string command)
2013{
2014 string c = strtolower(argv(0));
2015 FOREACH(SERVER_COMMANDS, it.m_name == c, {
2016 it.m_invokecmd(it, CMD_REQUEST_COMMAND, NULL, argc, command);
2017 return true;
2018 });
2019 return false;
2020}
2021
2023{
2024 string c = strtolower(argv(1));
2025 FOREACH(SERVER_COMMANDS, it.m_name == c, {
2026 it.m_invokecmd(it, CMD_REQUEST_USAGE, NULL, argc, "");
2027 return true;
2028 });
2029 return false;
2030}
2031
2033{
2034 FOREACH(SERVER_COMMANDS, true, { CMD_Write_Alias("qc_cmd_sv", it.m_name, it.m_description); });
2035}
2036
2037
2038// =========================================
2039// Main Function Called By Engine (sv_cmd)
2040// =========================================
2041// If this function exists, game code handles gamecommand instead of the engine code.
2042
2043void GameCommand(string command)
2044{
2045 int argc = tokenize_console(command);
2046
2047 // Guide for working with argc arguments by example:
2048 // argc: 1 - 2 - 3 - 4
2049 // argv: 0 - 1 - 2 - 3
2050 // cmd vote - master - login - password
2051
2052 if (strtolower(argv(0)) == "help")
2053 {
2054 if (argc == 1)
2055 {
2056 LOG_HELP("Server console commands:");
2058
2059 LOG_HELP("\nBanning commands:");
2061
2062 LOG_HELP("\nCommon networked commands:");
2064
2065 LOG_HELP("\nGeneric commands shared by all programs:");
2067
2068 LOG_HELP("\nUsage:^3 sv_cmd <command>^7, where possible commands are listed above.\n"
2069 "For help about a specific command, type sv_cmd help <command>");
2070
2071 return;
2072 }
2073 else if (BanCommand_macro_usage(argc)) // Instead of trying to call a command, we're going to see detailed information about it
2074 return;
2075 else if (CommonCommand_macro_usage(argc, NULL)) // same here, but for common commands instead
2076 return;
2077 else if (GenericCommand_macro_usage(argc)) // same here, but for generic commands instead
2078 return;
2079 else if (GameCommand_macro_usage(argc)) // finally try for normal commands too
2080 return;
2081 }
2082 else if (MUTATOR_CALLHOOK(SV_ParseServerCommand, strtolower(argv(0)), argc, command))
2083 return; // handled by a mutator
2084 else if (BanCommand(command))
2085 return; // handled by server/command/ipban.qc
2086 else if (CommonCommand_macro_command(argc, NULL, command))
2087 return; // handled by server/command/common.qc
2088 else if (GenericCommand(command))
2089 return; // handled by common/command/generic.qc
2090 else if (GameCommand_macro_command(argc, command)) // continue as usual and scan for normal commands
2091 return; // handled by one of the above GameCommand_* functions
2092
2093 // nothing above caught the command, must be invalid
2094 LOG_INFO(((command != "") ? strcat("Unknown server command \"", command, "\"") : "No command provided"), ". For a list of supported commands, try sv_cmd help.");
2095}
void bot_resetqueues()
entity find_bot_by_number(float number)
Definition scripting.qc:246
bool bot_fixcount(bool multiple_per_frame)
Definition bot.qc:623
entity find_bot_by_name(string name)
Definition scripting.qc:235
void bot_queuecommand(entity bot, string cmdstring)
Definition scripting.qc:30
void bot_cmdhelp(string scmd)
Definition scripting.qc:331
bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float end_height, float movemode)
void bot_list_commands()
Definition scripting.qc:444
void BanCommand_macro_help()
Definition banning.qc:480
float BanCommand_macro_usage(int argc)
Definition banning.qc:500
float BanCommand(string command)
Definition banning.qc:520
#define MUTATOR_CALLHOOK(name,...)
Definition base.qh:186
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
bool warmup_stage
Definition main.qh:120
entity gametype
Definition main.qh:43
const int CMD_REQUEST_COMMAND
Definition command.qh:3
const int CMD_REQUEST_USAGE
Definition command.qh:4
#define IS_PLAYER(s)
Definition player.qh:242
#define autocvar_timelimit
Definition stats.qh:92
float game_starttime
Definition stats.qh:82
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2180
float cvar_settemp(string tmp_cvar, string tmp_value)
Definition util.qc:809
string strtolower(string s)
const vector PL_MIN_CONST
Definition constants.qh:53
const vector PL_MAX_CONST
Definition constants.qh:52
const float MOVE_NOMONSTERS
float DPCONTENTS_BOTCLIP
float GETTIME_HIRES
float gettaginfo_parent
float DPCONTENTS_SOLID
const float MOVE_NORMAL
float DPCONTENTS_BODY
vector gettaginfo_forward
string gettaginfo_name
const float FILE_READ
const float FILE_WRITE
float DPCONTENTS_PLAYERCLIP
float time
vector trace_endpos
float trace_startsolid
float nextthink
float MOVE_WORLDONLY
vector gettaginfo_right
vector gettaginfo_up
float trace_fraction
vector gettaginfo_offset
#define spawn
ERASEABLE string MakeConsoleSafe(string input)
Escape the string to make it safe for consoles.
Definition cvar.qh:24
#define strstrofs
#define gettagindex
#define tokenize_console
#define argv_start_index
#define particleeffectnum(e)
Definition effect.qh:3
model
Definition ent_cs.qc:164
float GenericCommand_macro_usage(int argc)
Definition generic.qc:582
void GenericCommand_macro_help()
Definition generic.qc:567
float GenericCommand(string command)
Definition generic.qc:603
#define CMD_Write_Alias(execute, command, description)
Definition generic.qh:34
#define stoi(s)
Definition int.qh:4
void Set_NextMap(string mapname)
string GetMapname()
string GotoMap(string m)
#define get_nextmap()
#define FOREACH(list, cond, body)
Definition iter.qh:19
#define LOG_HELP(...)
Definition log.qh:83
#define LOG_INFO(...)
Definition log.qh:62
#define LOG_TRACE(...)
Definition log.qh:74
#define LOG_HELPF(...)
Definition log.qh:84
#define LOG_INFOF(...)
Definition log.qh:63
ERASEABLE void db_close(int db)
Definition map.qh:86
ERASEABLE int db_load(string filename)
Definition map.qh:35
ERASEABLE int db_create()
Definition map.qh:25
ERASEABLE void db_save(int db, string filename)
Definition map.qh:8
ERASEABLE string db_get(int db, string key)
Definition map.qh:93
ERASEABLE void db_put(int db, string key, string value)
Definition map.qh:103
ERASEABLE void db_dump(int db, string filename)
Definition map.qh:70
float MapInfo_FilterGametype(Gametype pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
Definition mapinfo.qc:176
int MapInfo_RequiredFlags()
Definition mapinfo.qc:1675
float _MapInfo_FilterGametype(vector pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
Definition mapinfo.qc:180
Gametype MapInfo_CurrentGametype()
Definition mapinfo.qc:1487
int MapInfo_ForbiddenFlags()
Definition mapinfo.qc:1660
int MapInfo_CurrentFeatures()
Definition mapinfo.qc:1477
Gametype MapInfo_Type_FromString(string gtype, bool dowarn, bool is_q3compat)
Definition mapinfo.qc:620
void MapInfo_Enumerate()
Definition mapinfo.qc:134
vector MAPINFO_TYPE_ALL
Definition mapinfo.qh:27
#define M_PI
pi
Definition mathlib.qh:114
bool autocvar_g_campaign
Definition menu.qc:752
void localcmd(string command,...)
void cvar_set(string name, string value)
string fgets(float fhandle)
void fclose(float fhandle)
float stof(string val,...)
entity nextent(entity e)
string substring(string s, float start, float length)
float cvar(string name)
void sprint(float clientnum, string text,...)
float fopen(string filename, float mode)
float gettime(void)
vector stov(string s)
float random(void)
void bprint(string text,...)
const string cvar_string(string name)
float vlen(vector v)
vector randomvec(void)
void centerprint(string text,...)
string vtos(vector v)
float min(float f,...)
float rint(float f)
vector normalize(vector v)
string ftos(float f)
string argv(float n)
float max(float f,...)
#define etof(e)
Definition misc.qh:25
string string_null
Definition nil.qh:9
s2 minigame1_name minigames s f2
Definition all.inc:574
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
s2 minigame1_name minigames s f1
Definition all.inc:570
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1500
string PlayerStats_GetGametype()
int autocvar_g_playerstats_balance_stability
int autocvar_g_playerstats_balance_maxdifference
int autocvar_g_playerstats_balance_scorefactor
string autocvar_g_playerstats_balance_uri
float autocvar_g_playerstats_balance_sigmarange
const string PLAYERSTATS_ALIVETIME
#define PlayerStats_GameReport_Event_Player(ent, eventid, val)
#define NULL
Definition post.qh:14
#define world
Definition post.qh:15
#define gettaginfo
Definition post.qh:32
#define error
Definition pre.qh:6
#define stuffcmd(cl,...)
Definition progsdefs.qh:23
bool RadarMap_Make(int argc)
Definition radarmap.qh:3
#define AVAILABLE_TEAMS
Number of teams that exist currently.
entity bot_cmd
Definition scripting.qh:59
#define setthink(e, f)
vector
Definition self.qh:96
vector org
Definition self.qh:96
void CampaignLevelWarp(float n)
Definition campaign.qc:262
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition client.qc:261
entity GetIndexedEntity(int argc, int start_index)
Definition common.qc:82
int VerifyClientEntity(entity client, bool must_be_real, bool must_be_bots)
Definition common.qc:47
string GetCallerName(entity caller)
Definition common.qc:33
entity GetFilteredEntity(string input)
Definition common.qc:143
float CommonCommand_macro_usage(int argc, entity caller)
Definition common.qh:178
void CommonCommand_macro_help(entity caller)
Definition common.qh:163
#define GetClientErrorString(clienterror, original_input)
Definition common.qh:87
int next_token
Definition common.qh:71
float CommonCommand_macro_command(int argc, entity caller, string command)
Definition common.qh:168
#define SERVER_COMMAND(id, description)
Definition reg.qh:9
string ValidateMap(string validated_map, entity caller)
Definition vote.qc:678
void ReadyRestart(bool forceWarmupEnd)
Definition vote.qc:532
#define PlayerScore_Get(player, scorefield)
Returns the player's score.
Definition scores.qh:42
void anticheat_report_to_eventlog(entity this)
Definition anticheat.qc:210
ERASEABLE string car(string s)
Returns first word.
Definition string.qh:258
ERASEABLE string cdr(string s)
Returns all but first word.
Definition string.qh:268
void GameCommand_stuffto(int request, int argc)
Definition sv_cmd.qc:1628
void GameCommand_effectindexdump(int request)
Definition sv_cmd.qc:876
void GameCommand_cointoss(int request, int argc)
Definition sv_cmd.qc:690
void GameCommand_unlockteams(int request)
Definition sv_cmd.qc:1867
void GameCommand_radarmap(int request, int argc)
Definition sv_cmd.qc:1458
void GameCommand_delrec(int request, int argc)
Definition sv_cmd.qc:840
void GameCommand_gotomap(int request, int argc)
Definition sv_cmd.qc:1141
void GameCommand_allready(int request)
Definition sv_cmd.qc:171
float GameCommand_macro_command(int argc, string command)
Definition sv_cmd.qc:2012
void GameCommand_resetmatch(int request)
Definition sv_cmd.qc:1603
void print_Effect_Index(int d, string effect_name)
Definition sv_cmd.qc:869
void GameCommand_animbench(int request, int argc)
Definition sv_cmd.qc:1084
void changematchtime(float delta, float mi, float ma)
Definition sv_cmd.qc:50
void GameCommand_balanceteams(int request, int argc)
Definition sv_cmd.qc:452
void shuffleteams()
Definition sv_cmd.qc:1535
void GameCommand_database(int request, int argc)
Definition sv_cmd.qc:719
void GameCommand_bbox(int request)
Definition sv_cmd.qc:477
void GameCommand_defer_clear_all(int request)
Definition sv_cmd.qc:805
void balanceteams_handler(entity fh, entity p, float status)
Definition sv_cmd.qc:275
void GameCommand_extendmatchtime(int request)
Definition sv_cmd.qc:956
void GameCommand_setbots(int request, int argc)
Definition sv_cmd.qc:1507
void make_mapinfo_Think(entity this)
Definition sv_cmd.qc:34
void GameCommand_make_mapinfo(int request)
Definition sv_cmd.qc:1205
void GameCommand_shuffleteams(int request)
Definition sv_cmd.qc:1571
void GameCommand_gettaginfo(int request, int argc)
Definition sv_cmd.qc:1023
void GameCommand_lockteams(int request)
Definition sv_cmd.qc:1171
void GameCommand_nospectators(int request)
Definition sv_cmd.qc:1405
void balanceteams()
Definition sv_cmd.qc:386
void GameCommand_warp(int request, int argc)
Definition sv_cmd.qc:1899
void GameCommand_allspec(int request, int argc)
Definition sv_cmd.qc:200
void GameCommand_bot_cmd(int request, int argc, string command)
Definition sv_cmd.qc:550
void GameCommand_adminmsg(int request, int argc)
Definition sv_cmd.qc:94
void GameCommand_nextmap(int request, int argc)
Definition sv_cmd.qc:1369
void GameCommand_anticheat(int request, int argc)
Definition sv_cmd.qc:236
void GameCommand_moveplayer(int request, int argc)
Definition sv_cmd.qc:1229
void GameCommand_reducematchtime(int request)
Definition sv_cmd.qc:1481
void GameCommand(string command)
Definition sv_cmd.qc:2043
void GameCommand_macro_help()
Definition sv_cmd.qc:2007
float GameCommand_macro_usage(int argc)
Definition sv_cmd.qc:2022
void GameCommand_defer_clear(int request, int argc)
Definition sv_cmd.qc:762
void GameCommand_gametype(int request, int argc)
Definition sv_cmd.qc:982
void GameCommand_macro_write_aliases(float fh)
Definition sv_cmd.qc:2032
void GameCommand_printstats(int request)
Definition sv_cmd.qc:1432
void GameCommand_trace(int request, int argc)
Definition sv_cmd.qc:1680
bool shuffleteams_on_reset_map
Definition sv_cmd.qh:9
bool balanceteams_on_reset_map
Definition sv_cmd.qh:6
void race_deleteTime(string map, float pos)
Definition race.qc:461
bool GameTypeVote_SetGametype(Gametype type, string gametype_string, bool call_hooks)
#define INGAME(it)
Definition sv_rules.qh:24
int Entity_GetTeamIndex(entity this)
Returns the team index of the given entity.
Definition teamplay.qc:210
void TeamBalance_Destroy(entity balance)
Destroy the team balance entity.
Definition teamplay.qc:528
bool Player_HasRealForcedTeam(entity player)
Returns whether player has real forced team.
Definition teamplay.qc:340
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
Definition teamplay.qc:424
bool TeamBalance_IsTeamAllowed(entity balance, int index)
Returns whether the team change to the specified team is allowed.
Definition teamplay.qc:738
int TeamBalance_FindBestTeam(entity balance, entity player, bool ignore_player)
Finds the team that will make the game most balanced if the player joins it.
Definition teamplay.qc:892
bool MoveToTeam(entity client, int team_index, int type)
Moves player to the specified team.
Definition teamplay.qc:331
int teamplay_bitmask
The set of currently available teams (AVAILABLE_TEAMS is the number of them).
Definition teamplay.qh:18
bool lockteams
Definition teamplay.qh:20
bool Team_IsValidTeam(int team_num)
Returns whether team value is valid.
Definition teams.qh:133
int Team_TeamToBit(int team_num)
Converts team value into bit value that is used in team bitmasks.
Definition teams.qh:199
#define Static_Team_ColorName_Upper(teamid)
Definition teams.qh:227
int Team_TeamToIndex(int team_num)
Converts team value into team index.
Definition teams.qh:184
#define Team_ColoredFullName(teamid)
Definition teams.qh:232
int Team_IndexToTeam(int index)
Converts team index into team value.
Definition teams.qh:169
const int NUM_TEAMS
Max number of teams that could exist, prefer AVAILABLE_TEAMS.
Definition teams.qh:3
bool teamplay
Definition teams.qh:59
float Team_ColorToTeam(string team_color)
Definition teams.qh:116
ERASEABLE string url_fgets(entity e)
Definition urllib.qc:291
ERASEABLE void url_fputs(entity e, string s)
Definition urllib.qc:312
ERASEABLE void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass)
Definition urllib.qc:90
ERASEABLE void url_fclose(entity e)
Definition urllib.qc:209
const float URL_READY_CLOSED
Definition urllib.qh:15
const float URL_READY_ERROR
Definition urllib.qh:14
const float URL_READY_CANREAD
Definition urllib.qh:17
const float URL_READY_CANWRITE
Definition urllib.qh:16
#define IS_OBSERVER(v)
Definition utils.qh:11
#define FOREACH_CLIENT_RANDOM(cond, body)
Definition utils.qh:58
#define IS_SPEC(v)
Definition utils.qh:10
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
void DumpStats(float final)
Definition world.qc:1272
string matchid
Definition world.qh:63
float autocvar_timelimit_max
Definition world.qh:27
float cvar_purechanges_count
Definition world.qh:47
int world_initialized
Definition world.qh:43
float autocvar_timelimit_min
Definition world.qh:26
float autocvar_timelimit_decrement
Definition world.qh:25
string modname
Definition world.qh:49
float autocvar_timelimit_increment
Definition world.qh:24
float ServerProgsDB
Definition world.qh:127