PageRenderTime 36ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/darkplaces/host_cmd.c

#
C | 2867 lines | 2254 code | 269 blank | 344 comment | 545 complexity | 2aa521e4f6db41dcd6ea1f7ad1890f4a MD5 | raw file
Possible License(s): GPL-2.0
  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. #include "quakedef.h"
  16. #include "sv_demo.h"
  17. #include "image.h"
  18. // for secure rcon authentication
  19. #include "hmac.h"
  20. #include "mdfour.h"
  21. #include <time.h>
  22. int current_skill;
  23. cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
  24. cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
  25. cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
  26. cvar_t sv_status_show_qcstatus = {CVAR_SAVE, "sv_status_show_qcstatus", "0", "show the 'qcstatus' field in status replies, not the 'frags' field. Turn this on if your mod uses this field, and the 'frags' field on the other hand has no meaningful value."};
  27. cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
  28. cvar_t rcon_secure = {CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
  29. cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
  30. cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
  31. cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
  32. cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
  33. cvar_t r_fixtrans_auto = {0, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"};
  34. qboolean allowcheats = false;
  35. extern qboolean host_shuttingdown;
  36. extern cvar_t developer_entityparsing;
  37. /*
  38. ==================
  39. Host_Quit_f
  40. ==================
  41. */
  42. void Host_Quit_f (void)
  43. {
  44. if(host_shuttingdown)
  45. Con_Printf("shutting down already!\n");
  46. else
  47. Sys_Quit (0);
  48. }
  49. /*
  50. ==================
  51. Host_Status_f
  52. ==================
  53. */
  54. void Host_Status_f (void)
  55. {
  56. char qcstatus[256];
  57. client_t *client;
  58. int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
  59. void (*print) (const char *fmt, ...);
  60. char ip[22];
  61. int frags;
  62. if (cmd_source == src_command)
  63. {
  64. // if running a client, try to send over network so the client's status report parser will see the report
  65. if (cls.state == ca_connected)
  66. {
  67. Cmd_ForwardToServer ();
  68. return;
  69. }
  70. print = Con_Printf;
  71. }
  72. else
  73. print = SV_ClientPrintf;
  74. if (!sv.active)
  75. return;
  76. if(cmd_source == src_command)
  77. SV_VM_Begin();
  78. in = 0;
  79. if (Cmd_Argc() == 2)
  80. {
  81. if (strcmp(Cmd_Argv(1), "1") == 0)
  82. in = 1;
  83. else if (strcmp(Cmd_Argv(1), "2") == 0)
  84. in = 2;
  85. }
  86. for (players = 0, i = 0;i < svs.maxclients;i++)
  87. if (svs.clients[i].active)
  88. players++;
  89. print ("host: %s\n", Cvar_VariableString ("hostname"));
  90. print ("version: %s build %s\n", gamename, buildstring);
  91. print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
  92. print ("map: %s\n", sv.name);
  93. print ("timing: %s\n", Host_TimingReport());
  94. print ("players: %i active (%i max)\n\n", players, svs.maxclients);
  95. if (in == 1)
  96. print ("^2IP %%pl ping time frags no name\n");
  97. else if (in == 2)
  98. print ("^5IP no name\n");
  99. for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
  100. {
  101. if (!client->active)
  102. continue;
  103. ++k;
  104. if (in == 0 || in == 1)
  105. {
  106. seconds = (int)(realtime - client->connecttime);
  107. minutes = seconds / 60;
  108. if (minutes)
  109. {
  110. seconds -= (minutes * 60);
  111. hours = minutes / 60;
  112. if (hours)
  113. minutes -= (hours * 60);
  114. }
  115. else
  116. hours = 0;
  117. packetloss = 0;
  118. if (client->netconnection)
  119. for (j = 0;j < NETGRAPH_PACKETS;j++)
  120. if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
  121. packetloss++;
  122. packetloss = packetloss * 100 / NETGRAPH_PACKETS;
  123. ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
  124. }
  125. if(sv_status_privacy.integer && cmd_source != src_command)
  126. strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
  127. else
  128. strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
  129. frags = client->frags;
  130. if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
  131. {
  132. const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
  133. if(str && *str)
  134. {
  135. char *p;
  136. const char *q;
  137. p = qcstatus;
  138. for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
  139. if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
  140. *p++ = *q;
  141. *p = 0;
  142. if(*qcstatus)
  143. frags = atoi(qcstatus);
  144. }
  145. }
  146. if (in == 0) // default layout
  147. {
  148. print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
  149. print (" %s\n", ip);
  150. }
  151. else if (in == 1) // extended layout
  152. {
  153. print ("%s%-21s %2i %4i %2i:%02i:%02i %4i #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, packetloss, ping, hours, minutes, seconds, frags, i+1, client->name);
  154. }
  155. else if (in == 2) // reduced layout
  156. {
  157. print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
  158. }
  159. }
  160. if(cmd_source == src_command)
  161. SV_VM_End();
  162. }
  163. /*
  164. ==================
  165. Host_God_f
  166. Sets client to godmode
  167. ==================
  168. */
  169. void Host_God_f (void)
  170. {
  171. if (!allowcheats)
  172. {
  173. SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
  174. return;
  175. }
  176. host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
  177. if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
  178. SV_ClientPrint("godmode OFF\n");
  179. else
  180. SV_ClientPrint("godmode ON\n");
  181. }
  182. void Host_Notarget_f (void)
  183. {
  184. if (!allowcheats)
  185. {
  186. SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
  187. return;
  188. }
  189. host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
  190. if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
  191. SV_ClientPrint("notarget OFF\n");
  192. else
  193. SV_ClientPrint("notarget ON\n");
  194. }
  195. qboolean noclip_anglehack;
  196. void Host_Noclip_f (void)
  197. {
  198. if (!allowcheats)
  199. {
  200. SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
  201. return;
  202. }
  203. if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
  204. {
  205. noclip_anglehack = true;
  206. host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
  207. SV_ClientPrint("noclip ON\n");
  208. }
  209. else
  210. {
  211. noclip_anglehack = false;
  212. host_client->edict->fields.server->movetype = MOVETYPE_WALK;
  213. SV_ClientPrint("noclip OFF\n");
  214. }
  215. }
  216. /*
  217. ==================
  218. Host_Fly_f
  219. Sets client to flymode
  220. ==================
  221. */
  222. void Host_Fly_f (void)
  223. {
  224. if (!allowcheats)
  225. {
  226. SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
  227. return;
  228. }
  229. if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
  230. {
  231. host_client->edict->fields.server->movetype = MOVETYPE_FLY;
  232. SV_ClientPrint("flymode ON\n");
  233. }
  234. else
  235. {
  236. host_client->edict->fields.server->movetype = MOVETYPE_WALK;
  237. SV_ClientPrint("flymode OFF\n");
  238. }
  239. }
  240. /*
  241. ==================
  242. Host_Ping_f
  243. ==================
  244. */
  245. void Host_Pings_f (void); // called by Host_Ping_f
  246. void Host_Ping_f (void)
  247. {
  248. int i;
  249. client_t *client;
  250. void (*print) (const char *fmt, ...);
  251. if (cmd_source == src_command)
  252. {
  253. // if running a client, try to send over network so the client's ping report parser will see the report
  254. if (cls.state == ca_connected)
  255. {
  256. Cmd_ForwardToServer ();
  257. return;
  258. }
  259. print = Con_Printf;
  260. }
  261. else
  262. print = SV_ClientPrintf;
  263. if (!sv.active)
  264. return;
  265. print("Client ping times:\n");
  266. for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
  267. {
  268. if (!client->active)
  269. continue;
  270. print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
  271. }
  272. // now call the Pings command also, which will send a report that contains packet loss for the scoreboard (as well as a simpler ping report)
  273. // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
  274. //Host_Pings_f();
  275. }
  276. /*
  277. ===============================================================================
  278. SERVER TRANSITIONS
  279. ===============================================================================
  280. */
  281. /*
  282. ======================
  283. Host_Map_f
  284. handle a
  285. map <servername>
  286. command from the console. Active clients are kicked off.
  287. ======================
  288. */
  289. void Host_Map_f (void)
  290. {
  291. char level[MAX_QPATH];
  292. if (Cmd_Argc() != 2)
  293. {
  294. Con_Print("map <levelname> : start a new game (kicks off all players)\n");
  295. return;
  296. }
  297. // GAME_DELUXEQUAKE - clear warpmark (used by QC)
  298. if (gamemode == GAME_DELUXEQUAKE)
  299. Cvar_Set("warpmark", "");
  300. cls.demonum = -1; // stop demo loop in case this fails
  301. CL_Disconnect ();
  302. Host_ShutdownServer();
  303. if(svs.maxclients != svs.maxclients_next)
  304. {
  305. svs.maxclients = svs.maxclients_next;
  306. if (svs.clients)
  307. Mem_Free(svs.clients);
  308. svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
  309. }
  310. // remove menu
  311. key_dest = key_game;
  312. svs.serverflags = 0; // haven't completed an episode yet
  313. allowcheats = sv_cheats.integer != 0;
  314. strlcpy(level, Cmd_Argv(1), sizeof(level));
  315. SV_SpawnServer(level);
  316. if (sv.active && cls.state == ca_disconnected)
  317. CL_EstablishConnection("local:1");
  318. }
  319. /*
  320. ==================
  321. Host_Changelevel_f
  322. Goes to a new map, taking all clients along
  323. ==================
  324. */
  325. void Host_Changelevel_f (void)
  326. {
  327. char level[MAX_QPATH];
  328. if (Cmd_Argc() != 2)
  329. {
  330. Con_Print("changelevel <levelname> : continue game on a new level\n");
  331. return;
  332. }
  333. // HACKHACKHACK
  334. if (!sv.active) {
  335. Host_Map_f();
  336. return;
  337. }
  338. // remove menu
  339. key_dest = key_game;
  340. SV_VM_Begin();
  341. SV_SaveSpawnparms ();
  342. SV_VM_End();
  343. allowcheats = sv_cheats.integer != 0;
  344. strlcpy(level, Cmd_Argv(1), sizeof(level));
  345. SV_SpawnServer(level);
  346. if (sv.active && cls.state == ca_disconnected)
  347. CL_EstablishConnection("local:1");
  348. }
  349. /*
  350. ==================
  351. Host_Restart_f
  352. Restarts the current server for a dead player
  353. ==================
  354. */
  355. void Host_Restart_f (void)
  356. {
  357. char mapname[MAX_QPATH];
  358. if (Cmd_Argc() != 1)
  359. {
  360. Con_Print("restart : restart current level\n");
  361. return;
  362. }
  363. if (!sv.active)
  364. {
  365. Con_Print("Only the server may restart\n");
  366. return;
  367. }
  368. // remove menu
  369. key_dest = key_game;
  370. allowcheats = sv_cheats.integer != 0;
  371. strlcpy(mapname, sv.name, sizeof(mapname));
  372. SV_SpawnServer(mapname);
  373. if (sv.active && cls.state == ca_disconnected)
  374. CL_EstablishConnection("local:1");
  375. }
  376. /*
  377. ==================
  378. Host_Reconnect_f
  379. This command causes the client to wait for the signon messages again.
  380. This is sent just before a server changes levels
  381. ==================
  382. */
  383. void Host_Reconnect_f (void)
  384. {
  385. char temp[128];
  386. // if not connected, reconnect to the most recent server
  387. if (!cls.netcon)
  388. {
  389. // if we have connected to a server recently, the userinfo
  390. // will still contain its IP address, so get the address...
  391. InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
  392. if (temp[0])
  393. CL_EstablishConnection(temp);
  394. else
  395. Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
  396. return;
  397. }
  398. // if connected, do something based on protocol
  399. if (cls.protocol == PROTOCOL_QUAKEWORLD)
  400. {
  401. // quakeworld can just re-login
  402. if (cls.qw_downloadmemory) // don't change when downloading
  403. return;
  404. S_StopAllSounds();
  405. if (cls.state == ca_connected && cls.signon < SIGNONS)
  406. {
  407. Con_Printf("reconnecting...\n");
  408. MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
  409. MSG_WriteString(&cls.netcon->message, "new");
  410. }
  411. }
  412. else
  413. {
  414. // netquake uses reconnect on level changes (silly)
  415. if (Cmd_Argc() != 1)
  416. {
  417. Con_Print("reconnect : wait for signon messages again\n");
  418. return;
  419. }
  420. if (!cls.signon)
  421. {
  422. Con_Print("reconnect: no signon, ignoring reconnect\n");
  423. return;
  424. }
  425. cls.signon = 0; // need new connection messages
  426. }
  427. }
  428. /*
  429. =====================
  430. Host_Connect_f
  431. User command to connect to server
  432. =====================
  433. */
  434. void Host_Connect_f (void)
  435. {
  436. if (Cmd_Argc() != 2)
  437. {
  438. Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
  439. return;
  440. }
  441. // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
  442. if(!rcon_secure.integer)
  443. Cvar_SetQuick(&rcon_password, "");
  444. CL_EstablishConnection(Cmd_Argv(1));
  445. }
  446. /*
  447. ===============================================================================
  448. LOAD / SAVE GAME
  449. ===============================================================================
  450. */
  451. #define SAVEGAME_VERSION 5
  452. void Host_Savegame_to (const char *name)
  453. {
  454. qfile_t *f;
  455. int i, lightstyles = 64;
  456. char comment[SAVEGAME_COMMENT_LENGTH+1];
  457. qboolean isserver;
  458. // first we have to figure out if this can be saved in 64 lightstyles
  459. // (for Quake compatibility)
  460. for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
  461. if (sv.lightstyles[i][0])
  462. lightstyles = i+1;
  463. isserver = !strcmp(PRVM_NAME, "server");
  464. Con_Printf("Saving game to %s...\n", name);
  465. f = FS_OpenRealFile(name, "wb", false);
  466. if (!f)
  467. {
  468. Con_Print("ERROR: couldn't open.\n");
  469. return;
  470. }
  471. FS_Printf(f, "%i\n", SAVEGAME_VERSION);
  472. memset(comment, 0, sizeof(comment));
  473. if(isserver)
  474. dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog->edicts->fields.server->message), (int)prog->globals.server->killed_monsters, (int)prog->globals.server->total_monsters);
  475. else
  476. dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
  477. // convert space to _ to make stdio happy
  478. // LordHavoc: convert control characters to _ as well
  479. for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
  480. if (ISWHITESPACEORCONTROL(comment[i]))
  481. comment[i] = '_';
  482. comment[SAVEGAME_COMMENT_LENGTH] = '\0';
  483. FS_Printf(f, "%s\n", comment);
  484. if(isserver)
  485. {
  486. for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
  487. FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
  488. FS_Printf(f, "%d\n", current_skill);
  489. FS_Printf(f, "%s\n", sv.name);
  490. FS_Printf(f, "%f\n",sv.time);
  491. }
  492. else
  493. {
  494. for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
  495. FS_Printf(f, "(dummy)\n");
  496. FS_Printf(f, "%d\n", 0);
  497. FS_Printf(f, "%s\n", "(dummy)");
  498. FS_Printf(f, "%f\n", realtime);
  499. }
  500. // write the light styles
  501. for (i=0 ; i<lightstyles ; i++)
  502. {
  503. if (isserver && sv.lightstyles[i][0])
  504. FS_Printf(f, "%s\n", sv.lightstyles[i]);
  505. else
  506. FS_Print(f,"m\n");
  507. }
  508. PRVM_ED_WriteGlobals (f);
  509. for (i=0 ; i<prog->num_edicts ; i++)
  510. {
  511. FS_Printf(f,"// edict %d\n", i);
  512. //Con_Printf("edict %d...\n", i);
  513. PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
  514. }
  515. #if 1
  516. FS_Printf(f,"/*\n");
  517. FS_Printf(f,"// DarkPlaces extended savegame\n");
  518. // darkplaces extension - extra lightstyles, support for color lightstyles
  519. for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
  520. if (isserver && sv.lightstyles[i][0])
  521. FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
  522. // darkplaces extension - model precaches
  523. for (i=1 ; i<MAX_MODELS ; i++)
  524. if (sv.model_precache[i][0])
  525. FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
  526. // darkplaces extension - sound precaches
  527. for (i=1 ; i<MAX_SOUNDS ; i++)
  528. if (sv.sound_precache[i][0])
  529. FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
  530. FS_Printf(f,"*/\n");
  531. #endif
  532. FS_Close (f);
  533. Con_Print("done.\n");
  534. }
  535. /*
  536. ===============
  537. Host_Savegame_f
  538. ===============
  539. */
  540. void Host_Savegame_f (void)
  541. {
  542. char name[MAX_QPATH];
  543. if (!sv.active)
  544. {
  545. Con_Print("Can't save - no server running.\n");
  546. return;
  547. }
  548. if (cl.islocalgame)
  549. {
  550. // singleplayer checks
  551. if (cl.intermission)
  552. {
  553. Con_Print("Can't save in intermission.\n");
  554. return;
  555. }
  556. if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
  557. {
  558. Con_Print("Can't savegame with a dead player\n");
  559. return;
  560. }
  561. }
  562. else
  563. Con_Print("Warning: saving a multiplayer game may have strange results when restored (to properly resume, all players must join in the same player slots and then the game can be reloaded).\n");
  564. if (Cmd_Argc() != 2)
  565. {
  566. Con_Print("save <savename> : save a game\n");
  567. return;
  568. }
  569. if (strstr(Cmd_Argv(1), ".."))
  570. {
  571. Con_Print("Relative pathnames are not allowed.\n");
  572. return;
  573. }
  574. strlcpy (name, Cmd_Argv(1), sizeof (name));
  575. FS_DefaultExtension (name, ".sav", sizeof (name));
  576. SV_VM_Begin();
  577. Host_Savegame_to(name);
  578. SV_VM_End();
  579. }
  580. /*
  581. ===============
  582. Host_Loadgame_f
  583. ===============
  584. */
  585. void Host_Loadgame_f (void)
  586. {
  587. char filename[MAX_QPATH];
  588. char mapname[MAX_QPATH];
  589. float time;
  590. const char *start;
  591. const char *end;
  592. const char *t;
  593. char *text;
  594. prvm_edict_t *ent;
  595. int i;
  596. int entnum;
  597. int version;
  598. float spawn_parms[NUM_SPAWN_PARMS];
  599. if (Cmd_Argc() != 2)
  600. {
  601. Con_Print("load <savename> : load a game\n");
  602. return;
  603. }
  604. strlcpy (filename, Cmd_Argv(1), sizeof(filename));
  605. FS_DefaultExtension (filename, ".sav", sizeof (filename));
  606. Con_Printf("Loading game from %s...\n", filename);
  607. // stop playing demos
  608. if (cls.demoplayback)
  609. CL_Disconnect ();
  610. // remove menu
  611. key_dest = key_game;
  612. cls.demonum = -1; // stop demo loop in case this fails
  613. t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
  614. if (!text)
  615. {
  616. Con_Print("ERROR: couldn't open.\n");
  617. return;
  618. }
  619. if(developer_entityparsing.integer)
  620. Con_Printf("Host_Loadgame_f: loading version\n");
  621. // version
  622. COM_ParseToken_Simple(&t, false, false);
  623. version = atoi(com_token);
  624. if (version != SAVEGAME_VERSION)
  625. {
  626. Mem_Free(text);
  627. Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
  628. return;
  629. }
  630. if(developer_entityparsing.integer)
  631. Con_Printf("Host_Loadgame_f: loading description\n");
  632. // description
  633. COM_ParseToken_Simple(&t, false, false);
  634. for (i = 0;i < NUM_SPAWN_PARMS;i++)
  635. {
  636. COM_ParseToken_Simple(&t, false, false);
  637. spawn_parms[i] = atof(com_token);
  638. }
  639. // skill
  640. COM_ParseToken_Simple(&t, false, false);
  641. // this silliness is so we can load 1.06 save files, which have float skill values
  642. current_skill = (int)(atof(com_token) + 0.5);
  643. Cvar_SetValue ("skill", (float)current_skill);
  644. if(developer_entityparsing.integer)
  645. Con_Printf("Host_Loadgame_f: loading mapname\n");
  646. // mapname
  647. COM_ParseToken_Simple(&t, false, false);
  648. strlcpy (mapname, com_token, sizeof(mapname));
  649. if(developer_entityparsing.integer)
  650. Con_Printf("Host_Loadgame_f: loading time\n");
  651. // time
  652. COM_ParseToken_Simple(&t, false, false);
  653. time = atof(com_token);
  654. allowcheats = sv_cheats.integer != 0;
  655. if(developer_entityparsing.integer)
  656. Con_Printf("Host_Loadgame_f: spawning server\n");
  657. SV_SpawnServer (mapname);
  658. if (!sv.active)
  659. {
  660. Mem_Free(text);
  661. Con_Print("Couldn't load map\n");
  662. return;
  663. }
  664. sv.paused = true; // pause until all clients connect
  665. sv.loadgame = true;
  666. if(developer_entityparsing.integer)
  667. Con_Printf("Host_Loadgame_f: loading light styles\n");
  668. // load the light styles
  669. SV_VM_Begin();
  670. // -1 is the globals
  671. entnum = -1;
  672. for (i = 0;i < MAX_LIGHTSTYLES;i++)
  673. {
  674. // light style
  675. start = t;
  676. COM_ParseToken_Simple(&t, false, false);
  677. // if this is a 64 lightstyle savegame produced by Quake, stop now
  678. // we have to check this because darkplaces may save more than 64
  679. if (com_token[0] == '{')
  680. {
  681. t = start;
  682. break;
  683. }
  684. strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
  685. }
  686. if(developer_entityparsing.integer)
  687. Con_Printf("Host_Loadgame_f: skipping until globals\n");
  688. // now skip everything before the first opening brace
  689. // (this is for forward compatibility, so that older versions (at
  690. // least ones with this fix) can load savegames with extra data before the
  691. // first brace, as might be produced by a later engine version)
  692. for (;;)
  693. {
  694. start = t;
  695. if (!COM_ParseToken_Simple(&t, false, false))
  696. break;
  697. if (com_token[0] == '{')
  698. {
  699. t = start;
  700. break;
  701. }
  702. }
  703. // load the edicts out of the savegame file
  704. end = t;
  705. for (;;)
  706. {
  707. start = t;
  708. while (COM_ParseToken_Simple(&t, false, false))
  709. if (!strcmp(com_token, "}"))
  710. break;
  711. if (!COM_ParseToken_Simple(&start, false, false))
  712. {
  713. // end of file
  714. break;
  715. }
  716. if (strcmp(com_token,"{"))
  717. {
  718. Mem_Free(text);
  719. Host_Error ("First token isn't a brace");
  720. }
  721. if (entnum == -1)
  722. {
  723. if(developer_entityparsing.integer)
  724. Con_Printf("Host_Loadgame_f: loading globals\n");
  725. // parse the global vars
  726. PRVM_ED_ParseGlobals (start);
  727. }
  728. else
  729. {
  730. // parse an edict
  731. if (entnum >= MAX_EDICTS)
  732. {
  733. Mem_Free(text);
  734. Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
  735. }
  736. while (entnum >= prog->max_edicts)
  737. PRVM_MEM_IncreaseEdicts();
  738. ent = PRVM_EDICT_NUM(entnum);
  739. memset (ent->fields.server, 0, prog->progs->entityfields * 4);
  740. ent->priv.server->free = false;
  741. if(developer_entityparsing.integer)
  742. Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
  743. PRVM_ED_ParseEdict (start, ent);
  744. // link it into the bsp tree
  745. if (!ent->priv.server->free)
  746. SV_LinkEdict (ent, false);
  747. }
  748. end = t;
  749. entnum++;
  750. }
  751. prog->num_edicts = entnum;
  752. sv.time = time;
  753. for (i = 0;i < NUM_SPAWN_PARMS;i++)
  754. svs.clients[0].spawn_parms[i] = spawn_parms[i];
  755. if(developer_entityparsing.integer)
  756. Con_Printf("Host_Loadgame_f: skipping until extended data\n");
  757. // read extended data if present
  758. // the extended data is stored inside a /* */ comment block, which the
  759. // parser intentionally skips, so we have to check for it manually here
  760. if(end)
  761. {
  762. while (*end == '\r' || *end == '\n')
  763. end++;
  764. if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
  765. {
  766. if(developer_entityparsing.integer)
  767. Con_Printf("Host_Loadgame_f: loading extended data\n");
  768. Con_Printf("Loading extended DarkPlaces savegame\n");
  769. t = end + 2;
  770. memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
  771. memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
  772. memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
  773. while (COM_ParseToken_Simple(&t, false, false))
  774. {
  775. if (!strcmp(com_token, "sv.lightstyles"))
  776. {
  777. COM_ParseToken_Simple(&t, false, false);
  778. i = atoi(com_token);
  779. COM_ParseToken_Simple(&t, false, false);
  780. if (i >= 0 && i < MAX_LIGHTSTYLES)
  781. strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
  782. else
  783. Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
  784. }
  785. else if (!strcmp(com_token, "sv.model_precache"))
  786. {
  787. COM_ParseToken_Simple(&t, false, false);
  788. i = atoi(com_token);
  789. COM_ParseToken_Simple(&t, false, false);
  790. if (i >= 0 && i < MAX_MODELS)
  791. {
  792. strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
  793. sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
  794. }
  795. else
  796. Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
  797. }
  798. else if (!strcmp(com_token, "sv.sound_precache"))
  799. {
  800. COM_ParseToken_Simple(&t, false, false);
  801. i = atoi(com_token);
  802. COM_ParseToken_Simple(&t, false, false);
  803. if (i >= 0 && i < MAX_SOUNDS)
  804. strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
  805. else
  806. Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
  807. }
  808. // skip any trailing text or unrecognized commands
  809. while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
  810. ;
  811. }
  812. }
  813. }
  814. Mem_Free(text);
  815. if(developer_entityparsing.integer)
  816. Con_Printf("Host_Loadgame_f: finished\n");
  817. SV_VM_End();
  818. // make sure we're connected to loopback
  819. if (sv.active && cls.state == ca_disconnected)
  820. CL_EstablishConnection("local:1");
  821. }
  822. //============================================================================
  823. /*
  824. ======================
  825. Host_Name_f
  826. ======================
  827. */
  828. cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
  829. void Host_Name_f (void)
  830. {
  831. int i, j;
  832. qboolean valid_colors;
  833. const char *newNameSource;
  834. char newName[sizeof(host_client->name)];
  835. if (Cmd_Argc () == 1)
  836. {
  837. Con_Printf("name: %s\n", cl_name.string);
  838. return;
  839. }
  840. if (Cmd_Argc () == 2)
  841. newNameSource = Cmd_Argv(1);
  842. else
  843. newNameSource = Cmd_Args();
  844. strlcpy(newName, newNameSource, sizeof(newName));
  845. if (cmd_source == src_command)
  846. {
  847. Cvar_Set ("_cl_name", newName);
  848. if (strlen(newNameSource) >= sizeof(newName)) // overflowed
  849. {
  850. Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
  851. Con_Printf("name: %s\n", cl_name.string);
  852. }
  853. return;
  854. }
  855. if (realtime < host_client->nametime)
  856. {
  857. SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
  858. return;
  859. }
  860. host_client->nametime = realtime + 5;
  861. // point the string back at updateclient->name to keep it safe
  862. strlcpy (host_client->name, newName, sizeof (host_client->name));
  863. for (i = 0, j = 0;host_client->name[i];i++)
  864. if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
  865. host_client->name[j++] = host_client->name[i];
  866. host_client->name[j] = 0;
  867. if(host_client->name[0] == 1 || host_client->name[0] == 2)
  868. // may interfere with chat area, and will needlessly beep; so let's add a ^7
  869. {
  870. memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
  871. host_client->name[sizeof(host_client->name) - 1] = 0;
  872. host_client->name[0] = STRING_COLOR_TAG;
  873. host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
  874. }
  875. COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
  876. if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
  877. {
  878. size_t l;
  879. l = strlen(host_client->name);
  880. if(l < sizeof(host_client->name) - 1)
  881. {
  882. // duplicate the color tag to escape it
  883. host_client->name[i] = STRING_COLOR_TAG;
  884. host_client->name[i+1] = 0;
  885. //Con_DPrintf("abuse detected, adding another trailing color tag\n");
  886. }
  887. else
  888. {
  889. // remove the last character to fix the color code
  890. host_client->name[l-1] = 0;
  891. //Con_DPrintf("abuse detected, removing a trailing color tag\n");
  892. }
  893. }
  894. // find the last color tag offset and decide if we need to add a reset tag
  895. for (i = 0, j = -1;host_client->name[i];i++)
  896. {
  897. if (host_client->name[i] == STRING_COLOR_TAG)
  898. {
  899. if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
  900. {
  901. j = i;
  902. // if this happens to be a reset tag then we don't need one
  903. if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
  904. j = -1;
  905. i++;
  906. continue;
  907. }
  908. if (host_client->name[i+1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(host_client->name[i+2]) && isxdigit(host_client->name[i+3]) && isxdigit(host_client->name[i+4]))
  909. {
  910. j = i;
  911. i += 4;
  912. continue;
  913. }
  914. if (host_client->name[i+1] == STRING_COLOR_TAG)
  915. {
  916. i++;
  917. continue;
  918. }
  919. }
  920. }
  921. // does not end in the default color string, so add it
  922. if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
  923. memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
  924. host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
  925. if (strcmp(host_client->old_name, host_client->name))
  926. {
  927. if (host_client->spawned)
  928. SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
  929. strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
  930. // send notification to all clients
  931. MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
  932. MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
  933. MSG_WriteString (&sv.reliable_datagram, host_client->name);
  934. SV_WriteNetnameIntoDemo(host_client);
  935. }
  936. }
  937. /*
  938. ======================
  939. Host_Playermodel_f
  940. ======================
  941. */
  942. cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
  943. // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
  944. void Host_Playermodel_f (void)
  945. {
  946. int i, j;
  947. char newPath[sizeof(host_client->playermodel)];
  948. if (Cmd_Argc () == 1)
  949. {
  950. Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
  951. return;
  952. }
  953. if (Cmd_Argc () == 2)
  954. strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
  955. else
  956. strlcpy (newPath, Cmd_Args(), sizeof (newPath));
  957. for (i = 0, j = 0;newPath[i];i++)
  958. if (newPath[i] != '\r' && newPath[i] != '\n')
  959. newPath[j++] = newPath[i];
  960. newPath[j] = 0;
  961. if (cmd_source == src_command)
  962. {
  963. Cvar_Set ("_cl_playermodel", newPath);
  964. return;
  965. }
  966. /*
  967. if (realtime < host_client->nametime)
  968. {
  969. SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
  970. return;
  971. }
  972. host_client->nametime = realtime + 5;
  973. */
  974. // point the string back at updateclient->name to keep it safe
  975. strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
  976. if( prog->fieldoffsets.playermodel >= 0 )
  977. PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
  978. if (strcmp(host_client->old_model, host_client->playermodel))
  979. {
  980. strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
  981. /*// send notification to all clients
  982. MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
  983. MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
  984. MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
  985. }
  986. }
  987. /*
  988. ======================
  989. Host_Playerskin_f
  990. ======================
  991. */
  992. cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
  993. void Host_Playerskin_f (void)
  994. {
  995. int i, j;
  996. char newPath[sizeof(host_client->playerskin)];
  997. if (Cmd_Argc () == 1)
  998. {
  999. Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
  1000. return;
  1001. }
  1002. if (Cmd_Argc () == 2)
  1003. strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
  1004. else
  1005. strlcpy (newPath, Cmd_Args(), sizeof (newPath));
  1006. for (i = 0, j = 0;newPath[i];i++)
  1007. if (newPath[i] != '\r' && newPath[i] != '\n')
  1008. newPath[j++] = newPath[i];
  1009. newPath[j] = 0;
  1010. if (cmd_source == src_command)
  1011. {
  1012. Cvar_Set ("_cl_playerskin", newPath);
  1013. return;
  1014. }
  1015. /*
  1016. if (realtime < host_client->nametime)
  1017. {
  1018. SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
  1019. return;
  1020. }
  1021. host_client->nametime = realtime + 5;
  1022. */
  1023. // point the string back at updateclient->name to keep it safe
  1024. strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
  1025. if( prog->fieldoffsets.playerskin >= 0 )
  1026. PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
  1027. if (strcmp(host_client->old_skin, host_client->playerskin))
  1028. {
  1029. //if (host_client->spawned)
  1030. // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
  1031. strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
  1032. /*// send notification to all clients
  1033. MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
  1034. MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
  1035. MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
  1036. }
  1037. }
  1038. void Host_Version_f (void)
  1039. {
  1040. Con_Printf("Version: %s build %s\n", gamename, buildstring);
  1041. }
  1042. void Host_Say(qboolean teamonly)
  1043. {
  1044. client_t *save;
  1045. int j, quoted;
  1046. const char *p1;
  1047. char *p2;
  1048. // LordHavoc: long say messages
  1049. char text[1024];
  1050. qboolean fromServer = false;
  1051. if (cmd_source == src_command)
  1052. {
  1053. if (cls.state == ca_dedicated)
  1054. {
  1055. fromServer = true;
  1056. teamonly = false;
  1057. }
  1058. else
  1059. {
  1060. Cmd_ForwardToServer ();
  1061. return;
  1062. }
  1063. }
  1064. if (Cmd_Argc () < 2)
  1065. return;
  1066. if (!teamplay.integer)
  1067. teamonly = false;
  1068. p1 = Cmd_Args();
  1069. quoted = false;
  1070. if (*p1 == '\"')
  1071. {
  1072. quoted = true;
  1073. p1++;
  1074. }
  1075. // note this uses the chat prefix \001
  1076. if (!fromServer && !teamonly)
  1077. dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
  1078. else if (!fromServer && teamonly)
  1079. dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
  1080. else if(*(sv_adminnick.string))
  1081. dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
  1082. else
  1083. dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
  1084. p2 = text + strlen(text);
  1085. while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
  1086. {
  1087. if (p2[-1] == '\"' && quoted)
  1088. quoted = false;
  1089. p2[-1] = 0;
  1090. p2--;
  1091. }
  1092. strlcat(text, "\n", sizeof(text));
  1093. // note: save is not a valid edict if fromServer is true
  1094. save = host_client;
  1095. for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
  1096. if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
  1097. SV_ClientPrint(text);
  1098. host_client = save;
  1099. if (cls.state == ca_dedicated)
  1100. Con_Print(&text[1]);
  1101. }
  1102. void Host_Say_f(void)
  1103. {
  1104. Host_Say(false);
  1105. }
  1106. void Host_Say_Team_f(void)
  1107. {
  1108. Host_Say(true);
  1109. }
  1110. void Host_Tell_f(void)
  1111. {
  1112. const char *playername_start = NULL;
  1113. size_t playername_length = 0;
  1114. int playernumber = 0;
  1115. client_t *save;
  1116. int j;
  1117. const char *p1, *p2;
  1118. char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
  1119. qboolean fromServer = false;
  1120. if (cmd_source == src_command)
  1121. {
  1122. if (cls.state == ca_dedicated)
  1123. fromServer = true;
  1124. else
  1125. {
  1126. Cmd_ForwardToServer ();
  1127. return;
  1128. }
  1129. }
  1130. if (Cmd_Argc () < 2)
  1131. return;
  1132. // note this uses the chat prefix \001
  1133. if (!fromServer)
  1134. dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
  1135. else if(*(sv_adminnick.string))
  1136. dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
  1137. else
  1138. dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
  1139. p1 = Cmd_Args();
  1140. p2 = p1 + strlen(p1);
  1141. // remove the target name
  1142. while (p1 < p2 && *p1 == ' ')
  1143. p1++;
  1144. if(*p1 == '#')
  1145. {
  1146. ++p1;
  1147. while (p1 < p2 && *p1 == ' ')
  1148. p1++;
  1149. while (p1 < p2 && isdigit(*p1))
  1150. {
  1151. playernumber = playernumber * 10 + (*p1 - '0');
  1152. p1++;
  1153. }
  1154. --playernumber;
  1155. }
  1156. else if(*p1 == '"')
  1157. {
  1158. ++p1;
  1159. playername_start = p1;
  1160. while (p1 < p2 && *p1 != '"')
  1161. p1++;
  1162. playername_length = p1 - playername_start;
  1163. if(p1 < p2)
  1164. p1++;
  1165. }
  1166. else
  1167. {
  1168. playername_start = p1;
  1169. while (p1 < p2 && *p1 != ' ')
  1170. p1++;
  1171. playername_length = p1 - playername_start;
  1172. }
  1173. while (p1 < p2 && *p1 == ' ')
  1174. p1++;
  1175. if(playername_start)
  1176. {
  1177. // set playernumber to the right client
  1178. char namebuf[128];
  1179. if(playername_length >= sizeof(namebuf))
  1180. {
  1181. if (fromServer)
  1182. Con_Print("Host_Tell: too long player name/ID\n");
  1183. else
  1184. SV_ClientPrint("Host_Tell: too long player name/ID\n");
  1185. return;
  1186. }
  1187. memcpy(namebuf, playername_start, playername_length);
  1188. namebuf[playername_length] = 0;
  1189. for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
  1190. {
  1191. if (!svs.clients[playernumber].active)
  1192. continue;
  1193. if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
  1194. break;
  1195. }
  1196. }
  1197. if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
  1198. {
  1199. if (fromServer)
  1200. Con_Print("Host_Tell: invalid player name/ID\n");
  1201. else
  1202. SV_ClientPrint("Host_Tell: invalid player name/ID\n");
  1203. return;
  1204. }
  1205. // remove trailing newlines
  1206. while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
  1207. p2--;
  1208. // remove quotes if present
  1209. if (*p1 == '"')
  1210. {
  1211. p1++;
  1212. if (p2[-1] == '"')
  1213. p2--;
  1214. else if (fromServer)
  1215. Con_Print("Host_Tell: missing end quote\n");
  1216. else
  1217. SV_ClientPrint("Host_Tell: missing end quote\n");
  1218. }
  1219. while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
  1220. p2--;
  1221. if(p1 == p2)
  1222. return; // empty say
  1223. for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
  1224. text[j++] = *p1++;
  1225. text[j++] = '\n';
  1226. text[j++] = 0;
  1227. save = host_client;
  1228. host_client = svs.clients + playernumber;
  1229. SV_ClientPrint(text);
  1230. host_client = save;
  1231. }
  1232. /*
  1233. ==================
  1234. Host_Color_f
  1235. ==================
  1236. */
  1237. cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
  1238. void Host_Color(int changetop, int changebottom)
  1239. {
  1240. int top, bottom, playercolor;
  1241. // get top and bottom either from the provided values or the current values
  1242. // (allows changing only top or bottom, or both at once)
  1243. top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
  1244. bottom = changebottom >= 0 ? changebottom : cl_color.integer;
  1245. top &= 15;
  1246. bottom &= 15;
  1247. // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
  1248. //if (top > 13)
  1249. // top = 13;
  1250. //if (bottom > 13)
  1251. // bottom = 13;
  1252. playercolor = top*16 + bottom;
  1253. if (cmd_source == src_command)
  1254. {
  1255. Cvar_SetValueQuick(&cl_color, playercolor);
  1256. return;
  1257. }
  1258. if (cls.protocol == PROTOCOL_QUAKEWORLD)
  1259. return;
  1260. if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
  1261. {
  1262. Con_DPrint("Calling SV_ChangeTeam\n");
  1263. prog->globals.server->time = sv.time;
  1264. prog->globals.generic[OFS_PARM0] = playercolor;
  1265. prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
  1266. PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
  1267. }
  1268. else
  1269. {
  1270. prvm_eval_t *val;
  1271. if (host_client->edict)
  1272. {
  1273. if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
  1274. val->_float = playercolor;
  1275. host_client->edict->fields.server->team = bottom + 1;
  1276. }
  1277. host_client->colors = playercolor;
  1278. if (host_client->old_colors != host_client->colors)
  1279. {
  1280. host_client->old_colors = host_client->colors;
  1281. // send notification to all clients
  1282. MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
  1283. MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
  1284. MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
  1285. }
  1286. }
  1287. }
  1288. void Host_Color_f(void)
  1289. {
  1290. int top, bottom;
  1291. if (Cmd_Argc() == 1)
  1292. {
  1293. Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
  1294. Con_Print("color <0-15> [0-15]\n");
  1295. return;
  1296. }
  1297. if (Cmd_Argc() == 2)
  1298. top = bottom = atoi(Cmd_Argv(1));
  1299. else
  1300. {
  1301. top = atoi(Cmd_Argv(1));
  1302. bottom = atoi(Cmd_Argv(2));
  1303. }
  1304. Host_Color(top, bottom);
  1305. }
  1306. void Host_TopColor_f(void)
  1307. {
  1308. if (Cmd_Argc() == 1)
  1309. {
  1310. Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
  1311. Con_Print("topcolor <0-15>\n");
  1312. return;
  1313. }
  1314. Host_Color(atoi(Cmd_Argv(1)), -1);
  1315. }
  1316. void Host_BottomColor_f(void)
  1317. {
  1318. if (Cmd_Argc() == 1)
  1319. {
  1320. Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
  1321. Con_Print("bottomcolor <0-15>\n");
  1322. return;
  1323. }
  1324. Host_Color(-1, atoi(Cmd_Argv(1)));
  1325. }
  1326. cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
  1327. void Host_Rate_f(void)
  1328. {
  1329. int rate;
  1330. if (Cmd_Argc() != 2)
  1331. {
  1332. Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
  1333. Con_Print("rate <bytespersecond>\n");
  1334. return;
  1335. }
  1336. rate = atoi(Cmd_Argv(1));
  1337. if (cmd_source == src_command)
  1338. {
  1339. Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
  1340. return;
  1341. }
  1342. host_client->rate = rate;
  1343. }
  1344. /*
  1345. ==================
  1346. Host_Kill_f
  1347. ==================
  1348. */
  1349. void Host_Kill_f (void)
  1350. {
  1351. if (host_client->edict->fields.server->health <= 0)
  1352. {
  1353. SV_ClientPrint("Can't suicide -- already dead!\n");
  1354. return;
  1355. }
  1356. prog->globals.server->time = sv.time;
  1357. prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
  1358. PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
  1359. }
  1360. /*
  1361. ==================
  1362. Host_Pause_f
  1363. ==================
  1364. */
  1365. void Host_Pause_f (void)
  1366. {
  1367. if (!pausable.integer)
  1368. SV_ClientPrint("Pause not allowed.\n");
  1369. else
  1370. {
  1371. sv.paused ^= 1;
  1372. SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
  1373. // send notification to all clients
  1374. MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
  1375. MSG_WriteByte(&sv.reliable_datagram, sv.paused);
  1376. }
  1377. }
  1378. /*
  1379. ======================
  1380. Host_PModel_f
  1381. LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
  1382. LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
  1383. ======================
  1384. */
  1385. cvar_t cl_pmodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
  1386. static void Host_PModel_f (void)
  1387. {
  1388. int i;
  1389. prvm_eval_t *val;
  1390. if (Cmd_Argc () == 1)
  1391. {
  1392. Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
  1393. return;
  1394. }
  1395. i = atoi(Cmd_Argv(1));
  1396. if (cmd_source == src_command)
  1397. {
  1398. if (cl_pmodel.integer == i)
  1399. return;
  1400. Cvar_SetValue ("_cl_pmodel", i);
  1401. if (cls.state == ca_connected)
  1402. Cmd_ForwardToServer ();
  1403. return;
  1404. }
  1405. if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
  1406. val->_float = i;
  1407. }
  1408. //===========================================================================
  1409. /*
  1410. ==================
  1411. Host_PreSpawn_f
  1412. ==================
  1413. */
  1414. void Host_PreSpawn_f (void)
  1415. {
  1416. if (host_client->spawned)
  1417. {
  1418. Con_Print("prespawn not valid -- already spawned\n");
  1419. return;
  1420. }
  1421. if (host_client->netconnection)
  1422. {
  1423. SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
  1424. MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
  1425. MSG_WriteByte (&host_client->netconnection->message, 2);
  1426. host_client->sendsignon = 0; // enable unlimited sends again
  1427. }
  1428. // reset the name change timer because the client will send name soon
  1429. host_client->nametime = 0;
  1430. }
  1431. /*
  1432. ==================
  1433. Host_Spawn_f
  1434. ==================
  1435. */
  1436. void Host_Spawn_f (void)
  1437. {
  1438. int i;
  1439. client_t *client;
  1440. int stats[MAX_CL_STATS];
  1441. if (host_client->spawned)
  1442. {
  1443. Con_Print("Spawn not valid -- already spawned\n");
  1444. return;
  1445. }
  1446. // reset name change timer again because they might want to change name
  1447. // again in the first 5 seconds after connecting
  1448. host_client->nametime = 0;
  1449. // LordHavoc: moved this above the QC calls at FrikaC's request
  1450. // LordHavoc: commented this out
  1451. //if (host_client->netconnection)
  1452. // SZ_Clear (&host_client->netconnection->message);
  1453. // run the entrance script
  1454. if (sv.loadgame)
  1455. {
  1456. // loaded games are fully initialized already
  1457. if (prog->funcoffsets.RestoreGame)
  1458. {
  1459. Con_DPrint("Calling RestoreGame\n");
  1460. prog->globals.server->time = sv.time;
  1461. prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
  1462. PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
  1463. }
  1464. }
  1465. else
  1466. {
  1467. //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(host_client->edict->fields.server->netname), PRVM_GetString(host_client->edict->fields.server->netname), host_client->name);
  1468. // copy spawn parms out of the client_t
  1469. for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
  1470. (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
  1471. // call the spawn function
  1472. host_client->clientconnectcalled = true;
  1473. prog->globals.server->time = sv.time;
  1474. prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
  1475. PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
  1476. if (cls.state == ca_dedicated)
  1477. Con_Printf("%s connected\n", host_client->name);
  1478. PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
  1479. }
  1480. if (!host_client->netconnection)
  1481. return;
  1482. // send time of update
  1483. MSG_WriteByte (&host_client->netconnection->message, svc_time);
  1484. MSG_WriteFloat (&host_client->netconnection->message, sv.time);
  1485. // send all current names, colors, and frag counts
  1486. for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
  1487. {
  1488. if (!client->active)
  1489. continue;
  1490. MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
  1491. MSG_WriteByte (&host_client->netconnection->message, i);
  1492. MSG_WriteString (&host_client->netconnection->message, client->name);
  1493. MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
  1494. MSG_WriteByte (&host_client->netconnection->message, i);
  1495. MSG_WriteShort (&host_client->netconnection->message, client->frags);
  1496. MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
  1497. MSG_WriteByte (&host_client->netconnection->message, i);
  1498. MSG_WriteByte (&host_client->netconnection->message, client->colors);
  1499. }
  1500. // send all current light styles
  1501. for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
  1502. {
  1503. if (sv.lightstyles[i][0])
  1504. {
  1505. MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
  1506. MSG_WriteByte (&host_client->netconnection->message, (char)i);
  1507. MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
  1508. }
  1509. }
  1510. // send some stats
  1511. MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
  1512. MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
  1513. MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
  1514. MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
  1515. MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
  1516. MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
  1517. MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
  1518. MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
  1519. MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
  1520. MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
  1521. MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
  1522. MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
  1523. // send a fixangle
  1524. // Never send a roll angle, because savegames can catch the server
  1525. // in a state where it is expecting the client to correct the angle
  1526. // and it won't happen if the game was just loaded, so you wind up
  1527. // with a permanent head tilt
  1528. if (sv.loadgame)
  1529. {
  1530. MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
  1531. MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
  1532. MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
  1533. MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
  1534. }
  1535. else
  1536. {
  1537. MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
  1538. MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
  1539. MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
  1540. MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
  1541. }
  1542. SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
  1543. MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
  1544. MSG_WriteByte (&host_client->netconnection->message, 3);
  1545. }
  1546. /*
  1547. ==================
  1548. Host_Begin_f
  1549. ==================
  1550. */
  1551. void Host_Begin_f (void)
  1552. {
  1553. host_client->spawned = true;
  1554. // LordHavoc: note: this code also exists in SV_DropClient
  1555. if (sv.loadgame)
  1556. {
  1557. int i;
  1558. for (i = 0;i < svs.maxclients;i++)
  1559. if (svs.clients[i].active && !svs.clients[i].spawned)
  1560. break;
  1561. if (i == svs.maxclients)
  1562. {
  1563. Con_Printf("Loaded game, everyone rejoined - unpausing\n");
  1564. sv.paused = sv.loadgame = false; // we're basically done with loading now
  1565. }
  1566. }
  1567. }
  1568. //===========================================================================
  1569. /*
  1570. ==================
  1571. Host_Kick_f
  1572. Kicks a user off of the server
  1573. ==================
  1574. */
  1575. void Host_Kick_f (void)
  1576. {
  1577. char *who;
  1578. const char *message = NULL;
  1579. client_t *save;
  1580. int i;
  1581. qboolean byNumber = false;
  1582. if (!sv.active)
  1583. return;
  1584. SV_VM_Begin();
  1585. save = host_client;
  1586. if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
  1587. {
  1588. i = (int)(atof(Cmd_Argv(2)) - 1);
  1589. if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
  1590. return;
  1591. byNumber = true;
  1592. }
  1593. else
  1594. {
  1595. for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
  1596. {
  1597. if (!host_client->active)
  1598. continue;
  1599. if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
  1600. break;
  1601. }
  1602. }
  1603. if (i < svs.maxclients)
  1604. {
  1605. if (cmd_source == src_command)
  1606. {
  1607. if (cls.state == ca_dedicated)
  1608. who = "Console";
  1609. else
  1610. who = cl_name.string;
  1611. }
  1612. else
  1613. who = save->name;
  1614. // can't kick yourself!
  1615. if (host_client == save)
  1616. return;
  1617. if (Cmd_Argc() > 2)
  1618. {
  1619. message = Cmd_Args();
  1620. COM_ParseToken_Simple(&message, false, false);
  1621. if (byNumber)
  1622. {
  1623. message++; // skip the #
  1624. while (*message == ' ') // skip white space
  1625. message++;
  1626. message += strlen(Cmd_Argv(2)); // skip the number
  1627. }
  1628. while (*message && *message == ' ')
  1629. message++;
  1630. }
  1631. if (message)
  1632. SV_ClientPrintf("Kicked by %s: %s\n", who, message);
  1633. else
  1634. SV_ClientPrintf("Kicked by %s\n", who);
  1635. SV_DropClient (false); // kicked
  1636. }
  1637. host_client = save;
  1638. SV_VM_End();
  1639. }
  1640. /*
  1641. ===============================================================================
  1642. DEBUGGING TOOLS
  1643. ===============================================================================
  1644. */
  1645. /*
  1646. ==================
  1647. Host_Give_f
  1648. ==================
  1649. */
  1650. void Host_Give_f (void)
  1651. {
  1652. const char *t;
  1653. int v;
  1654. prvm_eval_t *val;
  1655. if (!allowcheats)
  1656. {
  1657. SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
  1658. return;
  1659. }
  1660. t = Cmd_Argv(1);
  1661. v = atoi (Cmd_Argv(2));
  1662. switch (t[0])
  1663. {
  1664. case '0':
  1665. case '1':
  1666. case '2':
  1667. case '3':
  1668. case '4':
  1669. case '5':
  1670. case '6':
  1671. case '7':
  1672. case '8':
  1673. case '9':
  1674. // MED 01/04/97 added hipnotic give stuff
  1675. if (gamemode == GAME_HIPNOTIC)
  1676. {
  1677. if (t[0] == '6')
  1678. {
  1679. if (t[1] == 'a')
  1680. host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
  1681. else
  1682. host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
  1683. }
  1684. else if (t[0] == '9')
  1685. host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
  1686. else if (t[0] == '0')
  1687. host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
  1688. else if (t[0] >= '2')
  1689. host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
  1690. }
  1691. else
  1692. {
  1693. if (t[0] >= '2')
  1694. host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
  1695. }
  1696. break;
  1697. case 's':
  1698. if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
  1699. val->_float = v;
  1700. host_client->edict->fields.server->ammo_shells = v;
  1701. break;
  1702. case 'n':
  1703. if (gamemode == GAME_ROGUE)
  1704. {
  1705. if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
  1706. {
  1707. val->_float = v;
  1708. if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
  1709. host_client->edict->fields.server->ammo_nails = v;
  1710. }
  1711. }
  1712. else
  1713. {
  1714. host_client->edict->fields.server->ammo_nails = v;
  1715. }
  1716. break;
  1717. case 'l':
  1718. if (gamemode == GAME_ROGUE)
  1719. {
  1720. val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
  1721. if (val)
  1722. {
  1723. val->_float = v;
  1724. if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
  1725. host_client->edict->fields.server->ammo_nails = v;
  1726. }
  1727. }
  1728. break;
  1729. case 'r':
  1730. if (gamemode == GAME_ROGUE)
  1731. {
  1732. val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
  1733. if (val)
  1734. {
  1735. val->_float = v;
  1736. if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
  1737. host_client->edict->fields.server->ammo_rockets = v;
  1738. }
  1739. }
  1740. else
  1741. {
  1742. host_client->edict->fields.server->ammo_rockets = v;
  1743. }
  1744. break;
  1745. case 'm':
  1746. if (gamemode == GAME_ROGUE)
  1747. {
  1748. val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
  1749. if (val)
  1750. {
  1751. val->_float = v;
  1752. if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
  1753. host_client->edict->fields.server->ammo_rockets = v;
  1754. }
  1755. }
  1756. break;
  1757. case 'h':
  1758. host_client->edict->fields.server->health = v;
  1759. break;
  1760. case 'c':
  1761. if (gamemode == GAME_ROGUE)
  1762. {
  1763. val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
  1764. if (val)
  1765. {
  1766. val->_float = v;
  1767. if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
  1768. host_client->edict->fields.server->ammo_cells = v;
  1769. }
  1770. }
  1771. else
  1772. {
  1773. host_client->edict->fields.server->ammo_cells = v;
  1774. }
  1775. break;
  1776. case 'p':
  1777. if (gamemode == GAME_ROGUE)
  1778. {
  1779. val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
  1780. if (val)
  1781. {
  1782. val->_float = v;
  1783. if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
  1784. host_client->edict->fields.server->ammo_cells = v;
  1785. }
  1786. }
  1787. break;
  1788. }
  1789. }
  1790. prvm_edict_t *FindViewthing (void)
  1791. {
  1792. int i;
  1793. prvm_edict_t *e;
  1794. for (i=0 ; i<prog->num_edicts ; i++)
  1795. {
  1796. e = PRVM_EDICT_NUM(i);
  1797. if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
  1798. return e;
  1799. }
  1800. Con_Print("No viewthing on map\n");
  1801. return NULL;
  1802. }
  1803. /*
  1804. ==================
  1805. Host_Viewmodel_f
  1806. ==================
  1807. */
  1808. void Host_Viewmodel_f (void)
  1809. {
  1810. prvm_edict_t *e;
  1811. dp_model_t *m;
  1812. if (!sv.active)
  1813. return;
  1814. SV_VM_Begin();
  1815. e = FindViewthing ();
  1816. SV_VM_End();
  1817. if (!e)
  1818. return;
  1819. m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
  1820. if (!m || !m->loaded || !m->Draw)
  1821. {
  1822. Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
  1823. return;
  1824. }
  1825. e->fields.server->frame = 0;
  1826. cl.model_precache[(int)e->fields.server->modelindex] = m;
  1827. }
  1828. /*
  1829. ==================
  1830. Host_Viewframe_f
  1831. ==================
  1832. */
  1833. void Host_Viewframe_f (void)
  1834. {
  1835. prvm_edict_t *e;
  1836. int f;
  1837. dp_model_t *m;
  1838. if (!sv.active)
  1839. return;
  1840. SV_VM_Begin();
  1841. e = FindViewthing ();
  1842. SV_VM_End();
  1843. if (!e)
  1844. return;
  1845. m = cl.model_precache[(int)e->fields.server->modelindex];
  1846. f = atoi(Cmd_Argv(1));
  1847. if (f >= m->numframes)
  1848. f = m->numframes-1;
  1849. e->fields.server->frame = f;
  1850. }
  1851. void PrintFrameName (dp_model_t *m, int frame)
  1852. {
  1853. if (m->animscenes)
  1854. Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
  1855. else
  1856. Con_Printf("frame %i\n", frame);
  1857. }
  1858. /*
  1859. ==================
  1860. Host_Viewnext_f
  1861. ==================
  1862. */
  1863. void Host_Viewnext_f (void)
  1864. {
  1865. prvm_edict_t *e;
  1866. dp_model_t *m;
  1867. if (!sv.active)
  1868. return;
  1869. SV_VM_Begin();
  1870. e = FindViewthing ();
  1871. SV_VM_End();
  1872. if (!e)
  1873. return;
  1874. m = cl.model_precache[(int)e->fields.server->modelindex];
  1875. e->fields.server->frame = e->fields.server->frame + 1;
  1876. if (e->fields.server->frame >= m->numframes)
  1877. e->fields.server->frame = m->numframes - 1;
  1878. PrintFrameName (m, (int)e->fields.server->frame);
  1879. }
  1880. /*
  1881. ==================
  1882. Host_Viewprev_f
  1883. ==================
  1884. */
  1885. void Host_Viewprev_f (void)
  1886. {
  1887. prvm_edict_t *e;
  1888. dp_model_t *m;
  1889. if (!sv.active)
  1890. return;
  1891. SV_VM_Begin();
  1892. e = FindViewthing ();
  1893. SV_VM_End();
  1894. if (!e)
  1895. return;
  1896. m = cl.model_precache[(int)e->fields.server->modelindex];
  1897. e->fields.server->frame = e->fields.server->frame - 1;
  1898. if (e->fields.server->frame < 0)
  1899. e->fields.server->frame = 0;
  1900. PrintFrameName (m, (int)e->fields.server->frame);
  1901. }
  1902. /*
  1903. ===============================================================================
  1904. DEMO LOOP CONTROL
  1905. ===============================================================================
  1906. */
  1907. /*
  1908. ==================
  1909. Host_Startdemos_f
  1910. ==================
  1911. */
  1912. void Host_Startdemos_f (void)
  1913. {
  1914. int i, c;
  1915. if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
  1916. return;
  1917. c = Cmd_Argc() - 1;
  1918. if (c > MAX_DEMOS)
  1919. {
  1920. Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
  1921. c = MAX_DEMOS;
  1922. }
  1923. Con_DPrintf("%i demo(s) in loop\n", c);
  1924. for (i=1 ; i<c+1 ; i++)
  1925. strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
  1926. // LordHavoc: clear the remaining slots
  1927. for (;i <= MAX_DEMOS;i++)
  1928. cls.demos[i-1][0] = 0;
  1929. if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
  1930. {
  1931. cls.demonum = 0;
  1932. CL_NextDemo ();
  1933. }
  1934. else
  1935. cls.demonum = -1;
  1936. }
  1937. /*
  1938. ==================
  1939. Host_Demos_f
  1940. Return to looping demos
  1941. ==================
  1942. */
  1943. void Host_Demos_f (void)
  1944. {
  1945. if (cls.state == ca_dedicated)
  1946. return;
  1947. if (cls.demonum == -1)
  1948. cls.demonum = 1;
  1949. CL_Disconnect_f ();
  1950. CL_NextDemo ();
  1951. }
  1952. /*
  1953. ==================
  1954. Host_Stopdemo_f
  1955. Return to looping demos
  1956. ==================
  1957. */
  1958. void Host_Stopdemo_f (void)
  1959. {
  1960. if (!cls.demoplayback)
  1961. return;
  1962. CL_Disconnect ();
  1963. Host_ShutdownServer ();
  1964. }
  1965. void Host_SendCvar_f (void)
  1966. {
  1967. int i;
  1968. cvar_t *c;
  1969. const char *cvarname;
  1970. client_t *old;
  1971. if(Cmd_Argc() != 2)
  1972. return;
  1973. cvarname = Cmd_Argv(1);
  1974. if (cls.state == ca_connected)
  1975. {
  1976. c = Cvar_FindVar(cvarname);
  1977. // LordHavoc: if there is no such cvar or if it is private, send a
  1978. // reply indicating that it has no value
  1979. if(!c || (c->flags & CVAR_PRIVATE))
  1980. Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
  1981. else
  1982. Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
  1983. return;
  1984. }
  1985. if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
  1986. return;
  1987. old = host_client;
  1988. if (cls.state != ca_dedicated)
  1989. i = 1;
  1990. else
  1991. i = 0;
  1992. for(;i<svs.maxclients;i++)
  1993. if(svs.clients[i].active && svs.clients[i].netconnection)
  1994. {
  1995. host_client = &svs.clients[i];
  1996. Host_ClientCommands("sendcvar %s\n", cvarname);
  1997. }
  1998. host_client = old;
  1999. }
  2000. static void MaxPlayers_f(void)
  2001. {
  2002. int n;
  2003. if (Cmd_Argc() != 2)
  2004. {
  2005. Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
  2006. return;
  2007. }
  2008. if (sv.active)
  2009. {
  2010. Con_Print("maxplayers can not be changed while a server is running.\n");
  2011. Con_Print("It will be changed on next server startup (\"map\" command).\n");
  2012. }
  2013. n = atoi(Cmd_Argv(1));
  2014. n = bound(1, n, MAX_SCOREBOARD);
  2015. Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
  2016. svs.maxclients_next = n;
  2017. if (n == 1)
  2018. Cvar_Set ("deathmatch", "0");
  2019. else
  2020. Cvar_Set ("deathmatch", "1");
  2021. }
  2022. /*
  2023. =====================
  2024. Host_PQRcon_f
  2025. ProQuake rcon support
  2026. =====================
  2027. */
  2028. void Host_PQRcon_f (void)
  2029. {
  2030. int i;
  2031. lhnetaddress_t to;
  2032. lhnetsocket_t *mysocket;
  2033. char peer_address[64];
  2034. if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer)
  2035. {
  2036. Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
  2037. return;
  2038. }
  2039. for (i = 0;rcon_password.string[i];i++)
  2040. {
  2041. if (ISWHITESPACE(rcon_password.string[i]))
  2042. {
  2043. Con_Printf("rcon_password is not allowed to have any whitespace.\n");
  2044. return;
  2045. }
  2046. }
  2047. if (cls.netcon)
  2048. {
  2049. InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
  2050. }
  2051. else
  2052. {
  2053. if (!rcon_address.string[0])
  2054. {
  2055. Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
  2056. return;
  2057. }
  2058. strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
  2059. }
  2060. LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
  2061. mysocket = NetConn_ChooseClientSocketForAddress(&to);
  2062. if (mysocket)
  2063. {
  2064. SZ_Clear(&net_message);
  2065. MSG_WriteLong (&net_message, 0);
  2066. MSG_WriteByte (&net_message, CCREQ_RCON);
  2067. MSG_WriteString (&net_message, rcon_password.string);
  2068. MSG_WriteString (&net_message, Cmd_Args());
  2069. *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
  2070. NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
  2071. SZ_Clear (&net_message);
  2072. }
  2073. }
  2074. //=============================================================================
  2075. // QuakeWorld commands
  2076. /*
  2077. =====================
  2078. Host_Rcon_f
  2079. Send the rest of the command line over as
  2080. an unconnected command.
  2081. =====================
  2082. */
  2083. void Host_Rcon_f (void) // credit: taken from QuakeWorld
  2084. {
  2085. int i;
  2086. lhnetaddress_t to;
  2087. lhnetsocket_t *mysocket;
  2088. if (!rcon_password.string || !rcon_password.string[0])
  2089. {
  2090. Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
  2091. return;
  2092. }
  2093. for (i = 0;rcon_password.string[i];i++)
  2094. {
  2095. if (ISWHITESPACE(rcon_password.string[i]))
  2096. {
  2097. Con_Printf("rcon_password is not allowed to have any whitespace.\n");
  2098. return;
  2099. }
  2100. }
  2101. if (cls.netcon)
  2102. to = cls.netcon->peeraddress;
  2103. else
  2104. {
  2105. if (!rcon_address.string[0])
  2106. {
  2107. Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
  2108. return;
  2109. }
  2110. LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
  2111. }
  2112. mysocket = NetConn_ChooseClientSocketForAddress(&to);
  2113. if (mysocket)
  2114. {
  2115. // simply put together the rcon packet and send it
  2116. if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer)
  2117. {
  2118. char buf[1500];
  2119. char argbuf[1500];
  2120. dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
  2121. memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
  2122. if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
  2123. {
  2124. buf[40] = ' ';
  2125. strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
  2126. NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
  2127. }
  2128. }
  2129. else
  2130. {
  2131. NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
  2132. }
  2133. }
  2134. }
  2135. /*
  2136. ====================
  2137. Host_User_f
  2138. user <name or userid>
  2139. Dump userdata / masterdata for a user
  2140. ====================
  2141. */
  2142. void Host_User_f (void) // credit: taken from QuakeWorld
  2143. {
  2144. int uid;
  2145. int i;
  2146. if (Cmd_Argc() != 2)
  2147. {
  2148. Con_Printf ("Usage: user <username / userid>\n");
  2149. return;
  2150. }
  2151. uid = atoi(Cmd_Argv(1));
  2152. for (i = 0;i < cl.maxclients;i++)
  2153. {
  2154. if (!cl.scores[i].name[0])
  2155. continue;
  2156. if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
  2157. {
  2158. InfoString_Print(cl.scores[i].qw_userinfo);
  2159. return;
  2160. }
  2161. }
  2162. Con_Printf ("User not in server.\n");
  2163. }
  2164. /*
  2165. ====================
  2166. Host_Users_f
  2167. Dump userids for all current players
  2168. ====================
  2169. */
  2170. void Host_Users_f (void) // credit: taken from QuakeWorld
  2171. {
  2172. int i;
  2173. int c;
  2174. c = 0;
  2175. Con_Printf ("userid frags name\n");
  2176. Con_Printf ("------ ----- ----\n");
  2177. for (i = 0;i < cl.maxclients;i++)
  2178. {
  2179. if (cl.scores[i].name[0])
  2180. {
  2181. Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
  2182. c++;
  2183. }
  2184. }
  2185. Con_Printf ("%i total users\n", c);
  2186. }
  2187. /*
  2188. ==================
  2189. Host_FullServerinfo_f
  2190. Sent by server when serverinfo changes
  2191. ==================
  2192. */
  2193. // TODO: shouldn't this be a cvar instead?
  2194. void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
  2195. {
  2196. char temp[512];
  2197. if (Cmd_Argc() != 2)
  2198. {
  2199. Con_Printf ("usage: fullserverinfo <complete info string>\n");
  2200. return;
  2201. }
  2202. strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
  2203. InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
  2204. cl.qw_teamplay = atoi(temp);
  2205. }
  2206. /*
  2207. ==================
  2208. Host_FullInfo_f
  2209. Allow clients to change userinfo
  2210. ==================
  2211. Casey was here :)
  2212. */
  2213. void Host_FullInfo_f (void) // credit: taken from QuakeWorld
  2214. {
  2215. char key[512];
  2216. char value[512];
  2217. char *o;
  2218. const char *s;
  2219. if (Cmd_Argc() != 2)
  2220. {
  2221. Con_Printf ("fullinfo <complete info string>\n");
  2222. return;
  2223. }
  2224. s = Cmd_Argv(1);
  2225. if (*s == '\\')
  2226. s++;
  2227. while (*s)
  2228. {
  2229. o = key;
  2230. while (*s && *s != '\\')
  2231. *o++ = *s++;
  2232. *o = 0;
  2233. if (!*s)
  2234. {
  2235. Con_Printf ("MISSING VALUE\n");
  2236. return;
  2237. }
  2238. o = value;
  2239. s++;
  2240. while (*s && *s != '\\')
  2241. *o++ = *s++;
  2242. *o = 0;
  2243. if (*s)
  2244. s++;
  2245. CL_SetInfo(key, value, false, false, false, false);
  2246. }
  2247. }
  2248. /*
  2249. ==================
  2250. CL_SetInfo_f
  2251. Allow clients to change userinfo
  2252. ==================
  2253. */
  2254. void Host_SetInfo_f (void) // credit: taken from QuakeWorld
  2255. {
  2256. if (Cmd_Argc() == 1)
  2257. {
  2258. InfoString_Print(cls.userinfo);
  2259. return;
  2260. }
  2261. if (Cmd_Argc() != 3)
  2262. {
  2263. Con_Printf ("usage: setinfo [ <key> <value> ]\n");
  2264. return;
  2265. }
  2266. CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
  2267. }
  2268. /*
  2269. ====================
  2270. Host_Packet_f
  2271. packet <destination> <contents>
  2272. Contents allows \n escape character
  2273. ====================
  2274. */
  2275. void Host_Packet_f (void) // credit: taken from QuakeWorld
  2276. {
  2277. char send[2048];
  2278. int i, l;
  2279. const char *in;
  2280. char *out;
  2281. lhnetaddress_t address;
  2282. lhnetsocket_t *mysocket;
  2283. if (Cmd_Argc() != 3)
  2284. {
  2285. Con_Printf ("packet <destination> <contents>\n");
  2286. return;
  2287. }
  2288. if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
  2289. {
  2290. Con_Printf ("Bad address\n");
  2291. return;
  2292. }
  2293. in = Cmd_Argv(2);
  2294. out = send+4;
  2295. send[0] = send[1] = send[2] = send[3] = -1;
  2296. l = (int)strlen (in);
  2297. for (i=0 ; i<l ; i++)
  2298. {
  2299. if (out >= send + sizeof(send) - 1)
  2300. break;
  2301. if (in[i] == '\\' && in[i+1] == 'n')
  2302. {
  2303. *out++ = '\n';
  2304. i++;
  2305. }
  2306. else if (in[i] == '\\' && in[i+1] == '0')
  2307. {
  2308. *out++ = '\0';
  2309. i++;
  2310. }
  2311. else if (in[i] == '\\' && in[i+1] == 't')
  2312. {
  2313. *out++ = '\t';
  2314. i++;
  2315. }
  2316. else if (in[i] == '\\' && in[i+1] == 'r')
  2317. {
  2318. *out++ = '\r';
  2319. i++;
  2320. }
  2321. else if (in[i] == '\\' && in[i+1] == '"')
  2322. {
  2323. *out++ = '\"';
  2324. i++;
  2325. }
  2326. else
  2327. *out++ = in[i];
  2328. }
  2329. mysocket = NetConn_ChooseClientSocketForAddress(&address);
  2330. if (!mysocket)
  2331. mysocket = NetConn_ChooseServerSocketForAddress(&address);
  2332. if (mysocket)
  2333. NetConn_Write(mysocket, send, out - send, &address);
  2334. }
  2335. /*
  2336. ====================
  2337. Host_Pings_f
  2338. Send back ping and packet loss update for all current players to this player
  2339. ====================
  2340. */
  2341. void Host_Pings_f (void)
  2342. {
  2343. int i, j, ping, packetloss;
  2344. char temp[128];
  2345. if (!host_client->netconnection)
  2346. return;
  2347. if (sv.protocol != PROTOCOL_QUAKEWORLD)
  2348. {
  2349. MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
  2350. MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
  2351. }
  2352. for (i = 0;i < svs.maxclients;i++)
  2353. {
  2354. packetloss = 0;
  2355. if (svs.clients[i].netconnection)
  2356. for (j = 0;j < NETGRAPH_PACKETS;j++)
  2357. if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
  2358. packetloss++;
  2359. packetloss = packetloss * 100 / NETGRAPH_PACKETS;
  2360. ping = (int)floor(svs.clients[i].ping*1000+0.5);
  2361. ping = bound(0, ping, 9999);
  2362. if (sv.protocol == PROTOCOL_QUAKEWORLD)
  2363. {
  2364. // send qw_svc_updateping and qw_svc_updatepl messages
  2365. MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
  2366. MSG_WriteShort(&host_client->netconnection->message, ping);
  2367. MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
  2368. MSG_WriteByte(&host_client->netconnection->message, packetloss);
  2369. }
  2370. else
  2371. {
  2372. // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
  2373. dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
  2374. MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
  2375. }
  2376. }
  2377. if (sv.protocol != PROTOCOL_QUAKEWORLD)
  2378. MSG_WriteString(&host_client->netconnection->message, "\n");
  2379. }
  2380. void Host_PingPLReport_f(void)
  2381. {
  2382. int i;
  2383. int l = Cmd_Argc();
  2384. if (l > cl.maxclients)
  2385. l = cl.maxclients;
  2386. for (i = 0;i < l;i++)
  2387. {
  2388. cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
  2389. cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
  2390. }
  2391. }
  2392. //=============================================================================
  2393. /*
  2394. ==================
  2395. Host_InitCommands
  2396. ==================
  2397. */
  2398. void Host_InitCommands (void)
  2399. {
  2400. dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
  2401. Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
  2402. Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
  2403. if (gamemode == GAME_NEHAHRA)
  2404. {
  2405. Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
  2406. Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
  2407. Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
  2408. Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
  2409. Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
  2410. }
  2411. else
  2412. {
  2413. Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
  2414. Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
  2415. Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
  2416. Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
  2417. Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
  2418. }
  2419. Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
  2420. Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
  2421. Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
  2422. Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
  2423. Cmd_AddCommand ("reconnect", Host_Reconnect_f, "reconnect to the last server you were on, or resets a quakeworld connection (do not use if currently playing on a netquake server)");
  2424. Cmd_AddCommand ("version", Host_Version_f, "print engine version");
  2425. Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
  2426. Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
  2427. Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
  2428. Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
  2429. Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
  2430. Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
  2431. Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
  2432. Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
  2433. Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
  2434. Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
  2435. Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
  2436. Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
  2437. Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
  2438. Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
  2439. Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
  2440. Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
  2441. Cvar_RegisterVariable (&cl_name);
  2442. Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
  2443. Cvar_RegisterVariable (&cl_color);
  2444. Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
  2445. Cvar_RegisterVariable (&cl_rate);
  2446. Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
  2447. if (gamemode == GAME_NEHAHRA)
  2448. {
  2449. Cvar_RegisterVariable (&cl_pmodel);
  2450. Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
  2451. }
  2452. // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
  2453. Cvar_RegisterVariable (&cl_playermodel);
  2454. Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
  2455. Cvar_RegisterVariable (&cl_playerskin);
  2456. Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
  2457. Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
  2458. Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
  2459. Cmd_AddCommand_WithClientCommand ("begin", NULL, Host_Begin_f, "signon 3 (client asks server to start sending entities, and will go to signon 4 (playing) when the first entity update is received)");
  2460. Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
  2461. Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
  2462. Cvar_RegisterVariable (&rcon_password);
  2463. Cvar_RegisterVariable (&rcon_address);
  2464. Cvar_RegisterVariable (&rcon_secure);
  2465. Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); note: if rcon_secure is set, client and server clocks must be synced e.g. via NTP");
  2466. Cmd_AddCommand ("srcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); this always works as if rcon_secure is set; note: client and server clocks must be synced e.g. via NTP");
  2467. Cmd_AddCommand ("pqrcon", Host_PQRcon_f, "sends a command to a proquake server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
  2468. Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
  2469. Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
  2470. Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
  2471. Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
  2472. Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
  2473. Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
  2474. Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
  2475. Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
  2476. Cmd_AddCommand_WithClientCommand ("pings", NULL, Host_Pings_f, "command sent by clients to request updated ping and packetloss of players on scoreboard (originally from QW, but also used on NQ servers)");
  2477. Cmd_AddCommand ("pingplreport", Host_PingPLReport_f, "command sent by server containing client ping and packet loss values for scoreboard, triggered by pings command from client (not used by QW servers)");
  2478. Cmd_AddCommand ("fixtrans", Image_FixTransparentPixels_f, "change alpha-zero pixels in an image file to sensible values, and write out a new TGA (warning: SLOW)");
  2479. Cvar_RegisterVariable (&r_fixtrans_auto);
  2480. Cvar_RegisterVariable (&team);
  2481. Cvar_RegisterVariable (&skin);
  2482. Cvar_RegisterVariable (&noaim);
  2483. Cvar_RegisterVariable(&sv_cheats);
  2484. Cvar_RegisterVariable(&sv_adminnick);
  2485. Cvar_RegisterVariable(&sv_status_privacy);
  2486. Cvar_RegisterVariable(&sv_status_show_qcstatus);
  2487. }
  2488. void Host_NoOperation_f(void)
  2489. {
  2490. }