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