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