2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include "prvm_cmds.h"
28 // for secure rcon authentication
34 cvar_t sv_cheats = {CVAR_SERVER | CVAR_NOTIFY, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
35 cvar_t sv_adminnick = {CVAR_SERVER | CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
36 cvar_t sv_status_privacy = {CVAR_SERVER | CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
37 cvar_t sv_status_show_qcstatus = {CVAR_SERVER | 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."};
38 cvar_t sv_namechangetimer = {CVAR_SERVER | CVAR_SAVE, "sv_namechangetimer", "5", "how often to allow name changes, in seconds (prevents people from using animated names and other tricks"};
39 cvar_t rcon_password = {CVAR_CLIENT | CVAR_SERVER | CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
40 cvar_t rcon_secure = {CVAR_CLIENT | CVAR_SERVER | CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication (1 = time based, 2 = challenge based); NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
41 cvar_t rcon_secure_challengetimeout = {CVAR_CLIENT, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"};
42 cvar_t rcon_address = {CVAR_CLIENT, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
43 cvar_t team = {CVAR_CLIENT | CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
44 cvar_t skin = {CVAR_CLIENT | CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
45 cvar_t noaim = {CVAR_CLIENT | CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
46 cvar_t r_fixtrans_auto = {CVAR_CLIENT, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"};
48 extern qboolean host_shuttingdown;
49 extern cvar_t developer_entityparsing;
57 void Host_Quit_f(cmd_state_t *cmd)
60 Con_Printf("shutting down already!\n");
70 static void Host_Status_f(cmd_state_t *cmd)
72 prvm_prog_t *prog = SVVM_prog;
75 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
76 void (*print) (const char *fmt, ...);
77 char ip[48]; // can contain a full length v6 address with [] and a port
81 if (cmd->source == src_command)
83 // if running a client, try to send over network so the client's status report parser will see the report
84 if (cls.state == ca_connected)
86 Cmd_ForwardToServer_f(cmd);
92 print = SV_ClientPrintf;
98 if (Cmd_Argc(cmd) == 2)
100 if (strcmp(Cmd_Argv(cmd, 1), "1") == 0)
102 else if (strcmp(Cmd_Argv(cmd, 1), "2") == 0)
106 for (players = 0, i = 0;i < svs.maxclients;i++)
107 if (svs.clients[i].active)
109 print ("host: %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
110 print ("version: %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
111 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
112 print ("map: %s\n", sv.name);
113 print ("timing: %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
114 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
117 print ("^2IP %%pl ping time frags no name\n");
119 print ("^5IP no name\n");
121 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
128 if (in == 0 || in == 1)
130 seconds = (int)(realtime - client->connecttime);
131 minutes = seconds / 60;
134 seconds -= (minutes * 60);
135 hours = minutes / 60;
137 minutes -= (hours * 60);
143 if (client->netconnection)
144 for (j = 0;j < NETGRAPH_PACKETS;j++)
145 if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
147 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
148 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
151 if(sv_status_privacy.integer && cmd->source != src_command)
152 strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
154 strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
156 frags = client->frags;
158 if(sv_status_show_qcstatus.integer)
160 prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
161 const char *str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
167 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
168 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
172 frags = atoi(qcstatus);
176 if (in == 0) // default layout
178 if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
180 // LadyHavoc: this is very touchy because we must maintain ProQuake compatible status output
181 print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
186 // LadyHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
187 print ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
191 else if (in == 1) // extended layout
193 print ("%s%-47s %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);
195 else if (in == 2) // reduced layout
197 print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
207 Sets client to godmode
210 static void Host_God_f(cmd_state_t *cmd)
212 prvm_prog_t *prog = SVVM_prog;
213 if (!sv_cheats.integer)
215 SV_ClientPrint("No cheats allowed. Set sv_cheats to 1 in the server console to enable.\n");
219 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
220 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
221 SV_ClientPrint("godmode OFF\n");
223 SV_ClientPrint("godmode ON\n");
226 static void Host_Notarget_f(cmd_state_t *cmd)
228 prvm_prog_t *prog = SVVM_prog;
229 if (!sv_cheats.integer)
231 SV_ClientPrint("No cheats allowed. Set sv_cheats to 1 in the server console to enable.\n");
235 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
236 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
237 SV_ClientPrint("notarget OFF\n");
239 SV_ClientPrint("notarget ON\n");
242 qboolean noclip_anglehack;
244 static void Host_Noclip_f(cmd_state_t *cmd)
246 prvm_prog_t *prog = SVVM_prog;
247 if (!sv_cheats.integer)
249 SV_ClientPrint("No cheats allowed. Set sv_cheats to 1 in the server console to enable.\n");
253 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
255 noclip_anglehack = true;
256 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
257 SV_ClientPrint("noclip ON\n");
261 noclip_anglehack = false;
262 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
263 SV_ClientPrint("noclip OFF\n");
271 Sets client to flymode
274 static void Host_Fly_f(cmd_state_t *cmd)
276 prvm_prog_t *prog = SVVM_prog;
277 if (!sv_cheats.integer)
279 SV_ClientPrint("No cheats allowed. Set sv_cheats to 1 in the server console to enable.\n");
283 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
285 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
286 SV_ClientPrint("flymode ON\n");
290 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
291 SV_ClientPrint("flymode OFF\n");
302 static void Host_Ping_f(cmd_state_t *cmd)
306 void (*print) (const char *fmt, ...);
308 if (cmd->source == src_command)
310 // if running a client, try to send over network so the client's ping report parser will see the report
311 if (cls.state == ca_connected)
313 Cmd_ForwardToServer_f(cmd);
319 print = SV_ClientPrintf;
324 print("Client ping times:\n");
325 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
329 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
333 // Disable cheats if sv_cheats is turned off
334 static void Host_DisableCheats_c(char *value)
336 prvm_prog_t *prog = SVVM_prog;
339 if (value[0] == '0' && !value[1])
341 while (svs.clients[i].edict)
343 if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_GODMODE))
344 PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_GODMODE;
345 if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_NOTARGET))
346 PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_NOTARGET;
347 if (PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_NOCLIP ||
348 PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_FLY)
350 noclip_anglehack = false;
351 PRVM_serveredictfloat(svs.clients[i].edict, movetype) = MOVETYPE_WALK;
359 ===============================================================================
363 ===============================================================================
367 ======================
372 command from the console. Active clients are kicked off.
373 ======================
375 static void Host_Map_f(cmd_state_t *cmd)
377 char level[MAX_QPATH];
379 if (Cmd_Argc(cmd) != 2)
381 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
385 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
386 if (gamemode == GAME_DELUXEQUAKE)
387 Cvar_Set(&cvars_all, "warpmark", "");
389 cls.demonum = -1; // stop demo loop in case this fails
392 Host_ShutdownServer();
394 if(svs.maxclients != svs.maxclients_next)
396 svs.maxclients = svs.maxclients_next;
398 Mem_Free(svs.clients);
399 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
404 if (key_dest == key_menu || key_dest == key_menu_grabbed)
409 svs.serverflags = 0; // haven't completed an episode yet
410 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
411 SV_SpawnServer(level);
412 if (sv.active && cls.state == ca_disconnected)
413 CL_EstablishConnection("local:1", -2);
420 Goes to a new map, taking all clients along
423 static void Host_Changelevel_f(cmd_state_t *cmd)
425 char level[MAX_QPATH];
427 if (Cmd_Argc(cmd) != 2)
429 Con_Print("changelevel <levelname> : continue game on a new level\n");
440 if (key_dest == key_menu || key_dest == key_menu_grabbed)
445 SV_SaveSpawnparms ();
446 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
447 SV_SpawnServer(level);
448 if (sv.active && cls.state == ca_disconnected)
449 CL_EstablishConnection("local:1", -2);
456 Restarts the current server for a dead player
459 static void Host_Restart_f(cmd_state_t *cmd)
461 char mapname[MAX_QPATH];
463 if (Cmd_Argc(cmd) != 1)
465 Con_Print("restart : restart current level\n");
470 Con_Print("Only the server may restart\n");
476 if (key_dest == key_menu || key_dest == key_menu_grabbed)
481 strlcpy(mapname, sv.name, sizeof(mapname));
482 SV_SpawnServer(mapname);
483 if (sv.active && cls.state == ca_disconnected)
484 CL_EstablishConnection("local:1", -2);
491 This command causes the client to wait for the signon messages again.
492 This is sent just before a server changes levels
495 void Host_Reconnect_f(cmd_state_t *cmd)
498 // if not connected, reconnect to the most recent server
501 // if we have connected to a server recently, the userinfo
502 // will still contain its IP address, so get the address...
503 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
505 CL_EstablishConnection(temp, -1);
507 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
510 // if connected, do something based on protocol
511 if (cls.protocol == PROTOCOL_QUAKEWORLD)
513 // quakeworld can just re-login
514 if (cls.qw_downloadmemory) // don't change when downloading
519 if (cls.state == ca_connected)
521 Con_Printf("Server is changing level...\n");
522 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
523 MSG_WriteString(&cls.netcon->message, "new");
528 // netquake uses reconnect on level changes (silly)
529 if (Cmd_Argc(cmd) != 1)
531 Con_Print("reconnect : wait for signon messages again\n");
536 Con_Print("reconnect: no signon, ignoring reconnect\n");
539 cls.signon = 0; // need new connection messages
544 =====================
547 User command to connect to server
548 =====================
550 static void Host_Connect_f(cmd_state_t *cmd)
552 if (Cmd_Argc(cmd) < 2)
554 Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
557 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
558 if(rcon_secure.integer <= 0)
559 Cvar_SetQuick(&rcon_password, "");
560 CL_EstablishConnection(Cmd_Argv(cmd, 1), 2);
565 ===============================================================================
569 ===============================================================================
572 #define SAVEGAME_VERSION 5
574 void Host_Savegame_to(prvm_prog_t *prog, const char *name)
577 int i, k, l, numbuffers, lightstyles = 64;
578 char comment[SAVEGAME_COMMENT_LENGTH+1];
579 char line[MAX_INPUTLINE];
583 // first we have to figure out if this can be saved in 64 lightstyles
584 // (for Quake compatibility)
585 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
586 if (sv.lightstyles[i][0])
589 isserver = prog == SVVM_prog;
591 Con_Printf("Saving game to %s...\n", name);
592 f = FS_OpenRealFile(name, "wb", false);
595 Con_Print("ERROR: couldn't open.\n");
599 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
601 memset(comment, 0, sizeof(comment));
603 dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog, PRVM_serveredictstring(prog->edicts, message)), (int)PRVM_serverglobalfloat(killed_monsters), (int)PRVM_serverglobalfloat(total_monsters));
605 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", prog->name);
606 // convert space to _ to make stdio happy
607 // LadyHavoc: convert control characters to _ as well
608 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
609 if (ISWHITESPACEORCONTROL(comment[i]))
611 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
613 FS_Printf(f, "%s\n", comment);
616 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
617 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
618 FS_Printf(f, "%d\n", current_skill);
619 FS_Printf(f, "%s\n", sv.name);
620 FS_Printf(f, "%f\n",sv.time);
624 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
625 FS_Printf(f, "(dummy)\n");
626 FS_Printf(f, "%d\n", 0);
627 FS_Printf(f, "%s\n", "(dummy)");
628 FS_Printf(f, "%f\n", realtime);
631 // write the light styles
632 for (i=0 ; i<lightstyles ; i++)
634 if (isserver && sv.lightstyles[i][0])
635 FS_Printf(f, "%s\n", sv.lightstyles[i]);
640 PRVM_ED_WriteGlobals (prog, f);
641 for (i=0 ; i<prog->num_edicts ; i++)
643 FS_Printf(f,"// edict %d\n", i);
644 //Con_Printf("edict %d...\n", i);
645 PRVM_ED_Write (prog, f, PRVM_EDICT_NUM(i));
650 FS_Printf(f,"// DarkPlaces extended savegame\n");
651 // darkplaces extension - extra lightstyles, support for color lightstyles
652 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
653 if (isserver && sv.lightstyles[i][0])
654 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
656 // darkplaces extension - model precaches
657 for (i=1 ; i<MAX_MODELS ; i++)
658 if (sv.model_precache[i][0])
659 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
661 // darkplaces extension - sound precaches
662 for (i=1 ; i<MAX_SOUNDS ; i++)
663 if (sv.sound_precache[i][0])
664 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
666 // darkplaces extension - save buffers
667 numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
668 for (i = 0; i < numbuffers; i++)
670 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
671 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
673 FS_Printf(f,"sv.buffer %i %i \"string\"\n", i, stringbuffer->flags & STRINGBUFFER_QCFLAGS);
674 for(k = 0; k < stringbuffer->num_strings; k++)
676 if (!stringbuffer->strings[k])
678 // Parse the string a bit to turn special characters
679 // (like newline, specifically) into escape codes
680 s = stringbuffer->strings[k];
681 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
708 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
716 Con_Print("done.\n");
724 static void Host_Savegame_f(cmd_state_t *cmd)
726 prvm_prog_t *prog = SVVM_prog;
727 char name[MAX_QPATH];
728 qboolean deadflag = false;
732 Con_Print("Can't save - no server running.\n");
736 deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
740 // singleplayer checks
743 Con_Print("Can't save in intermission.\n");
749 Con_Print("Can't savegame with a dead player\n");
754 Con_Warn("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");
756 if (Cmd_Argc(cmd) != 2)
758 Con_Print("save <savename> : save a game\n");
762 if (strstr(Cmd_Argv(cmd, 1), ".."))
764 Con_Print("Relative pathnames are not allowed.\n");
768 strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
769 FS_DefaultExtension (name, ".sav", sizeof (name));
771 Host_Savegame_to(prog, name);
781 static void Host_Loadgame_f(cmd_state_t *cmd)
783 prvm_prog_t *prog = SVVM_prog;
784 char filename[MAX_QPATH];
785 char mapname[MAX_QPATH];
792 int i, k, numbuffers;
795 float spawn_parms[NUM_SPAWN_PARMS];
796 prvm_stringbuffer_t *stringbuffer;
798 if (Cmd_Argc(cmd) != 2)
800 Con_Print("load <savename> : load a game\n");
804 strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename));
805 FS_DefaultExtension (filename, ".sav", sizeof (filename));
807 Con_Printf("Loading game from %s...\n", filename);
809 // stop playing demos
810 if (cls.demoplayback)
815 if (key_dest == key_menu || key_dest == key_menu_grabbed)
820 cls.demonum = -1; // stop demo loop in case this fails
822 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
825 Con_Print("ERROR: couldn't open.\n");
829 if(developer_entityparsing.integer)
830 Con_Printf("Host_Loadgame_f: loading version\n");
833 COM_ParseToken_Simple(&t, false, false, true);
834 version = atoi(com_token);
835 if (version != SAVEGAME_VERSION)
838 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
842 if(developer_entityparsing.integer)
843 Con_Printf("Host_Loadgame_f: loading description\n");
846 COM_ParseToken_Simple(&t, false, false, true);
848 for (i = 0;i < NUM_SPAWN_PARMS;i++)
850 COM_ParseToken_Simple(&t, false, false, true);
851 spawn_parms[i] = atof(com_token);
854 COM_ParseToken_Simple(&t, false, false, true);
855 // this silliness is so we can load 1.06 save files, which have float skill values
856 current_skill = (int)(atof(com_token) + 0.5);
857 Cvar_SetValue (&cvars_all, "skill", (float)current_skill);
859 if(developer_entityparsing.integer)
860 Con_Printf("Host_Loadgame_f: loading mapname\n");
863 COM_ParseToken_Simple(&t, false, false, true);
864 strlcpy (mapname, com_token, sizeof(mapname));
866 if(developer_entityparsing.integer)
867 Con_Printf("Host_Loadgame_f: loading time\n");
870 COM_ParseToken_Simple(&t, false, false, true);
871 time = atof(com_token);
873 if(developer_entityparsing.integer)
874 Con_Printf("Host_Loadgame_f: spawning server\n");
876 SV_SpawnServer (mapname);
880 Con_Print("Couldn't load map\n");
883 sv.paused = true; // pause until all clients connect
886 if(developer_entityparsing.integer)
887 Con_Printf("Host_Loadgame_f: loading light styles\n");
889 // load the light styles
894 for (i = 0;i < MAX_LIGHTSTYLES;i++)
898 COM_ParseToken_Simple(&t, false, false, true);
899 // if this is a 64 lightstyle savegame produced by Quake, stop now
900 // we have to check this because darkplaces may save more than 64
901 if (com_token[0] == '{')
906 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
909 if(developer_entityparsing.integer)
910 Con_Printf("Host_Loadgame_f: skipping until globals\n");
912 // now skip everything before the first opening brace
913 // (this is for forward compatibility, so that older versions (at
914 // least ones with this fix) can load savegames with extra data before the
915 // first brace, as might be produced by a later engine version)
919 if (!COM_ParseToken_Simple(&t, false, false, true))
921 if (com_token[0] == '{')
928 // unlink all entities
929 World_UnlinkAll(&sv.world);
931 // load the edicts out of the savegame file
936 while (COM_ParseToken_Simple(&t, false, false, true))
937 if (!strcmp(com_token, "}"))
939 if (!COM_ParseToken_Simple(&start, false, false, true))
944 if (strcmp(com_token,"{"))
947 Host_Error ("First token isn't a brace");
952 if(developer_entityparsing.integer)
953 Con_Printf("Host_Loadgame_f: loading globals\n");
955 // parse the global vars
956 PRVM_ED_ParseGlobals (prog, start);
958 // restore the autocvar globals
959 Cvar_UpdateAllAutoCvars(prog->console_cmd->cvars);
964 if (entnum >= MAX_EDICTS)
967 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
969 while (entnum >= prog->max_edicts)
970 PRVM_MEM_IncreaseEdicts(prog);
971 ent = PRVM_EDICT_NUM(entnum);
972 memset(ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
973 ent->priv.server->free = false;
975 if(developer_entityparsing.integer)
976 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
978 PRVM_ED_ParseEdict (prog, start, ent);
980 // link it into the bsp tree
981 if (!ent->priv.server->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
989 prog->num_edicts = entnum;
992 for (i = 0;i < NUM_SPAWN_PARMS;i++)
993 svs.clients[0].spawn_parms[i] = spawn_parms[i];
995 if(developer_entityparsing.integer)
996 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
998 // read extended data if present
999 // the extended data is stored inside a /* */ comment block, which the
1000 // parser intentionally skips, so we have to check for it manually here
1003 while (*end == '\r' || *end == '\n')
1005 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
1007 if(developer_entityparsing.integer)
1008 Con_Printf("Host_Loadgame_f: loading extended data\n");
1010 Con_Printf("Loading extended DarkPlaces savegame\n");
1012 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
1013 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
1014 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
1017 while (COM_ParseToken_Simple(&t, false, false, true))
1019 if (!strcmp(com_token, "sv.lightstyles"))
1021 COM_ParseToken_Simple(&t, false, false, true);
1022 i = atoi(com_token);
1023 COM_ParseToken_Simple(&t, false, false, true);
1024 if (i >= 0 && i < MAX_LIGHTSTYLES)
1025 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
1027 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
1029 else if (!strcmp(com_token, "sv.model_precache"))
1031 COM_ParseToken_Simple(&t, false, false, true);
1032 i = atoi(com_token);
1033 COM_ParseToken_Simple(&t, false, false, true);
1034 if (i >= 0 && i < MAX_MODELS)
1036 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
1037 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
1040 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
1042 else if (!strcmp(com_token, "sv.sound_precache"))
1044 COM_ParseToken_Simple(&t, false, false, true);
1045 i = atoi(com_token);
1046 COM_ParseToken_Simple(&t, false, false, true);
1047 if (i >= 0 && i < MAX_SOUNDS)
1048 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1050 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1052 else if (!strcmp(com_token, "sv.buffer"))
1054 if (COM_ParseToken_Simple(&t, false, false, true))
1056 i = atoi(com_token);
1059 k = STRINGBUFFER_SAVED;
1060 if (COM_ParseToken_Simple(&t, false, false, true))
1061 k |= atoi(com_token);
1062 if (!BufStr_FindCreateReplace(prog, i, k, "string"))
1063 Con_Errorf("failed to create stringbuffer %i\n", i);
1066 Con_Printf("unsupported stringbuffer index %i \"%s\"\n", i, com_token);
1069 Con_Printf("unexpected end of line when parsing sv.buffer (expected buffer index)\n");
1071 else if (!strcmp(com_token, "sv.bufstr"))
1073 if (!COM_ParseToken_Simple(&t, false, false, true))
1074 Con_Printf("unexpected end of line when parsing sv.bufstr\n");
1077 i = atoi(com_token);
1078 stringbuffer = BufStr_FindCreateReplace(prog, i, STRINGBUFFER_SAVED, "string");
1081 if (COM_ParseToken_Simple(&t, false, false, true))
1083 k = atoi(com_token);
1084 if (COM_ParseToken_Simple(&t, false, false, true))
1085 BufStr_Set(prog, stringbuffer, k, com_token);
1087 Con_Printf("unexpected end of line when parsing sv.bufstr (expected string)\n");
1090 Con_Printf("unexpected end of line when parsing sv.bufstr (expected strindex)\n");
1093 Con_Errorf("failed to create stringbuffer %i \"%s\"\n", i, com_token);
1096 // skip any trailing text or unrecognized commands
1097 while (COM_ParseToken_Simple(&t, true, false, true) && strcmp(com_token, "\n"))
1104 // remove all temporary flagged string buffers (ones created with BufStr_FindCreateReplace)
1105 numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
1106 for (i = 0; i < numbuffers; i++)
1108 if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
1109 if (stringbuffer->flags & STRINGBUFFER_TEMP)
1110 BufStr_Del(prog, stringbuffer);
1113 if(developer_entityparsing.integer)
1114 Con_Printf("Host_Loadgame_f: finished\n");
1116 // make sure we're connected to loopback
1117 if (sv.active && cls.state == ca_disconnected)
1118 CL_EstablishConnection("local:1", -2);
1121 //============================================================================
1124 ======================
1126 ======================
1128 cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1129 static void Host_Name_f(cmd_state_t *cmd)
1131 prvm_prog_t *prog = SVVM_prog;
1133 qboolean valid_colors;
1134 const char *newNameSource;
1135 char newName[sizeof(host_client->name)];
1137 if (Cmd_Argc (cmd) == 1)
1139 if (cmd->source == src_command)
1141 Con_Printf("name: %s\n", cl_name.string);
1146 if (Cmd_Argc (cmd) == 2)
1147 newNameSource = Cmd_Argv(cmd, 1);
1149 newNameSource = Cmd_Args(cmd);
1151 strlcpy(newName, newNameSource, sizeof(newName));
1153 if (cmd->source == src_command)
1155 Cvar_Set (&cvars_all, "_cl_name", newName);
1156 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1158 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1159 Con_Printf("name: %s\n", cl_name.string);
1164 if (realtime < host_client->nametime)
1166 SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
1170 host_client->nametime = realtime + max(0.0f, sv_namechangetimer.value);
1172 // point the string back at updateclient->name to keep it safe
1173 strlcpy (host_client->name, newName, sizeof (host_client->name));
1175 for (i = 0, j = 0;host_client->name[i];i++)
1176 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1177 host_client->name[j++] = host_client->name[i];
1178 host_client->name[j] = 0;
1180 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1181 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1183 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1184 host_client->name[sizeof(host_client->name) - 1] = 0;
1185 host_client->name[0] = STRING_COLOR_TAG;
1186 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1189 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1190 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1193 l = strlen(host_client->name);
1194 if(l < sizeof(host_client->name) - 1)
1196 // duplicate the color tag to escape it
1197 host_client->name[i] = STRING_COLOR_TAG;
1198 host_client->name[i+1] = 0;
1199 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1203 // remove the last character to fix the color code
1204 host_client->name[l-1] = 0;
1205 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1209 // find the last color tag offset and decide if we need to add a reset tag
1210 for (i = 0, j = -1;host_client->name[i];i++)
1212 if (host_client->name[i] == STRING_COLOR_TAG)
1214 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1217 // if this happens to be a reset tag then we don't need one
1218 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1223 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]))
1229 if (host_client->name[i+1] == STRING_COLOR_TAG)
1236 // does not end in the default color string, so add it
1237 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1238 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1240 PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
1241 if (strcmp(host_client->old_name, host_client->name))
1243 if (host_client->begun)
1244 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1245 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1246 // send notification to all clients
1247 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1248 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1249 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1250 SV_WriteNetnameIntoDemo(host_client);
1255 ======================
1257 ======================
1259 cvar_t cl_playermodel = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz/Xonotic (changed by playermodel command)"};
1260 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1261 static void Host_Playermodel_f(cmd_state_t *cmd)
1263 prvm_prog_t *prog = SVVM_prog;
1265 char newPath[sizeof(host_client->playermodel)];
1267 if (Cmd_Argc (cmd) == 1)
1269 if (cmd->source == src_command)
1271 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1276 if (Cmd_Argc (cmd) == 2)
1277 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1279 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1281 for (i = 0, j = 0;newPath[i];i++)
1282 if (newPath[i] != '\r' && newPath[i] != '\n')
1283 newPath[j++] = newPath[i];
1286 if (cmd->source == src_command)
1288 Cvar_Set (&cvars_all, "_cl_playermodel", newPath);
1293 if (realtime < host_client->nametime)
1295 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1299 host_client->nametime = realtime + 5;
1302 // point the string back at updateclient->name to keep it safe
1303 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1304 PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
1305 if (strcmp(host_client->old_model, host_client->playermodel))
1307 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1308 /*// send notification to all clients
1309 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1310 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1311 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1316 ======================
1318 ======================
1320 cvar_t cl_playerskin = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
1321 static void Host_Playerskin_f(cmd_state_t *cmd)
1323 prvm_prog_t *prog = SVVM_prog;
1325 char newPath[sizeof(host_client->playerskin)];
1327 if (Cmd_Argc (cmd) == 1)
1329 if (cmd->source == src_command)
1331 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1336 if (Cmd_Argc (cmd) == 2)
1337 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1339 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1341 for (i = 0, j = 0;newPath[i];i++)
1342 if (newPath[i] != '\r' && newPath[i] != '\n')
1343 newPath[j++] = newPath[i];
1346 if (cmd->source == src_command)
1348 Cvar_Set (&cvars_all, "_cl_playerskin", newPath);
1353 if (realtime < host_client->nametime)
1355 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1359 host_client->nametime = realtime + 5;
1362 // point the string back at updateclient->name to keep it safe
1363 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1364 PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
1365 if (strcmp(host_client->old_skin, host_client->playerskin))
1367 //if (host_client->begun)
1368 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1369 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1370 /*// send notification to all clients
1371 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1372 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1373 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1377 static void Host_Version_f(cmd_state_t *cmd)
1379 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1382 static void Host_Say(cmd_state_t *cmd, qboolean teamonly)
1384 prvm_prog_t *prog = SVVM_prog;
1389 // LadyHavoc: long say messages
1391 qboolean fromServer = false;
1393 if (cmd->source == src_command)
1395 if (cls.state == ca_dedicated)
1402 Cmd_ForwardToServer_f(cmd);
1407 if (Cmd_Argc (cmd) < 2)
1410 if (!teamplay.integer)
1420 // note this uses the chat prefix \001
1421 if (!fromServer && !teamonly)
1422 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1423 else if (!fromServer && teamonly)
1424 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1425 else if(*(sv_adminnick.string))
1426 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1428 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1429 p2 = text + strlen(text);
1430 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1432 if (p2[-1] == '\"' && quoted)
1437 strlcat(text, "\n", sizeof(text));
1439 // note: save is not a valid edict if fromServer is true
1441 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1442 if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
1443 SV_ClientPrint(text);
1446 if (cls.state == ca_dedicated)
1447 Con_Print(&text[1]);
1451 static void Host_Say_f(cmd_state_t *cmd)
1453 Host_Say(cmd, false);
1457 static void Host_Say_Team_f(cmd_state_t *cmd)
1459 Host_Say(cmd, true);
1463 static void Host_Tell_f(cmd_state_t *cmd)
1465 const char *playername_start = NULL;
1466 size_t playername_length = 0;
1467 int playernumber = 0;
1470 const char *p1, *p2;
1471 char text[MAX_INPUTLINE]; // LadyHavoc: FIXME: temporary buffer overflow fix (was 64)
1472 qboolean fromServer = false;
1474 if (cmd->source == src_command)
1476 if (cls.state == ca_dedicated)
1480 Cmd_ForwardToServer_f(cmd);
1485 if (Cmd_Argc (cmd) < 2)
1488 // note this uses the chat prefix \001
1490 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1491 else if(*(sv_adminnick.string))
1492 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1494 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1497 p2 = p1 + strlen(p1);
1498 // remove the target name
1499 while (p1 < p2 && *p1 == ' ')
1504 while (p1 < p2 && *p1 == ' ')
1506 while (p1 < p2 && isdigit(*p1))
1508 playernumber = playernumber * 10 + (*p1 - '0');
1516 playername_start = p1;
1517 while (p1 < p2 && *p1 != '"')
1519 playername_length = p1 - playername_start;
1525 playername_start = p1;
1526 while (p1 < p2 && *p1 != ' ')
1528 playername_length = p1 - playername_start;
1530 while (p1 < p2 && *p1 == ' ')
1532 if(playername_start)
1534 // set playernumber to the right client
1536 if(playername_length >= sizeof(namebuf))
1539 Con_Print("Host_Tell: too long player name/ID\n");
1541 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1544 memcpy(namebuf, playername_start, playername_length);
1545 namebuf[playername_length] = 0;
1546 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1548 if (!svs.clients[playernumber].active)
1550 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1554 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1557 Con_Print("Host_Tell: invalid player name/ID\n");
1559 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1562 // remove trailing newlines
1563 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1565 // remove quotes if present
1571 else if (fromServer)
1572 Con_Print("Host_Tell: missing end quote\n");
1574 SV_ClientPrint("Host_Tell: missing end quote\n");
1576 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1579 return; // empty say
1580 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1586 host_client = svs.clients + playernumber;
1587 SV_ClientPrint(text);
1597 cvar_t cl_color = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1598 static void Host_Color(cmd_state_t *cmd, int changetop, int changebottom)
1600 prvm_prog_t *prog = SVVM_prog;
1601 int top, bottom, playercolor;
1603 // get top and bottom either from the provided values or the current values
1604 // (allows changing only top or bottom, or both at once)
1605 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1606 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1610 // LadyHavoc: allowing skin colormaps 14 and 15 by commenting this out
1616 playercolor = top*16 + bottom;
1618 if (cmd->source == src_command)
1620 Cvar_SetValueQuick(&cl_color, playercolor);
1624 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1627 if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
1629 Con_DPrint("Calling SV_ChangeTeam\n");
1630 prog->globals.fp[OFS_PARM0] = playercolor;
1631 PRVM_serverglobalfloat(time) = sv.time;
1632 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1633 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1637 if (host_client->edict)
1639 PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1640 PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1642 host_client->colors = playercolor;
1643 if (host_client->old_colors != host_client->colors)
1645 host_client->old_colors = host_client->colors;
1646 // send notification to all clients
1647 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1648 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1649 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1654 static void Host_Color_f(cmd_state_t *cmd)
1658 if (Cmd_Argc(cmd) == 1)
1660 if (cmd->source == src_command)
1662 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1663 Con_Print("color <0-15> [0-15]\n");
1668 if (Cmd_Argc(cmd) == 2)
1669 top = bottom = atoi(Cmd_Argv(cmd, 1));
1672 top = atoi(Cmd_Argv(cmd, 1));
1673 bottom = atoi(Cmd_Argv(cmd, 2));
1675 Host_Color(cmd, top, bottom);
1678 static void Host_TopColor_f(cmd_state_t *cmd)
1680 if (Cmd_Argc(cmd) == 1)
1682 if (cmd->source == src_command)
1684 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1685 Con_Print("topcolor <0-15>\n");
1690 Host_Color(cmd, atoi(Cmd_Argv(cmd, 1)), -1);
1693 static void Host_BottomColor_f(cmd_state_t *cmd)
1695 if (Cmd_Argc(cmd) == 1)
1697 if (cmd->source == src_command)
1699 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1700 Con_Print("bottomcolor <0-15>\n");
1705 Host_Color(cmd, -1, atoi(Cmd_Argv(cmd, 1)));
1708 cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1709 cvar_t cl_rate_burstsize = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate_burstsize", "1024", "internal storage cvar for current rate control burst size (changed by rate_burstsize command)"};
1710 static void Host_Rate_f(cmd_state_t *cmd)
1714 if (Cmd_Argc(cmd) != 2)
1716 if (cmd->source == src_command)
1718 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1719 Con_Print("rate <bytespersecond>\n");
1724 rate = atoi(Cmd_Argv(cmd, 1));
1726 if (cmd->source == src_command)
1728 Cvar_SetValue (&cvars_all, "_cl_rate", max(NET_MINRATE, rate));
1732 host_client->rate = rate;
1734 static void Host_Rate_BurstSize_f(cmd_state_t *cmd)
1738 if (Cmd_Argc(cmd) != 2)
1740 Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
1741 Con_Print("rate_burstsize <bytes>\n");
1745 rate_burstsize = atoi(Cmd_Argv(cmd, 1));
1747 if (cmd->source == src_command)
1749 Cvar_SetValue (&cvars_all, "_cl_rate_burstsize", rate_burstsize);
1753 host_client->rate_burstsize = rate_burstsize;
1761 static void Host_Kill_f(cmd_state_t *cmd)
1763 prvm_prog_t *prog = SVVM_prog;
1764 if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
1766 SV_ClientPrint("Can't suicide -- already dead!\n");
1770 PRVM_serverglobalfloat(time) = sv.time;
1771 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1772 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
1781 static void Host_Pause_f(cmd_state_t *cmd)
1783 void (*print) (const char *fmt, ...);
1784 if (cmd->source == src_command)
1786 // if running a client, try to send over network so the pause is handled by the server
1787 if (cls.state == ca_connected)
1789 Cmd_ForwardToServer_f(cmd);
1795 print = SV_ClientPrintf;
1797 if (!pausable.integer)
1799 if (cmd->source == src_client)
1801 if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
1803 print("Pause not allowed.\n");
1810 if (cmd->source != src_command)
1811 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1812 else if(*(sv_adminnick.string))
1813 SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
1815 SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
1816 // send notification to all clients
1817 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1818 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1822 ======================
1824 LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1825 LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1826 ======================
1828 cvar_t cl_pmodel = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
1829 static void Host_PModel_f(cmd_state_t *cmd)
1831 prvm_prog_t *prog = SVVM_prog;
1834 if (Cmd_Argc (cmd) == 1)
1836 if (cmd->source == src_command)
1838 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1842 i = atoi(Cmd_Argv(cmd, 1));
1844 if (cmd->source == src_command)
1846 if (cl_pmodel.integer == i)
1848 Cvar_SetValue (&cvars_all, "_cl_pmodel", i);
1849 if (cls.state == ca_connected)
1850 Cmd_ForwardToServer_f(cmd);
1854 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1857 //===========================================================================
1865 static void Host_PreSpawn_f(cmd_state_t *cmd)
1867 if (host_client->prespawned)
1869 Con_Print("prespawn not valid -- already prespawned\n");
1872 host_client->prespawned = true;
1874 if (host_client->netconnection)
1876 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1877 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1878 MSG_WriteByte (&host_client->netconnection->message, 2);
1879 host_client->sendsignon = 0; // enable unlimited sends again
1882 // reset the name change timer because the client will send name soon
1883 host_client->nametime = 0;
1891 static void Host_Spawn_f(cmd_state_t *cmd)
1893 prvm_prog_t *prog = SVVM_prog;
1896 int stats[MAX_CL_STATS];
1898 if (!host_client->prespawned)
1900 Con_Print("Spawn not valid -- not yet prespawned\n");
1903 if (host_client->spawned)
1905 Con_Print("Spawn not valid -- already spawned\n");
1908 host_client->spawned = true;
1910 // reset name change timer again because they might want to change name
1911 // again in the first 5 seconds after connecting
1912 host_client->nametime = 0;
1914 // LadyHavoc: moved this above the QC calls at FrikaC's request
1915 // LadyHavoc: commented this out
1916 //if (host_client->netconnection)
1917 // SZ_Clear (&host_client->netconnection->message);
1919 // run the entrance script
1922 // loaded games are fully initialized already
1923 if (PRVM_serverfunction(RestoreGame))
1925 Con_DPrint("Calling RestoreGame\n");
1926 PRVM_serverglobalfloat(time) = sv.time;
1927 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1928 prog->ExecuteProgram(prog, PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1933 //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), host_client->name);
1935 // copy spawn parms out of the client_t
1936 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1937 (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
1939 // call the spawn function
1940 host_client->clientconnectcalled = true;
1941 PRVM_serverglobalfloat(time) = sv.time;
1942 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1943 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1945 if (cls.state == ca_dedicated)
1946 Con_Printf("%s connected\n", host_client->name);
1948 PRVM_serverglobalfloat(time) = sv.time;
1949 prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1952 if (!host_client->netconnection)
1955 // send time of update
1956 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1957 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1959 // send all current names, colors, and frag counts
1960 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1962 if (!client->active)
1964 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1965 MSG_WriteByte (&host_client->netconnection->message, i);
1966 MSG_WriteString (&host_client->netconnection->message, client->name);
1967 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1968 MSG_WriteByte (&host_client->netconnection->message, i);
1969 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1970 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1971 MSG_WriteByte (&host_client->netconnection->message, i);
1972 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1975 // send all current light styles
1976 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1978 if (sv.lightstyles[i][0])
1980 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1981 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1982 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1987 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1988 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1989 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1991 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1992 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1993 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1995 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1996 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1997 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1999 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
2000 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
2001 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
2004 // Never send a roll angle, because savegames can catch the server
2005 // in a state where it is expecting the client to correct the angle
2006 // and it won't happen if the game was just loaded, so you wind up
2007 // with a permanent head tilt
2010 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
2011 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
2012 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
2013 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
2017 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
2018 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
2019 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
2020 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
2023 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
2025 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
2026 MSG_WriteByte (&host_client->netconnection->message, 3);
2034 static void Host_Begin_f(cmd_state_t *cmd)
2036 if (!host_client->spawned)
2038 Con_Print("Begin not valid -- not yet spawned\n");
2041 if (host_client->begun)
2043 Con_Print("Begin not valid -- already begun\n");
2046 host_client->begun = true;
2048 // LadyHavoc: note: this code also exists in SV_DropClient
2052 for (i = 0;i < svs.maxclients;i++)
2053 if (svs.clients[i].active && !svs.clients[i].spawned)
2055 if (i == svs.maxclients)
2057 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
2058 sv.paused = sv.loadgame = false; // we're basically done with loading now
2063 //===========================================================================
2070 Kicks a user off of the server
2073 static void Host_Kick_f(cmd_state_t *cmd)
2076 const char *message = NULL;
2079 qboolean byNumber = false;
2086 if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
2088 i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
2089 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
2095 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2097 if (!host_client->active)
2099 if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
2104 if (i < svs.maxclients)
2106 if (cmd->source == src_command)
2108 if (cls.state == ca_dedicated)
2111 who = cl_name.string;
2116 // can't kick yourself!
2117 if (host_client == save)
2120 if (Cmd_Argc(cmd) > 2)
2122 message = Cmd_Args(cmd);
2123 COM_ParseToken_Simple(&message, false, false, true);
2126 message++; // skip the #
2127 while (*message == ' ') // skip white space
2129 message += strlen(Cmd_Argv(cmd, 2)); // skip the number
2131 while (*message && *message == ' ')
2135 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2137 SV_ClientPrintf("Kicked by %s\n", who);
2138 SV_DropClient (false); // kicked
2145 ===============================================================================
2149 ===============================================================================
2157 static void Host_Give_f(cmd_state_t *cmd)
2159 prvm_prog_t *prog = SVVM_prog;
2163 if (!sv_cheats.integer)
2165 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2169 t = Cmd_Argv(cmd, 1);
2170 v = atoi (Cmd_Argv(cmd, 2));
2184 // MED 01/04/97 added hipnotic give stuff
2185 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
2190 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2192 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2194 else if (t[0] == '9')
2195 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2196 else if (t[0] == '0')
2197 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2198 else if (t[0] >= '2')
2199 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2204 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2209 if (gamemode == GAME_ROGUE)
2210 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2212 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2215 if (gamemode == GAME_ROGUE)
2217 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2218 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2219 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2223 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2227 if (gamemode == GAME_ROGUE)
2229 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2230 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2231 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2235 if (gamemode == GAME_ROGUE)
2237 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2238 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2239 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2243 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2247 if (gamemode == GAME_ROGUE)
2249 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2250 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2251 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2255 PRVM_serveredictfloat(host_client->edict, health) = v;
2258 if (gamemode == GAME_ROGUE)
2260 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2261 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2262 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2266 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2270 if (gamemode == GAME_ROGUE)
2272 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2273 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2274 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2280 static prvm_edict_t *FindViewthing(prvm_prog_t *prog)
2285 for (i=0 ; i<prog->num_edicts ; i++)
2287 e = PRVM_EDICT_NUM(i);
2288 if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
2291 Con_Print("No viewthing on map\n");
2300 static void Host_Viewmodel_f(cmd_state_t *cmd)
2302 prvm_prog_t *prog = SVVM_prog;
2309 e = FindViewthing(prog);
2312 m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
2313 if (m && m->loaded && m->Draw)
2315 PRVM_serveredictfloat(e, frame) = 0;
2316 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2319 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
2328 static void Host_Viewframe_f(cmd_state_t *cmd)
2330 prvm_prog_t *prog = SVVM_prog;
2338 e = FindViewthing(prog);
2341 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2343 f = atoi(Cmd_Argv(cmd, 1));
2344 if (f >= m->numframes)
2347 PRVM_serveredictfloat(e, frame) = f;
2352 static void PrintFrameName (dp_model_t *m, int frame)
2355 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2357 Con_Printf("frame %i\n", frame);
2365 static void Host_Viewnext_f(cmd_state_t *cmd)
2367 prvm_prog_t *prog = SVVM_prog;
2374 e = FindViewthing(prog);
2377 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2379 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2380 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2381 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2383 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2392 static void Host_Viewprev_f(cmd_state_t *cmd)
2394 prvm_prog_t *prog = SVVM_prog;
2401 e = FindViewthing(prog);
2404 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2406 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2407 if (PRVM_serveredictfloat(e, frame) < 0)
2408 PRVM_serveredictfloat(e, frame) = 0;
2410 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2415 ===============================================================================
2419 ===============================================================================
2428 static void Host_Startdemos_f(cmd_state_t *cmd)
2432 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2435 c = Cmd_Argc(cmd) - 1;
2438 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2441 Con_DPrintf("%i demo(s) in loop\n", c);
2443 for (i=1 ; i<c+1 ; i++)
2444 strlcpy (cls.demos[i-1], Cmd_Argv(cmd, i), sizeof (cls.demos[i-1]));
2446 // LadyHavoc: clear the remaining slots
2447 for (;i <= MAX_DEMOS;i++)
2448 cls.demos[i-1][0] = 0;
2450 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2464 Return to looping demos
2467 static void Host_Demos_f(cmd_state_t *cmd)
2469 if (cls.state == ca_dedicated)
2471 if (cls.demonum == -1)
2473 CL_Disconnect_f (cmd);
2481 Return to looping demos
2484 static void Host_Stopdemo_f(cmd_state_t *cmd)
2486 if (!cls.demoplayback)
2489 Host_ShutdownServer ();
2492 static void Host_SendCvar_f(cmd_state_t *cmd)
2496 const char *cvarname;
2500 if(Cmd_Argc(cmd) != 2)
2502 cvarname = Cmd_Argv(cmd, 1);
2503 if (cls.state == ca_connected)
2505 c = Cvar_FindVar(&cvars_all, cvarname, CVAR_CLIENT | CVAR_SERVER, false);
2506 // LadyHavoc: if there is no such cvar or if it is private, send a
2507 // reply indicating that it has no value
2508 if(!c || (c->flags & CVAR_PRIVATE))
2509 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname));
2511 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", cvarname, c->string));
2514 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2518 if (cls.state != ca_dedicated)
2522 for(;i<svs.maxclients;i++)
2523 if(svs.clients[i].active && svs.clients[i].netconnection)
2525 host_client = &svs.clients[i];
2526 Host_ClientCommands("sendcvar %s\n", cvarname);
2531 static void MaxPlayers_f(cmd_state_t *cmd)
2535 if (Cmd_Argc(cmd) != 2)
2537 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2543 Con_Print("maxplayers can not be changed while a server is running.\n");
2544 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2547 n = atoi(Cmd_Argv(cmd, 1));
2548 n = bound(1, n, MAX_SCOREBOARD);
2549 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2551 svs.maxclients_next = n;
2553 Cvar_Set (&cvars_all, "deathmatch", "0");
2555 Cvar_Set (&cvars_all, "deathmatch", "1");
2559 =====================
2562 ProQuake rcon support
2563 =====================
2565 static void Host_PQRcon_f(cmd_state_t *cmd)
2569 lhnetsocket_t *mysocket;
2571 if (Cmd_Argc(cmd) == 1)
2573 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2577 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2579 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2583 e = strchr(rcon_password.string, ' ');
2584 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2587 cls.rcon_address = cls.netcon->peeraddress;
2590 if (!rcon_address.string[0])
2592 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2595 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2597 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2601 unsigned char bufdata[64];
2604 MSG_WriteLong(&buf, 0);
2605 MSG_WriteByte(&buf, CCREQ_RCON);
2606 SZ_Write(&buf, (const unsigned char*)rcon_password.string, n);
2607 MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string
2608 MSG_WriteString(&buf, Cmd_Args(cmd));
2609 StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK));
2610 NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address);
2615 //=============================================================================
2617 // QuakeWorld commands
2620 =====================
2623 Send the rest of the command line over as
2624 an unconnected command.
2625 =====================
2627 static void Host_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2631 lhnetsocket_t *mysocket;
2633 if (Cmd_Argc(cmd) == 1)
2635 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2639 if (!rcon_password.string || !rcon_password.string[0])
2641 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2645 e = strchr(rcon_password.string, ' ');
2646 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2649 cls.rcon_address = cls.netcon->peeraddress;
2652 if (!rcon_address.string[0])
2654 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2657 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2659 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2660 if (mysocket && Cmd_Args(cmd)[0])
2662 // simply put together the rcon packet and send it
2663 if(Cmd_Argv(cmd, 0)[0] == 's' || rcon_secure.integer > 1)
2665 if(cls.rcon_commands[cls.rcon_ringpos][0])
2668 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2669 Con_Printf("rcon to %s (for command %s) failed: too many buffered commands (possibly increase MAX_RCONS)\n", s, cls.rcon_commands[cls.rcon_ringpos]);
2670 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2673 for (i = 0;i < MAX_RCONS;i++)
2674 if(cls.rcon_commands[i][0])
2675 if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i]))
2679 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later
2680 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2681 cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address;
2682 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2683 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2685 else if(rcon_secure.integer > 0)
2689 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args(cmd));
2690 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2691 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2694 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2695 NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address);
2701 memcpy(buf, "\377\377\377\377", 4);
2702 dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s", n, rcon_password.string, Cmd_Args(cmd));
2703 NetConn_WriteString(mysocket, buf, &cls.rcon_address);
2709 ====================
2712 user <name or userid>
2714 Dump userdata / masterdata for a user
2715 ====================
2717 static void Host_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2722 if (Cmd_Argc(cmd) != 2)
2724 Con_Printf ("Usage: user <username / userid>\n");
2728 uid = atoi(Cmd_Argv(cmd, 1));
2730 for (i = 0;i < cl.maxclients;i++)
2732 if (!cl.scores[i].name[0])
2734 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
2736 InfoString_Print(cl.scores[i].qw_userinfo);
2740 Con_Printf ("User not in server.\n");
2744 ====================
2747 Dump userids for all current players
2748 ====================
2750 static void Host_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2756 Con_Printf ("userid frags name\n");
2757 Con_Printf ("------ ----- ----\n");
2758 for (i = 0;i < cl.maxclients;i++)
2760 if (cl.scores[i].name[0])
2762 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2767 Con_Printf ("%i total users\n", c);
2772 Host_FullServerinfo_f
2774 Sent by server when serverinfo changes
2777 // TODO: shouldn't this be a cvar instead?
2778 static void Host_FullServerinfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2781 if (Cmd_Argc(cmd) != 2)
2783 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2787 strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo));
2788 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2789 cl.qw_teamplay = atoi(temp);
2796 Allow clients to change userinfo
2800 static void Host_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2806 if (Cmd_Argc(cmd) != 2)
2808 Con_Printf ("fullinfo <complete info string>\n");
2812 s = Cmd_Argv(cmd, 1);
2817 size_t len = strcspn(s, "\\");
2818 if (len >= sizeof(key)) {
2819 len = sizeof(key) - 1;
2821 strlcpy(key, s, len + 1);
2825 Con_Printf ("MISSING VALUE\n");
2828 ++s; // Skip over backslash.
2830 len = strcspn(s, "\\");
2831 if (len >= sizeof(value)) {
2832 len = sizeof(value) - 1;
2834 strlcpy(value, s, len + 1);
2836 CL_SetInfo(key, value, false, false, false, false);
2843 ++s; // Skip over backslash.
2851 Allow clients to change userinfo
2854 static void Host_SetInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2856 if (Cmd_Argc(cmd) == 1)
2858 InfoString_Print(cls.userinfo);
2861 if (Cmd_Argc(cmd) != 3)
2863 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2866 CL_SetInfo(Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), true, false, false, false);
2870 ====================
2873 packet <destination> <contents>
2875 Contents allows \n escape character
2876 ====================
2878 static void Host_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2884 lhnetaddress_t address;
2885 lhnetsocket_t *mysocket;
2887 if (Cmd_Argc(cmd) != 3)
2889 Con_Printf ("packet <destination> <contents>\n");
2893 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(cmd, 1), sv_netport.integer))
2895 Con_Printf ("Bad address\n");
2899 in = Cmd_Argv(cmd, 2);
2901 send[0] = send[1] = send[2] = send[3] = -1;
2903 l = (int)strlen (in);
2904 for (i=0 ; i<l ; i++)
2906 if (out >= send + sizeof(send) - 1)
2908 if (in[i] == '\\' && in[i+1] == 'n')
2913 else if (in[i] == '\\' && in[i+1] == '0')
2918 else if (in[i] == '\\' && in[i+1] == 't')
2923 else if (in[i] == '\\' && in[i+1] == 'r')
2928 else if (in[i] == '\\' && in[i+1] == '"')
2937 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2939 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2941 NetConn_Write(mysocket, send, out - send, &address);
2945 ====================
2948 Send back ping and packet loss update for all current players to this player
2949 ====================
2951 static void Host_Pings_f(cmd_state_t *cmd)
2953 int i, j, ping, packetloss, movementloss;
2956 if (!host_client->netconnection)
2959 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2961 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2962 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2964 for (i = 0;i < svs.maxclients;i++)
2968 if (svs.clients[i].netconnection)
2970 for (j = 0;j < NETGRAPH_PACKETS;j++)
2971 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2973 for (j = 0;j < NETGRAPH_PACKETS;j++)
2974 if (svs.clients[i].movement_count[j] < 0)
2977 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2978 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2979 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2980 ping = bound(0, ping, 9999);
2981 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2983 // send qw_svc_updateping and qw_svc_updatepl messages
2984 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2985 MSG_WriteShort(&host_client->netconnection->message, ping);
2986 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2987 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2991 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2993 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2995 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2996 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2999 if (sv.protocol != PROTOCOL_QUAKEWORLD)
3000 MSG_WriteString(&host_client->netconnection->message, "\n");
3003 static void Host_PingPLReport_f(cmd_state_t *cmd)
3007 int l = Cmd_Argc(cmd);
3008 if (l > cl.maxclients)
3010 for (i = 0;i < l;i++)
3012 cl.scores[i].qw_ping = atoi(Cmd_Argv(cmd, 1+i*2));
3013 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(cmd, 1+i*2+1), &errbyte, 0);
3014 if(errbyte && *errbyte == ',')
3015 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
3017 cl.scores[i].qw_movementloss = 0;
3021 //=============================================================================
3028 void Host_InitCommands (void)
3030 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
3032 Cvar_RegisterVariable(&cl_name);
3033 Cvar_RegisterVariable(&cl_color);
3034 Cvar_RegisterVariable(&cl_rate);
3035 Cvar_RegisterVariable(&cl_rate_burstsize);
3036 Cvar_RegisterVariable(&cl_pmodel);
3037 Cvar_RegisterVariable(&cl_playermodel);
3038 Cvar_RegisterVariable(&cl_playerskin);
3039 Cvar_RegisterVariable(&rcon_password);
3040 Cvar_RegisterVariable(&rcon_address);
3041 Cvar_RegisterVariable(&rcon_secure);
3042 Cvar_RegisterVariable(&rcon_secure_challengetimeout);
3043 Cvar_RegisterVariable(&r_fixtrans_auto);
3044 Cvar_RegisterVariable(&team);
3045 Cvar_RegisterVariable(&skin);
3046 Cvar_RegisterVariable(&noaim);
3047 Cvar_RegisterVariable(&sv_cheats);
3048 Cvar_RegisterCallback(&sv_cheats, Host_DisableCheats_c);
3049 Cvar_RegisterVariable(&sv_adminnick);
3050 Cvar_RegisterVariable(&sv_status_privacy);
3051 Cvar_RegisterVariable(&sv_status_show_qcstatus);
3052 Cvar_RegisterVariable(&sv_namechangetimer);
3054 // client commands - this includes server commands because the client can host a server, so they must exist
3055 Cmd_AddCommand(&cmd_client, "quit", Host_Quit_f, "quit the game");
3056 Cmd_AddCommand(&cmd_client, "status", Host_Status_f, "print server status information");
3057 Cmd_AddCommand(&cmd_client, "map", Host_Map_f, "kick everyone off the server and start a new level");
3058 Cmd_AddCommand(&cmd_client, "restart", Host_Restart_f, "restart current level");
3059 Cmd_AddCommand(&cmd_client, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3060 Cmd_AddCommand(&cmd_client, "version", Host_Version_f, "print engine version");
3061 Cmd_AddCommand(&cmd_client, "say", Host_Say_f, "send a chat message to everyone on the server");
3062 Cmd_AddCommand(&cmd_client, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3063 Cmd_AddCommand(&cmd_client, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3064 Cmd_AddCommand(&cmd_client, "kick", Host_Kick_f, "kick a player off the server by number or name");
3065 Cmd_AddCommand(&cmd_client, "ping", Host_Ping_f, "print ping times of all players on the server");
3066 Cmd_AddCommand(&cmd_client, "load", Host_Loadgame_f, "load a saved game file");
3067 Cmd_AddCommand(&cmd_client, "save", Host_Savegame_f, "save the game to a file");
3068 Cmd_AddCommand(&cmd_client, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3069 Cmd_AddCommand(&cmd_client, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3070 Cmd_AddCommand(&cmd_client, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3071 Cmd_AddCommand(&cmd_client, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3072 Cmd_AddCommand(&cmd_client, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3073 Cmd_AddCommand(&cmd_client, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3074 Cmd_AddCommand(&cmd_client, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3076 // dedicated server commands
3077 Cmd_AddCommand(&cmd_server, "quit", Host_Quit_f, "quit the game");
3078 Cmd_AddCommand(&cmd_server, "status", Host_Status_f, "print server status information");
3079 Cmd_AddCommand(&cmd_server, "map", Host_Map_f, "kick everyone off the server and start a new level");
3080 Cmd_AddCommand(&cmd_server, "restart", Host_Restart_f, "restart current level");
3081 Cmd_AddCommand(&cmd_server, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3082 Cmd_AddCommand(&cmd_server, "version", Host_Version_f, "print engine version");
3083 Cmd_AddCommand(&cmd_server, "say", Host_Say_f, "send a chat message to everyone on the server");
3084 Cmd_AddCommand(&cmd_server, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3085 Cmd_AddCommand(&cmd_server, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3086 Cmd_AddCommand(&cmd_server, "kick", Host_Kick_f, "kick a player off the server by number or name");
3087 Cmd_AddCommand(&cmd_server, "ping", Host_Ping_f, "print ping times of all players on the server");
3088 Cmd_AddCommand(&cmd_server, "load", Host_Loadgame_f, "load a saved game file");
3089 Cmd_AddCommand(&cmd_server, "save", Host_Savegame_f, "save the game to a file");
3090 Cmd_AddCommand(&cmd_server, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3091 Cmd_AddCommand(&cmd_server, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3092 Cmd_AddCommand(&cmd_server, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3093 Cmd_AddCommand(&cmd_server, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3094 Cmd_AddCommand(&cmd_server, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3095 Cmd_AddCommand(&cmd_server, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3096 Cmd_AddCommand(&cmd_server, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3098 // commands that do not have automatic forwarding from cmd_client, these are internal details of the network protocol and not of interest to users (if they know what they are doing they can still use a generic "cmd prespawn" or similar)
3099 Cmd_AddCommand(&cmd_serverfromclient, "prespawn", Host_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
3100 Cmd_AddCommand(&cmd_serverfromclient, "spawn", Host_Spawn_f, "internal use - signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
3101 Cmd_AddCommand(&cmd_serverfromclient, "begin", Host_Begin_f, "internal use - signon 3 (client asks server to start sending entities, and will go to signon 4 (playing) when the first entity update is received)");
3102 Cmd_AddCommand(&cmd_serverfromclient, "pings", Host_Pings_f, "internal use - command sent by clients to request updated ping and packetloss of players on scoreboard (originally from QW, but also used on NQ servers)");
3104 Cmd_AddCommand(&cmd_serverfromclient, "status", Host_Status_f, "print server status information");
3105 Cmd_AddCommand(&cmd_serverfromclient, "god", Host_God_f, "god mode (invulnerability)");
3106 Cmd_AddCommand(&cmd_serverfromclient, "notarget", Host_Notarget_f, "notarget mode (monsters do not see you)");
3107 Cmd_AddCommand(&cmd_serverfromclient, "fly", Host_Fly_f, "fly mode (flight)");
3108 Cmd_AddCommand(&cmd_serverfromclient, "noclip", Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
3109 Cmd_AddCommand(&cmd_serverfromclient, "give", Host_Give_f, "alter inventory");
3110 Cmd_AddCommand(&cmd_serverfromclient, "say", Host_Say_f, "send a chat message to everyone on the server");
3111 Cmd_AddCommand(&cmd_serverfromclient, "say_team", Host_Say_Team_f, "send a chat message to your team on the server");
3112 Cmd_AddCommand(&cmd_serverfromclient, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3113 Cmd_AddCommand(&cmd_serverfromclient, "kill", Host_Kill_f, "die instantly");
3114 Cmd_AddCommand(&cmd_serverfromclient, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3115 Cmd_AddCommand(&cmd_serverfromclient, "ping", Host_Ping_f, "print ping times of all players on the server");
3116 Cmd_AddCommand(&cmd_serverfromclient, "name", Host_Name_f, "change your player name");
3117 Cmd_AddCommand(&cmd_serverfromclient, "color", Host_Color_f, "change your player shirt and pants colors");
3118 Cmd_AddCommand(&cmd_serverfromclient, "rate", Host_Rate_f, "change your network connection speed");
3119 Cmd_AddCommand(&cmd_serverfromclient, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3120 Cmd_AddCommand(&cmd_serverfromclient, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3121 Cmd_AddCommand(&cmd_serverfromclient, "playermodel", Host_Playermodel_f, "change your player model");
3122 Cmd_AddCommand(&cmd_serverfromclient, "playerskin", Host_Playerskin_f, "change your player skin number");
3124 // client commands that require a connection and are simply forwarded to server
3125 Cmd_AddCommand(&cmd_client, "god", Cmd_ForwardToServer_f, "god mode (invulnerability)");
3126 Cmd_AddCommand(&cmd_client, "notarget", Cmd_ForwardToServer_f, "notarget mode (monsters do not see you)");
3127 Cmd_AddCommand(&cmd_client, "fly", Cmd_ForwardToServer_f, "fly mode (flight)");
3128 Cmd_AddCommand(&cmd_client, "noclip", Cmd_ForwardToServer_f, "noclip mode (flight without collisions, move through walls)");
3129 Cmd_AddCommand(&cmd_client, "give", Cmd_ForwardToServer_f, "alter inventory");
3130 Cmd_AddCommand(&cmd_client, "say_team", Cmd_ForwardToServer_f, "send a chat message to your team on the server");
3131 Cmd_AddCommand(&cmd_client, "kill", Cmd_ForwardToServer_f, "die instantly");
3133 Cmd_AddCommand(&cmd_client, "connect", Host_Connect_f, "connect to a server by IP address or hostname");
3134 Cmd_AddCommand(&cmd_client, "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)");
3135 Cmd_AddCommand(&cmd_clientfromserver, "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)");
3136 Cmd_AddCommand(&cmd_client, "startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
3137 Cmd_AddCommand(&cmd_client, "demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
3138 Cmd_AddCommand(&cmd_client, "stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
3139 Cmd_AddCommand(&cmd_client, "sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
3140 Cmd_AddCommand(&cmd_client, "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");
3141 Cmd_AddCommand(&cmd_client, "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");
3142 Cmd_AddCommand(&cmd_client, "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)");
3143 Cmd_AddCommand(&cmd_client, "fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
3144 Cmd_AddCommand(&cmd_client, "setinfo", Host_SetInfo_f, "modifies your userinfo");
3145 Cmd_AddCommand(&cmd_client, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3146 Cmd_AddCommand(&cmd_clientfromserver, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3147 Cmd_AddCommand(&cmd_client, "topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
3148 Cmd_AddCommand(&cmd_client, "bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
3149 Cmd_AddCommand(&cmd_client, "fixtrans", Image_FixTransparentPixels_f, "change alpha-zero pixels in an image file to sensible values, and write out a new TGA (warning: SLOW)");
3151 // client commands that also exist as cmd_serverfromclient and are often forwarded
3152 Cmd_AddCommand(&cmd_client, "name", Host_Name_f, "change your player name");
3153 Cmd_AddCommand(&cmd_client, "color", Host_Color_f, "change your player shirt and pants colors");
3154 Cmd_AddCommand(&cmd_client, "rate", Host_Rate_f, "change your network connection speed");
3155 Cmd_AddCommand(&cmd_client, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3156 Cmd_AddCommand(&cmd_client, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3157 Cmd_AddCommand(&cmd_client, "playermodel", Host_Playermodel_f, "change your player model");
3158 Cmd_AddCommand(&cmd_client, "playerskin", Host_Playerskin_f, "change your player skin number");
3160 // commands that are only sent by server to client for execution
3161 Cmd_AddCommand(&cmd_clientfromserver, "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)");
3162 Cmd_AddCommand(&cmd_clientfromserver, "fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
3165 void Host_NoOperation_f(cmd_state_t *cmd)