PageRenderTime 55ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/legacy/trunk/util/command.cpp

#
C++ | 1081 lines | 994 code | 42 blank | 45 comment | 11 complexity | 05d308aceb4accd1e2ffe9bda9ec1f18 MD5 | raw file
Possible License(s): GPL-2.0
  1. // Emacs style mode select -*- C++ -*-
  2. //-----------------------------------------------------------------------------
  3. //
  4. // $Id: command.cpp 527 2008-01-27 02:25:14Z smite-meister $
  5. //
  6. // Copyright (C) 1998-2007 by DooM Legacy Team.
  7. //
  8. // This program is free software; you can redistribute it and/or
  9. // modify it under the terms of the GNU General Public License
  10. // as published by the Free Software Foundation; either version 2
  11. // of the License, or (at your option) any later version.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. //-----------------------------------------------------------------------------
  19. /// \file
  20. /// \brief Command buffer and console variables
  21. ///
  22. /// Parse and execute commands from console input/scripts/
  23. /// and remote server.
  24. ///
  25. /// Handles console variables, which are a simplified version
  26. /// of commands. Each consvar can have a function which is called
  27. /// when it is modified.
  28. ///
  29. /// Code shamelessly inspired by the QuakeC sources, thanks Id :)
  30. #include "tnl/tnlBitStream.h"
  31. #include "doomdef.h"
  32. #include "command.h"
  33. #include "console.h"
  34. #include "z_zone.h"
  35. #include "n_interface.h"
  36. #include "m_misc.h"
  37. #include "m_fixed.h"
  38. #include "g_game.h"
  39. #include "g_player.h"
  40. static void COM_Alias_f();
  41. static void COM_Echo_f();
  42. static void COM_Exec_f();
  43. static void COM_Wait_f();
  44. static void COM_Help_f();
  45. static void COM_Toggle_f();
  46. //=========================================================================
  47. // COMMAND BUFFER
  48. //=========================================================================
  49. /// console commands
  50. struct xcommand_t
  51. {
  52. const char *name;
  53. xcommand_t *next;
  54. com_func_t function;
  55. };
  56. /// command aliases
  57. struct cmdalias_t
  58. {
  59. cmdalias_t *next;
  60. char *name;
  61. char *value; // the command string to replace the alias
  62. };
  63. command_buffer_t COM;
  64. command_buffer_t::command_buffer_t()
  65. {
  66. com_commands = NULL;
  67. com_alias = NULL;
  68. com_maxsize = 256;
  69. com_wait = 0;
  70. com_argc = 0;
  71. com_args = NULL;
  72. for (int k=0; k<MAX_ARGS; k++)
  73. com_argv[k] = NULL;
  74. }
  75. // Initialise command buffer and add basic commands
  76. void command_buffer_t::Init()
  77. {
  78. CONS_Printf("Initializing the command buffer.\n");
  79. #define COM_BUF_SIZE 8192 // command buffer size
  80. // set command buffer maximum size
  81. com_maxsize = COM_BUF_SIZE;
  82. // add standard commands
  83. AddCommand("alias",COM_Alias_f);
  84. AddCommand("echo", COM_Echo_f);
  85. AddCommand("exec", COM_Exec_f);
  86. AddCommand("wait", COM_Wait_f);
  87. AddCommand("help", COM_Help_f);
  88. AddCommand("toggle", COM_Toggle_f);
  89. }
  90. // Add text in the command buffer (for later execution)
  91. //
  92. void command_buffer_t::AppendText(const char *text)
  93. {
  94. unsigned len = strlen(text);
  95. if (com_text.length() + len >= com_maxsize)
  96. {
  97. CONS_Printf("Command buffer full!\n");
  98. return;
  99. }
  100. com_text.append(text);
  101. }
  102. // Adds command text in front of the command buffer (immediately after the current command)
  103. // Adds a \n to the text
  104. void command_buffer_t::PrependText(const char *text)
  105. {
  106. com_text.insert(0, text);
  107. }
  108. // Flush (execute) console commands in buffer
  109. // does only one if com_wait
  110. //
  111. void command_buffer_t::BufExecute()
  112. {
  113. int i;
  114. if (com_wait)
  115. {
  116. com_wait--;
  117. return;
  118. }
  119. while (!com_text.empty())
  120. {
  121. // find a '\n' or ; line break
  122. const char *text = com_text.c_str();
  123. int n = com_text.length();
  124. int quotes = 0;
  125. for (i=0; i < n; i++)
  126. {
  127. if (text[i] == '"')
  128. quotes++;
  129. if ( !(quotes&1) && text[i] == ';')
  130. break; // don't break if inside a quoted string
  131. if (text[i] == '\n' || text[i] == '\r')
  132. break;
  133. }
  134. char line[1024];
  135. memcpy(line, text, i);
  136. line[i] = 0;
  137. // flush the command text from the command buffer, _BEFORE_
  138. // executing, to avoid that 'recursive' aliases overflow the
  139. // command text buffer, in that case, new commands are inserted
  140. // at the beginning, in place of the actual, so it doesn't
  141. // overflow
  142. if (i == n)
  143. // the last command was just flushed
  144. com_text.clear();
  145. else
  146. {
  147. i++;
  148. com_text.erase(0, i);
  149. }
  150. // execute the command line
  151. COM_ExecuteString(line);
  152. // delay following commands if a wait was encountered
  153. if (com_wait)
  154. {
  155. com_wait--;
  156. break;
  157. }
  158. }
  159. }
  160. // =========================================================================
  161. // COMMAND EXECUTION
  162. // =========================================================================
  163. PlayerInfo *com_player; // player associated with the current command.
  164. // Only "interactive" commands may require this.
  165. int command_buffer_t::CheckParm(const char *check)
  166. {
  167. for (int i = 1; i < com_argc; i++)
  168. {
  169. if (!strcasecmp(check, com_argv[i]))
  170. return i;
  171. }
  172. return 0;
  173. }
  174. // Parses the given string into command line tokens.
  175. //
  176. // Takes a null terminated string. Does not need to be /n terminated.
  177. // breaks the string up into arg tokens.
  178. void command_buffer_t::COM_TokenizeString(byte *text)
  179. {
  180. // clear the args from the last string
  181. for (int i=0 ; i<com_argc ; i++)
  182. if (com_argv[i])
  183. {
  184. Z_Free(com_argv[i]);
  185. com_argv[i] = NULL;
  186. }
  187. com_argc = 0;
  188. com_args = NULL;
  189. while (1)
  190. {
  191. // skip whitespace up to a /n
  192. while (*text && *text <= ' ' && *text != '\n')
  193. text++;
  194. if (*text == '\n')
  195. { // a newline means end of command in buffer,
  196. // thus end of this command's args too
  197. text++;
  198. break;
  199. }
  200. if (!*text)
  201. return;
  202. if (com_argc == 1)
  203. com_args = reinterpret_cast<char *>(text);
  204. text = COM_Parse(text);
  205. if (!text)
  206. return;
  207. if (com_argc < MAX_ARGS)
  208. {
  209. com_argv[com_argc] = (char *)ZZ_Alloc(strlen(com_token) + 1);
  210. strcpy(com_argv[com_argc], com_token);
  211. com_argc++;
  212. }
  213. }
  214. }
  215. // Add a command before existing ones.
  216. //
  217. void command_buffer_t::AddCommand(const char *name, com_func_t func)
  218. {
  219. xcommand_t *cmd;
  220. // fail if the command is a variable name
  221. if (consvar_t::FindVar(name))
  222. {
  223. CONS_Printf ("%s is a variable name\n", name);
  224. return;
  225. }
  226. // fail if the command already exists
  227. for (cmd=com_commands ; cmd ; cmd=cmd->next)
  228. {
  229. if (!strcasecmp(name, cmd->name))
  230. {
  231. CONS_Printf("Command %s already exists\n", name);
  232. return;
  233. }
  234. }
  235. cmd = static_cast<xcommand_t*>(Z_Malloc(sizeof(xcommand_t), PU_STATIC, NULL));
  236. cmd->name = name;
  237. cmd->function = func;
  238. cmd->next = com_commands;
  239. com_commands = cmd;
  240. }
  241. // Returns true if a command by the name given exists
  242. //
  243. bool command_buffer_t::Exists(const char *com_name)
  244. {
  245. for (xcommand_t *cmd = com_commands ; cmd ; cmd=cmd->next)
  246. {
  247. if (!strcasecmp(com_name,cmd->name))
  248. return true;
  249. }
  250. return false;
  251. }
  252. // Command completion using TAB key like '4dos'
  253. // Will skip 'skips' commands
  254. //
  255. const char *command_buffer_t::CompleteCommand(char *partial, int skips)
  256. {
  257. int len = strlen(partial);
  258. if (!len)
  259. return NULL;
  260. // check functions
  261. for (xcommand_t *cmd=com_commands ; cmd ; cmd=cmd->next)
  262. if (!strncasecmp(partial,cmd->name, len))
  263. if (!skips--)
  264. return cmd->name;
  265. return NULL;
  266. }
  267. // Parses a single line of text into arguments and tries to execute it.
  268. // The text can come from the command buffer, a remote client, or stdin.
  269. //
  270. void command_buffer_t::COM_ExecuteString(char *text)
  271. {
  272. xcommand_t *cmd;
  273. cmdalias_t *a;
  274. COM_TokenizeString(reinterpret_cast<byte*>(text)); // UTF-8 is easier to handle using unsigned chars
  275. // execute the command line
  276. if (!Argc())
  277. return; // no tokens
  278. // try to find the player "using" the command buffer
  279. for (int i=0; i<NUM_LOCALPLAYERS; i++)
  280. {
  281. com_player = LocalPlayers[0].info;
  282. if (com_player)
  283. break;
  284. }
  285. // check functions
  286. for (cmd=com_commands ; cmd ; cmd=cmd->next)
  287. {
  288. if (!strcasecmp(com_argv[0],cmd->name))
  289. {
  290. cmd->function();
  291. return;
  292. }
  293. }
  294. // check aliases
  295. for (a=com_alias ; a ; a=a->next)
  296. {
  297. if (!strcasecmp(com_argv[0], a->name))
  298. {
  299. PrependText (a->value);
  300. return;
  301. }
  302. }
  303. // check cvars
  304. // Hurdler: added at Ebola's request ;)
  305. // (don't flood the console in software mode with bad gr_xxx command)
  306. if (!consvar_t::Command())
  307. {
  308. CONS_Printf("Unknown command '%s'\n", Argv(0));
  309. }
  310. }
  311. //============================================================================
  312. // SCRIPT PARSE
  313. //============================================================================
  314. // Parse a token out of a string, handles script files too
  315. // returns the data pointer after the token
  316. byte *command_buffer_t::COM_Parse(byte *data)
  317. {
  318. if (!data)
  319. return NULL;
  320. int c;
  321. int len = 0;
  322. com_token[0] = 0;
  323. // skip whitespace
  324. skipwhite:
  325. while ( (c = *data) <= ' ')
  326. {
  327. if (c == 0)
  328. return NULL; // end of file;
  329. data++;
  330. }
  331. // skip // comments
  332. if (c=='/' && data[1] == '/')
  333. {
  334. while (*data && *data != '\n')
  335. data++;
  336. goto skipwhite;
  337. }
  338. // handle quoted strings specially
  339. if (c == '\"')
  340. {
  341. data++;
  342. while (1)
  343. {
  344. c = *data++;
  345. if (c=='\"' || !c)
  346. {
  347. com_token[len] = 0;
  348. return data;
  349. }
  350. com_token[len] = c;
  351. len++;
  352. }
  353. }
  354. // parse single characters
  355. if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
  356. {
  357. com_token[len] = c;
  358. len++;
  359. com_token[len] = 0;
  360. return data+1;
  361. }
  362. // parse a regular word
  363. do
  364. {
  365. com_token[len] = c;
  366. data++;
  367. len++;
  368. c = *data;
  369. if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
  370. break;
  371. } while (c>32);
  372. com_token[len] = 0;
  373. return data;
  374. }
  375. // =========================================================================
  376. // SCRIPT COMMANDS
  377. // =========================================================================
  378. // alias command : a command name that replaces another command
  379. //
  380. static void COM_Alias_f()
  381. {
  382. cmdalias_t *a;
  383. char cmd[1024];
  384. int i, c;
  385. if (COM.Argc()<3)
  386. {
  387. CONS_Printf("alias <name> <command>\n");
  388. return;
  389. }
  390. a = (cmdalias_t *)ZZ_Alloc(sizeof(cmdalias_t));
  391. a->next = COM.com_alias;
  392. COM.com_alias = a;
  393. a->name = Z_StrDup (COM.Argv(1));
  394. // copy the rest of the command line
  395. cmd[0] = 0; // start out with a null string
  396. c = COM.Argc();
  397. for (i=2 ; i< c ; i++)
  398. {
  399. strcat(cmd, COM.Argv(i));
  400. if (i != c)
  401. strcat(cmd, " ");
  402. }
  403. strcat (cmd, "\n");
  404. a->value = Z_StrDup(cmd);
  405. }
  406. // Echo a line of text to console
  407. //
  408. static void COM_Echo_f()
  409. {
  410. for (int i = 1; i < COM.Argc(); i++)
  411. CONS_Printf("%s ",COM.Argv(i));
  412. CONS_Printf("\n");
  413. }
  414. // Execute a script file
  415. //
  416. static void COM_Exec_f()
  417. {
  418. if (COM.Argc() != 2)
  419. {
  420. CONS_Printf ("exec <filename> : run a script file\n");
  421. return;
  422. }
  423. byte *buf = NULL;
  424. // load file
  425. FIL_ReadFile(COM.Argv(1), &buf);
  426. if (!buf)
  427. {
  428. CONS_Printf ("Couldn't execute file %s\n",COM.Argv(1));
  429. return;
  430. }
  431. CONS_Printf ("Executing %s\n",COM.Argv(1));
  432. // insert text file into the command buffer
  433. COM.PrependText((char *)buf);
  434. // free buffer
  435. Z_Free(buf);
  436. }
  437. // Delay execution of the rest of the commands to the next frame,
  438. // allows sequences of commands like "jump; fire; backward"
  439. //
  440. static void COM_Wait_f()
  441. {
  442. if (COM.Argc()>1)
  443. COM.com_wait = atoi(COM.Argv(1));
  444. else
  445. COM.com_wait = 1; // 1 frame
  446. }
  447. static void COM_Help_f()
  448. {
  449. consvar_t *cvar;
  450. if (COM.Argc() > 1)
  451. {
  452. cvar = consvar_t::FindVar(COM.Argv(1));
  453. if (cvar)
  454. {
  455. CONS_Printf("Variable %s:\n", cvar->name);
  456. CONS_Printf(" flags :");
  457. if( cvar->flags & CV_SAVE )
  458. CONS_Printf("AUTOSAVE ");
  459. if( cvar->flags & CV_FLOAT )
  460. CONS_Printf("FLOAT ");
  461. if( cvar->flags & CV_NETVAR )
  462. CONS_Printf("NETVAR ");
  463. if( cvar->flags & CV_CALL )
  464. CONS_Printf("ACTION ");
  465. CONS_Printf("\n");
  466. if (cvar->PossibleValue)
  467. {
  468. if (!strcmp(cvar->PossibleValue[0].strvalue, "MIN"))
  469. {
  470. CONS_Printf(" range from %d to %d\n",cvar->PossibleValue[0].value, cvar->PossibleValue[1].value);
  471. }
  472. else
  473. {
  474. CONS_Printf(" possible values:\n");
  475. for (int i=0; cvar->PossibleValue[i].strvalue; i++)
  476. CONS_Printf(" %-2d : %s\n",cvar->PossibleValue[i].value, cvar->PossibleValue[i].strvalue);
  477. }
  478. }
  479. }
  480. else
  481. CONS_Printf("No Help for this command/variable\n");
  482. }
  483. else
  484. {
  485. int i=0;
  486. // commands
  487. CONS_Printf("\2Commands:\n");
  488. for (xcommand_t *cmd = COM.com_commands; cmd; cmd=cmd->next)
  489. {
  490. CONS_Printf("%s ",cmd->name);
  491. i++;
  492. }
  493. // variables
  494. CONS_Printf("\2\nVariables:\n");
  495. for (cvar = consvar_t::cvar_list; cvar; cvar = cvar->next)
  496. {
  497. CONS_Printf("%s ",cvar->name);
  498. i++;
  499. }
  500. CONS_Printf("\2\nread docs/console.html for more or type help <command or variable>\n");
  501. if (devparm)
  502. CONS_Printf("\2Total : %d\n",i);
  503. }
  504. }
  505. static void COM_Toggle_f()
  506. {
  507. if (COM.Argc() != 2 && COM.Argc() != 3)
  508. {
  509. CONS_Printf("Toggle <cvar_name> [-1]\n"
  510. "Toggle the value of a cvar\n");
  511. return;
  512. }
  513. consvar_t *cvar = consvar_t::FindVar(COM.Argv(1));
  514. if (!cvar)
  515. {
  516. CONS_Printf("%s is not a cvar\n",COM.Argv(1));
  517. return;
  518. }
  519. // netcvar don't change imediately
  520. cvar->flags |= CV_ANNOUNCE_ONCE;
  521. if (COM.Argc() == 3)
  522. cvar->AddValue(atol(COM.Argv(2)));
  523. else
  524. cvar->AddValue(1);
  525. }
  526. //==========================================================================
  527. //
  528. // CONSOLE VARIABLES
  529. //
  530. // console variables are a simple way of changing variables of the game
  531. // through the console or code, at run time.
  532. //
  533. // console vars acts like simplified commands, because a function can be
  534. // attached to them, and called whenever a console var is modified
  535. //
  536. //==========================================================================
  537. consvar_t *consvar_t::cvar_list = NULL;
  538. CV_PossibleValue_t CV_OnOff[] = {{0,"Off"}, {1,"On"}, {0,NULL}};
  539. CV_PossibleValue_t CV_YesNo[] = {{0,"No"} , {1,"Yes"}, {0,NULL}};
  540. CV_PossibleValue_t CV_Unsigned[] = {{0,"MIN"}, {999999999,"MAX"}, {0,NULL}};
  541. // Search if a variable has been registered
  542. // returns true if given variable has been registered
  543. consvar_t *consvar_t::FindVar(const char *name)
  544. {
  545. for (consvar_t *cvar = cvar_list; cvar; cvar = cvar->next)
  546. if (!strcasecmp(name, cvar->name))
  547. return cvar;
  548. return NULL;
  549. }
  550. // Build a unique Net Variable identifier number, that is used
  551. // in network packets instead of the fullname
  552. Uint16 consvar_t::ComputeNetid(const char *s)
  553. {
  554. static int premiers[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
  555. Uint16 ret = 0;
  556. int i = 0;
  557. while (*s)
  558. {
  559. ret += (*s)*premiers[i];
  560. s++;
  561. i = (i+1)%16;
  562. }
  563. return ret;
  564. }
  565. // Return the Net Variable, from it's identifier number
  566. consvar_t *consvar_t::FindNetVar(unsigned short netid)
  567. {
  568. for (consvar_t *cvar = cvar_list; cvar; cvar = cvar->next)
  569. if (cvar->netid == netid)
  570. return cvar;
  571. return NULL;
  572. }
  573. //
  574. // set value to the variable, no checking, only for internal use
  575. //
  576. bool consvar_t::Setvalue(const char *s)
  577. {
  578. if (PossibleValue)
  579. {
  580. char *tail;
  581. int v = strtol(s, &tail, 0);
  582. if (!strcmp(PossibleValue[0].strvalue, "MIN"))
  583. {
  584. // bounded cvar
  585. if (v < PossibleValue[0].value)
  586. v = PossibleValue[0].value;
  587. else if (v > PossibleValue[1].value)
  588. v = PossibleValue[1].value;
  589. value = v;
  590. sprintf(str, "%d", v);
  591. }
  592. else
  593. {
  594. // array of value/name pairs
  595. int i;
  596. if (tail == s)
  597. {
  598. // no succesful number conversion, so it's a string
  599. for (i=0; PossibleValue[i].strvalue; i++)
  600. if (!strcasecmp(PossibleValue[i].strvalue, s))
  601. break;
  602. }
  603. else
  604. {
  605. // int value then
  606. for (i=0; PossibleValue[i].strvalue; i++)
  607. if (v == PossibleValue[i].value)
  608. break;
  609. }
  610. if (!PossibleValue[i].strvalue)
  611. {
  612. CONS_Printf("\"%s\" is not a possible value for \"%s\"\n", s, name);
  613. return false;
  614. }
  615. value = PossibleValue[i].value;
  616. strncpy(str, PossibleValue[i].strvalue, CV_STRLEN);
  617. }
  618. }
  619. else
  620. {
  621. strncpy(str, s, CV_STRLEN);
  622. if (flags & CV_FLOAT)
  623. value = int(atof(str) * fixed_t::UNIT); // 16.16 fixed point
  624. else
  625. value = atoi(str);
  626. }
  627. if (flags & CV_ANNOUNCE_ONCE || flags & CV_ANNOUNCE)
  628. {
  629. CONS_Printf("%s set to %s\n", name, str);
  630. flags &= ~CV_ANNOUNCE_ONCE;
  631. }
  632. flags |= CV_MODIFIED;
  633. // raise 'on change' code
  634. if (flags & CV_CALL)
  635. func();
  636. return true;
  637. }
  638. // Register a variable, that can be used later at the console
  639. bool consvar_t::Reg()
  640. {
  641. // link the variable in, unless it is for internal use only
  642. if (!(flags & CV_HIDDEN))
  643. {
  644. // first check to see if it has already been defined
  645. if (FindVar(name))
  646. {
  647. CONS_Printf("Variable %s is already defined\n", name);
  648. return false;
  649. }
  650. // check for overlap with a command
  651. if (COM.Exists(name))
  652. {
  653. I_Error("%s is a command name\n", name);
  654. return false;
  655. }
  656. // check net variables
  657. if (flags & CV_NETVAR)
  658. {
  659. netid = ComputeNetid(name);
  660. if (FindNetVar(netid))
  661. {
  662. I_Error("Variable %s has same netid\n", name);
  663. return false;
  664. }
  665. }
  666. next = cvar_list;
  667. cvar_list = this;
  668. }
  669. else
  670. {
  671. netid = 0;
  672. next = NULL;
  673. }
  674. str[0] = '\0';
  675. if (flags & CV_HANDLER)
  676. {
  677. flags &= ~CV_CALL;
  678. value = 0;
  679. return true;
  680. }
  681. if ((flags & CV_NOINIT) && !(flags & CV_CALL))
  682. I_Error("variable %s has CV_NOINIT without CV_CALL\n", name);
  683. if ((flags & CV_CALL) && !func)
  684. I_Error("variable %s has CV_CALL without func", name);
  685. // check possible values list
  686. // It must either be NULL, a terminated array of value/name combinations, or a MIN/MAX pair.
  687. if (PossibleValue)
  688. {
  689. if (!strcmp(PossibleValue[0].strvalue, "MIN"))
  690. {
  691. // MIN/MAX pair
  692. if (strcmp(PossibleValue[1].strvalue, "MAX"))
  693. I_Error("Bounded cvar \"%s\" without maximum!", name);
  694. if (PossibleValue[0].value >= PossibleValue[1].value)
  695. I_Error("Bounded cvar \"%s\" has no proper range!", name);
  696. }
  697. }
  698. if (flags & CV_NOINIT)
  699. flags &= ~CV_CALL;
  700. if (!Setvalue(defaultvalue))
  701. I_Error("Variable %s default value \"%s\" is not a possible value\n", name, defaultvalue);
  702. if (flags & CV_NOINIT)
  703. flags |= CV_CALL;
  704. // the SetValue will set this bit
  705. flags &= ~CV_MODIFIED;
  706. return true;
  707. }
  708. // Completes the name of a console var
  709. const char *consvar_t::CompleteVar(const char *partial, int skips)
  710. {
  711. int len = strlen(partial);
  712. if (!len)
  713. return NULL;
  714. // check functions
  715. for (consvar_t *cvar = cvar_list; cvar; cvar = cvar->next)
  716. if (!strncasecmp(partial, cvar->name, len))
  717. if (!skips--)
  718. return cvar->name;
  719. return NULL;
  720. }
  721. // set a new value to a netvar
  722. void consvar_t::GotNetVar(unsigned short id, const char *str)
  723. {
  724. consvar_t *cvar = consvar_t::FindNetVar(id);
  725. if (!cvar)
  726. {
  727. CONS_Printf("\2Netvar not found\n");
  728. return;
  729. }
  730. cvar->Setvalue(str);
  731. }
  732. // write the netvars into a packet
  733. void consvar_t::SaveNetVars(TNL::BitStream &s)
  734. {
  735. for (consvar_t *cvar = cvar_list; cvar; cvar = cvar->next)
  736. if (cvar->flags & CV_NETVAR)
  737. {
  738. s.write(cvar->netid);
  739. s.writeString(cvar->str);
  740. }
  741. }
  742. // read the netvars from a packet
  743. void consvar_t::LoadNetVars(TNL::BitStream &s)
  744. {
  745. for (consvar_t *cvar = cvar_list; cvar; cvar = cvar->next)
  746. if (cvar->flags & CV_NETVAR)
  747. {
  748. // the for loop is just used for count
  749. unsigned short id;
  750. s.read(&id);
  751. char temp[256];
  752. s.readString(temp);
  753. GotNetVar(id, temp);
  754. }
  755. }
  756. // as if "<varname> <value>" is entered at the console
  757. void consvar_t::Set(const char *s)
  758. {
  759. if (!(flags & CV_HIDDEN))
  760. {
  761. consvar_t *cv;
  762. // am i registered?
  763. for (cv = cvar_list; cv; cv = cv->next)
  764. if (cv == this)
  765. break;
  766. if (!cv)
  767. Reg();
  768. if (flags & CV_NOTINNET && game.netgame)
  769. {
  770. CONS_Printf("This variable cannot be changed while in a netgame.\n");
  771. return;
  772. }
  773. }
  774. #ifdef PARANOIA
  775. if (!str)
  776. I_Error("CV_Set : %s no string set ?!\n",name);
  777. #endif
  778. if (!strcmp(str, s))
  779. return; // no changes
  780. if (flags & CV_NETVAR && game.netgame)
  781. {
  782. // send the value of the variable
  783. if (!game.server)
  784. {
  785. CONS_Printf("Only the server can change this variable\n");
  786. return;
  787. }
  788. if (Setvalue(s))
  789. game.net->SendNetVar(netid, str);
  790. return;
  791. }
  792. Setvalue(s);
  793. }
  794. // Expands value to string before calling CV_Set()
  795. void consvar_t::Set(int newval)
  796. {
  797. char val[32];
  798. sprintf(val, "%d", newval);
  799. Set(val);
  800. }
  801. // increments the cvar
  802. void consvar_t::AddValue(int increment)
  803. {
  804. if (flags & CV_HANDLER)
  805. {
  806. reinterpret_cast<void (*)(consvar_t*, int)>(func)(this, increment);
  807. return;
  808. }
  809. int newvalue = value + increment;
  810. if (PossibleValue)
  811. {
  812. if (!strcmp(PossibleValue[0].strvalue, "MIN"))
  813. {
  814. // bounded cvar
  815. int minval = PossibleValue[0].value;
  816. int maxval = PossibleValue[1].value;
  817. // wrap
  818. if (newvalue < minval)
  819. newvalue = maxval;
  820. else if (newvalue > maxval)
  821. newvalue = minval;
  822. }
  823. else
  824. {
  825. int n; // count all possible values
  826. int i = -1;
  827. // values must not be repeated in possiblevalues array
  828. for (n=0; PossibleValue[n].strvalue; n++)
  829. if (PossibleValue[n].value == value)
  830. i = n;
  831. #ifdef PARANOIA
  832. if (i == -1)
  833. I_Error("consvar_t::AddValue: current value %d not found in possible values!\n", value);
  834. #endif
  835. i = (i + increment + n) % n;
  836. if (i < 0)
  837. i = 0; // if increment is -1000 or something
  838. Set(PossibleValue[i].strvalue);
  839. return;
  840. }
  841. }
  842. Set(newvalue);
  843. }
  844. /// Returns value as fixed_t, handles CV_FLOAT correctly.
  845. fixed_t consvar_t::Get() const
  846. {
  847. fixed_t res;
  848. if (flags & CV_FLOAT)
  849. res.setvalue(value);
  850. else
  851. res = value;
  852. return res;
  853. }
  854. // Displays or changes variable from the console
  855. //
  856. // Returns false if the passed command was not recognised as
  857. // console variable.
  858. bool consvar_t::Command()
  859. {
  860. // check variables
  861. consvar_t *v = FindVar(COM.Argv(0));
  862. if (!v)
  863. return false;
  864. // perform a variable print or set
  865. if (COM.Argc() == 1)
  866. {
  867. CONS_Printf("\"%s\" is \"%s\" default is \"%s\"\n", v->name, v->str, v->defaultvalue);
  868. return true;
  869. }
  870. v->Set(COM.Argv(1));
  871. return true;
  872. }
  873. // Save console variables that have the CV_SAVE flag set
  874. void consvar_t::SaveVariables(FILE *f)
  875. {
  876. for (consvar_t *cvar = cvar_list; cvar; cvar=cvar->next)
  877. if (cvar->flags & CV_SAVE)
  878. fprintf(f, "%s \"%s\"\n", cvar->name, cvar->str);
  879. }