PageRenderTime 71ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/cmd.c

https://gitlab.com/xonotic/darkplaces
C | 2235 lines | 1949 code | 94 blank | 192 comment | 206 complexity | 81156f971c9bfbfe64bbde4defff54cf MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. // cmd.c -- Quake script command processing module
  16. #include "quakedef.h"
  17. #include "thread.h"
  18. typedef struct cmdalias_s
  19. {
  20. struct cmdalias_s *next;
  21. char name[MAX_ALIAS_NAME];
  22. char *value;
  23. qboolean initstate; // indicates this command existed at init
  24. char *initialvalue; // backup copy of value at init
  25. } cmdalias_t;
  26. static cmdalias_t *cmd_alias;
  27. static qboolean cmd_wait;
  28. static mempool_t *cmd_mempool;
  29. static char cmd_tokenizebuffer[CMD_TOKENIZELENGTH];
  30. static int cmd_tokenizebufferpos = 0;
  31. //=============================================================================
  32. /*
  33. ============
  34. Cmd_Wait_f
  35. Causes execution of the remainder of the command buffer to be delayed until
  36. next frame. This allows commands like:
  37. bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
  38. ============
  39. */
  40. static void Cmd_Wait_f (void)
  41. {
  42. cmd_wait = true;
  43. }
  44. typedef struct cmddeferred_s
  45. {
  46. struct cmddeferred_s *next;
  47. char *value;
  48. double delay;
  49. } cmddeferred_t;
  50. static cmddeferred_t *cmd_deferred_list = NULL;
  51. /*
  52. ============
  53. Cmd_Defer_f
  54. Cause a command to be executed after a delay.
  55. ============
  56. */
  57. static void Cmd_Defer_f (void)
  58. {
  59. if(Cmd_Argc() == 1)
  60. {
  61. cmddeferred_t *next = cmd_deferred_list;
  62. if(!next)
  63. Con_Printf("No commands are pending.\n");
  64. while(next)
  65. {
  66. Con_Printf("-> In %9.2f: %s\n", next->delay, next->value);
  67. next = next->next;
  68. }
  69. } else if(Cmd_Argc() == 2 && !strcasecmp("clear", Cmd_Argv(1)))
  70. {
  71. while(cmd_deferred_list)
  72. {
  73. cmddeferred_t *cmd = cmd_deferred_list;
  74. cmd_deferred_list = cmd->next;
  75. Mem_Free(cmd->value);
  76. Mem_Free(cmd);
  77. }
  78. } else if(Cmd_Argc() == 3)
  79. {
  80. const char *value = Cmd_Argv(2);
  81. cmddeferred_t *defcmd = (cmddeferred_t*)Mem_Alloc(tempmempool, sizeof(*defcmd));
  82. size_t len = strlen(value);
  83. defcmd->delay = atof(Cmd_Argv(1));
  84. defcmd->value = (char*)Mem_Alloc(tempmempool, len+1);
  85. memcpy(defcmd->value, value, len+1);
  86. defcmd->next = NULL;
  87. if(cmd_deferred_list)
  88. {
  89. cmddeferred_t *next = cmd_deferred_list;
  90. while(next->next)
  91. next = next->next;
  92. next->next = defcmd;
  93. } else
  94. cmd_deferred_list = defcmd;
  95. /* Stupid me... this changes the order... so commands with the same delay go blub :S
  96. defcmd->next = cmd_deferred_list;
  97. cmd_deferred_list = defcmd;*/
  98. } else {
  99. Con_Printf("usage: defer <seconds> <command>\n"
  100. " defer clear\n");
  101. return;
  102. }
  103. }
  104. /*
  105. ============
  106. Cmd_Centerprint_f
  107. Print something to the center of the screen using SCR_Centerprint
  108. ============
  109. */
  110. static void Cmd_Centerprint_f (void)
  111. {
  112. char msg[MAX_INPUTLINE];
  113. unsigned int i, c, p;
  114. c = Cmd_Argc();
  115. if(c >= 2)
  116. {
  117. strlcpy(msg, Cmd_Argv(1), sizeof(msg));
  118. for(i = 2; i < c; ++i)
  119. {
  120. strlcat(msg, " ", sizeof(msg));
  121. strlcat(msg, Cmd_Argv(i), sizeof(msg));
  122. }
  123. c = (unsigned int)strlen(msg);
  124. for(p = 0, i = 0; i < c; ++i)
  125. {
  126. if(msg[i] == '\\')
  127. {
  128. if(msg[i+1] == 'n')
  129. msg[p++] = '\n';
  130. else if(msg[i+1] == '\\')
  131. msg[p++] = '\\';
  132. else {
  133. msg[p++] = '\\';
  134. msg[p++] = msg[i+1];
  135. }
  136. ++i;
  137. } else {
  138. msg[p++] = msg[i];
  139. }
  140. }
  141. msg[p] = '\0';
  142. SCR_CenterPrint(msg);
  143. }
  144. }
  145. /*
  146. =============================================================================
  147. COMMAND BUFFER
  148. =============================================================================
  149. */
  150. static sizebuf_t cmd_text;
  151. static unsigned char cmd_text_buf[CMDBUFSIZE];
  152. void *cmd_text_mutex = NULL;
  153. /*
  154. ============
  155. Cbuf_AddText
  156. Adds command text at the end of the buffer
  157. ============
  158. */
  159. void Cbuf_AddText (const char *text)
  160. {
  161. int l;
  162. l = (int)strlen(text);
  163. Cbuf_LockThreadMutex();
  164. if (cmd_text.cursize + l >= cmd_text.maxsize)
  165. Con_Print("Cbuf_AddText: overflow\n");
  166. else
  167. SZ_Write(&cmd_text, (const unsigned char *)text, l);
  168. Cbuf_UnlockThreadMutex();
  169. }
  170. /*
  171. ============
  172. Cbuf_InsertText
  173. Adds command text immediately after the current command
  174. Adds a \n to the text
  175. FIXME: actually change the command buffer to do less copying
  176. ============
  177. */
  178. void Cbuf_InsertText (const char *text)
  179. {
  180. size_t l = strlen(text);
  181. Cbuf_LockThreadMutex();
  182. // we need to memmove the existing text and stuff this in before it...
  183. if (cmd_text.cursize + l >= (size_t)cmd_text.maxsize)
  184. Con_Print("Cbuf_InsertText: overflow\n");
  185. else
  186. {
  187. // we don't have a SZ_Prepend, so...
  188. memmove(cmd_text.data + l, cmd_text.data, cmd_text.cursize);
  189. cmd_text.cursize += (int)l;
  190. memcpy(cmd_text.data, text, l);
  191. }
  192. Cbuf_UnlockThreadMutex();
  193. }
  194. /*
  195. ============
  196. Cbuf_Execute_Deferred --blub
  197. ============
  198. */
  199. static void Cbuf_Execute_Deferred (void)
  200. {
  201. static double oldrealtime = 0;
  202. cmddeferred_t *cmd, *prev;
  203. double eat;
  204. if (realtime - oldrealtime < 0 || realtime - oldrealtime > 1800) oldrealtime = realtime;
  205. eat = realtime - oldrealtime;
  206. if (eat < (1.0 / 120.0))
  207. return;
  208. oldrealtime = realtime;
  209. prev = NULL;
  210. cmd = cmd_deferred_list;
  211. while(cmd)
  212. {
  213. cmd->delay -= eat;
  214. if(cmd->delay <= 0)
  215. {
  216. Cbuf_AddText(cmd->value);
  217. Cbuf_AddText(";\n");
  218. Mem_Free(cmd->value);
  219. if(prev) {
  220. prev->next = cmd->next;
  221. Mem_Free(cmd);
  222. cmd = prev->next;
  223. } else {
  224. cmd_deferred_list = cmd->next;
  225. Mem_Free(cmd);
  226. cmd = cmd_deferred_list;
  227. }
  228. continue;
  229. }
  230. prev = cmd;
  231. cmd = cmd->next;
  232. }
  233. }
  234. /*
  235. ============
  236. Cbuf_Execute
  237. ============
  238. */
  239. static qboolean Cmd_PreprocessString( const char *intext, char *outtext, unsigned maxoutlen, cmdalias_t *alias );
  240. void Cbuf_Execute (void)
  241. {
  242. int i;
  243. char *text;
  244. char line[MAX_INPUTLINE];
  245. char preprocessed[MAX_INPUTLINE];
  246. char *firstchar;
  247. qboolean quotes;
  248. char *comment;
  249. // LordHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes
  250. cmd_tokenizebufferpos = 0;
  251. while (cmd_text.cursize)
  252. {
  253. // find a \n or ; line break
  254. text = (char *)cmd_text.data;
  255. quotes = false;
  256. comment = NULL;
  257. for (i=0 ; i < cmd_text.cursize ; i++)
  258. {
  259. if(!comment)
  260. {
  261. if (text[i] == '"')
  262. quotes = !quotes;
  263. if(quotes)
  264. {
  265. // make sure i doesn't get > cursize which causes a negative
  266. // size in memmove, which is fatal --blub
  267. if (i < (cmd_text.cursize-1) && (text[i] == '\\' && (text[i+1] == '"' || text[i+1] == '\\')))
  268. i++;
  269. }
  270. else
  271. {
  272. if(text[i] == '/' && text[i + 1] == '/' && (i == 0 || ISWHITESPACE(text[i-1])))
  273. comment = &text[i];
  274. if(text[i] == ';')
  275. break; // don't break if inside a quoted string or comment
  276. }
  277. }
  278. if (text[i] == '\r' || text[i] == '\n')
  279. break;
  280. }
  281. // better than CRASHING on overlong input lines that may SOMEHOW enter the buffer
  282. if(i >= MAX_INPUTLINE)
  283. {
  284. Con_Printf("Warning: console input buffer had an overlong line. Ignored.\n");
  285. line[0] = 0;
  286. }
  287. else
  288. {
  289. memcpy (line, text, comment ? (comment - text) : i);
  290. line[comment ? (comment - text) : i] = 0;
  291. }
  292. // delete the text from the command buffer and move remaining commands down
  293. // this is necessary because commands (exec, alias) can insert data at the
  294. // beginning of the text buffer
  295. if (i == cmd_text.cursize)
  296. cmd_text.cursize = 0;
  297. else
  298. {
  299. i++;
  300. cmd_text.cursize -= i;
  301. memmove (cmd_text.data, text+i, cmd_text.cursize);
  302. }
  303. // execute the command line
  304. firstchar = line;
  305. while(*firstchar && ISWHITESPACE(*firstchar))
  306. ++firstchar;
  307. if(
  308. (strncmp(firstchar, "alias", 5) || !ISWHITESPACE(firstchar[5]))
  309. &&
  310. (strncmp(firstchar, "bind", 4) || !ISWHITESPACE(firstchar[4]))
  311. &&
  312. (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7]))
  313. )
  314. {
  315. if(Cmd_PreprocessString( line, preprocessed, sizeof(preprocessed), NULL ))
  316. Cmd_ExecuteString (preprocessed, src_command, false);
  317. }
  318. else
  319. {
  320. Cmd_ExecuteString (line, src_command, false);
  321. }
  322. if (cmd_wait)
  323. { // skip out while text still remains in buffer, leaving it
  324. // for next frame
  325. cmd_wait = false;
  326. break;
  327. }
  328. }
  329. }
  330. void Cbuf_Frame(void)
  331. {
  332. Cbuf_Execute_Deferred();
  333. if (cmd_text.cursize)
  334. {
  335. SV_LockThreadMutex();
  336. Cbuf_Execute();
  337. SV_UnlockThreadMutex();
  338. }
  339. }
  340. /*
  341. ==============================================================================
  342. SCRIPT COMMANDS
  343. ==============================================================================
  344. */
  345. /*
  346. ===============
  347. Cmd_StuffCmds_f
  348. Adds command line parameters as script statements
  349. Commands lead with a +, and continue until a - or another +
  350. quake +prog jctest.qp +cmd amlev1
  351. quake -nosound +cmd amlev1
  352. ===============
  353. */
  354. qboolean host_stuffcmdsrun = false;
  355. static void Cmd_StuffCmds_f (void)
  356. {
  357. int i, j, l;
  358. // this is for all commandline options combined (and is bounds checked)
  359. char build[MAX_INPUTLINE];
  360. if (Cmd_Argc () != 1)
  361. {
  362. Con_Print("stuffcmds : execute command line parameters\n");
  363. return;
  364. }
  365. // no reason to run the commandline arguments twice
  366. if (host_stuffcmdsrun)
  367. return;
  368. host_stuffcmdsrun = true;
  369. build[0] = 0;
  370. l = 0;
  371. for (i = 0;i < com_argc;i++)
  372. {
  373. if (com_argv[i] && com_argv[i][0] == '+' && (com_argv[i][1] < '0' || com_argv[i][1] > '9') && l + strlen(com_argv[i]) - 1 <= sizeof(build) - 1)
  374. {
  375. j = 1;
  376. while (com_argv[i][j])
  377. build[l++] = com_argv[i][j++];
  378. i++;
  379. for (;i < com_argc;i++)
  380. {
  381. if (!com_argv[i])
  382. continue;
  383. if ((com_argv[i][0] == '+' || com_argv[i][0] == '-') && (com_argv[i][1] < '0' || com_argv[i][1] > '9'))
  384. break;
  385. if (l + strlen(com_argv[i]) + 4 > sizeof(build) - 1)
  386. break;
  387. build[l++] = ' ';
  388. if (strchr(com_argv[i], ' '))
  389. build[l++] = '\"';
  390. for (j = 0;com_argv[i][j];j++)
  391. build[l++] = com_argv[i][j];
  392. if (strchr(com_argv[i], ' '))
  393. build[l++] = '\"';
  394. }
  395. build[l++] = '\n';
  396. i--;
  397. }
  398. }
  399. // now terminate the combined string and prepend it to the command buffer
  400. // we already reserved space for the terminator
  401. build[l++] = 0;
  402. Cbuf_InsertText (build);
  403. }
  404. static void Cmd_Exec(const char *filename)
  405. {
  406. char *f;
  407. size_t filenameLen = strlen(filename);
  408. qboolean isdefaultcfg =
  409. !strcmp(filename, "default.cfg") ||
  410. (filenameLen >= 12 && !strcmp(filename + filenameLen - 12, "/default.cfg"));
  411. if (!strcmp(filename, "config.cfg"))
  412. {
  413. filename = CONFIGFILENAME;
  414. if (COM_CheckParm("-noconfig"))
  415. return; // don't execute config.cfg
  416. }
  417. f = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
  418. if (!f)
  419. {
  420. Con_Printf("couldn't exec %s\n",filename);
  421. return;
  422. }
  423. Con_Printf("execing %s\n",filename);
  424. // if executing default.cfg for the first time, lock the cvar defaults
  425. // it may seem backwards to insert this text BEFORE the default.cfg
  426. // but Cbuf_InsertText inserts before, so this actually ends up after it.
  427. if (isdefaultcfg)
  428. Cbuf_InsertText("\ncvar_lockdefaults\n");
  429. // insert newline after the text to make sure the last line is terminated (some text editors omit the trailing newline)
  430. // (note: insertion order here is backwards from execution order, so this adds it after the text, by calling it before...)
  431. Cbuf_InsertText ("\n");
  432. Cbuf_InsertText (f);
  433. Mem_Free(f);
  434. if (isdefaultcfg)
  435. {
  436. // special defaults for specific games go here, these execute before default.cfg
  437. // Nehahra pushable crates malfunction in some levels if this is on
  438. // Nehahra NPC AI is confused by blowupfallenzombies
  439. switch(gamemode)
  440. {
  441. case GAME_NORMAL:
  442. Cbuf_InsertText("\n"
  443. "sv_gameplayfix_blowupfallenzombies 0\n"
  444. "sv_gameplayfix_findradiusdistancetobox 0\n"
  445. "sv_gameplayfix_grenadebouncedownslopes 0\n"
  446. "sv_gameplayfix_slidemoveprojectiles 0\n"
  447. "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
  448. "sv_gameplayfix_setmodelrealbox 0\n"
  449. "sv_gameplayfix_droptofloorstartsolid 0\n"
  450. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
  451. "sv_gameplayfix_noairborncorpse 0\n"
  452. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
  453. "sv_gameplayfix_easierwaterjump 0\n"
  454. "sv_gameplayfix_delayprojectiles 0\n"
  455. "sv_gameplayfix_multiplethinksperframe 0\n"
  456. "sv_gameplayfix_fixedcheckwatertransition 0\n"
  457. "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
  458. "sv_gameplayfix_swiminbmodels 0\n"
  459. "sv_gameplayfix_downtracesupportsongroundflag 0\n"
  460. "sys_ticrate 0.01388889\n"
  461. "r_shadow_gloss 1\n"
  462. "r_shadow_bumpscale_basetexture 0\n"
  463. );
  464. break;
  465. case GAME_NEHAHRA:
  466. Cbuf_InsertText("\n"
  467. "sv_gameplayfix_blowupfallenzombies 0\n"
  468. "sv_gameplayfix_findradiusdistancetobox 0\n"
  469. "sv_gameplayfix_grenadebouncedownslopes 0\n"
  470. "sv_gameplayfix_slidemoveprojectiles 0\n"
  471. "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
  472. "sv_gameplayfix_setmodelrealbox 0\n"
  473. "sv_gameplayfix_droptofloorstartsolid 0\n"
  474. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
  475. "sv_gameplayfix_noairborncorpse 0\n"
  476. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
  477. "sv_gameplayfix_easierwaterjump 0\n"
  478. "sv_gameplayfix_delayprojectiles 0\n"
  479. "sv_gameplayfix_multiplethinksperframe 0\n"
  480. "sv_gameplayfix_fixedcheckwatertransition 0\n"
  481. "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
  482. "sv_gameplayfix_swiminbmodels 0\n"
  483. "sv_gameplayfix_downtracesupportsongroundflag 0\n"
  484. "sys_ticrate 0.01388889\n"
  485. "r_shadow_gloss 1\n"
  486. "r_shadow_bumpscale_basetexture 0\n"
  487. );
  488. break;
  489. // 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.
  490. // 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
  491. // hipnotic mission pack has issues in their proximity mine sticking code, which causes them to bounce off.
  492. case GAME_HIPNOTIC:
  493. case GAME_QUOTH:
  494. Cbuf_InsertText("\n"
  495. "sv_gameplayfix_blowupfallenzombies 0\n"
  496. "sv_gameplayfix_findradiusdistancetobox 0\n"
  497. "sv_gameplayfix_grenadebouncedownslopes 0\n"
  498. "sv_gameplayfix_slidemoveprojectiles 0\n"
  499. "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
  500. "sv_gameplayfix_setmodelrealbox 0\n"
  501. "sv_gameplayfix_droptofloorstartsolid 0\n"
  502. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
  503. "sv_gameplayfix_noairborncorpse 0\n"
  504. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
  505. "sv_gameplayfix_easierwaterjump 0\n"
  506. "sv_gameplayfix_delayprojectiles 0\n"
  507. "sv_gameplayfix_multiplethinksperframe 0\n"
  508. "sv_gameplayfix_fixedcheckwatertransition 0\n"
  509. "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
  510. "sv_gameplayfix_swiminbmodels 0\n"
  511. "sv_gameplayfix_downtracesupportsongroundflag 0\n"
  512. "sys_ticrate 0.02\n"
  513. "r_shadow_gloss 1\n"
  514. "r_shadow_bumpscale_basetexture 0\n"
  515. );
  516. break;
  517. // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
  518. case GAME_ROGUE:
  519. Cbuf_InsertText("\n"
  520. "sv_gameplayfix_blowupfallenzombies 0\n"
  521. "sv_gameplayfix_findradiusdistancetobox 0\n"
  522. "sv_gameplayfix_grenadebouncedownslopes 0\n"
  523. "sv_gameplayfix_slidemoveprojectiles 0\n"
  524. "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
  525. "sv_gameplayfix_setmodelrealbox 0\n"
  526. "sv_gameplayfix_droptofloorstartsolid 0\n"
  527. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
  528. "sv_gameplayfix_noairborncorpse 0\n"
  529. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
  530. "sv_gameplayfix_easierwaterjump 0\n"
  531. "sv_gameplayfix_delayprojectiles 0\n"
  532. "sv_gameplayfix_multiplethinksperframe 0\n"
  533. "sv_gameplayfix_fixedcheckwatertransition 0\n"
  534. "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
  535. "sv_gameplayfix_swiminbmodels 0\n"
  536. "sv_gameplayfix_downtracesupportsongroundflag 0\n"
  537. "sys_ticrate 0.01388889\n"
  538. "r_shadow_gloss 1\n"
  539. "r_shadow_bumpscale_basetexture 0\n"
  540. );
  541. break;
  542. case GAME_TENEBRAE:
  543. Cbuf_InsertText("\n"
  544. "sv_gameplayfix_blowupfallenzombies 0\n"
  545. "sv_gameplayfix_findradiusdistancetobox 0\n"
  546. "sv_gameplayfix_grenadebouncedownslopes 0\n"
  547. "sv_gameplayfix_slidemoveprojectiles 0\n"
  548. "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
  549. "sv_gameplayfix_setmodelrealbox 0\n"
  550. "sv_gameplayfix_droptofloorstartsolid 0\n"
  551. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
  552. "sv_gameplayfix_noairborncorpse 0\n"
  553. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
  554. "sv_gameplayfix_easierwaterjump 0\n"
  555. "sv_gameplayfix_delayprojectiles 0\n"
  556. "sv_gameplayfix_multiplethinksperframe 0\n"
  557. "sv_gameplayfix_fixedcheckwatertransition 0\n"
  558. "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
  559. "sv_gameplayfix_swiminbmodels 0\n"
  560. "sv_gameplayfix_downtracesupportsongroundflag 0\n"
  561. "sys_ticrate 0.01388889\n"
  562. "r_shadow_gloss 2\n"
  563. "r_shadow_bumpscale_basetexture 4\n"
  564. );
  565. break;
  566. case GAME_NEXUIZ:
  567. Cbuf_InsertText("\n"
  568. "sv_gameplayfix_blowupfallenzombies 1\n"
  569. "sv_gameplayfix_findradiusdistancetobox 1\n"
  570. "sv_gameplayfix_grenadebouncedownslopes 1\n"
  571. "sv_gameplayfix_slidemoveprojectiles 1\n"
  572. "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
  573. "sv_gameplayfix_setmodelrealbox 1\n"
  574. "sv_gameplayfix_droptofloorstartsolid 1\n"
  575. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
  576. "sv_gameplayfix_noairborncorpse 1\n"
  577. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
  578. "sv_gameplayfix_easierwaterjump 1\n"
  579. "sv_gameplayfix_delayprojectiles 1\n"
  580. "sv_gameplayfix_multiplethinksperframe 1\n"
  581. "sv_gameplayfix_fixedcheckwatertransition 1\n"
  582. "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
  583. "sv_gameplayfix_swiminbmodels 1\n"
  584. "sv_gameplayfix_downtracesupportsongroundflag 1\n"
  585. "sys_ticrate 0.01388889\n"
  586. "sv_gameplayfix_q2airaccelerate 1\n"
  587. "sv_gameplayfix_stepmultipletimes 1\n"
  588. );
  589. break;
  590. // Steel Storm: Burning Retribution csqc misinterprets CSQC_InputEvent if type is a value other than 0 or 1
  591. case GAME_STEELSTORM:
  592. Cbuf_InsertText("\n"
  593. "sv_gameplayfix_blowupfallenzombies 1\n"
  594. "sv_gameplayfix_findradiusdistancetobox 1\n"
  595. "sv_gameplayfix_grenadebouncedownslopes 1\n"
  596. "sv_gameplayfix_slidemoveprojectiles 1\n"
  597. "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
  598. "sv_gameplayfix_setmodelrealbox 1\n"
  599. "sv_gameplayfix_droptofloorstartsolid 1\n"
  600. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
  601. "sv_gameplayfix_noairborncorpse 1\n"
  602. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
  603. "sv_gameplayfix_easierwaterjump 1\n"
  604. "sv_gameplayfix_delayprojectiles 1\n"
  605. "sv_gameplayfix_multiplethinksperframe 1\n"
  606. "sv_gameplayfix_fixedcheckwatertransition 1\n"
  607. "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
  608. "sv_gameplayfix_swiminbmodels 1\n"
  609. "sv_gameplayfix_downtracesupportsongroundflag 1\n"
  610. "sys_ticrate 0.01388889\n"
  611. "cl_csqc_generatemousemoveevents 0\n"
  612. );
  613. break;
  614. default:
  615. Cbuf_InsertText("\n"
  616. "sv_gameplayfix_blowupfallenzombies 1\n"
  617. "sv_gameplayfix_findradiusdistancetobox 1\n"
  618. "sv_gameplayfix_grenadebouncedownslopes 1\n"
  619. "sv_gameplayfix_slidemoveprojectiles 1\n"
  620. "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
  621. "sv_gameplayfix_setmodelrealbox 1\n"
  622. "sv_gameplayfix_droptofloorstartsolid 1\n"
  623. "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
  624. "sv_gameplayfix_noairborncorpse 1\n"
  625. "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
  626. "sv_gameplayfix_easierwaterjump 1\n"
  627. "sv_gameplayfix_delayprojectiles 1\n"
  628. "sv_gameplayfix_multiplethinksperframe 1\n"
  629. "sv_gameplayfix_fixedcheckwatertransition 1\n"
  630. "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
  631. "sv_gameplayfix_swiminbmodels 1\n"
  632. "sv_gameplayfix_downtracesupportsongroundflag 1\n"
  633. "sys_ticrate 0.01388889\n"
  634. );
  635. break;
  636. }
  637. }
  638. }
  639. /*
  640. ===============
  641. Cmd_Exec_f
  642. ===============
  643. */
  644. static void Cmd_Exec_f (void)
  645. {
  646. fssearch_t *s;
  647. int i;
  648. if (Cmd_Argc () != 2)
  649. {
  650. Con_Print("exec <filename> : execute a script file\n");
  651. return;
  652. }
  653. s = FS_Search(Cmd_Argv(1), true, true);
  654. if(!s || !s->numfilenames)
  655. {
  656. Con_Printf("couldn't exec %s\n",Cmd_Argv(1));
  657. return;
  658. }
  659. for(i = 0; i < s->numfilenames; ++i)
  660. Cmd_Exec(s->filenames[i]);
  661. FS_FreeSearch(s);
  662. }
  663. /*
  664. ===============
  665. Cmd_Echo_f
  666. Just prints the rest of the line to the console
  667. ===============
  668. */
  669. static void Cmd_Echo_f (void)
  670. {
  671. int i;
  672. for (i=1 ; i<Cmd_Argc() ; i++)
  673. Con_Printf("%s ",Cmd_Argv(i));
  674. Con_Print("\n");
  675. }
  676. // DRESK - 5/14/06
  677. // Support Doom3-style Toggle Console Command
  678. /*
  679. ===============
  680. Cmd_Toggle_f
  681. Toggles a specified console variable amongst the values specified (default is 0 and 1)
  682. ===============
  683. */
  684. static void Cmd_Toggle_f(void)
  685. {
  686. // Acquire Number of Arguments
  687. int nNumArgs = Cmd_Argc();
  688. if(nNumArgs == 1)
  689. // No Arguments Specified; Print Usage
  690. 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");
  691. else
  692. { // Correct Arguments Specified
  693. // Acquire Potential CVar
  694. cvar_t* cvCVar = Cvar_FindVar( Cmd_Argv(1) );
  695. if(cvCVar != NULL)
  696. { // Valid CVar
  697. if(nNumArgs == 2)
  698. { // Default Usage
  699. if(cvCVar->integer)
  700. Cvar_SetValueQuick(cvCVar, 0);
  701. else
  702. Cvar_SetValueQuick(cvCVar, 1);
  703. }
  704. else
  705. if(nNumArgs == 3)
  706. { // 0 and Specified Usage
  707. if(cvCVar->integer == atoi(Cmd_Argv(2) ) )
  708. // CVar is Specified Value; // Reset to 0
  709. Cvar_SetValueQuick(cvCVar, 0);
  710. else
  711. if(cvCVar->integer == 0)
  712. // CVar is 0; Specify Value
  713. Cvar_SetQuick(cvCVar, Cmd_Argv(2) );
  714. else
  715. // CVar does not match; Reset to 0
  716. Cvar_SetValueQuick(cvCVar, 0);
  717. }
  718. else
  719. { // Variable Values Specified
  720. int nCnt;
  721. int bFound = 0;
  722. for(nCnt = 2; nCnt < nNumArgs; nCnt++)
  723. { // Cycle through Values
  724. if( strcmp(cvCVar->string, Cmd_Argv(nCnt) ) == 0)
  725. { // Current Value Located; Increment to Next
  726. if( (nCnt + 1) == nNumArgs)
  727. // Max Value Reached; Reset
  728. Cvar_SetQuick(cvCVar, Cmd_Argv(2) );
  729. else
  730. // Next Value
  731. Cvar_SetQuick(cvCVar, Cmd_Argv(nCnt + 1) );
  732. // End Loop
  733. nCnt = nNumArgs;
  734. // Assign Found
  735. bFound = 1;
  736. }
  737. }
  738. if(!bFound)
  739. // Value not Found; Reset to Original
  740. Cvar_SetQuick(cvCVar, Cmd_Argv(2) );
  741. }
  742. }
  743. else
  744. { // Invalid CVar
  745. Con_Printf("ERROR : CVar '%s' not found\n", Cmd_Argv(1) );
  746. }
  747. }
  748. }
  749. /*
  750. ===============
  751. Cmd_Alias_f
  752. Creates a new command that executes a command string (possibly ; seperated)
  753. ===============
  754. */
  755. static void Cmd_Alias_f (void)
  756. {
  757. cmdalias_t *a;
  758. char cmd[MAX_INPUTLINE];
  759. int i, c;
  760. const char *s;
  761. size_t alloclen;
  762. if (Cmd_Argc() == 1)
  763. {
  764. Con_Print("Current alias commands:\n");
  765. for (a = cmd_alias ; a ; a=a->next)
  766. Con_Printf("%s : %s", a->name, a->value);
  767. return;
  768. }
  769. s = Cmd_Argv(1);
  770. if (strlen(s) >= MAX_ALIAS_NAME)
  771. {
  772. Con_Print("Alias name is too long\n");
  773. return;
  774. }
  775. // if the alias already exists, reuse it
  776. for (a = cmd_alias ; a ; a=a->next)
  777. {
  778. if (!strcmp(s, a->name))
  779. {
  780. Z_Free (a->value);
  781. break;
  782. }
  783. }
  784. if (!a)
  785. {
  786. cmdalias_t *prev, *current;
  787. a = (cmdalias_t *)Z_Malloc (sizeof(cmdalias_t));
  788. strlcpy (a->name, s, sizeof (a->name));
  789. // insert it at the right alphanumeric position
  790. for( prev = NULL, current = cmd_alias ; current && strcmp( current->name, a->name ) < 0 ; prev = current, current = current->next )
  791. ;
  792. if( prev ) {
  793. prev->next = a;
  794. } else {
  795. cmd_alias = a;
  796. }
  797. a->next = current;
  798. }
  799. // copy the rest of the command line
  800. cmd[0] = 0; // start out with a null string
  801. c = Cmd_Argc();
  802. for (i=2 ; i < c ; i++)
  803. {
  804. if (i != 2)
  805. strlcat (cmd, " ", sizeof (cmd));
  806. strlcat (cmd, Cmd_Argv(i), sizeof (cmd));
  807. }
  808. strlcat (cmd, "\n", sizeof (cmd));
  809. alloclen = strlen (cmd) + 1;
  810. if(alloclen >= 2)
  811. cmd[alloclen - 2] = '\n'; // to make sure a newline is appended even if too long
  812. a->value = (char *)Z_Malloc (alloclen);
  813. memcpy (a->value, cmd, alloclen);
  814. }
  815. /*
  816. ===============
  817. Cmd_UnAlias_f
  818. Remove existing aliases.
  819. ===============
  820. */
  821. static void Cmd_UnAlias_f (void)
  822. {
  823. cmdalias_t *a, *p;
  824. int i;
  825. const char *s;
  826. if(Cmd_Argc() == 1)
  827. {
  828. Con_Print("unalias: Usage: unalias alias1 [alias2 ...]\n");
  829. return;
  830. }
  831. for(i = 1; i < Cmd_Argc(); ++i)
  832. {
  833. s = Cmd_Argv(i);
  834. p = NULL;
  835. for(a = cmd_alias; a; p = a, a = a->next)
  836. {
  837. if(!strcmp(s, a->name))
  838. {
  839. if (a->initstate) // we can not remove init aliases
  840. continue;
  841. if(a == cmd_alias)
  842. cmd_alias = a->next;
  843. if(p)
  844. p->next = a->next;
  845. Z_Free(a->value);
  846. Z_Free(a);
  847. break;
  848. }
  849. }
  850. if(!a)
  851. Con_Printf("unalias: %s alias not found\n", s);
  852. }
  853. }
  854. /*
  855. =============================================================================
  856. COMMAND EXECUTION
  857. =============================================================================
  858. */
  859. typedef struct cmd_function_s
  860. {
  861. struct cmd_function_s *next;
  862. const char *name;
  863. const char *description;
  864. xcommand_t consolefunction;
  865. xcommand_t clientfunction;
  866. qboolean csqcfunc;
  867. qboolean initstate; // indicates this command existed at init
  868. } cmd_function_t;
  869. static int cmd_argc;
  870. static const char *cmd_argv[MAX_ARGS];
  871. static const char *cmd_null_string = "";
  872. static const char *cmd_args;
  873. cmd_source_t cmd_source;
  874. static cmd_function_t *cmd_functions; // possible commands to execute
  875. static const char *Cmd_GetDirectCvarValue(const char *varname, cmdalias_t *alias, qboolean *is_multiple)
  876. {
  877. cvar_t *cvar;
  878. long argno;
  879. char *endptr;
  880. static char vabuf[1024]; // cmd_mutex
  881. if(is_multiple)
  882. *is_multiple = false;
  883. if(!varname || !*varname)
  884. return NULL;
  885. if(alias)
  886. {
  887. if(!strcmp(varname, "*"))
  888. {
  889. if(is_multiple)
  890. *is_multiple = true;
  891. return Cmd_Args();
  892. }
  893. else if(!strcmp(varname, "#"))
  894. {
  895. return va(vabuf, sizeof(vabuf), "%d", Cmd_Argc());
  896. }
  897. else if(varname[strlen(varname) - 1] == '-')
  898. {
  899. argno = strtol(varname, &endptr, 10);
  900. if(endptr == varname + strlen(varname) - 1)
  901. {
  902. // whole string is a number, apart from the -
  903. const char *p = Cmd_Args();
  904. for(; argno > 1; --argno)
  905. if(!COM_ParseToken_Console(&p))
  906. break;
  907. if(p)
  908. {
  909. if(is_multiple)
  910. *is_multiple = true;
  911. // kill pre-argument whitespace
  912. for (;*p && ISWHITESPACE(*p);p++)
  913. ;
  914. return p;
  915. }
  916. }
  917. }
  918. else
  919. {
  920. argno = strtol(varname, &endptr, 10);
  921. if(*endptr == 0)
  922. {
  923. // whole string is a number
  924. // NOTE: we already made sure we don't have an empty cvar name!
  925. if(argno >= 0 && argno < Cmd_Argc())
  926. return Cmd_Argv(argno);
  927. }
  928. }
  929. }
  930. if((cvar = Cvar_FindVar(varname)) && !(cvar->flags & CVAR_PRIVATE))
  931. return cvar->string;
  932. return NULL;
  933. }
  934. qboolean Cmd_QuoteString(char *out, size_t outlen, const char *in, const char *quoteset, qboolean putquotes)
  935. {
  936. qboolean quote_quot = !!strchr(quoteset, '"');
  937. qboolean quote_backslash = !!strchr(quoteset, '\\');
  938. qboolean quote_dollar = !!strchr(quoteset, '$');
  939. if(putquotes)
  940. {
  941. if(outlen <= 2)
  942. {
  943. *out++ = 0;
  944. return false;
  945. }
  946. *out++ = '"'; --outlen;
  947. --outlen;
  948. }
  949. while(*in)
  950. {
  951. if(*in == '"' && quote_quot)
  952. {
  953. if(outlen <= 2)
  954. goto fail;
  955. *out++ = '\\'; --outlen;
  956. *out++ = '"'; --outlen;
  957. }
  958. else if(*in == '\\' && quote_backslash)
  959. {
  960. if(outlen <= 2)
  961. goto fail;
  962. *out++ = '\\'; --outlen;
  963. *out++ = '\\'; --outlen;
  964. }
  965. else if(*in == '$' && quote_dollar)
  966. {
  967. if(outlen <= 2)
  968. goto fail;
  969. *out++ = '$'; --outlen;
  970. *out++ = '$'; --outlen;
  971. }
  972. else
  973. {
  974. if(outlen <= 1)
  975. goto fail;
  976. *out++ = *in; --outlen;
  977. }
  978. ++in;
  979. }
  980. if(putquotes)
  981. *out++ = '"';
  982. *out++ = 0;
  983. return true;
  984. fail:
  985. if(putquotes)
  986. *out++ = '"';
  987. *out++ = 0;
  988. return false;
  989. }
  990. static const char *Cmd_GetCvarValue(const char *var, size_t varlen, cmdalias_t *alias)
  991. {
  992. static char varname[MAX_INPUTLINE]; // cmd_mutex
  993. static char varval[MAX_INPUTLINE]; // cmd_mutex
  994. const char *varstr = NULL;
  995. char *varfunc;
  996. qboolean required = false;
  997. qboolean optional = false;
  998. static char asis[] = "asis"; // just to suppress const char warnings
  999. if(varlen >= MAX_INPUTLINE)
  1000. varlen = MAX_INPUTLINE - 1;
  1001. memcpy(varname, var, varlen);
  1002. varname[varlen] = 0;
  1003. varfunc = strchr(varname, ' ');
  1004. if(varfunc)
  1005. {
  1006. *varfunc = 0;
  1007. ++varfunc;
  1008. }
  1009. if(*var == 0)
  1010. {
  1011. // empty cvar name?
  1012. if(alias)
  1013. Con_Printf("Warning: Could not expand $ in alias %s\n", alias->name);
  1014. else
  1015. Con_Printf("Warning: Could not expand $\n");
  1016. return "$";
  1017. }
  1018. if(varfunc)
  1019. {
  1020. char *p;
  1021. // ? means optional
  1022. while((p = strchr(varfunc, '?')))
  1023. {
  1024. optional = true;
  1025. memmove(p, p+1, strlen(p)); // with final NUL
  1026. }
  1027. // ! means required
  1028. while((p = strchr(varfunc, '!')))
  1029. {
  1030. required = true;
  1031. memmove(p, p+1, strlen(p)); // with final NUL
  1032. }
  1033. // kill spaces
  1034. while((p = strchr(varfunc, ' ')))
  1035. {
  1036. memmove(p, p+1, strlen(p)); // with final NUL
  1037. }
  1038. // if no function is left, NULL it
  1039. if(!*varfunc)
  1040. varfunc = NULL;
  1041. }
  1042. if(varname[0] == '$')
  1043. varstr = Cmd_GetDirectCvarValue(Cmd_GetDirectCvarValue(varname + 1, alias, NULL), alias, NULL);
  1044. else
  1045. {
  1046. qboolean is_multiple = false;
  1047. // Exception: $* and $n- don't use the quoted form by default
  1048. varstr = Cmd_GetDirectCvarValue(varname, alias, &is_multiple);
  1049. if(is_multiple)
  1050. if(!varfunc)
  1051. varfunc = asis;
  1052. }
  1053. if(!varstr)
  1054. {
  1055. if(required)
  1056. {
  1057. if(alias)
  1058. Con_Printf("Error: Could not expand $%s in alias %s\n", varname, alias->name);
  1059. else
  1060. Con_Printf("Error: Could not expand $%s\n", varname);
  1061. return NULL;
  1062. }
  1063. else if(optional)
  1064. {
  1065. return "";
  1066. }
  1067. else
  1068. {
  1069. if(alias)
  1070. Con_Printf("Warning: Could not expand $%s in alias %s\n", varname, alias->name);
  1071. else
  1072. Con_Printf("Warning: Could not expand $%s\n", varname);
  1073. dpsnprintf(varval, sizeof(varval), "$%s", varname);
  1074. return varval;
  1075. }
  1076. }
  1077. if(!varfunc || !strcmp(varfunc, "q")) // note: quoted form is default, use "asis" to override!
  1078. {
  1079. // quote it so it can be used inside double quotes
  1080. // we just need to replace " by \", and of course, double backslashes
  1081. Cmd_QuoteString(varval, sizeof(varval), varstr, "\"\\", false);
  1082. return varval;
  1083. }
  1084. else if(!strcmp(varfunc, "asis"))
  1085. {
  1086. return varstr;
  1087. }
  1088. else
  1089. Con_Printf("Unknown variable function %s\n", varfunc);
  1090. return varstr;
  1091. }
  1092. /*
  1093. Cmd_PreprocessString
  1094. Preprocesses strings and replaces $*, $param#, $cvar accordingly. Also strips comments.
  1095. */
  1096. static qboolean Cmd_PreprocessString( const char *intext, char *outtext, unsigned maxoutlen, cmdalias_t *alias ) {
  1097. const char *in;
  1098. size_t eat, varlen;
  1099. unsigned outlen;
  1100. const char *val;
  1101. // don't crash if there's no room in the outtext buffer
  1102. if( maxoutlen == 0 ) {
  1103. return false;
  1104. }
  1105. maxoutlen--; // because of \0
  1106. in = intext;
  1107. outlen = 0;
  1108. while( *in && outlen < maxoutlen ) {
  1109. if( *in == '$' ) {
  1110. // this is some kind of expansion, see what comes after the $
  1111. in++;
  1112. // The console does the following preprocessing:
  1113. //
  1114. // - $$ is transformed to a single dollar sign.
  1115. // - $var or ${var} are expanded to the contents of the named cvar,
  1116. // with quotation marks and backslashes quoted so it can safely
  1117. // be used inside quotation marks (and it should always be used
  1118. // that way)
  1119. // - ${var asis} inserts the cvar value as is, without doing this
  1120. // quoting
  1121. // - ${var ?} silently expands to the empty string if
  1122. // $var does not exist
  1123. // - ${var !} fails expansion and executes nothing if
  1124. // $var does not exist
  1125. // - prefix the cvar name with a dollar sign to do indirection;
  1126. // for example, if $x has the value timelimit, ${$x} will return
  1127. // the value of $timelimit
  1128. // - when expanding an alias, the special variable name $* refers
  1129. // to all alias parameters, and a number refers to that numbered
  1130. // alias parameter, where the name of the alias is $0, the first
  1131. // parameter is $1 and so on; as a special case, $* inserts all
  1132. // parameters, without extra quoting, so one can use $* to just
  1133. // pass all parameters around. All parameters starting from $n
  1134. // can be referred to as $n- (so $* is equivalent to $1-).
  1135. // - ${* q} and ${n- q} force quoting anyway
  1136. //
  1137. // Note: when expanding an alias, cvar expansion is done in the SAME step
  1138. // as alias expansion so that alias parameters or cvar values containing
  1139. // dollar signs have no unwanted bad side effects. However, this needs to
  1140. // be accounted for when writing complex aliases. For example,
  1141. // alias foo "set x NEW; echo $x"
  1142. // actually expands to
  1143. // "set x NEW; echo OLD"
  1144. // and will print OLD! To work around this, use a second alias:
  1145. // alias foo "set x NEW; foo2"
  1146. // alias foo2 "echo $x"
  1147. //
  1148. // Also note: lines starting with alias are exempt from cvar expansion.
  1149. // If you want cvar expansion, write "alias" instead:
  1150. //
  1151. // set x 1
  1152. // alias foo "echo $x"
  1153. // "alias" bar "echo $x"
  1154. // set x 2
  1155. //
  1156. // foo will print 2, because the variable $x will be expanded when the alias
  1157. // gets expanded. bar will print 1, because the variable $x was expanded
  1158. // at definition time. foo can be equivalently defined as
  1159. //
  1160. // "alias" foo "echo $$x"
  1161. //
  1162. // because at definition time, $$ will get replaced to a single $.
  1163. if( *in == '$' ) {
  1164. val = "$";
  1165. eat = 1;
  1166. } else if(*in == '{') {
  1167. varlen = strcspn(in + 1, "}");
  1168. if(in[varlen + 1] == '}')
  1169. {
  1170. val = Cmd_GetCvarValue(in + 1, varlen, alias);
  1171. if(!val)
  1172. return false;
  1173. eat = varlen + 2;
  1174. }
  1175. else
  1176. {
  1177. // ran out of data?
  1178. val = NULL;
  1179. eat = varlen + 1;
  1180. }
  1181. } else {
  1182. varlen = strspn(in, "#*0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-");
  1183. val = Cmd_GetCvarValue(in, varlen, alias);
  1184. if(!val)
  1185. return false;
  1186. eat = varlen;
  1187. }
  1188. if(val)
  1189. {
  1190. // insert the cvar value
  1191. while(*val && outlen < maxoutlen)
  1192. outtext[outlen++] = *val++;
  1193. in += eat;
  1194. }
  1195. else
  1196. {
  1197. // copy the unexpanded text
  1198. outtext[outlen++] = '$';
  1199. while(eat && outlen < maxoutlen)
  1200. {
  1201. outtext[outlen++] = *in++;
  1202. --eat;
  1203. }
  1204. }
  1205. }
  1206. else
  1207. outtext[outlen++] = *in++;
  1208. }
  1209. outtext[outlen] = 0;
  1210. return true;
  1211. }
  1212. /*
  1213. ============
  1214. Cmd_ExecuteAlias
  1215. Called for aliases and fills in the alias into the cbuffer
  1216. ============
  1217. */
  1218. static void Cmd_ExecuteAlias (cmdalias_t *alias)
  1219. {
  1220. static char buffer[ MAX_INPUTLINE ]; // cmd_mutex
  1221. static char buffer2[ MAX_INPUTLINE ]; // cmd_mutex
  1222. qboolean ret = Cmd_PreprocessString( alias->value, buffer, sizeof(buffer) - 2, alias );
  1223. if(!ret)
  1224. return;
  1225. // insert at start of command buffer, so that aliases execute in order
  1226. // (fixes bug introduced by Black on 20050705)
  1227. // Note: Cbuf_PreprocessString will be called on this string AGAIN! So we
  1228. // have to make sure that no second variable expansion takes place, otherwise
  1229. // alias parameters containing dollar signs can have bad effects.
  1230. Cmd_QuoteString(buffer2, sizeof(buffer2), buffer, "$", false);
  1231. Cbuf_InsertText( buffer2 );
  1232. }
  1233. /*
  1234. ========
  1235. Cmd_List
  1236. CmdList Added by EvilTypeGuy eviltypeguy@qeradiant.com
  1237. Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
  1238. ========
  1239. */
  1240. static void Cmd_List_f (void)
  1241. {
  1242. cmd_function_t *cmd;
  1243. const char *partial;
  1244. size_t len;
  1245. int count;
  1246. qboolean ispattern;
  1247. if (Cmd_Argc() > 1)
  1248. {
  1249. partial = Cmd_Argv (1);
  1250. len = strlen(partial);
  1251. ispattern = (strchr(partial, '*') || strchr(partial, '?'));
  1252. }
  1253. else
  1254. {
  1255. partial = NULL;
  1256. len = 0;
  1257. ispattern = false;
  1258. }
  1259. count = 0;
  1260. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  1261. {
  1262. if (partial && (ispattern ? !matchpattern_with_separator(cmd->name, partial, false, "", false) : strncmp(partial, cmd->name, len)))
  1263. continue;
  1264. Con_Printf("%s : %s\n", cmd->name, cmd->description);
  1265. count++;
  1266. }
  1267. if (len)
  1268. {
  1269. if(ispattern)
  1270. Con_Printf("%i Command%s matching \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
  1271. else
  1272. Con_Printf("%i Command%s beginning with \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
  1273. }
  1274. else
  1275. Con_Printf("%i Command%s\n\n", count, (count > 1) ? "s" : "");
  1276. }
  1277. static void Cmd_Apropos_f(void)
  1278. {
  1279. cmd_function_t *cmd;
  1280. cvar_t *cvar;
  1281. cmdalias_t *alias;
  1282. const char *partial;
  1283. int count;
  1284. qboolean ispattern;
  1285. char vabuf[1024];
  1286. if (Cmd_Argc() > 1)
  1287. partial = Cmd_Args();
  1288. else
  1289. {
  1290. Con_Printf("usage: apropos <string>\n");
  1291. return;
  1292. }
  1293. ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
  1294. if(!ispattern)
  1295. partial = va(vabuf, sizeof(vabuf), "*%s*", partial);
  1296. count = 0;
  1297. for (cvar = cvar_vars; cvar; cvar = cvar->next)
  1298. {
  1299. if (!matchpattern_with_separator(cvar->name, partial, true, "", false))
  1300. if (!matchpattern_with_separator(cvar->description, partial, true, "", false))
  1301. continue;
  1302. Con_Printf ("cvar ^3%s^7 is \"%s\" [\"%s\"] %s\n", cvar->name, cvar->string, cvar->defstring, cvar->description);
  1303. count++;
  1304. }
  1305. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  1306. {
  1307. if (!matchpattern_with_separator(cmd->name, partial, true, "", false))
  1308. if (!matchpattern_with_separator(cmd->description, partial, true, "", false))
  1309. continue;
  1310. Con_Printf("command ^2%s^7: %s\n", cmd->name, cmd->description);
  1311. count++;
  1312. }
  1313. for (alias = cmd_alias; alias; alias = alias->next)
  1314. {
  1315. // procede here a bit differently as an alias value always got a final \n
  1316. if (!matchpattern_with_separator(alias->name, partial, true, "", false))
  1317. if (!matchpattern_with_separator(alias->value, partial, true, "\n", false)) // when \n is as separator wildcards don't match it
  1318. continue;
  1319. Con_Printf("alias ^5%s^7: %s", alias->name, alias->value); // do not print an extra \n
  1320. count++;
  1321. }
  1322. Con_Printf("%i result%s\n\n", count, (count > 1) ? "s" : "");
  1323. }
  1324. /*
  1325. ============
  1326. Cmd_Init
  1327. ============
  1328. */
  1329. void Cmd_Init (void)
  1330. {
  1331. cmd_mempool = Mem_AllocPool("commands", 0, NULL);
  1332. // space for commands and script files
  1333. cmd_text.data = cmd_text_buf;
  1334. cmd_text.maxsize = sizeof(cmd_text_buf);
  1335. cmd_text.cursize = 0;
  1336. if (Thread_HasThreads())
  1337. cmd_text_mutex = Thread_CreateMutex();
  1338. }
  1339. void Cmd_Init_Commands (void)
  1340. {
  1341. //
  1342. // register our commands
  1343. //
  1344. Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f, "execute commandline parameters (must be present in quake.rc script)");
  1345. Cmd_AddCommand ("exec",Cmd_Exec_f, "execute a script file");
  1346. Cmd_AddCommand ("echo",Cmd_Echo_f, "print a message to the console (useful in scripts)");
  1347. Cmd_AddCommand ("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");
  1348. Cmd_AddCommand ("unalias",Cmd_UnAlias_f, "remove an alias");
  1349. Cmd_AddCommand ("cmd", Cmd_ForwardToServer, "send a console commandline to the server (used by some mods)");
  1350. Cmd_AddCommand ("wait", Cmd_Wait_f, "make script execution wait for next rendered frame");
  1351. Cmd_AddCommand ("set", Cvar_Set_f, "create or change the value of a console variable");
  1352. Cmd_AddCommand ("seta", Cvar_SetA_f, "create or change the value of a console variable that will be saved to config.cfg");
  1353. Cmd_AddCommand ("unset", Cvar_Del_f, "delete a cvar (does not work for static ones like _cl_name, or read-only ones)");
  1354. #ifdef FILLALLCVARSWITHRUBBISH
  1355. Cmd_AddCommand ("fillallcvarswithrubbish", Cvar_FillAll_f, "fill all cvars with a specified number of characters to provoke buffer overruns");
  1356. #endif /* FILLALLCVARSWITHRUBBISH */
  1357. // 2000-01-09 CmdList, CvarList commands By Matthias "Maddes" Buecher
  1358. // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
  1359. Cmd_AddCommand ("cmdlist", Cmd_List_f, "lists all console commands beginning with the specified prefix or matching the specified wildcard pattern");
  1360. Cmd_AddCommand ("cvarlist", Cvar_List_f, "lists all console variables beginning with the specified prefix or matching the specified wildcard pattern");
  1361. Cmd_AddCommand ("apropos", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description");
  1362. Cmd_AddCommand ("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");
  1363. Cmd_AddCommand ("cvar_resettodefaults_all", Cvar_ResetToDefaults_All_f, "sets all cvars to their locked default values");
  1364. Cmd_AddCommand ("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)");
  1365. Cmd_AddCommand ("cvar_resettodefaults_saveonly", Cvar_ResetToDefaults_SaveOnly_f, "sets all saved cvars to their locked default values (variables that will be saved to config.cfg)");
  1366. Cmd_AddCommand ("cprint", Cmd_Centerprint_f, "print something at the screen center");
  1367. Cmd_AddCommand ("defer", Cmd_Defer_f, "execute a command in the future");
  1368. // DRESK - 5/14/06
  1369. // Support Doom3-style Toggle Command
  1370. Cmd_AddCommand( "toggle", Cmd_Toggle_f, "toggles a console variable's values (use for more info)");
  1371. }
  1372. /*
  1373. ============
  1374. Cmd_Shutdown
  1375. ============
  1376. */
  1377. void Cmd_Shutdown(void)
  1378. {
  1379. if (cmd_text_mutex)
  1380. {
  1381. // we usually have this locked when we get here from Host_Quit_f
  1382. Cbuf_UnlockThreadMutex();
  1383. Thread_DestroyMutex(cmd_text_mutex);
  1384. }
  1385. cmd_text_mutex = NULL;
  1386. Mem_FreePool(&cmd_mempool);
  1387. }
  1388. /*
  1389. ============
  1390. Cmd_Argc
  1391. ============
  1392. */
  1393. int Cmd_Argc (void)
  1394. {
  1395. return cmd_argc;
  1396. }
  1397. /*
  1398. ============
  1399. Cmd_Argv
  1400. ============
  1401. */
  1402. const char *Cmd_Argv (int arg)
  1403. {
  1404. if (arg >= cmd_argc )
  1405. return cmd_null_string;
  1406. return cmd_argv[arg];
  1407. }
  1408. /*
  1409. ============
  1410. Cmd_Args
  1411. ============
  1412. */
  1413. const char *Cmd_Args (void)
  1414. {
  1415. return cmd_args;
  1416. }
  1417. /*
  1418. ============
  1419. Cmd_TokenizeString
  1420. Parses the given string into command line tokens.
  1421. ============
  1422. */
  1423. // AK: This function should only be called from ExcuteString because the current design is a bit of an hack
  1424. static void Cmd_TokenizeString (const char *text)
  1425. {
  1426. int l;
  1427. cmd_argc = 0;
  1428. cmd_args = NULL;
  1429. while (1)
  1430. {
  1431. // skip whitespace up to a /n
  1432. while (*text && ISWHITESPACE(*text) && *text != '\r' && *text != '\n')
  1433. text++;
  1434. // line endings:
  1435. // UNIX: \n
  1436. // Mac: \r
  1437. // Windows: \r\n
  1438. if (*text == '\n' || *text == '\r')
  1439. {
  1440. // a newline separates commands in the buffer
  1441. if (*text == '\r' && text[1] == '\n')
  1442. text++;
  1443. text++;
  1444. break;
  1445. }
  1446. if (!*text)
  1447. return;
  1448. if (cmd_argc == 1)
  1449. cmd_args = text;
  1450. if (!COM_ParseToken_Console(&text))
  1451. return;
  1452. if (cmd_argc < MAX_ARGS)
  1453. {
  1454. l = (int)strlen(com_token) + 1;
  1455. if (cmd_tokenizebufferpos + l > CMD_TOKENIZELENGTH)
  1456. {
  1457. Con_Printf("Cmd_TokenizeString: ran out of %i character buffer space for command arguements\n", CMD_TOKENIZELENGTH);
  1458. break;
  1459. }
  1460. memcpy (cmd_tokenizebuffer + cmd_tokenizebufferpos, com_token, l);
  1461. cmd_argv[cmd_argc] = cmd_tokenizebuffer + cmd_tokenizebufferpos;
  1462. cmd_tokenizebufferpos += l;
  1463. cmd_argc++;
  1464. }
  1465. }
  1466. }
  1467. /*
  1468. ============
  1469. Cmd_AddCommand
  1470. ============
  1471. */
  1472. void Cmd_AddCommand_WithClientCommand (const char *cmd_name, xcommand_t consolefunction, xcommand_t clientfunction, const char *description)
  1473. {
  1474. cmd_function_t *cmd;
  1475. cmd_function_t *prev, *current;
  1476. // fail if the command is a variable name
  1477. if (Cvar_FindVar( cmd_name ))
  1478. {
  1479. Con_Printf("Cmd_AddCommand: %s already defined as a var\n", cmd_name);
  1480. return;
  1481. }
  1482. // fail if the command already exists
  1483. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  1484. {
  1485. if (!strcmp (cmd_name, cmd->name))
  1486. {
  1487. if (consolefunction || clientfunction)
  1488. {
  1489. Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name);
  1490. return;
  1491. }
  1492. else //[515]: csqc
  1493. {
  1494. cmd->csqcfunc = true;
  1495. return;
  1496. }
  1497. }
  1498. }
  1499. cmd = (cmd_function_t *)Mem_Alloc(cmd_mempool, sizeof(cmd_function_t));
  1500. cmd->name = cmd_name;
  1501. cmd->consolefunction = consolefunction;
  1502. cmd->clientfunction = clientfunction;
  1503. cmd->description = description;
  1504. if(!consolefunction && !clientfunction) //[515]: csqc
  1505. cmd->csqcfunc = true;
  1506. cmd->next = cmd_functions;
  1507. // insert it at the right alphanumeric position
  1508. for( prev = NULL, current = cmd_functions ; current && strcmp( current->name, cmd->name ) < 0 ; prev = current, current = current->next )
  1509. ;
  1510. if( prev ) {
  1511. prev->next = cmd;
  1512. } else {
  1513. cmd_functions = cmd;
  1514. }
  1515. cmd->next = current;
  1516. }
  1517. void Cmd_AddCommand (const char *cmd_name, xcommand_t function, const char *description)
  1518. {
  1519. Cmd_AddCommand_WithClientCommand (cmd_name, function, NULL, description);
  1520. }
  1521. /*
  1522. ============
  1523. Cmd_Exists
  1524. ============
  1525. */
  1526. qboolean Cmd_Exists (const char *cmd_name)
  1527. {
  1528. cmd_function_t *cmd;
  1529. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  1530. if (!strcmp (cmd_name,cmd->name))
  1531. return true;
  1532. return false;
  1533. }
  1534. /*
  1535. ============
  1536. Cmd_CompleteCommand
  1537. ============
  1538. */
  1539. const char *Cmd_CompleteCommand (const char *partial)
  1540. {
  1541. cmd_function_t *cmd;
  1542. size_t len;
  1543. len = strlen(partial);
  1544. if (!len)
  1545. return NULL;
  1546. // check functions
  1547. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  1548. if (!strncasecmp(partial, cmd->name, len))
  1549. return cmd->name;
  1550. return NULL;
  1551. }
  1552. /*
  1553. Cmd_CompleteCountPossible
  1554. New function for tab-completion system
  1555. Added by EvilTypeGuy
  1556. Thanks to Fett erich@heintz.com
  1557. Thanks to taniwha
  1558. */
  1559. int Cmd_CompleteCountPossible (const char *partial)
  1560. {
  1561. cmd_function_t *cmd;
  1562. size_t len;
  1563. int h;
  1564. h = 0;
  1565. len = strlen(partial);
  1566. if (!len)
  1567. return 0;
  1568. // Loop through the command list and count all partial matches
  1569. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  1570. if (!strncasecmp(partial, cmd->name, len))
  1571. h++;
  1572. return h;
  1573. }
  1574. /*
  1575. Cmd_CompleteBuildList
  1576. New function for tab-completion system
  1577. Added by EvilTypeGuy
  1578. Thanks to Fett erich@heintz.com
  1579. Thanks to taniwha
  1580. */
  1581. const char **Cmd_CompleteBuildList (const char *partial)
  1582. {
  1583. cmd_function_t *cmd;
  1584. size_t len = 0;
  1585. size_t bpos = 0;
  1586. size_t sizeofbuf = (Cmd_CompleteCountPossible (partial) + 1) * sizeof (const char *);
  1587. const char **buf;
  1588. len = strlen(partial);
  1589. buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
  1590. // Loop through the alias list and print all matches
  1591. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  1592. if (!strncasecmp(partial, cmd->name, len))
  1593. buf[bpos++] = cmd->name;
  1594. buf[bpos] = NULL;
  1595. return buf;
  1596. }
  1597. // written by LordHavoc
  1598. void Cmd_CompleteCommandPrint (const char *partial)
  1599. {
  1600. cmd_function_t *cmd;
  1601. size_t len = strlen(partial);
  1602. // Loop through the command list and print all matches
  1603. for (cmd = cmd_functions; cmd; cmd = cmd->next)
  1604. if (!strncasecmp(partial, cmd->name, len))
  1605. Con_Printf("^2%s^7: %s\n", cmd->name, cmd->description);
  1606. }
  1607. /*
  1608. Cmd_CompleteAlias
  1609. New function for tab-completion system
  1610. Added by EvilTypeGuy
  1611. Thanks to Fett erich@heintz.com
  1612. Thanks to taniwha
  1613. */
  1614. const char *Cmd_CompleteAlias (const char *partial)
  1615. {
  1616. cmdalias_t *alias;
  1617. size_t len;
  1618. len = strlen(partial);
  1619. if (!len)
  1620. return NULL;
  1621. // Check functions
  1622. for (alias = cmd_alias; alias; alias = alias->next)
  1623. if (!strncasecmp(partial, alias->name, len))
  1624. return alias->name;
  1625. return NULL;
  1626. }
  1627. // written by LordHavoc
  1628. void Cmd_CompleteAliasPrint (const char *partial)
  1629. {
  1630. cmdalias_t *alias;
  1631. size_t len = strlen(partial);
  1632. // Loop through the alias list and print all matches
  1633. for (alias = cmd_alias; alias; alias = alias->next)
  1634. if (!strncasecmp(partial, alias->name, len))
  1635. Con_Printf("^5%s^7: %s", alias->name, alias->value);
  1636. }
  1637. /*
  1638. Cmd_CompleteAliasCountPossible
  1639. New function for tab-completion system
  1640. Added by EvilTypeGuy
  1641. Thanks to Fett erich@heintz.com
  1642. Thanks to taniwha
  1643. */
  1644. int Cmd_CompleteAliasCountPossible (const char *partial)
  1645. {
  1646. cmdalias_t *alias;
  1647. size_t len;
  1648. int h;
  1649. h = 0;
  1650. len = strlen(partial);
  1651. if (!len)
  1652. return 0;
  1653. // Loop through the command list and count all partial matches
  1654. for (alias = cmd_alias; alias; alias = alias->next)
  1655. if (!strncasecmp(partial, alias->name, len))
  1656. h++;
  1657. return h;
  1658. }
  1659. /*
  1660. Cmd_CompleteAliasBuildList
  1661. New function for tab-completion system
  1662. Added by EvilTypeGuy
  1663. Thanks to Fett erich@heintz.com
  1664. Thanks to taniwha
  1665. */
  1666. const char **Cmd_CompleteAliasBuildList (const char *partial)
  1667. {
  1668. cmdalias_t *alias;
  1669. size_t len = 0;
  1670. size_t bpos = 0;
  1671. size_t sizeofbuf = (Cmd_CompleteAliasCountPossible (partial) + 1) * sizeof (const char *);
  1672. const char **buf;
  1673. len = strlen(partial);
  1674. buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
  1675. // Loop through the alias list and print all matches
  1676. for (alias = cmd_alias; alias; alias = alias->next)
  1677. if (!strncasecmp(partial, alias->name, len))
  1678. buf[bpos++] = alias->name;
  1679. buf[bpos] = NULL;
  1680. return buf;
  1681. }
  1682. void Cmd_ClearCsqcFuncs (void)
  1683. {
  1684. cmd_function_t *cmd;
  1685. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  1686. cmd->csqcfunc = false;
  1687. }
  1688. /*
  1689. ============
  1690. Cmd_ExecuteString
  1691. A complete command line has been parsed, so try to execute it
  1692. FIXME: lookupnoadd the token to speed search?
  1693. ============
  1694. */
  1695. void Cmd_ExecuteString (const char *text, cmd_source_t src, qboolean lockmutex)
  1696. {
  1697. int oldpos;
  1698. int found;
  1699. cmd_function_t *cmd;
  1700. cmdalias_t *a;
  1701. if (lockmutex)
  1702. Cbuf_LockThreadMutex();
  1703. oldpos = cmd_tokenizebufferpos;
  1704. cmd_source = src;
  1705. found = false;
  1706. Cmd_TokenizeString (text);
  1707. // execute the command line
  1708. if (!Cmd_Argc())
  1709. goto done; // no tokens
  1710. // check functions
  1711. for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
  1712. {
  1713. if (!strcasecmp (cmd_argv[0],cmd->name))
  1714. {
  1715. if (cmd->csqcfunc && CL_VM_ConsoleCommand (text)) //[515]: csqc
  1716. goto done;
  1717. switch (src)
  1718. {
  1719. case src_command:
  1720. if (cmd->consolefunction)
  1721. cmd->consolefunction ();
  1722. else if (cmd->clientfunction)
  1723. {
  1724. if (cls.state == ca_connected)
  1725. {
  1726. // forward remote commands to the server for execution
  1727. Cmd_ForwardToServer();
  1728. }
  1729. else
  1730. Con_Printf("Can not send command \"%s\", not connected.\n", Cmd_Argv(0));
  1731. }
  1732. else
  1733. Con_Printf("Command \"%s\" can not be executed\n", Cmd_Argv(0));
  1734. found = true;
  1735. goto command_found;
  1736. case src_client:
  1737. if (cmd->clientfunction)
  1738. {
  1739. cmd->clientfunction ();
  1740. goto done;
  1741. }
  1742. break;
  1743. }
  1744. break;
  1745. }
  1746. }
  1747. comman

Large files files are truncated, but you can click here to view the full file