Xonotic
common.qc
Go to the documentation of this file.
1 #include "common.qh"
2 
3 #include <common/command/_mod.qh>
7 #include <common/stats.qh>
8 #include <common/vehicles/all.qh>
9 #include <common/weapons/_all.qh>
10 #include <lib/warpzone/common.qh>
11 #include <server/campaign.qh>
12 #include <server/chat.qh>
13 #include <server/client.qh>
14 #include <server/command/common.qh>
15 #include <server/mutators/_mod.qh>
16 #include <server/scores.qh>
17 #include <server/world.qh>
18 
19 
20 // ====================================================
21 // Shared code for server commands, written by Samual
22 // Last updated: December 27th, 2011
23 // ====================================================
24 
25 // select the proper prefix for usage and other messages
26 string GetCommandPrefix(entity caller)
27 {
28  if (caller) return "cmd";
29  else return "sv_cmd";
30 }
31 
32 // if client return player nickname, or if server return admin nickname
33 string GetCallerName(entity caller)
34 {
35  if (caller) return playername(caller.netname, caller.team, false);
36  else return ((autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : "SERVER ADMIN"); // autocvar_hostname
37 }
38 
39 // verify that the client provided is acceptable for kicking
41 {
42  if (!IS_REAL_CLIENT(client)) return CLIENT_NOT_REAL;
43  return CLIENT_ACCEPTABLE;
44 }
45 
46 // verify that the client provided is acceptable for use
47 float VerifyClientEntity(entity client, float must_be_real, float must_be_bots)
48 {
49  if (!IS_CLIENT(client)) return CLIENT_DOESNT_EXIST;
50  else if (must_be_real && !IS_REAL_CLIENT(client)) return CLIENT_NOT_REAL;
51  else if (must_be_bots && !IS_BOT_CLIENT(client)) return CLIENT_NOT_BOT;
52 
53  return CLIENT_ACCEPTABLE;
54 }
55 
56 // if the client is not acceptable, return a string to be used for error messages
57 string GetClientErrorString_color(float clienterror, string original_input, string col)
58 {
59  switch (clienterror)
60  {
62  { return strcat(col, "Client '", original_input, col, "' doesn't exist");
63  }
64  case CLIENT_NOT_REAL:
65  { return strcat(col, "Client '", original_input, col, "' is not real");
66  }
67  case CLIENT_NOT_BOT:
68  { return strcat(col, "Client '", original_input, col, "' is not a bot");
69  }
70  default:
71  { return "Incorrect usage of GetClientErrorString";
72  }
73  }
74 }
75 
76 // is this entity number even in the possible range of entities?
77 float VerifyClientNumber(float tmp_number)
78 {
79  if ((tmp_number < 1) || (tmp_number > maxclients)) return false;
80  else return true;
81 }
82 
83 entity GetIndexedEntity(int argc, float start_index)
84 {
85  entity selection;
86  float tmp_number, index;
87  string tmp_string;
88 
89  next_token = -1;
90  index = start_index;
91  selection = NULL;
92 
93  if (argc > start_index)
94  {
95  if (substring(argv(index), 0, 1) == "#")
96  {
97  tmp_string = substring(argv(index), 1, -1);
98  ++index;
99 
100  if (tmp_string != "") // is it all one token? like #1
101  {
102  tmp_number = stof(tmp_string);
103  }
104  else if (argc > index) // no, it's two tokens? # 1
105  {
106  tmp_number = stof(argv(index));
107  ++index;
108  }
109  else
110  {
111  tmp_number = 0;
112  }
113  }
114  else // maybe it's ONLY a number?
115  {
116  tmp_number = stof(argv(index));
117  ++index;
118  }
119 
120  if (VerifyClientNumber(tmp_number))
121  {
122  selection = edict_num(tmp_number); // yes, it was a number
123  }
124  else // no, maybe it's a name?
125  {
126  FOREACH_CLIENT(true, {
127  if(strdecolorize(it.netname) == strdecolorize(argv(start_index)))
128  {
129  selection = it;
130  break; // no reason to keep looking
131  }
132  });
133 
134  index = (start_index + 1);
135  }
136  }
137 
138  next_token = index;
139  // print(strcat("start_index: ", ftos(start_index), ", next_token: ", ftos(next_token), ", edict: ", ftos(num_for_edict(selection)), ".\n"));
140  return selection;
141 }
142 
143 // find a player which matches the input string, and return their entity
145 {
146  entity selection;
147  float tmp_number;
148 
149  if (substring(input, 0, 1) == "#") tmp_number = stof(substring(input, 1, -1));
150  else tmp_number = stof(input);
151 
152  if (VerifyClientNumber(tmp_number))
153  {
154  selection = edict_num(tmp_number);
155  }
156  else
157  {
158  selection = NULL;
159  FOREACH_CLIENT(true, {
160  if(strdecolorize(it.netname) == strdecolorize(input))
161  {
162  selection = it;
163  break; // no reason to keep looking
164  }
165  });
166  }
167 
168  return selection;
169 }
170 
171 // switch between sprint and print depending on whether the receiver is the server or a player
172 void print_to(entity to, string input)
173 {
174  if (to) sprint(to, strcat(input, "\n"));
175  else print(input, "\n");
176 }
177 
178 // ==========================================
179 // Supporting functions for common commands
180 // ==========================================
181 
182 // used by CommonCommand_timeout() and CommonCommand_timein() to handle game pausing and messaging and such.
184 {
186  timeout_time = 0;
187  timeout_leadtime = 0;
188 
189  delete(this);
190 }
191 
193 {
194  switch (timeout_status)
195  {
196  case TIMEOUT_ACTIVE:
197  {
198  if (timeout_time > 0) // countdown is still going
199  {
200  Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_TIMEOUT_ENDING, timeout_time);
201 
202  if (timeout_time == autocvar_sv_timeout_resumetime) // play a warning sound when only <sv_timeout_resumetime> seconds are left
203  Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_PREPARE);
204 
205  this.nextthink = time + TIMEOUT_SLOWMO_VALUE; // think again in one second
206  timeout_time -= 1; // decrease the time counter
207  }
208  else // time to end the timeout
209  {
210  Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_TIMEIN);
211  timeout_status = TIMEOUT_INACTIVE;
212 
213  // reset the slowmo value back to normal
214  cvar_set("slowmo", ftos(orig_slowmo));
215 
216  // unlock the view for players so they can move around again
218  it.fixangle = false;
219  });
220 
221  timeout_handler_reset(this);
222  }
223 
224  return;
225  }
226 
227  case TIMEOUT_LEADTIME:
228  {
229  if (timeout_leadtime > 0) // countdown is still going
230  {
231  Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_TIMEOUT_BEGINNING, timeout_leadtime);
232 
233  this.nextthink = time + 1; // think again in one second
234  timeout_leadtime -= 1; // decrease the time counter
235  }
236  else // time to begin the timeout
237  {
238  timeout_status = TIMEOUT_ACTIVE;
239 
240  // set the slowmo value to the timeout default slowmo value
241  cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
242 
243  // reset all the flood variables
244  FOREACH_CLIENT(true, {
245  it.nickspamcount = it.nickspamtime = it.floodcontrol_chat =
246  it.floodcontrol_chatteam = it.floodcontrol_chattell =
247  it.floodcontrol_voice = it.floodcontrol_voiceteam = 0;
248  });
249 
250  // copy .v_angle to .lastV_angle for every player in order to fix their view during pause (see PlayerPreThink)
252  it.lastV_angle = it.v_angle;
253  });
254 
255  this.nextthink = time; // think again next frame to handle it under TIMEOUT_ACTIVE code
256  }
257 
258  return;
259  }
260 
261 
262  case TIMEOUT_INACTIVE:
263  default:
264  {
265  timeout_handler_reset(this);
266  return;
267  }
268  }
269 }
270 
271 
272 // ===================================================
273 // Common commands used in both sv_cmd.qc and cmd.qc
274 // ===================================================
275 
276 void CommonCommand_cvar_changes(int request, entity caller)
277 {
278  switch (request)
279  {
280  case CMD_REQUEST_COMMAND:
281  {
282  print_to(caller, cvar_changes);
283  return; // never fall through to usage
284  }
285 
286  default:
287  case CMD_REQUEST_USAGE:
288  {
289  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " cvar_changes"));
290  print_to(caller, " No arguments required.");
291  print_to(caller, "See also: ^2cvar_purechanges^7");
292  return;
293  }
294  }
295 }
296 
297 void CommonCommand_cvar_purechanges(int request, entity caller)
298 {
299  switch (request)
300  {
301  case CMD_REQUEST_COMMAND:
302  {
303  print_to(caller, cvar_purechanges);
304  return; // never fall through to usage
305  }
306 
307  default:
308  case CMD_REQUEST_USAGE:
309  {
310  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " cvar_purechanges"));
311  print_to(caller, " No arguments required.");
312  print_to(caller, "See also: ^2cvar_changes^7");
313  return;
314  }
315  }
316 }
317 
318 void CommonCommand_editmob(int request, entity caller, int argc)
319 {
320  switch (request)
321  {
322  case CMD_REQUEST_COMMAND:
323  {
324  if (autocvar_g_campaign) { print_to(caller, "Monster editing is disabled in singleplayer"); return; }
325  // no checks for g_monsters here, as it may be toggled mid match which existing monsters
326 
327  if (caller)
328  {
329  makevectors(caller.v_angle);
330  WarpZone_TraceLine(caller.origin + caller.view_ofs, caller.origin + caller.view_ofs + v_forward * 100, MOVE_NORMAL, caller);
331  }
332 
333  entity mon = trace_ent;
334  bool is_visible = IS_MONSTER(mon);
335  string argument = argv(2);
336 
337  switch (argv(1))
338  {
339  case "name":
340  {
341  if (!caller) { print_to(caller, "Only players can edit monsters"); return; }
342  if (!argument) break; // escape to usage
343  if (!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; }
344  if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; }
345  if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; }
346 
347  string mon_oldname = mon.monster_name;
348 
349  mon.monster_name = argument;
350  if (mon.sprite) WaypointSprite_UpdateSprites(mon.sprite, WP_Monster, WP_Null, WP_Null);
351  print_to(caller, sprintf("Your pet '%s' is now known as '%s'", mon_oldname, mon.monster_name));
352  return;
353  }
354  case "spawn":
355  {
356  if (!caller) { print_to(caller, "Only players can spawn monsters"); return; }
357  if (!argv(2)) break; // escape to usage
358 
359  int moveflag, tmp_moncount = 0;
360  string arg_lower = strtolower(argument);
361  moveflag = (argv(3)) ? stof(argv(3)) : 1; // follow owner if not defined
362 
363  if (arg_lower == "list") { print_to(caller, monsterlist_reply); return; }
364 
365  IL_EACH(g_monsters, it.realowner == caller,
366  {
367  ++tmp_moncount;
368  });
369 
370  if (!autocvar_g_monsters) { print_to(caller, "Monsters are disabled"); return; }
371  if (autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { print_to(caller, "Monster spawning is disabled"); return; }
372  if (!IS_PLAYER(caller)) { print_to(caller, "You must be playing to spawn a monster"); return; }
373  if (MUTATOR_CALLHOOK(AllowMobSpawning, caller)) { print_to(caller, M_ARGV(1, string)); return; }
374  if (caller.vehicle) { print_to(caller, "You can't spawn monsters while driving a vehicle"); return; }
375  if (STAT(FROZEN, caller)) { print_to(caller, "You can't spawn monsters while frozen"); return; }
376  if (IS_DEAD(caller)) { print_to(caller, "You can't spawn monsters while dead"); return; }
377  if (tmp_moncount >= autocvar_g_monsters_max) { print_to(caller, "The maximum monster count has been reached"); return; }
378  if (tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; }
379 
380  bool found = false;
381  FOREACH(Monsters, it != MON_Null && it.netname == arg_lower,
382  {
383  found = true;
384  break;
385  });
386 
387  if (!found && arg_lower != "random" && arg_lower != "anyrandom") { print_to(caller, "Invalid monster"); return; }
388 
389  totalspawned += 1;
390  WarpZone_TraceBox(CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, true, caller);
391  mon = spawnmonster(spawn(), arg_lower, MON_Null, caller, caller, trace_endpos, false, false, moveflag);
392  print_to(caller, strcat("Spawned ", mon.monster_name));
393  return;
394  }
395  case "kill":
396  {
397  if (!caller) { print_to(caller, "Only players can kill monsters"); return; }
398  if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; }
399  if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; }
400 
401  Damage(mon, NULL, NULL, GetResource(mon, RES_HEALTH) + mon.max_health + 200, DEATH_KILL.m_id, DMG_NOWEP, mon.origin, '0 0 0');
402  print_to(caller, strcat("Your pet '", mon.monster_name, "' has been brutally mutilated"));
403  return;
404  }
405  case "skin":
406  {
407  if (!caller) { print_to(caller, "Only players can edit monsters"); return; }
408  if (!argument) break; // escape to usage
409  if (!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; }
410  if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; }
411  if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; }
412  if (mon.monsterdef == MON_MAGE) { print_to(caller, "Mage skins can't be changed"); return; } // TODO
413 
414  mon.skin = stof(argument);
415  print_to(caller, strcat("Monster skin successfully changed to ", ftos(mon.skin)));
416  return;
417  }
418  case "movetarget":
419  {
420  if (!caller) { print_to(caller, "Only players can edit monsters"); return; }
421  if (!argument) break; // escape to usage
422  if (!autocvar_g_monsters_edit) { print_to(caller, "Monster editing is disabled"); return; }
423  if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; }
424  if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; }
425 
426  mon.monster_moveflags = stof(argument);
427  print_to(caller, strcat("Monster move target successfully changed to ", ftos(mon.monster_moveflags)));
428  return;
429  }
430  case "butcher":
431  {
432  if (caller) { print_to(caller, "This command is not available to players"); return; }
433  if (MUTATOR_CALLHOOK(AllowMobButcher)) { LOG_INFO(M_ARGV(0, string)); return; }
434 
435  int tmp_remcount = 0;
436 
437  IL_EACH(g_monsters, true,
438  {
439  Monster_Remove(it);
440  ++tmp_remcount;
441  });
443 
445 
446  print_to(caller, (tmp_remcount) ? sprintf("Killed %d monster%s", tmp_remcount, (tmp_remcount == 1) ? "" : "s") : "No monsters to kill");
447  return;
448  }
449  }
450  }
451 
452  default:
453  case CMD_REQUEST_USAGE:
454  {
455  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " editmob <command> [<arguments>]"));
456  print_to(caller, " Where <command> can be butcher spawn skin movetarget kill name");
457  print_to(caller, " spawn, skin, movetarget and name require <arguments>");
458  print_to(caller, " spawn also takes arguments list and random");
459  print_to(caller, " Monster will follow owner if third argument of spawn command is not defined");
460  return;
461  }
462  }
463 }
464 
465 void CommonCommand_info(int request, entity caller, int argc)
466 {
467  switch (request)
468  {
469  case CMD_REQUEST_COMMAND:
470  {
471  string command = cvar_string(strcat("sv_info_", argv(1)));
472 
473  if (command) wordwrap_sprint(caller, command, 1000);
474  else print_to(caller, "ERROR: unsupported info command");
475 
476  return; // never fall through to usage
477  }
478 
479  default:
480  case CMD_REQUEST_USAGE:
481  {
482  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " info <request>"));
483  print_to(caller, " Where <request> is the suffixed string appended onto the request for cvar.");
484  return;
485  }
486  }
487 }
488 
489 void CommonCommand_ladder(int request, entity caller)
490 {
491  switch (request)
492  {
493  case CMD_REQUEST_COMMAND:
494  {
495  print_to(caller, ladder_reply);
496  return; // never fall through to usage
497  }
498 
499  default:
500  case CMD_REQUEST_USAGE:
501  {
502  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " ladder"));
503  print_to(caller, " No arguments required.");
504  return;
505  }
506  }
507 }
508 
509 void CommonCommand_lsmaps(int request, entity caller)
510 {
511  switch (request)
512  {
513  case CMD_REQUEST_COMMAND:
514  {
515  print_to(caller, lsmaps_reply);
516  return; // never fall through to usage
517  }
518 
519  default:
520  case CMD_REQUEST_USAGE:
521  {
522  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " lsmaps"));
523  print_to(caller, " No arguments required.");
524  return;
525  }
526  }
527 }
528 
529 void CommonCommand_printmaplist(int request, entity caller)
530 {
531  switch (request)
532  {
533  case CMD_REQUEST_COMMAND:
534  {
535  print_to(caller, maplist_reply);
536  return; // never fall through to usage
537  }
538 
539  default:
540  case CMD_REQUEST_USAGE:
541  {
542  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " printmaplist"));
543  print_to(caller, " No arguments required.");
544  return;
545  }
546  }
547 }
548 
549 void CommonCommand_rankings(int request, entity caller)
550 {
551  switch (request)
552  {
553  case CMD_REQUEST_COMMAND:
554  {
555  print_to(caller, rankings_reply);
556  return; // never fall through to usage
557  }
558 
559  default:
560  case CMD_REQUEST_USAGE:
561  {
562  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " rankings"));
563  print_to(caller, " No arguments required.");
564  return;
565  }
566  }
567 }
568 
569 void CommonCommand_records(int request, entity caller)
570 {
571  switch (request)
572  {
573  case CMD_REQUEST_COMMAND:
574  {
575  int num = stoi(argv(1));
576  if(num > 0 && num <= 10 && records_reply[num - 1] != "")
577  print_to(caller, records_reply[num - 1]);
578  else
579  {
580  for (int i = 0; i < 10; ++i)
581  if (records_reply[i] != "") print_to(caller, records_reply[i]);
582  }
583 
584  return; // never fall through to usage
585  }
586 
587  default:
588  case CMD_REQUEST_USAGE:
589  {
590  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " records [<pagenum>]"));
591  print_to(caller, " Without arguments it prints all records (all pages) for the current gametype,");
592  print_to(caller, " otherwise if there are multiple pages it only prints page <pagenum> (1..10),");
593  return;
594  }
595  }
596 }
597 
598 void CommonCommand_teamstatus(int request, entity caller)
599 {
600  switch (request)
601  {
602  case CMD_REQUEST_COMMAND:
603  {
604  Score_NicePrint(caller);
605  return; // never fall through to usage
606  }
607 
608  default:
609  case CMD_REQUEST_USAGE:
610  {
611  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " teamstatus"));
612  print_to(caller, " No arguments required.");
613  return;
614  }
615  }
616 }
617 
618 void CommonCommand_time(int request, entity caller)
619 {
620  switch (request)
621  {
622  case CMD_REQUEST_COMMAND:
623  {
624  print_to(caller, strcat("time = ", ftos(time)));
625  print_to(caller, strcat("frame start = ", ftos(gettime(GETTIME_FRAMESTART))));
626  print_to(caller, strcat("realtime = ", ftos(gettime(GETTIME_REALTIME))));
627  print_to(caller, strcat("hires = ", ftos(gettime(GETTIME_HIRES))));
628  print_to(caller, strcat("uptime = ", ftos(gettime(GETTIME_UPTIME))));
629  print_to(caller, strcat("localtime = ", strftime(true, "%a %b %d %H:%M:%S %Z %Y")));
630  print_to(caller, strcat("gmtime = ", strftime(false, "%a %b %d %H:%M:%S %Z %Y")));
631  return;
632  }
633 
634  default:
635  case CMD_REQUEST_USAGE:
636  {
637  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " time"));
638  print_to(caller, " No arguments required.");
639  return;
640  }
641  }
642 }
643 
644 void CommonCommand_timein(int request, entity caller)
645 {
646  switch (request)
647  {
648  case CMD_REQUEST_COMMAND:
649  {
650  if (!caller || autocvar_sv_timeout)
651  {
652  if (!timeout_status) { print_to(caller, "^7Error: There is no active timeout called."); }
653  else if (caller && (caller != timeout_caller))
654  {
655  print_to(caller, "^7Error: You are not allowed to stop the active timeout.");
656  }
657 
658  else // everything should be okay, continue aborting timeout
659  {
660  switch (timeout_status)
661  {
662  case TIMEOUT_LEADTIME:
663  {
664  timeout_status = TIMEOUT_INACTIVE;
665  timeout_time = 0;
666  timeout_handler.nextthink = time; // timeout_handler has to take care of it immediately
667  Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_TIMEOUT);
668  bprint(strcat("^7The timeout was aborted by ", GetCallerName(caller), " !\n"));
669  return;
670  }
671 
672  case TIMEOUT_ACTIVE:
673  {
675  timeout_handler.nextthink = time; // timeout_handler has to take care of it immediately
676  bprint(strcat("^1Attention: ^7", GetCallerName(caller), " resumed the game! Prepare for battle!\n"));
677  return;
678  }
679 
680  default: LOG_TRACE("timeout status was inactive, but this code was executed anyway?");
681  return;
682  }
683  }
684  }
685  else { print_to(caller, "^1Timeins are not allowed to be called, enable them with sv_timeout 1.\n"); }
686 
687  return; // never fall through to usage
688  }
689 
690  default:
691  case CMD_REQUEST_USAGE:
692  {
693  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " timein"));
694  print_to(caller, " No arguments required.");
695  return;
696  }
697  }
698 }
699 
700 void CommonCommand_timeout(int request, entity caller) // DEAR GOD THIS COMMAND IS TERRIBLE.
701 {
702  switch (request)
703  {
704  case CMD_REQUEST_COMMAND:
705  {
706  if (!caller || autocvar_sv_timeout)
707  {
708  float last_possible_timeout = ((autocvar_timelimit * 60) - autocvar_sv_timeout_leadtime - 1);
709 
710  if (timeout_status) { print_to(caller, "^7Error: A timeout is already active."); }
711  else if (vote_called)
712  {
713  print_to(caller, "^7Error: You can not call a timeout while a vote is active.");
714  }
716  {
717  print_to(caller, "^7Error: You can not call a timeout in warmup-stage.");
718  }
719  else if (time < game_starttime)
720  {
721  print_to(caller, "^7Error: You can not call a timeout while the map is being restarted.");
722  }
723  else if (caller && (CS(caller).allowed_timeouts < 1))
724  {
725  print_to(caller, "^7Error: You already used all your timeout calls for this map.");
726  }
727  else if (caller && !IS_PLAYER(caller))
728  {
729  print_to(caller, "^7Error: You must be a player to call a timeout.");
730  }
731  else if ((autocvar_timelimit) && (last_possible_timeout < time - game_starttime))
732  {
733  print_to(caller, "^7Error: It is too late to call a timeout now!");
734  }
735 
736  else // everything should be okay, proceed with starting the timeout
737  {
738  if (caller) CS(caller).allowed_timeouts -= 1;
739  // write a bprint who started the timeout (and how many they have left)
740  bprint(GetCallerName(caller), " ^7called a timeout", (caller ? strcat(" (", ftos(CS(caller).allowed_timeouts), " timeout(s) left)") : ""), "!\n");
741 
742  timeout_status = TIMEOUT_LEADTIME;
743  timeout_caller = caller;
746 
749  timeout_handler.nextthink = time; // always let the entity think asap
750 
751  Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_TIMEOUT);
752  }
753  }
754  else { print_to(caller, "^1Timeouts are not allowed to be called, enable them with sv_timeout 1.\n"); }
755 
756  return; // never fall through to usage
757  }
758 
759  default:
760  case CMD_REQUEST_USAGE:
761  {
762  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " timeout"));
763  print_to(caller, " No arguments required.");
764  return;
765  }
766  }
767 }
768 
769 void CommonCommand_who(int request, entity caller, int argc)
770 {
771  switch (request)
772  {
773  case CMD_REQUEST_COMMAND:
774  {
775  float total_listed_players, is_bot;
776 
777  float privacy = (caller && autocvar_sv_status_privacy);
778  string separator = strreplace("%", " ", strcat((argv(1) ? argv(1) : " "), "^7"));
779  string tmp_netaddress, tmp_crypto_idfp;
780 
781  print_to(caller, strcat("List of client information", (privacy ? " (some data is hidden for privacy)" : ""), ":"));
782  print_to(caller, sprintf(strreplace(" ", separator, " %-4s %-20s %-5s %-3s %-9s %-16s %s "),
783  "ent", "nickname", "ping", "pl", "time", "ip", "crypto_id"));
784 
785  total_listed_players = 0;
786  FOREACH_CLIENT(true, {
787  is_bot = (IS_BOT_CLIENT(it));
788 
789  if (is_bot)
790  {
791  tmp_netaddress = "null/botclient";
792  tmp_crypto_idfp = "null/botclient";
793  }
794  else if (privacy)
795  {
796  tmp_netaddress = "hidden";
797  tmp_crypto_idfp = "hidden";
798  }
799  else
800  {
801  tmp_netaddress = it.netaddress;
802  tmp_crypto_idfp = it.crypto_idfp;
803  }
804 
805  print_to(caller, sprintf(strreplace(" ", separator, " #%-3d %-20.20s %-5d %-3d %-9s %-16s %s "),
806  etof(it),
807  it.netname,
808  CS(it).ping,
809  CS(it).ping_packetloss,
810  process_time(1, time - CS(it).jointime),
811  tmp_netaddress,
812  tmp_crypto_idfp));
813 
814  ++total_listed_players;
815  });
816 
817  print_to(caller, strcat("Finished listing ", ftos(total_listed_players), " client(s) out of ", ftos(maxclients), " slots."));
818 
819  return; // never fall through to usage
820  }
821 
822  default:
823  case CMD_REQUEST_USAGE:
824  {
825  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " who [<separator>]"));
826  print_to(caller, " Where <separator> is the optional string to separate the values with, default is a space.");
827  return;
828  }
829  }
830 }
831 
832 /* use this when creating a new command, making sure to place it in alphabetical order... also,
833 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
834 void CommonCommand_(int request, entity caller)
835 {
836  switch(request)
837  {
838  case CMD_REQUEST_COMMAND:
839  {
840 
841  return; // never fall through to usage
842  }
843 
844  default:
845  case CMD_REQUEST_USAGE:
846  {
847  print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " "));
848  print_to(caller, " No arguments required.");
849  return;
850  }
851  }
852 }
853 */
bool autocvar_g_monsters_edit
Definition: sv_monsters.qh:6
bool autocvar_sv_timeout
Definition: common.qh:5
string GetClientErrorString_color(float clienterror, string original_input, string col)
Definition: common.qc:57
#define IL_EACH(this, cond, body)
void CommonCommand_time(int request, entity caller)
Definition: common.qc:618
void CommonCommand_printmaplist(int request, entity caller)
Definition: common.qc:529
int monsters_total
Definition: sv_monsters.qh:34
entity spawnmonster(entity e, string monster, Monster monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool removeifinvalid, int moveflag)
Definition: sv_spawn.qc:14
entity GetIndexedEntity(int argc, float start_index)
Definition: common.qc:83
#define IL_CLEAR(this)
Remove all elements.
const float CLIENT_NOT_BOT
Definition: common.qh:44
string GetCommandPrefix(entity caller)
Definition: common.qc:26
float autocvar_sv_timeout_length
Definition: common.qh:7
#define IS_CLIENT(v)
Definition: utils.qh:13
const int CMD_REQUEST_USAGE
Definition: command.qh:4
float GETTIME_FRAMESTART
void Score_NicePrint(entity to)
Prints the scores to the console of a player.
Definition: scores.qc:903
void WarpZone_TraceBox(vector org, vector mi, vector ma, vector end, float nomonsters, entity forent)
Definition: common.qc:333
void timeout_handler_think(entity this)
Definition: common.qc:192
bool autocvar_g_warmup_allow_timeout
Definition: world.qh:10
float autocvar_sv_timeout_leadtime
Definition: common.qh:6
entity() spawn
bool autocvar_sv_status_privacy
Definition: common.qh:4
const float MOVE_NORMAL
Definition: csprogsdefs.qc:252
ClientState CS(Client this)
Definition: state.qh:47
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
void CommonCommand_info(int request, entity caller, int argc)
Definition: common.qc:465
float VerifyClientEntity(entity client, float must_be_real, float must_be_bots)
Definition: common.qc:47
bool warmup_stage
Definition: main.qh:103
int autocvar_g_monsters_max
Definition: sv_monsters.qh:9
#define IS_MONSTER(v)
Definition: utils.qh:21
float maxclients
Definition: csprogsdefs.qc:21
ERASEABLE string process_time(float outputtype, int seconds)
Definition: counting.qh:120
entity to
Definition: self.qh:96
string autocvar_sv_adminnick
Definition: common.qh:3
void print_to(entity to, string input)
Definition: common.qc:172
float vote_called
Definition: vote.qh:43
void CommonCommand_lsmaps(int request, entity caller)
Definition: common.qc:509
float timeout_leadtime
Definition: common.qh:60
void CommonCommand_rankings(int request, entity caller)
Definition: common.qc:549
entity timeout_handler
Definition: common.qh:56
const float TIMEOUT_ACTIVE
Definition: common.qh:49
float ping
Definition: main.qh:138
void CommonCommand_records(int request, entity caller)
Definition: common.qc:569
void CommonCommand_timeout(int request, entity caller)
Definition: common.qc:700
void CommonCommand_editmob(int request, entity caller, int argc)
Definition: common.qc:318
const float CLIENT_ACCEPTABLE
Definition: common.qh:41
#define DMG_NOWEP
Definition: damage.qh:126
const float TIMEOUT_SLOWMO_VALUE
Definition: common.qh:52
entity GetFilteredEntity(string input)
Definition: common.qc:144
const float CLIENT_DOESNT_EXIST
Definition: common.qh:42
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
entity trace_ent
Definition: csprogsdefs.qc:40
#define GETTIME_REALTIME
Definition: static.qh:3
int autocvar_g_monsters_max_perplayer
Definition: sv_monsters.qh:10
#define stoi(s)
Definition: int.qh:4
float allowed_timeouts
Definition: common.qh:61
RES_HEALTH
Definition: ent_cs.qc:126
float next_token
Definition: common.qh:71
int monsters_killed
Definition: sv_monsters.qh:35
const float TIMEOUT_INACTIVE
Definition: common.qh:47
float GETTIME_UPTIME
float autocvar_g_monsters
Definition: sv_monsters.qh:5
float ping_packetloss
Definition: main.qh:138
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
float autocvar_sv_timeout_resumetime
Definition: common.qh:9
string cvar_changes
Definition: world.qh:41
float VerifyClientNumber(float tmp_number)
Definition: common.qc:77
#define NULL
Definition: post.qh:17
const int CMD_REQUEST_COMMAND
Definition: command.qh:3
#define LOG_INFO(...)
Definition: log.qh:70
string cvar_purechanges
Definition: world.qh:42
void Monster_Remove(entity this)
Definition: sv_monsters.qc:852
entity timeout_caller
Definition: common.qh:55
float timeout_time
Definition: common.qh:59
float jointime
Definition: client.qh:64
vector trace_endpos
Definition: csprogsdefs.qc:37
void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype,.entity weaponentity, vector hitloc, vector force)
Definition: damage.qc:583
string GetCallerName(entity caller)
Definition: common.qc:33
void CommonCommand_timein(int request, entity caller)
Definition: common.qc:644
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
float nextthink
Definition: csprogsdefs.qc:121
#define CENTER_OR_VIEWOFS(ent)
Definition: utils.qh:28
float GETTIME_HIRES
int totalspawned
number of monsters spawned with mobspawn command
Definition: sv_monsters.qh:124
void CommonCommand_who(int request, entity caller, int argc)
Definition: common.qc:769
const float CLIENT_NOT_REAL
Definition: common.qh:43
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
void CommonCommand_ladder(int request, entity caller)
Definition: common.qc:489
#define LOG_TRACE(...)
Definition: log.qh:81
const float TIMEOUT_LEADTIME
Definition: common.qh:48
void WarpZone_TraceLine(vector org, vector end, float nomonsters, entity forent)
Definition: common.qc:338
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
#define setthink(e, f)
bool autocvar_g_campaign
Definition: campaign.qh:6
void timeout_handler_reset(entity this)
Definition: common.qc:183
IntrusiveList g_monsters
Definition: sv_monsters.qh:144
float time
Definition: csprogsdefs.qc:16
void CommonCommand_cvar_purechanges(int request, entity caller)
Definition: common.qc:297
#define makevectors
Definition: post.qh:21
#define FOREACH(list, cond, body)
Definition: iter.qh:19
void CommonCommand_cvar_changes(int request, entity caller)
Definition: common.qc:276
#define IS_PLAYER(v)
Definition: utils.qh:9
float VerifyKickableEntity(entity client)
Definition: common.qc:40
vector v_forward
Definition: csprogsdefs.qc:31
void CommonCommand_teamstatus(int request, entity caller)
Definition: common.qc:598
float orig_slowmo
Definition: common.qh:58