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