]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/command/generic.qc
take3: format 903 files
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / command / generic.qc
1 #include "generic.qh"
2 #include "_mod.qh"
3 #include "reg.qh"
4
5 #include "markup.qh"
6 #include "rpn.qh"
7
8 #include "../mapinfo.qh"
9
10 #ifdef GAMEQC
11 #include "../notifications/all.qh"
12 #endif
13
14 #ifdef CSQC
15 #include <client/commands/cl_cmd.qh>
16 #endif
17
18 #ifdef SVQC
19 #include <server/command/_mod.qh>
20 #include <common/turrets/config.qh>
21 #include <common/weapons/config.qh>
22 #endif
23
24 // =========================================================
25 //  Generic program common command code, written by Samual
26 //  Last updated: February 19th, 2012
27 // =========================================================
28
29
30 // used by curl command
31 void Curl_URI_Get_Callback(int id, float status, string data)
32 {
33         int i = id - URI_GET_CURL;
34         float do_exec = curl_uri_get_exec[i];
35         string do_cvar = curl_uri_get_cvar[i];
36         if (status != 0) {
37                 LOG_TRACEF("error: status is %d", status);
38                 if (do_cvar) {
39                         strunzone(do_cvar);
40                 }
41                 return;
42         }
43         if (do_exec) {
44                 localcmd(data);
45         }
46         if (do_cvar) {
47                 cvar_set(do_cvar, data);
48                 strunzone(do_cvar);
49         }
50         if (!do_exec) {
51                 if (!do_cvar) {
52                         LOG_INFO(data);
53                 }
54         }
55 }
56
57
58 // =======================
59 //  Command Sub-Functions
60 // =======================
61
62 void GenericCommand_addtolist(float request, float argc)
63 {
64         switch (request) {
65                 case CMD_REQUEST_COMMAND:
66                 {
67                         if (argc >= 2) {
68                                 string original_cvar = argv(1);
69                                 string tmp_string = argv(2);
70
71                                 if (cvar_string(original_cvar) == "") { // cvar was empty
72                                         cvar_set(original_cvar, tmp_string);
73                                 } else { // add it to the end of the list if the list doesn't already have it
74                                         FOREACH_WORD(cvar_string(original_cvar), it == tmp_string,
75                                         {
76                                                 return; // already in the list
77                                         });
78
79                                         cvar_set(original_cvar, cons(cvar_string(original_cvar), tmp_string));
80                                 }
81                                 return;
82                         }
83                 }
84
85                 default:
86                         LOG_INFO("Incorrect parameters for ^2addtolist^7");
87                 case CMD_REQUEST_USAGE:
88                 {
89                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " addtolist variable value");
90                         LOG_INFO("  Where 'variable' is what to add 'value' to.");
91                         LOG_INFO("See also: ^2removefromlist^7");
92                         return;
93                 }
94         }
95 }
96
97 void GenericCommand_qc_curl(float request, float argc)
98 {
99         switch (request) {
100                 case CMD_REQUEST_COMMAND:
101                 {
102                         bool do_exec = false;
103                         string do_cvar = string_null;
104                         float key = -1;
105                         int i;
106                         for (i = 1; i + 1 < argc; ++i) {
107                                 if (argv(i) == "--cvar" && i + 2 < argc) {
108                                         ++i;
109                                         do_cvar = strzone(argv(i));
110                                         continue;
111                                 }
112                                 if (argv(i) == "--exec") {
113                                         do_exec = true;
114                                         continue;
115                                 }
116                                 if (argv(i) == "--key" && i + 2 < argc) {
117                                         ++i;
118                                         key = stof(argv(i));
119                                         continue;
120                                 }
121                                 break;
122                         }
123
124                         // now, argv(i) is the URL
125                         // following args may be POST parameters
126                         string url = argv(i);
127                         ++i;
128                         float buf = buf_create();
129                         int j;
130                         for (j = 0; i + 1 < argc; i += 2) {
131                                 bufstr_set(buf, ++j, sprintf("%s=%s", uri_escape(argv(i)), uri_escape(argv(i + 1))));
132                         }
133                         if (i < argc) {
134                                 bufstr_set(buf, ++j, sprintf("submit=%s", uri_escape(argv(i))));
135                         }
136
137                         float r;
138                         if (j == 0) { // no args: GET
139                                 r = crypto_uri_postbuf(url, URI_GET_CURL + curl_uri_get_pos, string_null, string_null, -1, key);
140                         } else { // with args: POST
141                                 r = crypto_uri_postbuf(url, URI_GET_CURL + curl_uri_get_pos, "application/x-www-form-urlencoded", "&", buf, key);
142                         }
143
144                         if (r) {
145                                 curl_uri_get_exec[curl_uri_get_pos] = do_exec;
146                                 curl_uri_get_cvar[curl_uri_get_pos] = do_cvar;
147                                 curl_uri_get_pos = (curl_uri_get_pos + 1) % (URI_GET_CURL_END - URI_GET_CURL + 1);
148                         } else {
149                                 LOG_INFO(_("error creating curl handle"));
150                         }
151
152                         buf_del(buf);
153
154                         return;
155                 }
156
157                 default:
158                 case CMD_REQUEST_USAGE:
159                 {
160                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " qc_curl [--key N] [--cvar] [--exec] URL [postargs...]");
161                         return;
162                 }
163         }
164 }
165
166 GENERIC_COMMAND(dumpcommands, "Dump all commands on the program to *_cmd_dump.txt")
167 {
168         switch (request) {
169                 case CMD_REQUEST_COMMAND:
170                 {
171                         float fh;
172                         string filename = strcat(GetProgramCommandPrefix(), "_dump.txt");
173                         fh = fopen(filename, FILE_WRITE);
174
175                         if (fh >= 0) {
176 #ifdef SVQC
177                                 CMD_Write("dump of server console commands:\n");
178                                 GameCommand_macro_write_aliases(fh);
179
180                                 CMD_Write("\ndump of networked client only commands:\n");
181                                 ClientCommand_macro_write_aliases(fh);
182
183                                 CMD_Write("\ndump of common commands:\n");
184                                 CommonCommand_macro_write_aliases(fh);
185
186                                 CMD_Write("\ndump of ban commands:\n");
187                                 BanCommand_macro_write_aliases(fh);
188 #endif
189
190 #ifdef CSQC
191                                 CMD_Write("dump of client commands:\n");
192                                 LocalCommand_macro_write_aliases(fh);
193 #endif
194
195                                 CMD_Write("\ndump of generic commands:\n");
196                                 GenericCommand_macro_write_aliases(fh);
197
198                                 LOG_INFO("Completed dump of aliases in ^2data/data/", GetProgramCommandPrefix(), "_dump.txt^7.");
199
200                                 fclose(fh);
201                         } else {
202                                 LOG_INFO("^1Error: ^7Could not dump to file!");
203                         }
204                         return;
205                 }
206
207                 default:
208                 case CMD_REQUEST_USAGE:
209                 {
210                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " dumpcommands");
211                         LOG_INFO("  No arguments required.");
212                         return;
213                 }
214         }
215 }
216
217 void GenericCommand_maplist(float request, float argc)
218 {
219         switch (request) {
220                 case CMD_REQUEST_COMMAND:
221                 {
222                         string tmp_string;
223                         float i;
224
225                         switch (argv(1)) {
226                                 case "add": // appends new maps to the maplist
227                                 {
228                                         if (argc == 3) {
229                                                 if (!fexists(strcat("maps/", argv(2), ".bsp"))) {
230                                                         LOG_INFO("maplist: ERROR: ", argv(2), " does not exist!");
231                                                         break;
232                                                 }
233
234                                                 if (cvar_string("g_maplist") == "") {
235                                                         cvar_set("g_maplist", argv(2));
236                                                 } else {
237                                                         cvar_set("g_maplist", strcat(argv(2), " ", cvar_string("g_maplist")));
238                                                 }
239
240                                                 return;
241                                         }
242                                         break;      // go to usage
243                                 }
244
245                                 case "cleanup": // scans maplist and only adds back the ones which are really usable
246                                 {
247                                         MapInfo_Enumerate();
248                                         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
249                                         string filtered = "";
250                                         FOREACH_WORD(cvar_string("g_maplist"), MapInfo_CheckMap(it), filtered = cons(filtered, it));
251                                         cvar_set("g_maplist", filtered);
252                                         return;
253                                 }
254
255                                 case "remove": // scans maplist and only adds back whatever maps were not provided in argv(2)
256                                 {
257                                         if (argc == 3) {
258                                                 argc = tokenizebyseparator(cvar_string("g_maplist"), " ");
259
260                                                 tmp_string = "";
261                                                 for (i = 0; i < argc; ++i) {
262                                                         if (argv(i) != argv(2)) {
263                                                                 tmp_string = strcat(tmp_string, " ", argv(i));
264                                                         }
265                                                 }
266
267                                                 tmp_string = substring(tmp_string, 1, strlen(tmp_string) - 1);
268                                                 cvar_set("g_maplist", tmp_string);
269
270                                                 return;
271                                         }
272                                         break;      // go to usage
273                                 }
274
275                                 case "shuffle": // randomly shuffle the maplist
276                                 {
277                                         cvar_set("g_maplist", shufflewords(cvar_string("g_maplist")));
278                                         return;
279                                 }
280
281                                 default: break;
282                         }
283                 }
284
285                 default:
286                         LOG_INFO("Incorrect parameters for ^2maplist^7");
287                 case CMD_REQUEST_USAGE:
288                 {
289                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " maplist action [map]");
290                         LOG_INFO("  Where 'action' is the command to complete,");
291                         LOG_INFO("  and 'map' is what it acts upon (if required).");
292                         LOG_INFO("  Full list of commands here: \"add, cleanup, remove, shuffle.\"");
293                         return;
294                 }
295         }
296 }
297
298 void GenericCommand_nextframe(float request, float arguments, string command)
299 {
300         switch (request) {
301                 case CMD_REQUEST_COMMAND:
302                 {
303                         queue_to_execute_next_frame(substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
304                         return;
305                 }
306
307                 default:
308                 case CMD_REQUEST_USAGE:
309                 {
310                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " nextframe command...");
311                         LOG_INFO("  Where command will be executed next frame of this VM");
312                         return;
313                 }
314         }
315 }
316
317 void GenericCommand_removefromlist(float request, float argc)
318 {
319         switch (request) {
320                 case CMD_REQUEST_COMMAND:
321                 {
322                         if (argc == 3) {
323                                 string original_cvar = argv(1);
324                                 string removal = argv(2);
325
326                                 string tmp_string = "";
327                                 FOREACH_WORD(cvar_string(original_cvar), it != removal,
328                                 {
329                                         tmp_string = cons(tmp_string, it);
330                                 });
331
332                                 cvar_set(original_cvar, tmp_string);
333
334                                 return;
335                         }
336                 }
337
338                 default:
339                         LOG_INFO("Incorrect parameters for ^2removefromlist^7");
340                 case CMD_REQUEST_USAGE:
341                 {
342                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " removefromlist variable value");
343                         LOG_INFO("  Where 'variable' is what cvar to remove 'value' from.");
344                         LOG_INFO("See also: ^2addtolist^7");
345                         return;
346                 }
347         }
348 }
349
350 void GenericCommand_restartnotifs(float request)
351 {
352         switch (request) {
353                 case CMD_REQUEST_COMMAND:
354                 {
355 #ifdef GAMEQC
356                         int NOTIF_ANNCE_COUNT   = 0;
357                         FOREACH(Notifications, it.nent_type == MSG_ANNCE,   { ++NOTIF_ANNCE_COUNT;
358                                 });
359                         int NOTIF_INFO_COUNT    = 0;
360                         FOREACH(Notifications, it.nent_type == MSG_INFO,    { ++NOTIF_INFO_COUNT;
361                                 });
362                         int NOTIF_CENTER_COUNT  = 0;
363                         FOREACH(Notifications, it.nent_type == MSG_CENTER,  { ++NOTIF_CENTER_COUNT;
364                                 });
365                         int NOTIF_MULTI_COUNT   = 0;
366                         FOREACH(Notifications, it.nent_type == MSG_MULTI,   { ++NOTIF_MULTI_COUNT;
367                                 });
368                         int NOTIF_CHOICE_COUNT  = 0;
369                         FOREACH(Notifications, it.nent_type == MSG_CHOICE,  { ++NOTIF_CHOICE_COUNT;
370                                 });
371                         LOG_INFOF(
372                                 (
373                                         "Restart_Notifications(): Restarting %d notifications... "
374                                         "Counts: MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d"
375                                 ),
376                                 (
377                                         NOTIF_ANNCE_COUNT
378                                         + NOTIF_INFO_COUNT
379                                         + NOTIF_CENTER_COUNT
380                                         + NOTIF_MULTI_COUNT
381                                         + NOTIF_CHOICE_COUNT
382                                 ),
383                                 NOTIF_ANNCE_COUNT,
384                                 NOTIF_INFO_COUNT,
385                                 NOTIF_CENTER_COUNT,
386                                 NOTIF_MULTI_COUNT,
387                                 NOTIF_CHOICE_COUNT
388                         );
389                         Destroy_All_Notifications();
390                         CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
391 #else
392                         LOG_INFO(_("Notification restart command only works with cl_cmd and sv_cmd."));
393 #endif
394                         return;
395                 }
396
397                 default:
398                 case CMD_REQUEST_USAGE:
399                 {
400                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " restartnotifs");
401                         LOG_INFO("  No arguments required.");
402                         return;
403                 }
404         }
405 }
406
407 void GenericCommand_settemp(float request, float argc)
408 {
409         switch (request) {
410                 case CMD_REQUEST_COMMAND:
411                 {
412                         if (argc >= 3) {
413                                 float f = cvar_settemp(argv(1), argv(2));
414                                 if (f == 1) {
415                                         LOG_TRACE("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily.");
416                                 } else if (f == -1) {
417                                         LOG_TRACE("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\".");
418                                 }
419                                 // else cvar_settemp itself errors out
420
421                                 return;
422                         }
423                 }
424
425                 default:
426                         LOG_INFO("Incorrect parameters for ^2settemp^7");
427                 case CMD_REQUEST_USAGE:
428                 {
429                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " settemp \"cvar\" \"arguments\"");
430                         LOG_INFO("  Where 'cvar' is the cvar you want to temporarily set with 'arguments'.");
431                         LOG_INFO("See also: ^2settemp_restore^7");
432                         return;
433                 }
434         }
435 }
436
437 void GenericCommand_settemp_restore(float request, float argc)
438 {
439         switch (request) {
440                 case CMD_REQUEST_COMMAND:
441                 {
442                         float i = cvar_settemp_restore();
443
444                         if (i) {
445                                 LOG_TRACE("Restored ", ftos(i), " temporary cvar settings to their original values.");
446                         } else {
447                                 LOG_TRACE("Nothing to restore.");
448                         }
449
450                         return;
451                 }
452
453                 default:
454                 case CMD_REQUEST_USAGE:
455                 {
456                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " settemp_restore");
457                         LOG_INFO("  No arguments required.");
458                         LOG_INFO("See also: ^2settemp^7");
459                         return;
460                 }
461         }
462 }
463
464 void GenericCommand_runtest(float request, float argc)
465 {
466         switch (request) {
467                 case CMD_REQUEST_COMMAND:
468                 {
469                         if (argc > 1) {
470                                 float i;
471                                 for (i = 1; i < argc; ++i) {
472                                         TEST_Run(argv(i));
473                                 }
474                         } else {
475                                 RUN_ALL_TESTS();
476                         }
477                         return;
478                 }
479
480                 default:
481                 case CMD_REQUEST_USAGE:
482                 {
483                         LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " [function to run]");
484                         return;
485                 }
486         }
487 }
488
489 /* use this when creating a new command, making sure to place it in alphabetical order... also,
490 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
491 void GenericCommand_(float request)
492 {
493     switch(request)
494     {
495         case CMD_REQUEST_COMMAND:
496         {
497
498             return;
499         }
500
501         default:
502         case CMD_REQUEST_USAGE:
503         {
504             print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " "));
505             print("  No arguments required.\n");
506             return;
507         }
508     }
509 }
510 */
511
512 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
513 GENERIC_COMMAND(addtolist, "Add a string to a cvar")
514 {
515         GenericCommand_addtolist(request, arguments);
516 }
517 GENERIC_COMMAND(maplist, "Automatic control of maplist")
518 {
519         GenericCommand_maplist(request, arguments);
520 }
521 GENERIC_COMMAND(nextframe, "Execute the given command next frame of this VM")
522 {
523         GenericCommand_nextframe(request, arguments, command);
524 }
525 GENERIC_COMMAND(qc_curl, "Queries a URL")
526 {
527         GenericCommand_qc_curl(request, arguments);
528 }
529 GENERIC_COMMAND(removefromlist, "Remove a string from a cvar")
530 {
531         GenericCommand_removefromlist(request, arguments);
532 }
533 GENERIC_COMMAND(restartnotifs, "Re-initialize all notifications")
534 {
535         GenericCommand_restartnotifs(request);
536 }
537 GENERIC_COMMAND(rpn, "RPN calculator")
538 {
539         GenericCommand_rpn(request, arguments, command);
540 }
541 GENERIC_COMMAND(settemp, "Temporarily set a value to a cvar which is restored later")
542 {
543         GenericCommand_settemp(request, arguments);
544 }
545 GENERIC_COMMAND(settemp_restore, "Restore all cvars set by settemp command")
546 {
547         GenericCommand_settemp_restore(request, arguments);
548 }
549 GENERIC_COMMAND(runtest, "Run unit tests")
550 {
551         GenericCommand_runtest(request, arguments);
552 }
553
554 void GenericCommand_macro_help()
555 {
556         FOREACH(GENERIC_COMMANDS, true, LOG_INFOF("  ^2%s^7: %s", it.m_name, it.m_description));
557 }
558
559 float GenericCommand_macro_command(float argc, string command)
560 {
561         string c = strtolower(argv(0));
562         FOREACH(GENERIC_COMMANDS, it.m_name == c, {
563                 it.m_invokecmd(it, CMD_REQUEST_COMMAND, NULL, argc, command);
564                 return true;
565         });
566         return false;
567 }
568
569 float GenericCommand_macro_usage(float argc)
570 {
571         string c = strtolower(argv(1));
572         FOREACH(GENERIC_COMMANDS, it.m_name == c, {
573                 it.m_invokecmd(it, CMD_REQUEST_USAGE, NULL, argc, "");
574                 return true;
575         });
576         return false;
577 }
578
579 void GenericCommand_macro_write_aliases(float fh)
580 {
581         FOREACH(GENERIC_COMMANDS, true, CMD_Write_Alias("qc_cmd_svmenu", it.m_name, it.m_description));
582 }
583
584
585 // ===========================================
586 //  Main Common Function For Generic Commands
587 // ===========================================
588 // Commands spread out among all programs (menu, client, and server)
589
590 float GenericCommand(string command)
591 {
592         float argc = tokenize_console(command);
593         float n, j, f, i;
594         string s, s2, c;
595         vector rgb;
596
597         // Guide for working with argc arguments by example:
598         // argc:   1    - 2      - 3     - 4
599         // argv:   0    - 1      - 2     - 3
600         // cmd     vote - master - login - password
601
602         if (GenericCommand_macro_command(argc, command)) { // continue as usual and scan for normal commands
603                 return true; // handled by one of the above GenericCommand_* functions
604         } else if (argc >= 3 && argv(0) == "red") {
605                 s = substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2));
606                 localcmd(strcat(argv(1), " ", GenericCommand_markup(s)));
607                 return true;
608         } else if (argc >= 3 && crc16(0, argv(0)) == 38566 && crc16(0, strcat(argv(0), argv(0), argv(0))) == 59830) {
609                 // other test case
610                 s = strconv(2, 0, 0, substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
611
612                 n = floor(random() * 6 + 2);
613
614                 s2 = "";
615                 for (i = 0; i < n; ++i) {
616                         s2 = strcat(s2, "AH");
617                 }
618
619                 if (random() < 0.1) {
620                         s2 = strcat(substring(s2, 1, strlen(s2) - 1), "A");
621                 }
622
623                 if (s == "") {
624                         s = s2;
625                 } else {
626                         if (random() < 0.8) {
627                                 s = strcat(s, " ", s2);
628                         } else {
629                                 s = strcat(s2, " ", s);
630                         }
631                 }
632
633                 s2 = substring(s, strlen(s) - 2, 2);
634                 if (s2 == "AH" || s2 == "AY") {
635                         s = strcat(s, "))");
636                 } else {
637                         s = strcat(s, " ))");
638                 }
639
640                 if (random() < 0.1) {
641                         s = substring(s, 0, strlen(s) - 1);
642                 }
643
644                 if (random() < 0.1) {
645                         s = strconv(1, 0, 0, s);
646                 }
647
648                 localcmd(strcat(argv(1), " ", s));
649
650                 return true;
651         } else if (argc >= 3 && crc16(0, argv(0)) == 3826 && crc16(0, strcat(argv(0), argv(0), argv(0))) == 55790) {
652                 // test case for terencehill's color codes
653                 s = strdecolorize(substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
654                 s2 = "";
655
656                 n = strlen(s);
657                 j = ((6 * max(1, floor(strlen(s) / 32 + random() * 2 - 1))) / n) * (1 - 2 * (random() > 0.5));
658                 f = random() * 6;
659
660                 for (i = 0; i < n; ++i) {
661                         c = substring(s, i, 1);
662
663                         if (c == ";") {
664                                 c = ":";
665                         } else if (c == "^") {
666                                 c = "^^";
667                                 if (substring(s, i + 1, 1) == "^") {
668                                         ++i;
669                                 }
670                         }
671
672                         if (c != " ") {
673                                 rgb = hsl_to_rgb('1 0 0' * (j * i + f) + '0 1 .5');
674                                 c = strcat(rgb_to_hexcolor(rgb), c);
675                         }
676                         s2 = strcat(s2, c);
677                 }
678
679                 localcmd(strcat(argv(1), " ", s2));
680
681                 return true;
682         }
683
684         return false;
685 }