]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/command/generic.qc
Merge branch 'master' into terencehill/menu_gametype_tooltips_2
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / command / generic.qc
1 #include "command.qh"
2 #include "generic.qh"
3
4 #include "markup.qh"
5 #include "rpn.qh"
6
7 #include "../mapinfo.qh"
8
9 #ifndef MENUQC
10         #include "../notifications.qh"
11 #endif
12
13 #ifdef CSQC
14         #include "../../client/command/cl_cmd.qh"
15 #endif
16
17 #ifdef SVQC
18         #include "../../server/command/banning.qh"
19         #include "../../server/command/cmd.qh"
20         #include "../../server/command/common.qh"
21         #include "../../server/command/sv_cmd.qh"
22         #include "../../common/turrets/config.qh"
23         #include "../../common/weapons/config.qh"
24 #endif
25
26 // =========================================================
27 //  Generic program common command code, written by Samual
28 //  Last updated: February 19th, 2012
29 // =========================================================
30
31 // used by generic commands for better help/usage information
32 string GetProgramCommandPrefix(void)
33 {
34         #ifdef SVQC
35         return "sv_cmd";
36         #endif
37         #ifdef CSQC
38         return "cl_cmd";
39         #endif
40         #ifdef MENUQC
41         return "menu_cmd";
42         #endif
43 }
44
45 // used by curl command
46 void Curl_URI_Get_Callback(int id, float status, string data)
47 {
48         int i = id - URI_GET_CURL;
49         float do_exec = curl_uri_get_exec[i];
50         string do_cvar = curl_uri_get_cvar[i];
51         if(status != 0)
52         {
53                 LOG_TRACEF("error: status is %d\n", status);
54                 if(do_cvar)
55                         strunzone(do_cvar);
56                 return;
57         }
58         if(do_exec)
59                 localcmd(data);
60         if(do_cvar)
61         {
62                 cvar_set(do_cvar, data);
63                 strunzone(do_cvar);
64         }
65         if(!do_exec)
66                 if (!do_cvar)
67                         LOG_INFO(data);
68 }
69
70
71 // =======================
72 //  Command Sub-Functions
73 // =======================
74
75 void GenericCommand_addtolist(float request, float argc)
76 {
77         switch(request)
78         {
79                 case CMD_REQUEST_COMMAND:
80                 {
81                         if(argc >= 2)
82                         {
83                                 string original_cvar = argv(1);
84                                 string tmp_string = argv(2);
85
86                                 if(cvar_string(original_cvar) == "") // cvar was empty
87                                 {
88                                         cvar_set(original_cvar, tmp_string);
89                                 }
90                                 else // add it to the end of the list if the list doesn't already have it
91                                 {
92                                         argc = tokenizebyseparator(cvar_string(original_cvar), " ");
93                                         int i;
94                                         for(i = 0; i < argc; ++i)
95                                                 if(argv(i) == tmp_string)
96                                                         return; // already in list
97
98                                         cvar_set(original_cvar, strcat(tmp_string, " ", cvar_string(original_cvar)));
99                                 }
100                                 return;
101                         }
102                 }
103
104                 default:
105                         LOG_INFO("Incorrect parameters for ^2addtolist^7\n");
106                 case CMD_REQUEST_USAGE:
107                 {
108                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " addtolist variable value\n"));
109                         LOG_INFO("  Where 'variable' is what to add 'value' to.\n");
110                         LOG_INFO("See also: ^2removefromlist^7\n");
111                         return;
112                 }
113         }
114 }
115
116 void GenericCommand_qc_curl(float request, float argc)
117 {
118         switch(request)
119         {
120                 case CMD_REQUEST_COMMAND:
121                 {
122                         bool do_exec = false;
123                         string do_cvar = string_null;
124                         float key = -1;
125                         int i;
126                         for(i = 1; i+1 < argc; ++i)
127                         {
128                                 if(argv(i) == "--cvar" && i+2 < argc)
129                                 {
130                                         ++i;
131                                         do_cvar = strzone(argv(i));
132                                         continue;
133                                 }
134                                 if(argv(i) == "--exec")
135                                 {
136                                         do_exec = true;
137                                         continue;
138                                 }
139                                 if(argv(i) == "--key" && i+2 < argc)
140                                 {
141                                         ++i;
142                                         key = stof(argv(i));
143                                         continue;
144                                 }
145                                 break;
146                         }
147
148                         // now, argv(i) is the URL
149                         // following args may be POST parameters
150                         string url = argv(i);
151                         ++i;
152                         float buf = buf_create();
153                         int j;
154                         for(j = 0; i+1 < argc; i += 2)
155                                 bufstr_set(buf, ++j, sprintf("%s=%s", uri_escape(argv(i)), uri_escape(argv(i+1))));
156                         if(i < argc)
157                                 bufstr_set(buf, ++j, sprintf("submit=%s", uri_escape(argv(i))));
158
159                         float r;
160                         if(j == 0) // no args: GET
161                                 r = crypto_uri_postbuf(url, URI_GET_CURL + curl_uri_get_pos, string_null, string_null, -1, key);
162                         else // with args: POST
163                                 r = crypto_uri_postbuf(url, URI_GET_CURL + curl_uri_get_pos, "application/x-www-form-urlencoded", "&", buf, key);
164
165                         if(r)
166                         {
167                                 curl_uri_get_exec[curl_uri_get_pos] = do_exec;
168                                 curl_uri_get_cvar[curl_uri_get_pos] = do_cvar;
169                                 curl_uri_get_pos = (curl_uri_get_pos + 1) % (URI_GET_CURL_END - URI_GET_CURL + 1);
170                         }
171                         else
172                                 LOG_INFO(_("error creating curl handle\n"));
173
174                         buf_del(buf);
175
176                         return;
177                 }
178
179                 default:
180                 case CMD_REQUEST_USAGE:
181                 {
182                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " qc_curl [--key N] [--cvar] [--exec] URL [postargs...]"));
183                         return;
184                 }
185         }
186 }
187
188 void GenericCommand_dumpcommands(float request)
189 {
190         switch(request)
191         {
192                 case CMD_REQUEST_COMMAND:
193                 {
194                         float fh;
195                         string filename = strcat(GetProgramCommandPrefix(), "_dump.txt");
196                         fh = fopen(filename, FILE_WRITE);
197
198                         if(fh >= 0)
199                         {
200                                 #ifdef SVQC
201                                         CMD_Write("dump of server console commands:\n");
202                                         GameCommand_macro_write_aliases(fh);
203
204                                         CMD_Write("\ndump of networked client only commands:\n");
205                                         ClientCommand_macro_write_aliases(fh);
206
207                                         CMD_Write("\ndump of common commands:\n");
208                                         CommonCommand_macro_write_aliases(fh);
209
210                                         CMD_Write("\ndump of ban commands:\n");
211                                         BanCommand_macro_write_aliases(fh);
212                                 #endif
213
214                                 #ifdef CSQC
215                                         CMD_Write("dump of client commands:\n");
216                                         LocalCommand_macro_write_aliases(fh);
217                                 #endif
218
219                                 CMD_Write("\ndump of generic commands:\n");
220                                 GenericCommand_macro_write_aliases(fh);
221
222                                 LOG_INFO("Completed dump of aliases in ^2data/data/", GetProgramCommandPrefix(), "_dump.txt^7.\n");
223
224                                 fclose(fh);
225                         }
226                         else
227                         {
228                                 LOG_INFO("^1Error: ^7Could not dump to file!\n");
229                         }
230                         return;
231                 }
232
233                 default:
234                 case CMD_REQUEST_USAGE:
235                 {
236                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpcommands"));
237                         LOG_INFO("  No arguments required.\n");
238                         return;
239                 }
240         }
241 }
242
243 #ifndef MENUQC
244 void effectinfo_dump(int fh, bool alsoprint);
245 #endif
246 void GenericCommand_dumpeffectinfo(float request)
247 {
248     switch (request) {
249         case CMD_REQUEST_COMMAND: {
250             #ifndef MENUQC
251             string filename = argv(1);
252                         bool alsoprint = false;
253             if (filename == "") {
254                 filename = "effectinfo_dump.txt";
255                 alsoprint = false;
256             } else if (filename == "-") {
257                 filename = "effectinfo_dump.txt";
258                 alsoprint = true;
259             }
260             int fh = fopen(filename, FILE_WRITE);
261             if (fh >= 0) {
262                 effectinfo_dump(fh, alsoprint);
263                 LOG_INFOF("Dumping effectinfo... File located at ^2data/data/%s^7.\n", filename);
264                                 LOG_INFOF("Reload with ^2cl_particles_reloadeffects data/%s^7.\n", filename);
265                 fclose(fh);
266             } else {
267                 LOG_WARNINGF("Could not open file '%s'!\n", filename);
268             }
269             #else
270             LOG_INFO(_("Effectinfo dump command only works with cl_cmd and sv_cmd.\n"));
271             #endif
272             return;
273         }
274         default:
275         case CMD_REQUEST_USAGE: {
276             LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpeffectinfo [filename]"));
277             LOG_INFO("  Where 'filename' is the file to write (default is effectinfo_dump.txt),\n");
278             LOG_INFO("  if supplied with '-' output to console as well as default,\n");
279             LOG_INFO("  if left blank, it will only write to default.\n");
280             return;
281         }
282     }
283 }
284 STATIC_INIT(dumpeffectinfo) { localcmd("alias dumpeffectinfo \"qc_cmd_svcl dumpeffectinfo ${* ?}\"\n"); }
285
286 void GenericCommand_dumpitems(float request)
287 {
288         switch(request)
289         {
290                 case CMD_REQUEST_COMMAND:
291                 {
292                         Dump_Items();
293                         return;
294                 }
295
296                 default:
297                 case CMD_REQUEST_USAGE:
298                 {
299                         LOG_INFOF("\nUsage:^3 %s dumpitems", GetProgramCommandPrefix());
300                         return;
301                 }
302         }
303 }
304
305 void GenericCommand_dumpnotifs(float request)
306 {
307         switch(request)
308         {
309                 case CMD_REQUEST_COMMAND:
310                 {
311                         #ifndef MENUQC
312                         float fh, alsoprint = false;
313                         string filename = argv(1);
314
315                         if(filename == "")
316                         {
317                                 filename = "notifications_dump.cfg";
318                                 alsoprint = false;
319                         }
320                         else if(filename == "-")
321                         {
322                                 filename = "notifications_dump.cfg";
323                                 alsoprint = true;
324                         }
325                         fh = fopen(filename, FILE_WRITE);
326
327                         if(fh >= 0)
328                         {
329                                 Dump_Notifications(fh, alsoprint);
330                                 LOG_INFOF("Dumping notifications... File located in ^2data/data/%s^7.\n", filename);
331                                 fclose(fh);
332                         }
333                         else
334                         {
335                                 LOG_INFOF("^1Error: ^7Could not open file '%s'!\n", filename);
336                         }
337                         #else
338                         LOG_INFO(_("Notification dump command only works with cl_cmd and sv_cmd.\n"));
339                         #endif
340                         return;
341                 }
342
343                 default:
344                 case CMD_REQUEST_USAGE:
345                 {
346                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpnotifs [filename]"));
347                         LOG_INFO("  Where 'filename' is the file to write (default is notifications_dump.cfg),\n");
348                         LOG_INFO("  if supplied with '-' output to console as well as default,\n");
349                         LOG_INFO("  if left blank, it will only write to default.\n");
350                         return;
351                 }
352         }
353 }
354
355 void GenericCommand_dumpweapons(float request) // WEAPONTODO: make this work with other progs than just server
356 {
357         switch(request)
358         {
359                 case CMD_REQUEST_COMMAND:
360                 {
361                         #ifdef SVQC
362                         wep_config_file = -1;
363                         wep_config_alsoprint = -1;
364                         string filename = argv(1);
365
366                         if(filename == "")
367                         {
368                                 filename = "weapons_dump.cfg";
369                                 wep_config_alsoprint = false;
370                         }
371                         else if(filename == "-")
372                         {
373                                 filename = "weapons_dump.cfg";
374                                 wep_config_alsoprint = true;
375                         }
376                         wep_config_file = fopen(filename, FILE_WRITE);
377
378                         if(wep_config_file >= 0)
379                         {
380                                 Dump_Weapon_Settings();
381                                 LOG_INFO(sprintf("Dumping weapons... File located in ^2data/data/%s^7.\n", filename));
382                                 fclose(wep_config_file);
383                                 wep_config_file = -1;
384                                 wep_config_alsoprint = -1;
385                         }
386                         else
387                         {
388                                 LOG_INFO(sprintf("^1Error: ^7Could not open file '%s'!\n", filename));
389                         }
390                         #else
391                         LOG_INFO(_("Weapons dump command only works with sv_cmd.\n"));
392                         #endif
393                         return;
394                 }
395
396                 default:
397                 case CMD_REQUEST_USAGE:
398                 {
399                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpweapons [filename]"));
400                         LOG_INFO("  Where 'filename' is the file to write (default is weapons_dump.cfg),\n");
401                         LOG_INFO("  if supplied with '-' output to console as well as default,\n");
402                         LOG_INFO("  if left blank, it will only write to default.\n");
403                         return;
404                 }
405         }
406 }
407
408 void GenericCommand_dumpturrets(float request)
409 {
410         switch(request)
411         {
412                 case CMD_REQUEST_COMMAND:
413                 {
414                         #ifdef SVQC
415                         tur_config_file = -1;
416                         tur_config_alsoprint = -1;
417                         string filename = argv(1);
418
419                         if(filename == "")
420                         {
421                                 filename = "turrets_dump.cfg";
422                                 tur_config_alsoprint = FALSE;
423                         }
424                         else if(filename == "-")
425                         {
426                                 filename = "turrets_dump.cfg";
427                                 tur_config_alsoprint = TRUE;
428                         }
429                         tur_config_file = fopen(filename, FILE_WRITE);
430
431                         if(tur_config_file >= 0)
432                         {
433                                 Dump_Turret_Settings();
434                                 LOG_INFO(sprintf("Dumping turrets... File located in ^2data/data/%s^7.\n", filename));
435                                 fclose(tur_config_file);
436                                 tur_config_file = -1;
437                                 tur_config_alsoprint = -1;
438                         }
439                         else
440                         {
441                                 LOG_INFO(sprintf("^1Error: ^7Could not open file '%s'!\n", filename));
442                         }
443                         #else
444                         LOG_INFO(_("Turrets dump command only works with sv_cmd.\n"));
445                         #endif
446                         return;
447                 }
448
449                 default:
450                 case CMD_REQUEST_USAGE:
451                 {
452                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpturrets [filename]"));
453                         LOG_INFO("  Where 'filename' is the file to write (default is turrets_dump.cfg),\n");
454                         LOG_INFO("  if supplied with '-' output to console as well as default,\n");
455                         LOG_INFO("  if left blank, it will only write to default.\n");
456                         return;
457                 }
458         }
459 }
460
461 void GenericCommand_maplist(float request, float argc)
462 {
463         switch(request)
464         {
465                 case CMD_REQUEST_COMMAND:
466                 {
467                         string tmp_string;
468                         float i;
469
470                         switch(argv(1))
471                         {
472                                 case "add": // appends new maps to the maplist
473                                 {
474                                         if(argc == 3)
475                                         {
476                                                 if (!fexists(strcat("maps/", argv(2), ".bsp")))
477                                                 {
478                                                         LOG_INFO("maplist: ERROR: ", argv(2), " does not exist!\n");
479                                                         break;
480                                                 }
481
482                                                 if(cvar_string("g_maplist") == "")
483                                                         cvar_set("g_maplist", argv(2));
484                                                 else
485                                                         cvar_set("g_maplist", strcat(argv(2), " ", cvar_string("g_maplist")));
486
487                                                 return;
488                                         }
489                                         break; // go to usage
490                                 }
491
492                                 case "cleanup": // scans maplist and only adds back the ones which are really usable
493                                 {
494                                         MapInfo_Enumerate();
495                                         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
496                                         argc = tokenizebyseparator(cvar_string("g_maplist"), " ");
497
498                                         tmp_string = "";
499                                         for(i = 0; i < argc; ++i)
500                                                 if(MapInfo_CheckMap(argv(i)))
501                                                         tmp_string = strcat(tmp_string, " ", argv(i));
502
503                                         tmp_string = substring(tmp_string, 1, strlen(tmp_string) - 1);
504                                         cvar_set("g_maplist", tmp_string);
505
506                                         return;
507                                 }
508
509                                 case "remove": // scans maplist and only adds back whatever maps were not provided in argv(2)
510                                 {
511                                         if(argc == 3)
512                                         {
513                                                 argc = tokenizebyseparator(cvar_string("g_maplist"), " ");
514
515                                                 tmp_string = "";
516                                                 for(i = 0; i < argc; ++i)
517                                                         if(argv(i) != argv(2))
518                                                                 tmp_string = strcat(tmp_string, " ", argv(i));
519
520                                                 tmp_string = substring(tmp_string, 1, strlen(tmp_string) - 1);
521                                                 cvar_set("g_maplist", tmp_string);
522
523                                                 return;
524                                         }
525                                         break; // go to usage
526                                 }
527
528                                 case "shuffle": // randomly shuffle the maplist
529                                 {
530                                         cvar_set("g_maplist", shufflewords(cvar_string("g_maplist")));
531                                         return;
532                                 }
533
534                                 default: break;
535                         }
536                 }
537
538                 default:
539                         LOG_INFO("Incorrect parameters for ^2maplist^7\n");
540                 case CMD_REQUEST_USAGE:
541                 {
542                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " maplist action [map]\n"));
543                         LOG_INFO("  Where 'action' is the command to complete,\n");
544                         LOG_INFO("  and 'map' is what it acts upon (if required).\n");
545                         LOG_INFO("  Full list of commands here: \"add, cleanup, remove, shuffle.\"\n");
546                         return;
547                 }
548         }
549 }
550
551 void GenericCommand_nextframe(float request, float arguments, string command)
552 {
553         switch(request)
554         {
555                 case CMD_REQUEST_COMMAND:
556                 {
557                         queue_to_execute_next_frame(substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)));
558                         return;
559                 }
560
561                 default:
562                 case CMD_REQUEST_USAGE:
563                 {
564                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " nextframe command...\n"));
565                         LOG_INFO("  Where command will be executed next frame of this VM\n");
566                         return;
567                 }
568         }
569 }
570
571 void GenericCommand_removefromlist(float request, float argc)
572 {
573         switch(request)
574         {
575                 case CMD_REQUEST_COMMAND:
576                 {
577                         if(argc == 3)
578                         {
579                                 float i;
580                                 string original_cvar = argv(1);
581                                 string removal = argv(2);
582                                 string tmp_string;
583
584                                 argc = tokenizebyseparator(cvar_string(original_cvar), " ");
585
586                                 tmp_string = "";
587                                 for(i = 0; i < argc; ++i)
588                                         if(argv(i) != removal)
589                                                 tmp_string = strcat(tmp_string, " ", argv(i));
590
591                                 tmp_string = substring(tmp_string, 1, strlen(tmp_string) - 1);
592                                 cvar_set(original_cvar, tmp_string);
593
594                                 return;
595                         }
596                 }
597
598                 default:
599                         LOG_INFO("Incorrect parameters for ^2removefromlist^7\n");
600                 case CMD_REQUEST_USAGE:
601                 {
602                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " removefromlist variable value\n"));
603                         LOG_INFO("  Where 'variable' is what cvar to remove 'value' from.\n");
604                         LOG_INFO("See also: ^2addtolist^7\n");
605                         return;
606                 }
607         }
608 }
609
610 void GenericCommand_restartnotifs(float request)
611 {
612         switch(request)
613         {
614                 case CMD_REQUEST_COMMAND:
615                 {
616                         #ifndef MENUQC
617                         LOG_INFOF(
618                                 strcat(
619                                         "Restart_Notifications(): Restarting %d notifications... ",
620                                         "Counts: MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d\n"
621                                 ),
622                                 (
623                                         NOTIF_ANNCE_COUNT +
624                                         NOTIF_INFO_COUNT +
625                                         NOTIF_CENTER_COUNT +
626                                         NOTIF_MULTI_COUNT +
627                                         NOTIF_CHOICE_COUNT
628                                 ),
629                                 NOTIF_ANNCE_COUNT,
630                                 NOTIF_INFO_COUNT,
631                                 NOTIF_CENTER_COUNT,
632                                 NOTIF_MULTI_COUNT,
633                                 NOTIF_CHOICE_COUNT
634                         );
635                         Destroy_All_Notifications();
636                         CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
637                         #else
638                         LOG_INFO(_("Notification restart command only works with cl_cmd and sv_cmd.\n"));
639                         #endif
640                         return;
641                 }
642
643                 default:
644                 case CMD_REQUEST_USAGE:
645                 {
646                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " restartnotifs"));
647                         LOG_INFO("  No arguments required.\n");
648                         return;
649                 }
650         }
651 }
652
653 void GenericCommand_settemp(float request, float argc)
654 {
655         switch(request)
656         {
657                 case CMD_REQUEST_COMMAND:
658                 {
659                         if(argc >= 3)
660                         {
661                                 float f = cvar_settemp(argv(1), argv(2));
662                                 if(f == 1)
663                                         LOG_TRACE("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily.\n");
664                                 else if(f == -1)
665                                         LOG_TRACE("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\".\n");
666                                 // else cvar_settemp itself errors out
667
668                                 return;
669                         }
670                 }
671
672                 default:
673                         LOG_INFO("Incorrect parameters for ^2settemp^7\n");
674                 case CMD_REQUEST_USAGE:
675                 {
676                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " settemp \"cvar\" \"arguments\"\n"));
677                         LOG_INFO("  Where 'cvar' is the cvar you want to temporarily set with 'arguments'.\n");
678                         LOG_INFO("See also: ^2settemp_restore^7\n");
679                         return;
680                 }
681         }
682 }
683
684 void GenericCommand_settemp_restore(float request, float argc)
685 {
686         switch(request)
687         {
688                 case CMD_REQUEST_COMMAND:
689                 {
690                         float i = cvar_settemp_restore();
691
692                         if(i)
693                                 LOG_TRACE("Restored ", ftos(i), " temporary cvar settings to their original values.\n");
694                         else
695                                 LOG_TRACE("Nothing to restore.\n");
696
697                         return;
698                 }
699
700                 default:
701                 case CMD_REQUEST_USAGE:
702                 {
703                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " settemp_restore\n"));
704                         LOG_INFO("  No arguments required.\n");
705                         LOG_INFO("See also: ^2settemp^7\n");
706                         return;
707                 }
708         }
709 }
710
711 void GenericCommand_runtest(float request, float argc)
712 {
713         switch(request)
714         {
715                 case CMD_REQUEST_COMMAND:
716                 {
717                         if(argc > 1)
718                         {
719                                 float i;
720                                 for(i = 1; i < argc; ++i)
721                                         TEST_Run(argv(i));
722                         }
723                         else
724                                 TEST_RunAll();
725                         return;
726                 }
727
728                 default:
729                 case CMD_REQUEST_USAGE:
730                 {
731                         LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " [function to run]"));
732                         return;
733                 }
734         }
735 }
736
737 /* use this when creating a new command, making sure to place it in alphabetical order... also,
738 ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION!
739 void GenericCommand_(float request)
740 {
741         switch(request)
742         {
743                 case CMD_REQUEST_COMMAND:
744                 {
745
746                         return;
747                 }
748
749                 default:
750                 case CMD_REQUEST_USAGE:
751                 {
752                         print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " "));
753                         print("  No arguments required.\n");
754                         return;
755                 }
756         }
757 }
758 */
759
760 // ==================================
761 //  Macro system for server commands
762 // ==================================
763
764 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
765 #define GENERIC_COMMANDS(request,arguments,command) \
766         GENERIC_COMMAND("addtolist", GenericCommand_addtolist(request, arguments), "Add a string to a cvar") \
767         GENERIC_COMMAND("dumpcommands", GenericCommand_dumpcommands(request), "Dump all commands on the program to *_cmd_dump.txt") \
768         GENERIC_COMMAND("dumpeffectinfo", GenericCommand_dumpeffectinfo(request), "Dump all effectinfo to effectinfo_dump.txt") \
769         GENERIC_COMMAND("dumpitems", GenericCommand_dumpitems(request), "Dump all items to the console") \
770         GENERIC_COMMAND("dumpnotifs", GenericCommand_dumpnotifs(request), "Dump all notifications into notifications_dump.txt") \
771         GENERIC_COMMAND("dumpturrets", GenericCommand_dumpturrets(request), "Dump all turrets into turrets_dump.txt") \
772         GENERIC_COMMAND("dumpweapons", GenericCommand_dumpweapons(request), "Dump all weapons into weapons_dump.txt") \
773         GENERIC_COMMAND("maplist", GenericCommand_maplist(request, arguments), "Automatic control of maplist") \
774         GENERIC_COMMAND("nextframe", GenericCommand_nextframe(request, arguments, command), "Execute the given command next frame of this VM") \
775         GENERIC_COMMAND("qc_curl", GenericCommand_qc_curl(request, arguments), "Queries a URL") \
776         GENERIC_COMMAND("removefromlist", GenericCommand_removefromlist(request, arguments), "Remove a string from a cvar") \
777         GENERIC_COMMAND("restartnotifs", GenericCommand_restartnotifs(request), "Re-initialize all notifications") \
778         GENERIC_COMMAND("rpn", GenericCommand_rpn(request, arguments, command), "RPN calculator") \
779         GENERIC_COMMAND("settemp", GenericCommand_settemp(request, arguments), "Temporarily set a value to a cvar which is restored later") \
780         GENERIC_COMMAND("settemp_restore", GenericCommand_settemp_restore(request, arguments), "Restore all cvars set by settemp command") \
781         GENERIC_COMMAND("runtest", GenericCommand_runtest(request, arguments), "Run unit tests") \
782         /* nothing */
783
784 void GenericCommand_macro_help()
785 {
786         #define GENERIC_COMMAND(name,function,description) \
787                 { LOG_INFO("  ^2", name, "^7: ", description, "\n"); }
788
789         GENERIC_COMMANDS(0, 0, "");
790         #undef GENERIC_COMMAND
791
792         return;
793 }
794
795 float GenericCommand_macro_command(float argc, string command)
796 {
797         #define GENERIC_COMMAND(name,function,description) \
798                 { if(name == strtolower(argv(0))) { function; return true; } }
799
800         GENERIC_COMMANDS(CMD_REQUEST_COMMAND, argc, command);
801         #undef GENERIC_COMMAND
802
803         return false;
804 }
805
806 float GenericCommand_macro_usage(float argc)
807 {
808         #define GENERIC_COMMAND(name,function,description) \
809                 { if(name == strtolower(argv(1))) { function; return true; } }
810
811         GENERIC_COMMANDS(CMD_REQUEST_USAGE, argc, "");
812         #undef GENERIC_COMMAND
813
814         return false;
815 }
816
817 void GenericCommand_macro_write_aliases(float fh)
818 {
819         #define GENERIC_COMMAND(name,function,description) \
820                 { CMD_Write_Alias("qc_cmd_svmenu", name, description); }
821
822         GENERIC_COMMANDS(0, 0, "");
823         #undef GENERIC_COMMAND
824
825         return;
826 }
827
828
829 // ===========================================
830 //  Main Common Function For Generic Commands
831 // ===========================================
832 // Commands spread out among all programs (menu, client, and server)
833
834 float GenericCommand(string command)
835 {
836         float argc = tokenize_console(command);
837         float n, j, f, i;
838         string s, s2, c;
839         vector rgb;
840
841         // Guide for working with argc arguments by example:
842         // argc:   1    - 2      - 3     - 4
843         // argv:   0    - 1      - 2     - 3
844         // cmd     vote - master - login - password
845
846         if(GenericCommand_macro_command(argc, command)) // continue as usual and scan for normal commands
847         {
848                 return true; // handled by one of the above GenericCommand_* functions
849         }
850         else if(argc >= 3 && argv(0) == "red")
851         {
852                 s = substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2));
853                 localcmd(strcat(argv(1), " ", GenericCommand_markup(s)));
854                 return true;
855         }
856         else if(argc >= 3 && crc16(0, argv(0)) == 38566 && crc16(0, strcat(argv(0), argv(0), argv(0))) == 59830)
857         {
858                 // other test case
859                 s = strconv(2, 0, 0, substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
860
861                 n = floor(random() * 6 + 2);
862
863                 s2 = "";
864                 for(i = 0; i < n; ++i)
865                 {
866                         s2 = strcat(s2, "AH");
867                 }
868
869                 if(random() < 0.1)
870                         s2 = strcat(substring(s2, 1, strlen(s2) - 1), "A");
871
872                 if(s == "")
873                         s = s2;
874                 else
875                         if(random() < 0.8)
876                                 s = strcat(s, " ", s2);
877                         else
878                                 s = strcat(s2, " ", s);
879
880                 s2 = substring(s, strlen(s) - 2, 2);
881                 if(s2 == "AH" || s2 == "AY")
882                         s = strcat(s, "))");
883                 else
884                         s = strcat(s, " ))");
885
886                 if(random() < 0.1)
887                         s = substring(s, 0, strlen(s) - 1);
888
889                 if(random() < 0.1)
890                         s = strconv(1, 0, 0, s);
891
892                 localcmd(strcat(argv(1), " ", s));
893
894                 return true;
895         }
896         else if(argc >= 3 && crc16(0, argv(0)) == 3826 && crc16(0, strcat(argv(0), argv(0), argv(0))) == 55790)
897         {
898                 // test case for terencehill's color codes
899                 s = strdecolorize(substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
900                 s2 = "";
901
902                 n = strlen(s);
903                 j = ((6 * max(1, floor(strlen(s)/32 + random() * 2 - 1))) / n) * (1 - 2 * (random() > 0.5));
904                 f = random() * 6;
905
906                 for(i = 0; i < n; ++i)
907                 {
908                         c = substring(s, i, 1);
909
910                         if(c == ";")
911                                 c = ":";
912                         else if(c == "^")
913                         {
914                                 c = "^^";
915                                 if(substring(s, i+1, 1) == "^")
916                                         ++i;
917                         }
918
919                         if(c != " ")
920                         {
921                                 rgb = hsl_to_rgb('1 0 0' * (j * i + f) + '0 1 .5');
922                                 c = strcat(rgb_to_hexcolor(rgb), c);
923                         }
924                         s2 = strcat(s2, c);
925                 }
926
927                 localcmd(strcat(argv(1), " ", s2));
928
929                 return true;
930         }
931
932         return false;
933 }