]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cmd.c
cmd: add CON_WARN colour to various non-fatal cvar and command errors
[xonotic/darkplaces.git] / cmd.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // cmd.c -- Quake script command processing module
21
22 #include "quakedef.h"
23 #include "thread.h"
24
25 cmd_state_t *cmd_local;
26 cmd_state_t *cmd_serverfromclient;
27
28 cmd_userdefined_t cmd_userdefined_all;
29 cmd_userdefined_t cmd_userdefined_null;
30
31 typedef struct cmd_iter_s {
32         cmd_state_t *cmd;
33 }
34 cmd_iter_t;
35
36 static cmd_iter_t *cmd_iter_all;
37
38 mempool_t *cbuf_mempool;
39
40 // we only run the +whatever commandline arguments once
41 qbool host_stuffcmdsrun = false;
42
43 //=============================================================================
44
45 void Cbuf_Lock(cmd_buf_t *cbuf)
46 {
47         Thread_LockMutex(cbuf->lock);
48 }
49
50 void Cbuf_Unlock(cmd_buf_t *cbuf)
51 {
52         Thread_UnlockMutex(cbuf->lock);
53 }
54
55
56 /*
57 ============
58 Cmd_Wait_f
59
60 Causes execution of the remainder of the command buffer to be delayed until
61 next frame.  This allows commands like:
62 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
63 ============
64 */
65 static void Cmd_Wait_f (cmd_state_t *cmd)
66 {
67         cmd->cbuf->wait = true;
68 }
69
70 /*
71 ============
72 Cmd_Defer_f
73
74 Cause a command to be executed after a delay.
75 ============
76 */
77 static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending);
78 static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize);
79 static void Cmd_Defer_f (cmd_state_t *cmd)
80 {
81         cmd_input_t *current;
82         cmd_buf_t *cbuf = cmd->cbuf;
83         unsigned int cmdsize;
84
85         if(Cmd_Argc(cmd) == 1)
86         {
87                 if(List_Is_Empty(&cbuf->deferred))
88                         Con_Printf("No commands are pending.\n");
89                 else
90                 {
91                         List_For_Each_Entry(current, &cbuf->deferred, cmd_input_t, list)
92                                 Con_Printf("-> In %9.2f: %s\n", current->delay, current->text);
93                 }
94         }
95         else if(Cmd_Argc(cmd) == 2 && !strcasecmp("clear", Cmd_Argv(cmd, 1)))
96         {
97                 while(!List_Is_Empty(&cbuf->deferred))
98                 {
99                         cbuf->size -= List_Entry(cbuf->deferred.next, cmd_input_t, list)->length;
100                         List_Move_Tail(cbuf->deferred.next, &cbuf->free);
101                 }
102         }
103         else if(Cmd_Argc(cmd) == 3 && (cmdsize = strlen(Cmd_Argv(cmd, 2))) )
104         {
105                 Cbuf_Lock(cbuf);
106
107                 Cbuf_LinkString(cmd, &cbuf->deferred, NULL, Cmd_Argv(cmd, 2), false, cmdsize);
108                 List_Entry(cbuf->deferred.prev, cmd_input_t, list)->delay = atof(Cmd_Argv(cmd, 1));
109
110                 Cbuf_Unlock(cbuf);
111         }
112         else
113         {
114                 Con_Printf("usage: defer <seconds> <command>\n"
115                            "       defer clear\n");
116                 return;
117         }
118 }
119
120 /*
121 ============
122 Cmd_Centerprint_f
123
124 Print something to the center of the screen using SCR_Centerprint
125 ============
126 */
127 static void Cmd_Centerprint_f (cmd_state_t *cmd)
128 {
129         char msg[MAX_INPUTLINE];
130         unsigned int i, c, p;
131         c = Cmd_Argc(cmd);
132         if(c >= 2)
133         {
134                 strlcpy(msg, Cmd_Argv(cmd,1), sizeof(msg));
135                 for(i = 2; i < c; ++i)
136                 {
137                         strlcat(msg, " ", sizeof(msg));
138                         strlcat(msg, Cmd_Argv(cmd, i), sizeof(msg));
139                 }
140                 c = (unsigned int)strlen(msg);
141                 for(p = 0, i = 0; i < c; ++i)
142                 {
143                         if(msg[i] == '\\')
144                         {
145                                 if(msg[i+1] == 'n')
146                                         msg[p++] = '\n';
147                                 else if(msg[i+1] == '\\')
148                                         msg[p++] = '\\';
149                                 else {
150                                         msg[p++] = '\\';
151                                         msg[p++] = msg[i+1];
152                                 }
153                                 ++i;
154                         } else {
155                                 msg[p++] = msg[i];
156                         }
157                 }
158                 msg[p] = '\0';
159                 SCR_CenterPrint(msg);
160         }
161 }
162
163 /*
164 =============================================================================
165
166                                                 COMMAND BUFFER
167
168  * The Quake command-line is super basic. It can be entered in the console
169  * or in config files. A semicolon is used to terminate a command and chain
170  * them together. Otherwise, a newline delineates command input.
171  *
172  * In most engines, the Quake command-line is a simple linear text buffer that
173  * is parsed when it executes. In Darkplaces, we use a linked list of command
174  * input and parse the input on the spot.
175  *
176  * This was done because Darkplaces allows multiple command interpreters on the
177  * same thread. Previously, each interpreter maintained its own buffer and this
178  * caused problems related to execution order, and maintaining a single simple
179  * buffer for all interpreters makes it non-trivial to keep track of which
180  * command should execute on which interpreter.
181
182 =============================================================================
183 */
184
185 /*
186 ============
187 Cbuf_NodeGet
188
189 Returns an existing buffer node for appending or reuse, or allocates a new one
190 ============
191 */
192 static cmd_input_t *Cbuf_NodeGet(cmd_buf_t *cbuf, cmd_input_t *existing)
193 {
194         cmd_input_t *node;
195         if(existing && existing->pending)
196                 node = existing;
197         else if(!List_Is_Empty(&cbuf->free))
198         {
199                 node = List_Entry(cbuf->free.next, cmd_input_t, list);
200                 node->length = node->pending = 0;
201         }
202         else
203         {
204                 node = (cmd_input_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_input_t));
205                 node->list.prev = node->list.next = &node->list;
206                 node->size = node->length = node->pending = 0;
207         }
208         return node;
209 }
210
211 /*
212 ============
213 Cbuf_LinkString
214
215 Copies a command string into a buffer node
216 ============
217 */
218 static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize)
219 {
220         cmd_buf_t *cbuf = cmd->cbuf;
221         cmd_input_t *node = Cbuf_NodeGet(cbuf, existing);
222         unsigned int offset = node->length; // > 0 if(pending)
223
224         // node will match existing if its text was pending continuation
225         if(node != existing)
226         {
227                 node->source = cmd;
228                 List_Move_Tail(&node->list, head);
229         }
230
231         node->length += cmdsize;
232         if(node->size < node->length)
233         {
234                 node->text = (char *)Mem_Realloc(cbuf_mempool, node->text, node->length + 1);
235                 node->size = node->length;
236         }
237         cbuf->size += cmdsize;
238
239         strlcpy(&node->text[offset], text, cmdsize + 1); // always sets the last char to \0
240         //Con_Printf("^5Cbuf_LinkString(): %s `^7%s^5`\n", node->pending ? "append" : "new", &node->text[offset]);
241         node->pending = leavepending;
242 }
243
244 /*
245 ============
246 Cbuf_ParseText
247
248 Parses text to isolate command strings for linking into the buffer
249 separators: \n \r or unquoted and uncommented ';'
250 ============
251 */
252 static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending)
253 {
254         unsigned int cmdsize = 0, start = 0, pos;
255         qbool quotes = false, comment = false;
256
257         for (pos = 0; text[pos]; ++pos)
258         {
259                 switch(text[pos])
260                 {
261                         case ';':
262                                 if (comment || quotes)
263                                         break;
264                         case '\r':
265                         case '\n':
266                                 comment = false;
267                                 quotes = false; // matches div0-stable
268                                 if (cmdsize)
269                                 {
270                                         Cbuf_LinkString(cmd, head, existing, &text[start], false, cmdsize);
271                                         cmdsize = 0;
272                                 }
273                                 else if (existing && existing->pending) // all I got was this lousy \n
274                                         existing->pending = false;
275                                 continue; // don't increment cmdsize
276
277                         case '/':
278                                 if (!quotes && text[pos + 1] == '/' && (pos == 0 || ISWHITESPACE(text[pos - 1])))
279                                         comment = true;
280                                 break;
281                         case '"':
282                                 if (!comment && (pos == 0 || text[pos - 1] != '\\'))
283                                         quotes = !quotes;
284                                 break;
285                 }
286
287                 if (!comment)
288                 {
289                         if (!cmdsize)
290                                 start = pos;
291                         ++cmdsize;
292                 }
293         }
294
295         if (cmdsize) // the line didn't end yet but we do have a string
296                 Cbuf_LinkString(cmd, head, existing, &text[start], allowpending, cmdsize);
297 }
298
299 /*
300 ============
301 Cbuf_AddText
302
303 Adds command text at the end of the buffer
304 ============
305 */
306 void Cbuf_AddText (cmd_state_t *cmd, const char *text)
307 {
308         size_t l = strlen(text);
309         cmd_buf_t *cbuf = cmd->cbuf;
310         llist_t llist = {&llist, &llist};
311
312         Cbuf_Lock(cbuf);
313
314         if (cbuf->maxsize - cbuf->size <= l)
315                 Con_Print(CON_WARN "Cbuf_AddText: overflow\n");
316         else
317         {
318                 // If the string terminates but the (last) line doesn't, the node will be left in the pending state (to be continued).
319                 Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.prev, cmd_input_t, list)), text, true);
320                 List_Splice_Tail(&llist, &cbuf->start);
321         }
322         Cbuf_Unlock(cbuf);
323 }
324
325 /*
326 ============
327 Cbuf_InsertText
328
329 Adds command text immediately after the current command
330 ============
331 */
332 void Cbuf_InsertText (cmd_state_t *cmd, const char *text)
333 {
334         cmd_buf_t *cbuf = cmd->cbuf;
335         llist_t llist = {&llist, &llist};
336         size_t l = strlen(text);
337
338         Cbuf_Lock(cbuf);
339
340         if (cbuf->size + l >= cbuf->maxsize)
341                 Con_Print(CON_WARN "Cbuf_InsertText: overflow\n");
342         else
343         {
344                 // bones_was_here assertion: when prepending to the buffer it never makes sense to leave node(s) in the `pending` state,
345                 // it would have been impossible to append to such text later in the old raw text buffer,
346                 // and allowing it causes bugs when .cfg files lack \n at EOF (see: https://gitlab.com/xonotic/darkplaces/-/issues/378).
347                 Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.next, cmd_input_t, list)), text, false);
348                 List_Splice(&llist, &cbuf->start);
349         }
350
351         Cbuf_Unlock(cbuf);
352 }
353
354 /*
355 ============
356 Cbuf_Execute_Deferred --blub
357 ============
358 */
359 static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf)
360 {
361         cmd_input_t *current, *n;
362         vec_t eat;
363
364         if (host.realtime - cbuf->deferred_oldtime < 0 || host.realtime - cbuf->deferred_oldtime > 1800)
365                 cbuf->deferred_oldtime = host.realtime;
366         eat = host.realtime - cbuf->deferred_oldtime;
367         if (eat < 1/128)
368                 return;
369         cbuf->deferred_oldtime = host.realtime;
370
371         List_For_Each_Entry_Safe(current, n, &cbuf->deferred, cmd_input_t, list)
372         {
373                 current->delay -= eat;
374                 if(current->delay <= 0)
375                 {
376                         Cbuf_AddText(current->source, current->text); // parse deferred string and append its cmdstring(s)
377                         List_Entry(cbuf->start.prev, cmd_input_t, list)->pending = false; // faster than div0-stable's Cbuf_AddText(";\n");
378                         List_Move_Tail(&current->list, &cbuf->free); // make deferred string memory available for reuse
379                         cbuf->size -= current->length;
380                 }
381         }
382 }
383
384 /*
385 ============
386 Cbuf_Execute
387 ============
388 */
389 extern qbool prvm_runawaycheck;
390 static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias );
391 void Cbuf_Execute (cmd_buf_t *cbuf)
392 {
393         cmd_input_t *current;
394         char preprocessed[MAX_INPUTLINE];
395         char *firstchar;
396         unsigned int i = 0;
397
398         // LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes
399         cbuf->tokenizebufferpos = 0;
400
401         while (!List_Is_Empty(&cbuf->start))
402         {
403                 /*
404                  * Delete the text from the command buffer and move remaining
405                  * commands down. This is necessary because commands (exec, alias)
406                  * can insert data at the beginning of the text buffer
407                  */
408                 current = List_Entry(cbuf->start.next, cmd_input_t, list);
409
410                 /*
411                  * Assume we're rolling with the current command-line and
412                  * always set this false because alias expansion or cbuf insertion
413                  * without a newline may set this true, and cause weirdness.
414                  */
415                 current->pending = false;
416
417                 cbuf->size -= current->length;
418
419                 firstchar = current->text;
420                 while(*firstchar && ISWHITESPACE(*firstchar))
421                         ++firstchar;
422                 if((strncmp(firstchar, "alias", 5)   || !ISWHITESPACE(firstchar[5])) &&
423                    (strncmp(firstchar, "bind", 4)    || !ISWHITESPACE(firstchar[4])) &&
424                    (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7])))
425                 {
426                         if(Cmd_PreprocessString(current->source, current->text, preprocessed, sizeof(preprocessed), NULL ))
427                                 Cmd_ExecuteString(current->source, preprocessed, src_local, false);
428                 }
429                 else
430                 {
431                         Cmd_ExecuteString (current->source, current->text, src_local, false);
432                 }
433
434                 // Recycle memory so using WASD doesn't cause a malloc and free
435                 List_Move_Tail(&current->list, &cbuf->free);
436
437                 current = NULL;
438
439                 if (cbuf->wait)
440                 {
441                         /*
442                          * Skip out while text still remains in
443                          * buffer, leaving it for next frame
444                          */
445                         cbuf->wait = false;
446                         break;
447                 }
448
449                 if (++i == 1000000 && prvm_runawaycheck)
450                 {
451                         Con_Printf(CON_WARN "Cbuf_Execute: runaway loop counter hit limit of %d commands, clearing command buffers!\n", i);
452                         Cbuf_Clear(cbuf);
453                 }
454         }
455 }
456
457 /*
458 ===================
459 Cbuf_Frame_Input
460
461 Add them exactly as if they had been typed at the console
462 ===================
463 */
464 static void Cbuf_Frame_Input(void)
465 {
466         char *line;
467
468         while ((line = Sys_ConsoleInput()))
469                         Cbuf_AddText(cmd_local, line);
470 }
471
472 void Cbuf_Frame(cmd_buf_t *cbuf)
473 {
474         // check for commands typed to the host
475         Cbuf_Frame_Input();
476
477 //      R_TimeReport("preconsole");
478
479         // execute commands queued with the defer command
480         Cbuf_Execute_Deferred(cbuf);
481         if (cbuf->size)
482         {
483                 SV_LockThreadMutex();
484                 Cbuf_Execute(cbuf);
485                 SV_UnlockThreadMutex();
486         }
487
488 //      R_TimeReport("console");
489 }
490
491 void Cbuf_Clear(cmd_buf_t *cbuf)
492 {
493         while (!List_Is_Empty(&cbuf->start))
494                 List_Move_Tail(cbuf->start.next, &cbuf->free);
495         while (!List_Is_Empty(&cbuf->deferred))
496                 List_Move_Tail(cbuf->deferred.next, &cbuf->free);
497         cbuf->size = 0;
498 }
499
500 /*
501 ==============================================================================
502
503                                                 SCRIPT COMMANDS
504
505 ==============================================================================
506 */
507
508 /*
509 ===============
510 Cmd_StuffCmds_f
511
512 Adds command line parameters as script statements
513 Commands lead with a +, and continue until a - or another +
514 quake +prog jctest.qp +cmd amlev1
515 quake -nosound +cmd amlev1
516 ===============
517 */
518 static void Cmd_StuffCmds_f (cmd_state_t *cmd)
519 {
520         int             i, j, l;
521         // this is for all commandline options combined (and is bounds checked)
522         char    build[MAX_INPUTLINE];
523
524         if (Cmd_Argc (cmd) != 1)
525         {
526                 Con_Print("stuffcmds : execute command line parameters\n");
527                 return;
528         }
529
530         // no reason to run the commandline arguments twice
531         if (host_stuffcmdsrun)
532                 return;
533
534         host_stuffcmdsrun = true;
535         build[0] = 0;
536         l = 0;
537         for (i = 0;i < sys.argc;i++)
538         {
539                 if (sys.argv[i] && sys.argv[i][0] == '+' && (sys.argv[i][1] < '0' || sys.argv[i][1] > '9') && l + strlen(sys.argv[i]) - 1 <= sizeof(build) - 1)
540                 {
541                         j = 1;
542                         while (sys.argv[i][j])
543                                 build[l++] = sys.argv[i][j++];
544                         i++;
545                         for (;i < sys.argc;i++)
546                         {
547                                 if (!sys.argv[i])
548                                         continue;
549                                 if ((sys.argv[i][0] == '+' || sys.argv[i][0] == '-') && (sys.argv[i][1] < '0' || sys.argv[i][1] > '9'))
550                                         break;
551                                 if (l + strlen(sys.argv[i]) + 4 > sizeof(build) - 1)
552                                         break;
553                                 build[l++] = ' ';
554                                 if (strchr(sys.argv[i], ' '))
555                                         build[l++] = '\"';
556                                 for (j = 0;sys.argv[i][j];j++)
557                                         build[l++] = sys.argv[i][j];
558                                 if (strchr(sys.argv[i], ' '))
559                                         build[l++] = '\"';
560                         }
561                         build[l++] = '\n';
562                         i--;
563                 }
564         }
565         // now terminate the combined string and prepend it to the command buffer
566         // we already reserved space for the terminator
567         build[l++] = 0;
568         Cbuf_InsertText (cmd, build);
569 }
570
571 static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
572 {
573         char *f;
574         size_t filenameLen = strlen(filename);
575         qbool isdefaultcfg =
576                 !strcmp(filename, "default.cfg") ||
577                 (filenameLen >= 12 && !strcmp(filename + filenameLen - 12, "/default.cfg"));
578
579         if (!strcmp(filename, "config.cfg"))
580         {
581                 filename = CONFIGFILENAME;
582                 if (Sys_CheckParm("-noconfig"))
583                         return; // don't execute config.cfg
584         }
585
586         f = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
587         if (!f)
588         {
589                 Con_Printf(CON_WARN "couldn't exec %s\n",filename);
590                 return;
591         }
592         Con_Printf("execing %s\n",filename);
593
594         // if executing default.cfg for the first time, lock the cvar defaults
595         // it may seem backwards to insert this text BEFORE the default.cfg
596         // but Cbuf_InsertText inserts before, so this actually ends up after it.
597         if (isdefaultcfg)
598                 Cbuf_InsertText(cmd, "\ncvar_lockdefaults\n");
599
600         Cbuf_InsertText (cmd, f);
601         Mem_Free(f);
602
603         if (isdefaultcfg)
604         {
605                 // special defaults for specific games go here, these execute before default.cfg
606                 // Nehahra pushable crates malfunction in some levels if this is on
607                 // Nehahra NPC AI is confused by blowupfallenzombies
608                 switch(gamemode)
609                 {
610                 case GAME_NORMAL:
611                         Cbuf_InsertText(cmd, "\n"
612 "sv_gameplayfix_blowupfallenzombies 0\n"
613 "sv_gameplayfix_findradiusdistancetobox 0\n"
614 "sv_gameplayfix_grenadebouncedownslopes 0\n"
615 "sv_gameplayfix_slidemoveprojectiles 0\n"
616 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
617 "sv_gameplayfix_setmodelrealbox 0\n"
618 "sv_gameplayfix_droptofloorstartsolid 0\n"
619 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
620 "sv_gameplayfix_noairborncorpse 0\n"
621 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
622 "sv_gameplayfix_easierwaterjump 0\n"
623 "sv_gameplayfix_delayprojectiles 0\n"
624 "sv_gameplayfix_multiplethinksperframe 0\n"
625 "sv_gameplayfix_fixedcheckwatertransition 0\n"
626 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
627 "sv_gameplayfix_swiminbmodels 0\n"
628 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
629 "sys_ticrate 0.01388889\n"
630 "r_shadow_gloss 1\n"
631 "r_shadow_bumpscale_basetexture 0\n"
632 "csqc_polygons_defaultmaterial_nocullface 0\n"
633                                 );
634                         break;
635                 case GAME_NEHAHRA:
636                         Cbuf_InsertText(cmd, "\n"
637 "sv_gameplayfix_blowupfallenzombies 0\n"
638 "sv_gameplayfix_findradiusdistancetobox 0\n"
639 "sv_gameplayfix_grenadebouncedownslopes 0\n"
640 "sv_gameplayfix_slidemoveprojectiles 0\n"
641 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
642 "sv_gameplayfix_setmodelrealbox 0\n"
643 "sv_gameplayfix_droptofloorstartsolid 0\n"
644 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
645 "sv_gameplayfix_noairborncorpse 0\n"
646 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
647 "sv_gameplayfix_easierwaterjump 0\n"
648 "sv_gameplayfix_delayprojectiles 0\n"
649 "sv_gameplayfix_multiplethinksperframe 0\n"
650 "sv_gameplayfix_fixedcheckwatertransition 0\n"
651 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
652 "sv_gameplayfix_swiminbmodels 0\n"
653 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
654 "sys_ticrate 0.01388889\n"
655 "r_shadow_gloss 1\n"
656 "r_shadow_bumpscale_basetexture 0\n"
657 "csqc_polygons_defaultmaterial_nocullface 0\n"
658                                 );
659                         break;
660                 // hipnotic mission pack has issues in their 'friendly monster' ai, which seem to attempt to attack themselves for some reason when findradius() returns non-solid entities.
661                 // hipnotic mission pack has issues with bobbing water entities 'jittering' between different heights on alternate frames at the default 0.0138889 ticrate, 0.02 avoids this issue
662                 // hipnotic mission pack has issues in their proximity mine sticking code, which causes them to bounce off.
663                 case GAME_HIPNOTIC:
664                 case GAME_QUOTH:
665                         Cbuf_InsertText(cmd, "\n"
666 "sv_gameplayfix_blowupfallenzombies 0\n"
667 "sv_gameplayfix_findradiusdistancetobox 0\n"
668 "sv_gameplayfix_grenadebouncedownslopes 0\n"
669 "sv_gameplayfix_slidemoveprojectiles 0\n"
670 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
671 "sv_gameplayfix_setmodelrealbox 0\n"
672 "sv_gameplayfix_droptofloorstartsolid 0\n"
673 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
674 "sv_gameplayfix_noairborncorpse 0\n"
675 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
676 "sv_gameplayfix_easierwaterjump 0\n"
677 "sv_gameplayfix_delayprojectiles 0\n"
678 "sv_gameplayfix_multiplethinksperframe 0\n"
679 "sv_gameplayfix_fixedcheckwatertransition 0\n"
680 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
681 "sv_gameplayfix_swiminbmodels 0\n"
682 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
683 "sys_ticrate 0.02\n"
684 "r_shadow_gloss 1\n"
685 "r_shadow_bumpscale_basetexture 0\n"
686 "csqc_polygons_defaultmaterial_nocullface 0\n"
687                                 );
688                         break;
689                 // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
690                 case GAME_ROGUE:
691                         Cbuf_InsertText(cmd, "\n"
692 "sv_gameplayfix_blowupfallenzombies 0\n"
693 "sv_gameplayfix_findradiusdistancetobox 0\n"
694 "sv_gameplayfix_grenadebouncedownslopes 0\n"
695 "sv_gameplayfix_slidemoveprojectiles 0\n"
696 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
697 "sv_gameplayfix_setmodelrealbox 0\n"
698 "sv_gameplayfix_droptofloorstartsolid 0\n"
699 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
700 "sv_gameplayfix_noairborncorpse 0\n"
701 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
702 "sv_gameplayfix_easierwaterjump 0\n"
703 "sv_gameplayfix_delayprojectiles 0\n"
704 "sv_gameplayfix_multiplethinksperframe 0\n"
705 "sv_gameplayfix_fixedcheckwatertransition 0\n"
706 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
707 "sv_gameplayfix_swiminbmodels 0\n"
708 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
709 "sys_ticrate 0.01388889\n"
710 "r_shadow_gloss 1\n"
711 "r_shadow_bumpscale_basetexture 0\n"
712 "csqc_polygons_defaultmaterial_nocullface 0\n"
713                                 );
714                         break;
715                 case GAME_TENEBRAE:
716                         Cbuf_InsertText(cmd, "\n"
717 "sv_gameplayfix_blowupfallenzombies 0\n"
718 "sv_gameplayfix_findradiusdistancetobox 0\n"
719 "sv_gameplayfix_grenadebouncedownslopes 0\n"
720 "sv_gameplayfix_slidemoveprojectiles 0\n"
721 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
722 "sv_gameplayfix_setmodelrealbox 0\n"
723 "sv_gameplayfix_droptofloorstartsolid 0\n"
724 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
725 "sv_gameplayfix_noairborncorpse 0\n"
726 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
727 "sv_gameplayfix_easierwaterjump 0\n"
728 "sv_gameplayfix_delayprojectiles 0\n"
729 "sv_gameplayfix_multiplethinksperframe 0\n"
730 "sv_gameplayfix_fixedcheckwatertransition 0\n"
731 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
732 "sv_gameplayfix_swiminbmodels 0\n"
733 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
734 "sys_ticrate 0.01388889\n"
735 "r_shadow_gloss 2\n"
736 "r_shadow_bumpscale_basetexture 4\n"
737 "csqc_polygons_defaultmaterial_nocullface 0\n"
738                                 );
739                         break;
740                 case GAME_NEXUIZ:
741                         Cbuf_InsertText(cmd, "\n"
742 "sv_gameplayfix_blowupfallenzombies 1\n"
743 "sv_gameplayfix_findradiusdistancetobox 1\n"
744 "sv_gameplayfix_grenadebouncedownslopes 1\n"
745 "sv_gameplayfix_slidemoveprojectiles 1\n"
746 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
747 "sv_gameplayfix_setmodelrealbox 1\n"
748 "sv_gameplayfix_droptofloorstartsolid 1\n"
749 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
750 "sv_gameplayfix_noairborncorpse 1\n"
751 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
752 "sv_gameplayfix_easierwaterjump 1\n"
753 "sv_gameplayfix_delayprojectiles 1\n"
754 "sv_gameplayfix_multiplethinksperframe 1\n"
755 "sv_gameplayfix_fixedcheckwatertransition 1\n"
756 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
757 "sv_gameplayfix_swiminbmodels 1\n"
758 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
759 "sys_ticrate 0.01388889\n"
760 "sv_gameplayfix_q2airaccelerate 1\n"
761 "sv_gameplayfix_stepmultipletimes 1\n"
762 "csqc_polygons_defaultmaterial_nocullface 1\n"
763 "con_chatsound_team_mask 13\n"
764                                 );
765                         break;
766                 case GAME_XONOTIC:
767                 case GAME_VORETOURNAMENT:
768                         // compatibility for versions prior to 2020-05-25, this can be overridden in newer versions to get the default behavior and be consistent with FTEQW engine
769                         Cbuf_InsertText(cmd, "\n"
770 "csqc_polygons_defaultmaterial_nocullface 1\n"
771 "con_chatsound_team_mask 13\n"
772 "sv_qcstats 1\n"
773 "mod_q1bsp_zero_hullsize_cutoff 8.03125\n"
774                                 );
775                         break;
776                 // Steel Storm: Burning Retribution csqc misinterprets CSQC_InputEvent if type is a value other than 0 or 1
777                 case GAME_STEELSTORM:
778                         Cbuf_InsertText(cmd, "\n"
779 "sv_gameplayfix_blowupfallenzombies 1\n"
780 "sv_gameplayfix_findradiusdistancetobox 1\n"
781 "sv_gameplayfix_grenadebouncedownslopes 1\n"
782 "sv_gameplayfix_slidemoveprojectiles 1\n"
783 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
784 "sv_gameplayfix_setmodelrealbox 1\n"
785 "sv_gameplayfix_droptofloorstartsolid 1\n"
786 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
787 "sv_gameplayfix_noairborncorpse 1\n"
788 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
789 "sv_gameplayfix_easierwaterjump 1\n"
790 "sv_gameplayfix_delayprojectiles 1\n"
791 "sv_gameplayfix_multiplethinksperframe 1\n"
792 "sv_gameplayfix_fixedcheckwatertransition 1\n"
793 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
794 "sv_gameplayfix_swiminbmodels 1\n"
795 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
796 "sys_ticrate 0.01388889\n"
797 "cl_csqc_generatemousemoveevents 0\n"
798 "csqc_polygons_defaultmaterial_nocullface 1\n"
799                                 );
800                         break;
801                 default:
802                         Cbuf_InsertText(cmd, "\n"
803 "sv_gameplayfix_blowupfallenzombies 1\n"
804 "sv_gameplayfix_findradiusdistancetobox 1\n"
805 "sv_gameplayfix_grenadebouncedownslopes 1\n"
806 "sv_gameplayfix_slidemoveprojectiles 1\n"
807 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
808 "sv_gameplayfix_setmodelrealbox 1\n"
809 "sv_gameplayfix_droptofloorstartsolid 1\n"
810 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
811 "sv_gameplayfix_noairborncorpse 1\n"
812 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
813 "sv_gameplayfix_easierwaterjump 1\n"
814 "sv_gameplayfix_delayprojectiles 1\n"
815 "sv_gameplayfix_multiplethinksperframe 1\n"
816 "sv_gameplayfix_fixedcheckwatertransition 1\n"
817 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
818 "sv_gameplayfix_swiminbmodels 1\n"
819 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
820 "sys_ticrate 0.01388889\n"
821 "csqc_polygons_defaultmaterial_nocullface 0\n"
822                                 );
823                         break;
824                 }
825         }
826 }
827
828 /*
829 ===============
830 Cmd_Exec_f
831 ===============
832 */
833 static void Cmd_Exec_f (cmd_state_t *cmd)
834 {
835         fssearch_t *s;
836         int i;
837
838         if (Cmd_Argc(cmd) != 2)
839         {
840                 Con_Print("exec <filename> : execute a script file\n");
841                 return;
842         }
843
844         s = FS_Search(Cmd_Argv(cmd, 1), true, true, NULL);
845         if(!s || !s->numfilenames)
846         {
847                 Con_Printf(CON_WARN "couldn't exec %s\n",Cmd_Argv(cmd, 1));
848                 return;
849         }
850
851         for(i = 0; i < s->numfilenames; ++i)
852                 Cmd_Exec(cmd, s->filenames[i]);
853
854         FS_FreeSearch(s);
855 }
856
857
858 /*
859 ===============
860 Cmd_Echo_f
861
862 Just prints the rest of the line to the console
863 ===============
864 */
865 static void Cmd_Echo_f (cmd_state_t *cmd)
866 {
867         int             i;
868
869         for (i=1 ; i<Cmd_Argc(cmd) ; i++)
870                 Con_Printf("%s ",Cmd_Argv(cmd, i));
871         Con_Print("\n");
872 }
873
874 // DRESK - 5/14/06
875 // Support Doom3-style Toggle Console Command
876 /*
877 ===============
878 Cmd_Toggle_f
879
880 Toggles a specified console variable amongst the values specified (default is 0 and 1)
881 ===============
882 */
883 static void Cmd_Toggle_f(cmd_state_t *cmd)
884 {
885         // Acquire Number of Arguments
886         int nNumArgs = Cmd_Argc(cmd);
887
888         if(nNumArgs == 1)
889                 // No Arguments Specified; Print Usage
890                 Con_Print("Toggle Console Variable - Usage\n  toggle <variable> - toggles between 0 and 1\n  toggle <variable> <value> - toggles between 0 and <value>\n  toggle <variable> [string 1] [string 2]...[string n] - cycles through all strings\n");
891         else
892         { // Correct Arguments Specified
893                 // Acquire Potential CVar
894                 cvar_t* cvCVar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 1), cmd->cvars_flagsmask);
895
896                 if(cvCVar != NULL)
897                 { // Valid CVar
898                         if(nNumArgs == 2)
899                         { // Default Usage
900                                 if(cvCVar->integer)
901                                         Cvar_SetValueQuick(cvCVar, 0);
902                                 else
903                                         Cvar_SetValueQuick(cvCVar, 1);
904                         }
905                         else
906                         if(nNumArgs == 3)
907                         { // 0 and Specified Usage
908                                 if(cvCVar->integer == atoi(Cmd_Argv(cmd, 2) ) )
909                                         // CVar is Specified Value; // Reset to 0
910                                         Cvar_SetValueQuick(cvCVar, 0);
911                                 else
912                                 if(cvCVar->integer == 0)
913                                         // CVar is 0; Specify Value
914                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
915                                 else
916                                         // CVar does not match; Reset to 0
917                                         Cvar_SetValueQuick(cvCVar, 0);
918                         }
919                         else
920                         { // Variable Values Specified
921                                 int nCnt;
922                                 int bFound = 0;
923
924                                 for(nCnt = 2; nCnt < nNumArgs; nCnt++)
925                                 { // Cycle through Values
926                                         if( strcmp(cvCVar->string, Cmd_Argv(cmd, nCnt) ) == 0)
927                                         { // Current Value Located; Increment to Next
928                                                 if( (nCnt + 1) == nNumArgs)
929                                                         // Max Value Reached; Reset
930                                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
931                                                 else
932                                                         // Next Value
933                                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, nCnt + 1) );
934
935                                                 // End Loop
936                                                 nCnt = nNumArgs;
937                                                 // Assign Found
938                                                 bFound = 1;
939                                         }
940                                 }
941                                 if(!bFound)
942                                         // Value not Found; Reset to Original
943                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
944                         }
945
946                 }
947                 else
948                 { // Invalid CVar
949                         Con_Printf(CON_WARN "ERROR : CVar '%s' not found\n", Cmd_Argv(cmd, 1) );
950                 }
951         }
952 }
953
954 /*
955 ===============
956 Cmd_Alias_f
957
958 Creates a new command that executes a command string (possibly ; seperated)
959 ===============
960 */
961 static void Cmd_Alias_f (cmd_state_t *cmd)
962 {
963         cmd_alias_t     *a;
964         char            line[MAX_INPUTLINE];
965         int                     i, c;
966         const char              *s;
967         size_t          alloclen;
968
969         if (Cmd_Argc(cmd) == 1)
970         {
971                 Con_Print("Current alias commands:\n");
972                 for (a = cmd->userdefined->alias ; a ; a=a->next)
973                         Con_Printf("%s : %s", a->name, a->value);
974                 return;
975         }
976
977         s = Cmd_Argv(cmd, 1);
978         if (strlen(s) >= MAX_ALIAS_NAME)
979         {
980                 Con_Print(CON_WARN "Alias name is too long\n");
981                 return;
982         }
983
984         // if the alias already exists, reuse it
985         for (a = cmd->userdefined->alias ; a ; a=a->next)
986         {
987                 if (!strcmp(s, a->name))
988                 {
989                         Z_Free (a->value);
990                         break;
991                 }
992         }
993
994         if (!a)
995         {
996                 cmd_alias_t *prev, *current;
997
998                 a = (cmd_alias_t *)Z_Malloc (sizeof(cmd_alias_t));
999                 strlcpy (a->name, s, sizeof (a->name));
1000                 // insert it at the right alphanumeric position
1001                 for( prev = NULL, current = cmd->userdefined->alias ; current && strcmp( current->name, a->name ) < 0 ; prev = current, current = current->next )
1002                         ;
1003                 if( prev ) {
1004                         prev->next = a;
1005                 } else {
1006                         cmd->userdefined->alias = a;
1007                 }
1008                 a->next = current;
1009         }
1010
1011
1012 // copy the rest of the command line
1013         line[0] = 0;            // start out with a null string
1014         c = Cmd_Argc(cmd);
1015         for (i=2 ; i < c ; i++)
1016         {
1017                 if (i != 2)
1018                         strlcat (line, " ", sizeof (line));
1019                 strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
1020         }
1021         strlcat (line, "\n", sizeof (line));
1022
1023         alloclen = strlen (line) + 1;
1024         if(alloclen >= 2)
1025                 line[alloclen - 2] = '\n'; // to make sure a newline is appended even if too long
1026         a->value = (char *)Z_Malloc (alloclen);
1027         memcpy (a->value, line, alloclen);
1028 }
1029
1030 /*
1031 ===============
1032 Cmd_UnAlias_f
1033
1034 Remove existing aliases.
1035 ===============
1036 */
1037 static void Cmd_UnAlias_f (cmd_state_t *cmd)
1038 {
1039         cmd_alias_t     *a, *p;
1040         int i;
1041         const char *s;
1042
1043         if(Cmd_Argc(cmd) == 1)
1044         {
1045                 Con_Print("unalias: Usage: unalias alias1 [alias2 ...]\n");
1046                 return;
1047         }
1048
1049         for(i = 1; i < Cmd_Argc(cmd); ++i)
1050         {
1051                 s = Cmd_Argv(cmd, i);
1052                 p = NULL;
1053                 for(a = cmd->userdefined->alias; a; p = a, a = a->next)
1054                 {
1055                         if(!strcmp(s, a->name))
1056                         {
1057                                 if (a->initstate) // we can not remove init aliases
1058                                         continue;
1059                                 if(a == cmd->userdefined->alias)
1060                                         cmd->userdefined->alias = a->next;
1061                                 if(p)
1062                                         p->next = a->next;
1063                                 Z_Free(a->value);
1064                                 Z_Free(a);
1065                                 break;
1066                         }
1067                 }
1068                 if(!a)
1069                         Con_Printf("unalias: %s alias not found\n", s);
1070         }
1071 }
1072
1073 /*
1074 =============================================================================
1075
1076                                         COMMAND EXECUTION
1077
1078 =============================================================================
1079 */
1080
1081 static const char *Cmd_GetDirectCvarValue(cmd_state_t *cmd, const char *varname, cmd_alias_t *alias, qbool *is_multiple)
1082 {
1083         cvar_t *cvar;
1084         long argno;
1085         char *endptr;
1086         static char vabuf[1024]; // cmd_mutex
1087
1088         if(is_multiple)
1089                 *is_multiple = false;
1090
1091         if(!varname || !*varname)
1092                 return NULL;
1093
1094         if(alias)
1095         {
1096                 if(!strcmp(varname, "*"))
1097                 {
1098                         if(is_multiple)
1099                                 *is_multiple = true;
1100                         return Cmd_Args(cmd);
1101                 }
1102                 else if(!strcmp(varname, "#"))
1103                 {
1104                         return va(vabuf, sizeof(vabuf), "%d", Cmd_Argc(cmd));
1105                 }
1106                 else if(varname[strlen(varname) - 1] == '-')
1107                 {
1108                         argno = strtol(varname, &endptr, 10);
1109                         if(endptr == varname + strlen(varname) - 1)
1110                         {
1111                                 // whole string is a number, apart from the -
1112                                 const char *p = Cmd_Args(cmd);
1113                                 for(; argno > 1; --argno)
1114                                         if(!COM_ParseToken_Console(&p))
1115                                                 break;
1116                                 if(p)
1117                                 {
1118                                         if(is_multiple)
1119                                                 *is_multiple = true;
1120
1121                                         // kill pre-argument whitespace
1122                                         for (;*p && ISWHITESPACE(*p);p++)
1123                                                 ;
1124
1125                                         return p;
1126                                 }
1127                         }
1128                 }
1129                 else
1130                 {
1131                         argno = strtol(varname, &endptr, 10);
1132                         if(*endptr == 0)
1133                         {
1134                                 // whole string is a number
1135                                 // NOTE: we already made sure we don't have an empty cvar name!
1136                                 if(argno >= 0 && argno < Cmd_Argc(cmd))
1137                                         return Cmd_Argv(cmd, argno);
1138                         }
1139                 }
1140         }
1141
1142         if((cvar = Cvar_FindVar(cmd->cvars, varname, cmd->cvars_flagsmask)) && !(cvar->flags & CF_PRIVATE))
1143                 return cvar->string;
1144
1145         return NULL;
1146 }
1147
1148 qbool Cmd_QuoteString(char *out, size_t outlen, const char *in, const char *quoteset, qbool putquotes)
1149 {
1150         qbool quote_quot = !!strchr(quoteset, '"');
1151         qbool quote_backslash = !!strchr(quoteset, '\\');
1152         qbool quote_dollar = !!strchr(quoteset, '$');
1153
1154         if(putquotes)
1155         {
1156                 if(outlen <= 2)
1157                 {
1158                         *out++ = 0;
1159                         return false;
1160                 }
1161                 *out++ = '"'; --outlen;
1162                 --outlen;
1163         }
1164
1165         while(*in)
1166         {
1167                 if(*in == '"' && quote_quot)
1168                 {
1169                         if(outlen <= 2)
1170                                 goto fail;
1171                         *out++ = '\\'; --outlen;
1172                         *out++ = '"'; --outlen;
1173                 }
1174                 else if(*in == '\\' && quote_backslash)
1175                 {
1176                         if(outlen <= 2)
1177                                 goto fail;
1178                         *out++ = '\\'; --outlen;
1179                         *out++ = '\\'; --outlen;
1180                 }
1181                 else if(*in == '$' && quote_dollar)
1182                 {
1183                         if(outlen <= 2)
1184                                 goto fail;
1185                         *out++ = '$'; --outlen;
1186                         *out++ = '$'; --outlen;
1187                 }
1188                 else
1189                 {
1190                         if(outlen <= 1)
1191                                 goto fail;
1192                         *out++ = *in; --outlen;
1193                 }
1194                 ++in;
1195         }
1196         if(putquotes)
1197                 *out++ = '"';
1198         *out++ = 0;
1199         return true;
1200 fail:
1201         if(putquotes)
1202                 *out++ = '"';
1203         *out++ = 0;
1204         return false;
1205 }
1206
1207 static const char *Cmd_GetCvarValue(cmd_state_t *cmd, const char *var, size_t varlen, cmd_alias_t *alias)
1208 {
1209         static char varname[MAX_INPUTLINE]; // cmd_mutex
1210         static char varval[MAX_INPUTLINE]; // cmd_mutex
1211         const char *varstr = NULL;
1212         char *varfunc;
1213         qbool required = false;
1214         qbool optional = false;
1215         static char asis[] = "asis"; // just to suppress const char warnings
1216
1217         if(varlen >= MAX_INPUTLINE)
1218                 varlen = MAX_INPUTLINE - 1;
1219         memcpy(varname, var, varlen);
1220         varname[varlen] = 0;
1221         varfunc = strchr(varname, ' ');
1222
1223         if(varfunc)
1224         {
1225                 *varfunc = 0;
1226                 ++varfunc;
1227         }
1228
1229         if(*var == 0)
1230         {
1231                 // empty cvar name?
1232                 if(alias)
1233                         Con_Printf(CON_WARN "Warning: Could not expand $ in alias %s\n", alias->name);
1234                 else
1235                         Con_Printf(CON_WARN "Warning: Could not expand $\n");
1236                 return "$";
1237         }
1238
1239         if(varfunc)
1240         {
1241                 char *p;
1242                 // ? means optional
1243                 while((p = strchr(varfunc, '?')))
1244                 {
1245                         optional = true;
1246                         memmove(p, p+1, strlen(p)); // with final NUL
1247                 }
1248                 // ! means required
1249                 while((p = strchr(varfunc, '!')))
1250                 {
1251                         required = true;
1252                         memmove(p, p+1, strlen(p)); // with final NUL
1253                 }
1254                 // kill spaces
1255                 while((p = strchr(varfunc, ' ')))
1256                 {
1257                         memmove(p, p+1, strlen(p)); // with final NUL
1258                 }
1259                 // if no function is left, NULL it
1260                 if(!*varfunc)
1261                         varfunc = NULL;
1262         }
1263
1264         if(varname[0] == '$')
1265                 varstr = Cmd_GetDirectCvarValue(cmd, Cmd_GetDirectCvarValue(cmd, varname + 1, alias, NULL), alias, NULL);
1266         else
1267         {
1268                 qbool is_multiple = false;
1269                 // Exception: $* and $n- don't use the quoted form by default
1270                 varstr = Cmd_GetDirectCvarValue(cmd, varname, alias, &is_multiple);
1271                 if(is_multiple)
1272                         if(!varfunc)
1273                                 varfunc = asis;
1274         }
1275
1276         if(!varstr)
1277         {
1278                 if(required)
1279                 {
1280                         if(alias)
1281                                 Con_Printf(CON_ERROR "Error: Could not expand $%s in alias %s\n", varname, alias->name);
1282                         else
1283                                 Con_Printf(CON_ERROR "Error: Could not expand $%s\n", varname);
1284                         return NULL;
1285                 }
1286                 else if(optional)
1287                 {
1288                         return "";
1289                 }
1290                 else
1291                 {
1292                         if(alias)
1293                                 Con_Printf(CON_WARN "Warning: Could not expand $%s in alias %s\n", varname, alias->name);
1294                         else
1295                                 Con_Printf(CON_WARN "Warning: Could not expand $%s\n", varname);
1296                         dpsnprintf(varval, sizeof(varval), "$%s", varname);
1297                         return varval;
1298                 }
1299         }
1300
1301         if(!varfunc || !strcmp(varfunc, "q")) // note: quoted form is default, use "asis" to override!
1302         {
1303                 // quote it so it can be used inside double quotes
1304                 // we just need to replace " by \", and of course, double backslashes
1305                 Cmd_QuoteString(varval, sizeof(varval), varstr, "\"\\", false);
1306                 return varval;
1307         }
1308         else if(!strcmp(varfunc, "asis"))
1309         {
1310                 return varstr;
1311         }
1312         else
1313                 Con_Printf("Unknown variable function %s\n", varfunc);
1314
1315         return varstr;
1316 }
1317
1318 /*
1319 Cmd_PreprocessString
1320
1321 Preprocesses strings and replaces $*, $param#, $cvar accordingly. Also strips comments.
1322 */
1323 static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias ) {
1324         const char *in;
1325         size_t eat, varlen;
1326         unsigned outlen;
1327         const char *val;
1328
1329         // don't crash if there's no room in the outtext buffer
1330         if( maxoutlen == 0 ) {
1331                 return false;
1332         }
1333         maxoutlen--; // because of \0
1334
1335         in = intext;
1336         outlen = 0;
1337
1338         while( *in && outlen < maxoutlen ) {
1339                 if( *in == '$' ) {
1340                         // this is some kind of expansion, see what comes after the $
1341                         in++;
1342
1343                         // The console does the following preprocessing:
1344                         //
1345                         // - $$ is transformed to a single dollar sign.
1346                         // - $var or ${var} are expanded to the contents of the named cvar,
1347                         //   with quotation marks and backslashes quoted so it can safely
1348                         //   be used inside quotation marks (and it should always be used
1349                         //   that way)
1350                         // - ${var asis} inserts the cvar value as is, without doing this
1351                         //   quoting
1352                         // - ${var ?} silently expands to the empty string if
1353                         //   $var does not exist
1354                         // - ${var !} fails expansion and executes nothing if
1355                         //   $var does not exist
1356                         // - prefix the cvar name with a dollar sign to do indirection;
1357                         //   for example, if $x has the value timelimit, ${$x} will return
1358                         //   the value of $timelimit
1359                         // - when expanding an alias, the special variable name $* refers
1360                         //   to all alias parameters, and a number refers to that numbered
1361                         //   alias parameter, where the name of the alias is $0, the first
1362                         //   parameter is $1 and so on; as a special case, $* inserts all
1363                         //   parameters, without extra quoting, so one can use $* to just
1364                         //   pass all parameters around. All parameters starting from $n
1365                         //   can be referred to as $n- (so $* is equivalent to $1-).
1366                         // - ${* q} and ${n- q} force quoting anyway
1367                         //
1368                         // Note: when expanding an alias, cvar expansion is done in the SAME step
1369                         // as alias expansion so that alias parameters or cvar values containing
1370                         // dollar signs have no unwanted bad side effects. However, this needs to
1371                         // be accounted for when writing complex aliases. For example,
1372                         //   alias foo "set x NEW; echo $x"
1373                         // actually expands to
1374                         //   "set x NEW; echo OLD"
1375                         // and will print OLD! To work around this, use a second alias:
1376                         //   alias foo "set x NEW; foo2"
1377                         //   alias foo2 "echo $x"
1378                         //
1379                         // Also note: lines starting with alias are exempt from cvar expansion.
1380                         // If you want cvar expansion, write "alias" instead:
1381                         //
1382                         //   set x 1
1383                         //   alias foo "echo $x"
1384                         //   "alias" bar "echo $x"
1385                         //   set x 2
1386                         //
1387                         // foo will print 2, because the variable $x will be expanded when the alias
1388                         // gets expanded. bar will print 1, because the variable $x was expanded
1389                         // at definition time. foo can be equivalently defined as
1390                         //
1391                         //   "alias" foo "echo $$x"
1392                         //
1393                         // because at definition time, $$ will get replaced to a single $.
1394
1395                         if( *in == '$' ) {
1396                                 val = "$";
1397                                 eat = 1;
1398                         } else if(*in == '{') {
1399                                 varlen = strcspn(in + 1, "}");
1400                                 if(in[varlen + 1] == '}')
1401                                 {
1402                                         val = Cmd_GetCvarValue(cmd, in + 1, varlen, alias);
1403                                         if(!val)
1404                                                 return false;
1405                                         eat = varlen + 2;
1406                                 }
1407                                 else
1408                                 {
1409                                         // ran out of data?
1410                                         val = NULL;
1411                                         eat = varlen + 1;
1412                                 }
1413                         } else {
1414                                 varlen = strspn(in, "#*0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-");
1415                                 val = Cmd_GetCvarValue(cmd, in, varlen, alias);
1416                                 if(!val)
1417                                         return false;
1418                                 eat = varlen;
1419                         }
1420                         if(val)
1421                         {
1422                                 // insert the cvar value
1423                                 while(*val && outlen < maxoutlen)
1424                                         outtext[outlen++] = *val++;
1425                                 in += eat;
1426                         }
1427                         else
1428                         {
1429                                 // copy the unexpanded text
1430                                 outtext[outlen++] = '$';
1431                                 while(eat && outlen < maxoutlen)
1432                                 {
1433                                         outtext[outlen++] = *in++;
1434                                         --eat;
1435                                 }
1436                         }
1437                 }
1438                 else 
1439                         outtext[outlen++] = *in++;
1440         }
1441         outtext[outlen] = 0;
1442         return true;
1443 }
1444
1445 /*
1446 ============
1447 Cmd_ExecuteAlias
1448
1449 Called for aliases and fills in the alias into the cbuffer
1450 ============
1451 */
1452 static void Cmd_ExecuteAlias (cmd_state_t *cmd, cmd_alias_t *alias)
1453 {
1454         static char buffer[ MAX_INPUTLINE ]; // cmd_mutex
1455         static char buffer2[ MAX_INPUTLINE ]; // cmd_mutex
1456         qbool ret = Cmd_PreprocessString( cmd, alias->value, buffer, sizeof(buffer) - 2, alias );
1457         if(!ret)
1458                 return;
1459         // insert at start of command buffer, so that aliases execute in order
1460         // (fixes bug introduced by Black on 20050705)
1461
1462         // Note: Cbuf_PreprocessString will be called on this string AGAIN! So we
1463         // have to make sure that no second variable expansion takes place, otherwise
1464         // alias parameters containing dollar signs can have bad effects.
1465         Cmd_QuoteString(buffer2, sizeof(buffer2), buffer, "$", false);
1466         Cbuf_InsertText(cmd, buffer2);
1467 }
1468
1469 /*
1470 ========
1471 Cmd_List
1472
1473         CmdList Added by EvilTypeGuy eviltypeguy@qeradiant.com
1474         Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
1475
1476 ========
1477 */
1478 static void Cmd_List_f (cmd_state_t *cmd)
1479 {
1480         cmd_function_t *func;
1481         const char *partial;
1482         size_t len;
1483         int count;
1484         qbool ispattern;
1485
1486         if (Cmd_Argc(cmd) > 1)
1487         {
1488                 partial = Cmd_Argv(cmd, 1);
1489                 len = strlen(partial);
1490                 ispattern = (strchr(partial, '*') || strchr(partial, '?'));
1491         }
1492         else
1493         {
1494                 partial = NULL;
1495                 len = 0;
1496                 ispattern = false;
1497         }
1498
1499         count = 0;
1500         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1501         {
1502                 if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
1503                         continue;
1504                 Con_Printf("%s : %s\n", func->name, func->description);
1505                 count++;
1506         }
1507         for (func = cmd->engine_functions; func; func = func->next)
1508         {
1509                 if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
1510                         continue;
1511                 Con_Printf("%s : %s\n", func->name, func->description);
1512                 count++;
1513         }
1514
1515         if (len)
1516         {
1517                 if(ispattern)
1518                         Con_Printf("%i Command%s matching \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
1519                 else
1520                         Con_Printf("%i Command%s beginning with \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
1521         }
1522         else
1523                 Con_Printf("%i Command%s\n\n", count, (count > 1) ? "s" : "");
1524 }
1525
1526 static void Cmd_Apropos_f(cmd_state_t *cmd)
1527 {
1528         cmd_function_t *func;
1529         cvar_t *cvar;
1530         cmd_alias_t *alias;
1531         const char *partial;
1532         int count;
1533         qbool ispattern;
1534         char vabuf[1024];
1535
1536         if (Cmd_Argc(cmd) > 1)
1537                 partial = Cmd_Args(cmd);
1538         else
1539         {
1540                 Con_Printf("usage: %s <string>\n",Cmd_Argv(cmd, 0));
1541                 return;
1542         }
1543
1544         ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
1545         if(!ispattern)
1546                 partial = va(vabuf, sizeof(vabuf), "*%s*", partial);
1547
1548         count = 0;
1549         for (cvar = cmd->cvars->vars; cvar; cvar = cvar->next)
1550         {
1551                 if (matchpattern_with_separator(cvar->name, partial, true, "", false) ||
1552                     matchpattern_with_separator(cvar->description, partial, true, "", false))
1553                 {
1554                         Con_Printf ("cvar ");
1555                         Cvar_PrintHelp(cvar, cvar->name, true);
1556                         count++;
1557                 }
1558                 for (char **cvar_alias = cvar->aliases; cvar_alias && *cvar_alias; cvar_alias++)
1559                 {
1560                         if (matchpattern_with_separator(*cvar_alias, partial, true, "", false))
1561                         {
1562                                 Con_Printf ("cvar ");
1563                                 Cvar_PrintHelp(cvar, *cvar_alias, true);
1564                                 count++;
1565                         }
1566                 }
1567         }
1568         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1569         {
1570                 if (!matchpattern_with_separator(func->name, partial, true, "", false))
1571                         if (!matchpattern_with_separator(func->description, partial, true, "", false))
1572                                 continue;
1573                 Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
1574                 count++;
1575         }
1576         for (func = cmd->engine_functions; func; func = func->next)
1577         {
1578                 if (!matchpattern_with_separator(func->name, partial, true, "", false))
1579                 if (!matchpattern_with_separator(func->description, partial, true, "", false))
1580                         continue;
1581                 Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
1582                 count++;
1583         }
1584         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
1585         {
1586                 // procede here a bit differently as an alias value always got a final \n
1587                 if (!matchpattern_with_separator(alias->name, partial, true, "", false))
1588                 if (!matchpattern_with_separator(alias->value, partial, true, "\n", false)) // when \n is as separator wildcards don't match it
1589                         continue;
1590                 Con_Printf("alias ^5%s^7: %s", alias->name, alias->value); // do not print an extra \n
1591                 count++;
1592         }
1593         Con_Printf("%i result%s\n\n", count, (count > 1) ? "s" : "");
1594 }
1595
1596 static cmd_state_t *Cmd_AddInterpreter(cmd_buf_t *cbuf, cvar_state_t *cvars, int cvars_flagsmask, int cmds_flagsmask, cmd_userdefined_t *userdefined)
1597 {
1598         cmd_state_t *cmd = (cmd_state_t *)Mem_Alloc(tempmempool, sizeof(cmd_state_t));
1599         
1600         cmd->mempool = Mem_AllocPool("commands", 0, NULL);
1601         // space for commands and script files
1602         cmd->cbuf = cbuf;
1603         cmd->null_string = "";
1604
1605         cmd->cvars = cvars;
1606         cmd->cvars_flagsmask = cvars_flagsmask;
1607         cmd->cmd_flags = cmds_flagsmask;
1608         cmd->userdefined = userdefined;
1609
1610         return cmd;
1611 }
1612
1613 /*
1614 ============
1615 Cmd_Init
1616 ============
1617 */
1618 void Cmd_Init(void)
1619 {
1620         cmd_buf_t *cbuf;
1621         cbuf_mempool = Mem_AllocPool("Command buffer", 0, NULL);
1622         cbuf = (cmd_buf_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_buf_t));
1623         cbuf->maxsize = CMDBUFSIZE;
1624         cbuf->lock = Thread_CreateMutex();
1625         cbuf->wait = false;
1626         host.cbuf = cbuf;
1627
1628         cbuf->start.prev = cbuf->start.next = &(cbuf->start);
1629         cbuf->deferred.prev = cbuf->deferred.next = &(cbuf->deferred);
1630         cbuf->free.prev = cbuf->free.next = &(cbuf->free);
1631
1632         // FIXME: Get rid of cmd_iter_all eventually. This is just a hack to reduce the amount of work to make the interpreters dynamic.
1633         cmd_iter_all = (cmd_iter_t *)Mem_Alloc(tempmempool, sizeof(cmd_iter_t) * 3);
1634
1635         // local console
1636         cmd_iter_all[0].cmd = cmd_local = Cmd_AddInterpreter(cbuf, &cvars_all, CF_CLIENT | CF_SERVER, CF_CLIENT | CF_CLIENT_FROM_SERVER | CF_SERVER_FROM_CLIENT, &cmd_userdefined_all);
1637         cmd_local->Handle = Cmd_CL_Callback;
1638         cmd_local->NotFound = NULL;
1639
1640         // server commands received from clients have no reason to access cvars, cvar expansion seems perilous.
1641         cmd_iter_all[1].cmd = cmd_serverfromclient = Cmd_AddInterpreter(cbuf, &cvars_null, 0, CF_SERVER_FROM_CLIENT | CF_USERINFO, &cmd_userdefined_null);
1642         cmd_serverfromclient->Handle = Cmd_SV_Callback;
1643         cmd_serverfromclient->NotFound = Cmd_SV_NotFound;
1644
1645         cmd_iter_all[2].cmd = NULL;
1646 //
1647 // register our commands
1648 //
1649         // client-only commands
1650         Cmd_AddCommand(CF_SHARED, "wait", Cmd_Wait_f, "make script execution wait for next rendered frame");
1651         Cmd_AddCommand(CF_CLIENT, "cprint", Cmd_Centerprint_f, "print something at the screen center");
1652
1653         // maintenance commands used for upkeep of cvars and saved configs
1654         Cmd_AddCommand(CF_SHARED, "stuffcmds", Cmd_StuffCmds_f, "execute commandline parameters (must be present in quake.rc script)");
1655         Cmd_AddCommand(CF_SHARED, "cvar_lockdefaults", Cvar_LockDefaults_f, "stores the current values of all cvars into their default values, only used once during startup after parsing default.cfg");
1656         Cmd_AddCommand(CF_SHARED, "cvar_resettodefaults_all", Cvar_ResetToDefaults_All_f, "sets all cvars to their locked default values");
1657         Cmd_AddCommand(CF_SHARED, "cvar_resettodefaults_nosaveonly", Cvar_ResetToDefaults_NoSaveOnly_f, "sets all non-saved cvars to their locked default values (variables that will not be saved to config.cfg)");
1658         Cmd_AddCommand(CF_SHARED, "cvar_resettodefaults_saveonly", Cvar_ResetToDefaults_SaveOnly_f, "sets all saved cvars to their locked default values (variables that will be saved to config.cfg)");
1659
1660         // general console commands used in multiple environments
1661         Cmd_AddCommand(CF_SHARED, "exec", Cmd_Exec_f, "execute a script file");
1662         Cmd_AddCommand(CF_SHARED, "echo",Cmd_Echo_f, "print a message to the console (useful in scripts)");
1663         Cmd_AddCommand(CF_SHARED, "alias",Cmd_Alias_f, "create a script function (parameters are passed in as $X (being X a number), $* for all parameters, $X- for all parameters starting from $X). Without arguments show the list of all alias");
1664         Cmd_AddCommand(CF_SHARED, "unalias",Cmd_UnAlias_f, "remove an alias");
1665         Cmd_AddCommand(CF_SHARED, "set", Cvar_Set_f, "create or change the value of a console variable");
1666         Cmd_AddCommand(CF_SHARED, "seta", Cvar_SetA_f, "create or change the value of a console variable that will be saved to config.cfg");
1667         Cmd_AddCommand(CF_SHARED, "unset", Cvar_Del_f, "delete a cvar (does not work for static ones like _cl_name, or read-only ones)");
1668
1669 #ifdef FILLALLCVARSWITHRUBBISH
1670         Cmd_AddCommand(CF_SHARED, "fillallcvarswithrubbish", Cvar_FillAll_f, "fill all cvars with a specified number of characters to provoke buffer overruns");
1671 #endif /* FILLALLCVARSWITHRUBBISH */
1672
1673         // 2000-01-09 CmdList, CvarList commands By Matthias "Maddes" Buecher
1674         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1675         Cmd_AddCommand(CF_SHARED, "cmdlist", Cmd_List_f, "lists all console commands beginning with the specified prefix or matching the specified wildcard pattern");
1676         Cmd_AddCommand(CF_SHARED, "cvarlist", Cvar_List_f, "lists all console variables beginning with the specified prefix or matching the specified wildcard pattern");
1677         Cmd_AddCommand(CF_SHARED, "apropos", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description");
1678         Cmd_AddCommand(CF_SHARED, "find", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description");
1679
1680         Cmd_AddCommand(CF_SHARED, "defer", Cmd_Defer_f, "execute a command in the future");
1681
1682         // DRESK - 5/14/06
1683         // Support Doom3-style Toggle Command
1684         Cmd_AddCommand(CF_SHARED | CF_CLIENT_FROM_SERVER, "toggle", Cmd_Toggle_f, "toggles a console variable's values (use for more info)");
1685 }
1686
1687 /*
1688 ============
1689 Cmd_Shutdown
1690 ============
1691 */
1692 void Cmd_Shutdown(void)
1693 {
1694         cmd_iter_t *cmd_iter;
1695         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
1696         {
1697                 cmd_state_t *cmd = cmd_iter->cmd;
1698
1699                 if (cmd->cbuf->lock)
1700                 {
1701                         // we usually have this locked when we get here from Host_Quit_f
1702                         Cbuf_Unlock(cmd->cbuf);
1703                 }
1704
1705                 Mem_FreePool(&cmd->mempool);
1706         }
1707 }
1708
1709 /*
1710 ============
1711 Cmd_Argc
1712 ============
1713 */
1714 inline int Cmd_Argc (cmd_state_t *cmd)
1715 {
1716         return cmd->argc;
1717 }
1718
1719 /*
1720 ============
1721 Cmd_Argv
1722 ============
1723 */
1724 inline const char *Cmd_Argv(cmd_state_t *cmd, int arg)
1725 {
1726         if (arg >= cmd->argc )
1727                 return cmd->null_string;
1728         return cmd->argv[arg];
1729 }
1730
1731 /*
1732 ============
1733 Cmd_Args
1734 ============
1735 */
1736 inline const char *Cmd_Args (cmd_state_t *cmd)
1737 {
1738         return cmd->args;
1739 }
1740
1741 /*
1742 ============
1743 Cmd_TokenizeString
1744
1745 Parses the given string into command line tokens.
1746 ============
1747 */
1748 // AK: This function should only be called from ExcuteString because the current design is a bit of an hack
1749 static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text)
1750 {
1751         int l;
1752
1753         cmd->argc = 0;
1754         cmd->args = NULL;
1755         cmd->cmdline = NULL;
1756
1757         while (1)
1758         {
1759                 // skip whitespace up to a /n
1760                 while (*text && ISWHITESPACE(*text) && *text != '\r' && *text != '\n')
1761                         text++;
1762
1763                 // line endings:
1764                 // UNIX: \n
1765                 // Mac: \r
1766                 // Windows: \r\n
1767                 if (*text == '\n' || *text == '\r')
1768                 {
1769                         // a newline separates commands in the buffer
1770                         if (*text == '\r' && text[1] == '\n')
1771                                 text++;
1772                         text++;
1773                         break;
1774                 }
1775
1776                 if (!*text)
1777                         return;
1778
1779                 if(!cmd->argc)
1780                         cmd->cmdline = text;
1781                 if (cmd->argc == 1)
1782                         cmd->args = text;
1783
1784                 if (!COM_ParseToken_Console(&text))
1785                         return;
1786
1787                 if (cmd->argc < MAX_ARGS)
1788                 {
1789                         l = (int)strlen(com_token) + 1;
1790                         if (cmd->cbuf->tokenizebufferpos + l > CMD_TOKENIZELENGTH)
1791                         {
1792                                 Con_Printf(CON_WARN "Cmd_TokenizeString: ran out of %i character buffer space for command arguments\n", CMD_TOKENIZELENGTH);
1793                                 break;
1794                         }
1795                         memcpy (cmd->cbuf->tokenizebuffer + cmd->cbuf->tokenizebufferpos, com_token, l);
1796                         cmd->argv[cmd->argc] = cmd->cbuf->tokenizebuffer + cmd->cbuf->tokenizebufferpos;
1797                         cmd->cbuf->tokenizebufferpos += l;
1798                         cmd->argc++;
1799                 }
1800         }
1801 }
1802
1803
1804 /*
1805 ============
1806 Cmd_AddCommand
1807 ============
1808 */
1809 void Cmd_AddCommand(int flags, const char *cmd_name, xcommand_t function, const char *description)
1810 {
1811         cmd_function_t *func;
1812         cmd_function_t *prev, *current;
1813         cmd_state_t *cmd;
1814         int i;
1815
1816         for (i = 0; i < 2; i++)
1817         {
1818                 cmd = cmd_iter_all[i].cmd;
1819                 if (flags & cmd->cmd_flags)
1820                 {
1821                         // fail if the command is a variable name
1822                         if (Cvar_FindVar(cmd->cvars, cmd_name, ~0))
1823                         {
1824                                 Con_Printf(CON_WARN "Cmd_AddCommand: %s already defined as a var\n", cmd_name);
1825                                 return;
1826                         }
1827
1828                         if (function)
1829                         {
1830                                 // fail if the command already exists in this interpreter
1831                                 for (func = cmd->engine_functions; func; func = func->next)
1832                                 {
1833                                         if (!strcmp(cmd_name, func->name))
1834                                         {
1835                                                 Con_Printf(CON_WARN "Cmd_AddCommand: %s already defined\n", cmd_name);
1836                                                 continue;
1837                                         }
1838                                 }
1839
1840                                 func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
1841                                 func->flags = flags;
1842                                 func->name = cmd_name;
1843                                 func->function = function;
1844                                 func->description = description;
1845                                 func->next = cmd->engine_functions;
1846
1847                                 // insert it at the right alphanumeric position
1848                                 for (prev = NULL, current = cmd->engine_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
1849                                         ;
1850                                 if (prev) {
1851                                         prev->next = func;
1852                                 }
1853                                 else {
1854                                         cmd->engine_functions = func;
1855                                 }
1856                                 func->next = current;
1857                         }
1858                         else
1859                         {
1860                                 // mark qcfunc if the function already exists in the qc_functions list
1861                                 for (func = cmd->userdefined->qc_functions; func; func = func->next)
1862                                 {
1863                                         if (!strcmp(cmd_name, func->name))
1864                                         {
1865                                                 func->qcfunc = true; //[515]: csqc
1866                                                 continue;
1867                                         }
1868                                 }
1869
1870                                 func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
1871                                 func->flags = flags;
1872                                 func->name = cmd_name;
1873                                 func->function = function;
1874                                 func->description = description;
1875                                 func->qcfunc = true; //[515]: csqc
1876                                 func->next = cmd->userdefined->qc_functions;
1877
1878                                 // bones_was_here: if this QC command overrides an engine command, store its pointer
1879                                 // to avoid doing this search at invocation if QC declines to handle this command.
1880                                 for (cmd_function_t *f = cmd->engine_functions; f; f = f->next)
1881                                 {
1882                                         if (!strcmp(cmd_name, f->name))
1883                                         {
1884                                                 Con_DPrintf("Adding QC override of engine command %s\n", cmd_name);
1885                                                 func->overridden = f;
1886                                                 break;
1887                                         }
1888                                 }
1889
1890                                 // insert it at the right alphanumeric position
1891                                 for (prev = NULL, current = cmd->userdefined->qc_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
1892                                         ;
1893                                 if (prev) {
1894                                         prev->next = func;
1895                                 }
1896                                 else {
1897                                         cmd->userdefined->qc_functions = func;
1898                                 }
1899                                 func->next = current;
1900                         }
1901                 }
1902         }
1903 }
1904
1905 /*
1906 ============
1907 Cmd_Exists
1908 ============
1909 */
1910 qbool Cmd_Exists (cmd_state_t *cmd, const char *cmd_name)
1911 {
1912         cmd_function_t  *func;
1913
1914         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1915                 if (!strcmp(cmd_name, func->name))
1916                         return true;
1917
1918         for (func=cmd->engine_functions ; func ; func=func->next)
1919                 if (!strcmp (cmd_name,func->name))
1920                         return true;
1921
1922         return false;
1923 }
1924
1925
1926 /*
1927 ============
1928 Cmd_CompleteCommand
1929 ============
1930 */
1931 const char *Cmd_CompleteCommand (cmd_state_t *cmd, const char *partial)
1932 {
1933         cmd_function_t *func;
1934         size_t len;
1935
1936         len = strlen(partial);
1937
1938         if (!len)
1939                 return NULL;
1940
1941 // check functions
1942         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1943                 if (!strncasecmp(partial, func->name, len))
1944                         return func->name;
1945
1946         for (func = cmd->engine_functions; func; func = func->next)
1947                 if (!strncasecmp(partial, func->name, len))
1948                         return func->name;
1949
1950         return NULL;
1951 }
1952
1953 /*
1954         Cmd_CompleteCountPossible
1955
1956         New function for tab-completion system
1957         Added by EvilTypeGuy
1958         Thanks to Fett erich@heintz.com
1959         Thanks to taniwha
1960
1961 */
1962 int Cmd_CompleteCountPossible (cmd_state_t *cmd, const char *partial)
1963 {
1964         cmd_function_t *func;
1965         size_t len;
1966         int h;
1967
1968         h = 0;
1969         len = strlen(partial);
1970
1971         if (!len)
1972                 return 0;
1973
1974         // Loop through the command list and count all partial matches
1975         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1976                 if (!strncasecmp(partial, func->name, len))
1977                         h++;
1978
1979         for (func = cmd->engine_functions; func; func = func->next)
1980                 if (!strncasecmp(partial, func->name, len))
1981                         h++;
1982
1983         return h;
1984 }
1985
1986 /*
1987         Cmd_CompleteBuildList
1988
1989         New function for tab-completion system
1990         Added by EvilTypeGuy
1991         Thanks to Fett erich@heintz.com
1992         Thanks to taniwha
1993
1994 */
1995 const char **Cmd_CompleteBuildList (cmd_state_t *cmd, const char *partial)
1996 {
1997         cmd_function_t *func;
1998         size_t len = 0;
1999         size_t bpos = 0;
2000         size_t sizeofbuf = (Cmd_CompleteCountPossible (cmd, partial) + 1) * sizeof (const char *);
2001         const char **buf;
2002
2003         len = strlen(partial);
2004         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
2005         // Loop through the functions lists and print all matches
2006         for (func = cmd->userdefined->qc_functions; func; func = func->next)
2007                 if (!strncasecmp(partial, func->name, len))
2008                         buf[bpos++] = func->name;
2009         for (func = cmd->engine_functions; func; func = func->next)
2010                 if (!strncasecmp(partial, func->name, len))
2011                         buf[bpos++] = func->name;
2012
2013         buf[bpos] = NULL;
2014         return buf;
2015 }
2016
2017 // written by LadyHavoc
2018 void Cmd_CompleteCommandPrint (cmd_state_t *cmd, const char *partial)
2019 {
2020         cmd_function_t *func;
2021         size_t len = strlen(partial);
2022         // Loop through the command list and print all matches
2023         for (func = cmd->userdefined->qc_functions; func; func = func->next)
2024                 if (!strncasecmp(partial, func->name, len))
2025                         Con_Printf("^2%s^7: %s\n", func->name, func->description);
2026         for (func = cmd->engine_functions; func; func = func->next)
2027                 if (!strncasecmp(partial, func->name, len))
2028                         Con_Printf("^2%s^7: %s\n", func->name, func->description);
2029 }
2030
2031 /*
2032         Cmd_CompleteAlias
2033
2034         New function for tab-completion system
2035         Added by EvilTypeGuy
2036         Thanks to Fett erich@heintz.com
2037         Thanks to taniwha
2038
2039 */
2040 const char *Cmd_CompleteAlias (cmd_state_t *cmd, const char *partial)
2041 {
2042         cmd_alias_t *alias;
2043         size_t len;
2044
2045         len = strlen(partial);
2046
2047         if (!len)
2048                 return NULL;
2049
2050         // Check functions
2051         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2052                 if (!strncasecmp(partial, alias->name, len))
2053                         return alias->name;
2054
2055         return NULL;
2056 }
2057
2058 // written by LadyHavoc
2059 void Cmd_CompleteAliasPrint (cmd_state_t *cmd, const char *partial)
2060 {
2061         cmd_alias_t *alias;
2062         size_t len = strlen(partial);
2063         // Loop through the alias list and print all matches
2064         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2065                 if (!strncasecmp(partial, alias->name, len))
2066                         Con_Printf("^5%s^7: %s", alias->name, alias->value);
2067 }
2068
2069
2070 /*
2071         Cmd_CompleteAliasCountPossible
2072
2073         New function for tab-completion system
2074         Added by EvilTypeGuy
2075         Thanks to Fett erich@heintz.com
2076         Thanks to taniwha
2077
2078 */
2079 int Cmd_CompleteAliasCountPossible (cmd_state_t *cmd, const char *partial)
2080 {
2081         cmd_alias_t     *alias;
2082         size_t          len;
2083         int                     h;
2084
2085         h = 0;
2086
2087         len = strlen(partial);
2088
2089         if (!len)
2090                 return 0;
2091
2092         // Loop through the command list and count all partial matches
2093         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2094                 if (!strncasecmp(partial, alias->name, len))
2095                         h++;
2096
2097         return h;
2098 }
2099
2100 /*
2101         Cmd_CompleteAliasBuildList
2102
2103         New function for tab-completion system
2104         Added by EvilTypeGuy
2105         Thanks to Fett erich@heintz.com
2106         Thanks to taniwha
2107
2108 */
2109 const char **Cmd_CompleteAliasBuildList (cmd_state_t *cmd, const char *partial)
2110 {
2111         cmd_alias_t *alias;
2112         size_t len = 0;
2113         size_t bpos = 0;
2114         size_t sizeofbuf = (Cmd_CompleteAliasCountPossible (cmd, partial) + 1) * sizeof (const char *);
2115         const char **buf;
2116
2117         len = strlen(partial);
2118         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
2119         // Loop through the alias list and print all matches
2120         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2121                 if (!strncasecmp(partial, alias->name, len))
2122                         buf[bpos++] = alias->name;
2123
2124         buf[bpos] = NULL;
2125         return buf;
2126 }
2127
2128 // TODO: Make this more generic?
2129 void Cmd_ClearCSQCCommands (cmd_state_t *cmd)
2130 {
2131         cmd_function_t *func;
2132         cmd_function_t **next = &cmd->userdefined->qc_functions;
2133         
2134         while(*next)
2135         {
2136                 func = *next;
2137                 *next = func->next;
2138                 Z_Free(func);
2139         }
2140 }
2141
2142 extern cvar_t sv_cheats;
2143
2144 /*
2145  * Cloudwalk FIXME: This idea sounded great in my head but...
2146  * How do we handle commands that can be received by the client,
2147  * but which the server can also execute locally?
2148  * 
2149  * If we create a callback where the engine will forward to server
2150  * but try to execute the command locally if it's dedicated,
2151  * we're back to intermixing client and server code which I'm
2152  * trying to avoid. There's no other way I can think of to
2153  * implement that behavior that doesn't involve an #ifdef, or
2154  * making a mess of hooks.
2155  */
2156 qbool Cmd_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
2157 {
2158         if (func->function)
2159                 func->function(cmd);
2160         else
2161                 Con_Printf(CON_WARN "Command \"%s\" can not be executed\n", Cmd_Argv(cmd, 0));
2162         return true;
2163 }
2164
2165 qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
2166 {
2167         // TODO: Assign these functions to QC commands directly?
2168         if(func->qcfunc)
2169         {
2170                 if(((func->flags & CF_CLIENT) && CL_VM_ConsoleCommand(text)) ||
2171                    ((func->flags & CF_SERVER) && SV_VM_ConsoleCommand(text)))
2172                         return true;
2173
2174                 if (func->overridden) // If this QC command overrides an engine command,
2175                         func = func->overridden; // fall back to that command.
2176         }
2177         if (func->flags & CF_SERVER_FROM_CLIENT)
2178         {
2179                 if(host_isclient.integer)
2180                 {
2181                         CL_ForwardToServer_f(cmd);
2182                         return true;
2183                 }
2184                 else if(!(func->flags & CF_SERVER))
2185                 {
2186                         Con_Printf(CON_WARN "Cannot execute client commands from a dedicated server console.\n");
2187                         return true;
2188                 }
2189         }
2190         return Cmd_Callback(cmd, func, text, src);
2191 }
2192
2193 qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
2194 {
2195         if(func->qcfunc && (func->flags & CF_SERVER))
2196                 return SV_VM_ConsoleCommand(text);
2197         else if (src == src_client)
2198         {
2199                 if((func->flags & CF_CHEAT) && !sv_cheats.integer)
2200                         SV_ClientPrintf(CON_WARN "No cheats allowed. The server must have sv_cheats set to 1\n");
2201                 else
2202                         func->function(cmd);
2203                 return true;
2204         }
2205         return false;
2206 }
2207
2208 qbool Cmd_SV_NotFound(cmd_state_t *cmd, cmd_function_t *func, const char *text, cmd_source_t src)
2209 {
2210         if (cmd->source == src_client)
2211         {
2212                 Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text);
2213                 return true;
2214         }
2215         return false;
2216 }
2217 /*
2218 ============
2219 Cmd_ExecuteString
2220
2221 A complete command line has been parsed, so try to execute it
2222 FIXME: lookupnoadd the token to speed search?
2223 ============
2224 */
2225 void Cmd_ExecuteString (cmd_state_t *cmd, const char *text, cmd_source_t src, qbool lockmutex)
2226 {
2227         int oldpos;
2228         cmd_function_t *func;
2229         cmd_alias_t *a;
2230         if (lockmutex)
2231                 Cbuf_Lock(cmd->cbuf);
2232         oldpos = cmd->cbuf->tokenizebufferpos;
2233         cmd->source = src;
2234
2235         Cmd_TokenizeString (cmd, text);
2236
2237 // execute the command line
2238         if (!Cmd_Argc(cmd))
2239                 goto done; // no tokens
2240
2241 // check functions
2242         for (func = cmd->userdefined->qc_functions; func; func = func->next)
2243         {
2244                 if (!strcasecmp(cmd->argv[0], func->name))
2245                 {
2246                         if(cmd->Handle(cmd, func, text, src))
2247                                 goto done;
2248                 }
2249         }
2250
2251         for (func = cmd->engine_functions; func; func=func->next)
2252         {
2253                 if (!strcasecmp (cmd->argv[0], func->name))
2254                 {
2255                         if(cmd->Handle(cmd, func, text, src))
2256                                 goto done;
2257                 }
2258         }
2259
2260         // if it's a client command and no command was found, say so.
2261         if(cmd->NotFound)
2262         {
2263                 if(cmd->NotFound(cmd, func, text, src))
2264                         goto done;
2265         }
2266
2267 // check alias
2268         for (a=cmd->userdefined->alias ; a ; a=a->next)
2269         {
2270                 if (!strcasecmp (cmd->argv[0], a->name))
2271                 {
2272                         Cmd_ExecuteAlias(cmd, a);
2273                         goto done;
2274                 }
2275         }
2276
2277 // check cvars
2278         if (!Cvar_Command(cmd) && host.framecount > 0)
2279                 Con_Printf(CON_WARN "Unknown command \"%s\"\n", Cmd_Argv(cmd, 0));
2280 done:
2281         cmd->cbuf->tokenizebufferpos = oldpos;
2282         if (lockmutex)
2283                 Cbuf_Unlock(cmd->cbuf);
2284 }
2285
2286 /*
2287 ================
2288 Cmd_CheckParm
2289
2290 Returns the position (1 to argc-1) in the command's argument list
2291 where the given parameter apears, or 0 if not present
2292 ================
2293 */
2294
2295 int Cmd_CheckParm (cmd_state_t *cmd, const char *parm)
2296 {
2297         int i;
2298
2299         if (!parm)
2300         {
2301                 Con_Printf(CON_WARN "Cmd_CheckParm: NULL");
2302                 return 0;
2303         }
2304
2305         for (i = 1; i < Cmd_Argc (cmd); i++)
2306                 if (!strcasecmp (parm, Cmd_Argv(cmd, i)))
2307                         return i;
2308
2309         return 0;
2310 }
2311
2312
2313
2314 void Cmd_SaveInitState(void)
2315 {
2316         cmd_iter_t *cmd_iter;
2317         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
2318         {
2319                 cmd_state_t *cmd = cmd_iter->cmd;
2320                 cmd_function_t *f;
2321                 cmd_alias_t *a;
2322                 for (f = cmd->userdefined->qc_functions; f; f = f->next)
2323                         f->initstate = true;
2324                 for (f = cmd->engine_functions; f; f = f->next)
2325                         f->initstate = true;
2326                 for (a = cmd->userdefined->alias; a; a = a->next)
2327                 {
2328                         a->initstate = true;
2329                         a->initialvalue = Mem_strdup(zonemempool, a->value);
2330                 }
2331         }
2332         Cvar_SaveInitState(&cvars_all);
2333 }
2334
2335 void Cmd_RestoreInitState(void)
2336 {
2337         cmd_iter_t *cmd_iter;
2338         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
2339         {
2340                 cmd_state_t *cmd = cmd_iter->cmd;
2341                 cmd_function_t *f, **fp;
2342                 cmd_alias_t *a, **ap;
2343                 for (fp = &cmd->userdefined->qc_functions; (f = *fp);)
2344                 {
2345                         if (f->initstate)
2346                                 fp = &f->next;
2347                         else
2348                         {
2349                                 // destroy this command, it didn't exist at init
2350                                 Con_DPrintf("Cmd_RestoreInitState: Destroying command %s\n", f->name);
2351                                 *fp = f->next;
2352                                 Z_Free(f);
2353                         }
2354                 }
2355                 for (fp = &cmd->engine_functions; (f = *fp);)
2356                 {
2357                         if (f->initstate)
2358                                 fp = &f->next;
2359                         else
2360                         {
2361                                 // destroy this command, it didn't exist at init
2362                                 Con_DPrintf("Cmd_RestoreInitState: Destroying command %s\n", f->name);
2363                                 *fp = f->next;
2364                                 Z_Free(f);
2365                         }
2366                 }
2367                 for (ap = &cmd->userdefined->alias; (a = *ap);)
2368                 {
2369                         if (a->initstate)
2370                         {
2371                                 // restore this alias, it existed at init
2372                                 if (strcmp(a->value ? a->value : "", a->initialvalue ? a->initialvalue : ""))
2373                                 {
2374                                         Con_DPrintf("Cmd_RestoreInitState: Restoring alias %s\n", a->name);
2375                                         if (a->value)
2376                                                 Z_Free(a->value);
2377                                         a->value = Mem_strdup(zonemempool, a->initialvalue);
2378                                 }
2379                                 ap = &a->next;
2380                         }
2381                         else
2382                         {
2383                                 // free this alias, it didn't exist at init...
2384                                 Con_DPrintf("Cmd_RestoreInitState: Destroying alias %s\n", a->name);
2385                                 *ap = a->next;
2386                                 if (a->value)
2387                                         Z_Free(a->value);
2388                                 Z_Free(a);
2389                         }
2390                 }
2391         }
2392         Cvar_RestoreInitState(&cvars_all);
2393 }
2394
2395 void Cmd_NoOperation_f(cmd_state_t *cmd)
2396 {
2397 }