]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/command/common.qc
Add "separator" support to who command, limit nicknames to 20 characters, better...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / command / common.qc
1 // ====================================================
2 //  Shared code for server commands, written by Samual
3 //  Last updated: December 17th, 2011
4 // ====================================================
5
6 string GetCommandPrefix(entity caller)
7 {
8         if(caller)
9                 return "cmd";
10         else
11                 return "sv_cmd";
12 }
13
14 string GetCallerName(entity caller)
15 {
16         if(caller)
17                 return caller.netname;
18         else
19                 return ((autocvar_sv_adminnick != "") ? autocvar_sv_adminnick : autocvar_hostname);
20 }
21
22 entity GetFilteredEntity(string input)
23 {
24         entity tmp_player, selection;
25         float tmp_number;
26         
27         if(substring(input, 0, 1) == "#")
28                 tmp_number = stof(substring(input, 1, -1));
29         else
30                 tmp_number = stof(input);
31         
32         if(tmp_number)
33         {
34                 selection = edict_num(tmp_number);
35         }
36         else
37         {
38                 FOR_EACH_CLIENT(tmp_player)
39                         if (strdecolorize(tmp_player.netname) == strdecolorize(input))
40                                 selection = tmp_player;
41         }
42         
43         return selection;
44 }
45
46 // find a player which matches the input string, and return their entity number
47 float GetFilteredNumber(string input)
48 {
49         entity selection = GetFilteredEntity(input);
50         float output;
51         
52         if(selection) { output = num_for_edict(selection); }
53
54         print(strcat("input: ", input, ", output: ", ftos(output), ",\n")); // todo remove after done debugging
55         return output;
56 }
57
58 // switch between sprint and print depending on whether the reciever is the server or a player
59 void print_to(entity to, string input)
60 {
61     if(to)
62         sprint(to, strcat(input, "\n"));
63     else
64         print(input, "\n");
65 }
66
67
68 // ===================================================
69 //  Common commands used in both sv_cmd.qc and cmd.qc
70 // ===================================================
71
72 void CommonCommand_cvar_changes(float request, entity caller)
73 {
74         switch(request)
75         {
76                 case CMD_REQUEST_COMMAND:
77                 {
78                         print_to(caller, cvar_changes);
79                         return; // never fall through to usage
80                 }
81                         
82                 default:
83                 case CMD_REQUEST_USAGE:
84                 {
85                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " cvar_changes"));
86                         print_to(caller, "  No arguments required.");
87                         print_to(caller, "See also: ^2cvar_purechanges^7");
88                         return;
89                 }
90         }
91 }
92
93 void CommonCommand_cvar_purechanges(float request, entity caller)
94 {
95         switch(request)
96         {
97                 case CMD_REQUEST_COMMAND:
98                 {
99                         print_to(caller, cvar_purechanges);
100                         return; // never fall through to usage
101                 }
102                         
103                 default:
104                 case CMD_REQUEST_USAGE:
105                 {
106                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " cvar_purechanges"));
107                         print_to(caller, "  No arguments required.");
108                         print_to(caller, "See also: ^2cvar_changes^7");
109                         return;
110                 }
111         }
112 }
113
114 void CommonCommand_info(float request, entity caller, float argc) // todo: figure out how this works?
115 {       
116         switch(request)
117         {
118                 case CMD_REQUEST_COMMAND:
119                 {
120                         string command;
121                         
122                         command = builtin_cvar_string(strcat("sv_info_", argv(1))); 
123                         if(command)
124                                 wordwrap_sprint(command, 1111); // why 1111?
125                         else
126                                 print_to(caller, "ERROR: unsupported info command");
127                                 
128                         return; // never fall through to usage
129                 }
130                         
131                 default:
132                 case CMD_REQUEST_USAGE:
133                 {
134                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " info request"));
135                         print_to(caller, "  Where 'request' is the suffixed string appended onto the request for cvar.");
136                         return;
137                 }
138         }
139 }
140
141 void CommonCommand_ladder(float request, entity caller)
142 {
143         switch(request)
144         {
145                 case CMD_REQUEST_COMMAND:
146                 {
147                         print_to(caller, ladder_reply);
148                         return; // never fall through to usage
149                 }
150                         
151                 default:
152                 case CMD_REQUEST_USAGE:
153                 {
154                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " ladder"));
155                         print_to(caller, "  No arguments required.");
156                         return;
157                 }
158         }
159 }
160
161 void CommonCommand_lsmaps(float request, entity caller)
162 {
163         switch(request)
164         {
165                 case CMD_REQUEST_COMMAND:
166                 {
167                         print_to(caller, lsmaps_reply);
168                         return; // never fall through to usage
169                 }
170                         
171                 default:
172                 case CMD_REQUEST_USAGE:
173                 {
174                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " lsmaps"));
175                         print_to(caller, "  No arguments required.");
176                         return;
177                 }
178         }
179 }
180
181 void CommonCommand_lsnewmaps(float request, entity caller)
182 {
183         switch(request)
184         {
185                 case CMD_REQUEST_COMMAND:
186                 {
187                         print_to(caller, lsnewmaps_reply);
188                         return; // never fall through to usage
189                 }
190                         
191                 default:
192                 case CMD_REQUEST_USAGE:
193                 {
194                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " lsnewmaps"));
195                         print_to(caller, "  No arguments required.");
196                         return;
197                 }
198         }
199 }
200
201 void CommonCommand_maplist(float request, entity caller)
202 {
203         switch(request)
204         {
205                 case CMD_REQUEST_COMMAND:
206                 {
207                         print_to(caller, maplist_reply);
208                         return; // never fall through to usage
209                 }
210                         
211                 default:
212                 case CMD_REQUEST_USAGE:
213                 {
214                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " maplist"));
215                         print_to(caller, "  No arguments required.");
216                         return;
217                 }
218         }
219 }
220
221 void GameCommand_rankings(float request) // this is OLD.... jeez.
222 {
223         switch(request)
224         {
225                 case CMD_REQUEST_COMMAND:
226                 {
227                         strunzone(rankings_reply);
228                         rankings_reply = strzone(getrankings());
229                         print(rankings_reply);
230                         return;
231                 }
232                         
233                 default:
234                 case CMD_REQUEST_USAGE:
235                 {
236                         print("\nUsage:^3 sv_cmd rankings");
237                         print("  No arguments required.");
238                         return;
239                 }
240         }
241 }
242
243 void CommonCommand_rankings(float request, entity caller)
244 {
245         switch(request)
246         {
247                 case CMD_REQUEST_COMMAND:
248                 {
249                         print_to(caller, rankings_reply);
250                         return; // never fall through to usage
251                 }
252                         
253                 default:
254                 case CMD_REQUEST_USAGE:
255                 {
256                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " rankings"));
257                         print_to(caller, "  No arguments required.");
258                         return;
259                 }
260         }
261 }
262
263 void CommonCommand_records(float request, entity caller) // TODO: Isn't this flooding with the sprint messages? Old code, but perhaps bad?
264 {       
265         switch(request)
266         {
267                 case CMD_REQUEST_COMMAND:
268                 {
269                         float i;
270                         
271                         for(i = 0; i < 10; ++i)
272                                 print_to(caller, records_reply[i]);
273                                 
274                         return; // never fall through to usage
275                 }
276                         
277                 default:
278                 case CMD_REQUEST_USAGE:
279                 {
280                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " records"));
281                         print_to(caller, "  No arguments required.");
282                         return;
283                 }
284         }
285 }
286
287 void CommonCommand_teamstatus(float request, entity caller)
288 {
289         switch(request)
290         {
291                 case CMD_REQUEST_COMMAND:
292                 {
293                         Score_NicePrint(caller);
294                         return; // never fall through to usage
295                 }
296                         
297                 default:
298                 case CMD_REQUEST_USAGE:
299                 {
300                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " teamstatus"));
301                         print_to(caller, "  No arguments required.");
302                         return;
303                 }
304         }
305 }
306
307 void CommonCommand_time(float request, entity caller)
308 {
309         switch(request)
310         {
311                 case CMD_REQUEST_COMMAND:
312                 {
313                         print_to(caller, strcat("time = ", ftos(time), "\n"));
314                         print_to(caller, strcat("frame start = ", ftos(gettime(GETTIME_FRAMESTART)), "\n"));
315                         print_to(caller, strcat("realtime = ", ftos(gettime(GETTIME_REALTIME)), "\n"));
316                         print_to(caller, strcat("hires = ", ftos(gettime(GETTIME_HIRES)), "\n"));
317                         print_to(caller, strcat("uptime = ", ftos(gettime(GETTIME_UPTIME)), "\n"));
318                         print_to(caller, strcat("localtime = ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n")); // todo: Why is strftime broken? is engine problem, I think.
319                         print_to(caller, strcat("gmtime = ", strftime(FALSE, "%a %b %e %H:%M:%S %Z %Y"), "\n"));
320                         return;
321                 }
322                         
323                 default:
324                 case CMD_REQUEST_USAGE:
325                 {
326                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " time"));
327                         print_to(caller, "  No arguments required.");
328                         return;
329                 }
330         }
331 }
332
333 void CommonCommand_timein(float request, entity caller)
334 {
335         switch(request)
336         {
337                 case CMD_REQUEST_COMMAND:
338                 {
339                         if(caller.flags & FL_CLIENT)
340                         {
341                                 if(autocvar_sv_timeout)
342                                 {
343                                         if (!timeoutStatus)
344                                                 return print_to(caller, "^7Error: There is no active timeout which could be aborted!");
345                                         if (caller != timeoutInitiator)
346                                                 return print_to(caller, "^7Error: You may not abort the active timeout. Only the player who called it can do that!");
347                                                 
348                                         if (timeoutStatus == 1) 
349                                         {
350                                                 remainingTimeoutTime = timeoutStatus = 0;
351                                                 timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
352                                                 bprint(strcat("^7The timeout was aborted by ", caller.netname, " !\n"));
353                                         }
354                                         else if (timeoutStatus == 2) 
355                                         {
356                                                 //only shorten the remainingTimeoutTime if it makes sense
357                                                 if( remainingTimeoutTime > (autocvar_sv_timeout_resumetime + 1) ) 
358                                                 {
359                                                         bprint(strcat("^1Attention: ^7", caller.netname, " resumed the game! Prepare for battle!\n"));
360                                                         remainingTimeoutTime = autocvar_sv_timeout_resumetime;
361                                                         timeoutHandler.nextthink = time; //timeoutHandler has to take care of it immediately
362                                                 }
363                                                 else
364                                                         print_to(caller, "^7Error: Your resumegame call was discarded!");
365                                         }
366                                 }
367                         }
368                         return; // never fall through to usage
369                 }
370                         
371                 default:
372                 case CMD_REQUEST_USAGE:
373                 {
374                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " timein"));
375                         print_to(caller, "  No arguments required.");
376                         return;
377                 }
378         }
379 }
380
381 void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMAND IS TERRIBLE.
382 {
383         switch(request)
384         {
385                 case CMD_REQUEST_COMMAND:
386                 {
387                         if(caller.flags & FL_CLIENT)
388                         {
389                                 if(autocvar_sv_timeout) 
390                                 {
391                                         if(caller.classname == "player") 
392                                         {
393                                                 if(vote_called)
394                                                         print_to(caller, "^7Error: you can not call a timeout while a vote is active!");
395                                                 else
396                                                 {
397                                                         if (inWarmupStage && !g_warmup_allow_timeout)
398                                                                 return print_to(caller, "^7Error: You can not call a timeout in warmup-stage!");
399                                                         if (time < game_starttime )
400                                                                 return print_to(caller, "^7Error: You can not call a timeout while the map is being restarted!");
401                                                                 
402                                                         if (timeoutStatus != 2) {
403                                                                 //if the map uses a timelimit make sure that timeout cannot be called right before the map ends
404                                                                 if (autocvar_timelimit) {
405                                                                         //a timelimit was used
406                                                                         float myTl;
407                                                                         myTl = autocvar_timelimit;
408
409                                                                         float lastPossibleTimeout;
410                                                                         lastPossibleTimeout = (myTl*60) - autocvar_sv_timeout_leadtime - 1;
411
412                                                                         if (lastPossibleTimeout < time - game_starttime)
413                                                                                 return print_to(caller, "^7Error: It is too late to call a timeout now!");
414                                                                 }
415                                                         }
416                                                         
417                                                         //player may not call a timeout if he has no calls left
418                                                         if (caller.allowedTimeouts < 1)
419                                                                 return print_to(caller, "^7Error: You already used all your timeout calls for this map!");
420                                                                 
421                                                                 
422                                                         //now all required checks are passed
423                                                         caller.allowedTimeouts -= 1;
424                                                         bprint(caller.netname, " ^7called a timeout (", ftos(caller.allowedTimeouts), " timeouts left)!\n"); //write a bprint who started the timeout (and how many he has left)
425                                                         remainingTimeoutTime = autocvar_sv_timeout_length;
426                                                         remainingLeadTime = autocvar_sv_timeout_leadtime;
427                                                         timeoutInitiator = caller;
428                                                         if (timeoutStatus == 0) { //if another timeout was already active, don't change its status (which was 1 or 2) to 1, only change it to 1 if no timeout was active yet
429                                                                 timeoutStatus = 1;
430                                                                 //create the timeout indicator which centerprints the information to all players and takes care of pausing/unpausing
431                                                                 timeoutHandler = spawn();
432                                                                 timeoutHandler.think = timeoutHandler_Think;
433                                                         }
434                                                         timeoutHandler.nextthink = time; //always let the entity think asap
435
436                                                         //inform all connected clients about the timeout call
437                                                         Announce("timeoutcalled");
438                                                 }
439                                         }
440                                         else
441                                                 print_to(caller, "^7Error: only players can call a timeout!");
442                                 }
443                         }
444                         return; // never fall through to usage
445                 }
446                         
447                 default:
448                 case CMD_REQUEST_USAGE:
449                 {
450                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " timeout"));
451                         print_to(caller, "  No arguments required.");
452                         return;
453                 }
454         }
455 }
456
457 void CommonCommand_who(float request, entity caller, float argc)
458 {
459         switch(request)
460         {
461                 case CMD_REQUEST_COMMAND:
462                 {
463                         float total_listed_players, tmp_hours, tmp_minutes, tmp_seconds;
464                         entity tmp_player;                      
465                         
466                         string separator = strcat((argv(1) ? argv(1) : " "), "^7");
467                         float privacy = (caller && autocvar_sv_status_privacy);
468                         
469                         print_to(caller, strcat("List of client information", (privacy ? " (some data is hidden for privacy)" : string_null), ":"));
470                         print_to(caller, sprintf(strreplace(" ", separator, " %-4s %-20s %-5s %-3s %-9s %-16s %s "), 
471                                 "ent", "nickname", "ping", "pl", "time", "ip", "crypto_id"));
472                         
473                         FOR_EACH_CLIENT(tmp_player)
474                         {
475                                 tmp_hours = tmp_minutes = tmp_seconds = 0;
476                                 
477                                 tmp_seconds = floor(time - tmp_player.jointime);
478                                 tmp_minutes = floor(tmp_seconds / 60);
479                                 tmp_hours = floor(tmp_minutes / 60);
480
481                                 if(tmp_minutes) { tmp_seconds -= (tmp_minutes * 60); }                          
482                                 if(tmp_hours) { tmp_minutes -= (tmp_hours * 60); }
483
484                                 print_to(caller, sprintf(strreplace(" ", separator, " %-4s %-20.20s %-5d %-3d %-9s %-16s %s "), 
485                                         strcat("#", ftos(num_for_edict(tmp_player))), 
486                                         tmp_player.netname,
487                                         tmp_player.ping, 
488                                         tmp_player.ping_packetloss, 
489                                         sprintf("%02d:%02d:%02d", tmp_hours, tmp_minutes, tmp_seconds),
490                                         (privacy ? "hidden" : tmp_player.netaddress),
491                                         (privacy ? "hidden" : tmp_player.crypto_idfp)));
492                                         
493                                 ++total_listed_players;
494                         }
495                         
496                         print_to(caller, strcat("Finished listing ", ftos(total_listed_players), " client(s)."));
497                         
498                         return; // never fall through to usage
499                 }
500                         
501                 default:
502                 case CMD_REQUEST_USAGE:
503                 {
504                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " who [separator]"));
505                         print_to(caller, "  Where 'separator' is the optional string to separate the values with, default is a space.");
506                         return;
507                 }
508         }
509 }
510
511 /* use this when creating a new command, making sure to place it in alphabetical order.
512 void CommonCommand_(float request, entity caller)
513 {
514         switch(request)
515         {
516                 case CMD_REQUEST_COMMAND:
517                 {
518                         
519                         return; // never fall through to usage
520                 }
521                         
522                 default:
523                 case CMD_REQUEST_USAGE:
524                 {
525                         print_to(caller, strcat("\nUsage:^3 ", GetCommandPrefix(caller), " "));
526                         print_to(caller, "  No arguments required.");
527                         return;
528                 }
529         }
530 }
531 */