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, "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)"};
47 qboolean allowcheats = false;
49 extern qboolean host_shuttingdown;
50 extern cvar_t developer_entityparsing;
58 void Host_Quit_f(cmd_state_t *cmd)
61 Con_Printf("shutting down already!\n");
71 static void Host_Status_f(cmd_state_t *cmd)
73 prvm_prog_t *prog = SVVM_prog;
76 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
77 void (*print) (const char *fmt, ...);
78 char ip[48]; // can contain a full length v6 address with [] and a port
82 if (cmd->source == src_command)
84 // if running a client, try to send over network so the client's status report parser will see the report
85 if (cls.state == ca_connected)
87 Cmd_ForwardToServer_f(cmd);
93 print = SV_ClientPrintf;
99 if (Cmd_Argc(cmd) == 2)
101 if (strcmp(Cmd_Argv(cmd, 1), "1") == 0)
103 else if (strcmp(Cmd_Argv(cmd, 1), "2") == 0)
107 for (players = 0, i = 0;i < svs.maxclients;i++)
108 if (svs.clients[i].active)
110 print ("host: %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
111 print ("version: %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
112 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
113 print ("map: %s\n", sv.name);
114 print ("timing: %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
115 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
118 print ("^2IP %%pl ping time frags no name\n");
120 print ("^5IP no name\n");
122 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
129 if (in == 0 || in == 1)
131 seconds = (int)(realtime - client->connecttime);
132 minutes = seconds / 60;
135 seconds -= (minutes * 60);
136 hours = minutes / 60;
138 minutes -= (hours * 60);
144 if (client->netconnection)
145 for (j = 0;j < NETGRAPH_PACKETS;j++)
146 if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
148 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
149 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
152 if(sv_status_privacy.integer && cmd->source != src_command)
153 strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
155 strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 48);
157 frags = client->frags;
159 if(sv_status_show_qcstatus.integer)
161 prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
162 const char *str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
168 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
169 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
173 frags = atoi(qcstatus);
177 if (in == 0) // default layout
179 if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
181 // LadyHavoc: this is very touchy because we must maintain ProQuake compatible status output
182 print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
187 // LadyHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
188 print ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
192 else if (in == 1) // extended layout
194 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);
196 else if (in == 2) // reduced layout
198 print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
208 Sets client to godmode
211 static void Host_God_f(cmd_state_t *cmd)
213 prvm_prog_t *prog = SVVM_prog;
216 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
220 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
221 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
222 SV_ClientPrint("godmode OFF\n");
224 SV_ClientPrint("godmode ON\n");
227 static void Host_Notarget_f(cmd_state_t *cmd)
229 prvm_prog_t *prog = SVVM_prog;
232 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
236 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
237 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
238 SV_ClientPrint("notarget OFF\n");
240 SV_ClientPrint("notarget ON\n");
243 qboolean noclip_anglehack;
245 static void Host_Noclip_f(cmd_state_t *cmd)
247 prvm_prog_t *prog = SVVM_prog;
250 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
254 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
256 noclip_anglehack = true;
257 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
258 SV_ClientPrint("noclip ON\n");
262 noclip_anglehack = false;
263 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
264 SV_ClientPrint("noclip OFF\n");
272 Sets client to flymode
275 static void Host_Fly_f(cmd_state_t *cmd)
277 prvm_prog_t *prog = SVVM_prog;
280 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
284 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
286 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
287 SV_ClientPrint("flymode ON\n");
291 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
292 SV_ClientPrint("flymode OFF\n");
303 static void Host_Ping_f(cmd_state_t *cmd)
307 void (*print) (const char *fmt, ...);
309 if (cmd->source == src_command)
311 // if running a client, try to send over network so the client's ping report parser will see the report
312 if (cls.state == ca_connected)
314 Cmd_ForwardToServer_f(cmd);
320 print = SV_ClientPrintf;
325 print("Client ping times:\n");
326 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
330 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
335 ===============================================================================
339 ===============================================================================
343 ======================
348 command from the console. Active clients are kicked off.
349 ======================
351 static void Host_Map_f(cmd_state_t *cmd)
353 char level[MAX_QPATH];
355 if (Cmd_Argc(cmd) != 2)
357 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
361 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
362 if (gamemode == GAME_DELUXEQUAKE)
363 Cvar_Set(&cvars_all, "warpmark", "");
365 cls.demonum = -1; // stop demo loop in case this fails
368 Host_ShutdownServer();
370 if(svs.maxclients != svs.maxclients_next)
372 svs.maxclients = svs.maxclients_next;
374 Mem_Free(svs.clients);
375 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
380 if (key_dest == key_menu || key_dest == key_menu_grabbed)
385 svs.serverflags = 0; // haven't completed an episode yet
386 allowcheats = sv_cheats.integer != 0;
387 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
388 SV_SpawnServer(level);
389 if (sv.active && cls.state == ca_disconnected)
390 CL_EstablishConnection("local:1", -2);
397 Goes to a new map, taking all clients along
400 static void Host_Changelevel_f(cmd_state_t *cmd)
402 char level[MAX_QPATH];
404 if (Cmd_Argc(cmd) != 2)
406 Con_Print("changelevel <levelname> : continue game on a new level\n");
417 if (key_dest == key_menu || key_dest == key_menu_grabbed)
422 SV_SaveSpawnparms ();
423 allowcheats = sv_cheats.integer != 0;
424 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
425 SV_SpawnServer(level);
426 if (sv.active && cls.state == ca_disconnected)
427 CL_EstablishConnection("local:1", -2);
434 Restarts the current server for a dead player
437 static void Host_Restart_f(cmd_state_t *cmd)
439 char mapname[MAX_QPATH];
441 if (Cmd_Argc(cmd) != 1)
443 Con_Print("restart : restart current level\n");
448 Con_Print("Only the server may restart\n");
454 if (key_dest == key_menu || key_dest == key_menu_grabbed)
459 allowcheats = sv_cheats.integer != 0;
460 strlcpy(mapname, sv.name, sizeof(mapname));
461 SV_SpawnServer(mapname);
462 if (sv.active && cls.state == ca_disconnected)
463 CL_EstablishConnection("local:1", -2);
470 This command causes the client to wait for the signon messages again.
471 This is sent just before a server changes levels
474 void Host_Reconnect_f(cmd_state_t *cmd)
477 // if not connected, reconnect to the most recent server
480 // if we have connected to a server recently, the userinfo
481 // will still contain its IP address, so get the address...
482 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
484 CL_EstablishConnection(temp, -1);
486 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
489 // if connected, do something based on protocol
490 if (cls.protocol == PROTOCOL_QUAKEWORLD)
492 // quakeworld can just re-login
493 if (cls.qw_downloadmemory) // don't change when downloading
498 if (cls.state == ca_connected && cls.signon < SIGNONS)
500 Con_Printf("reconnecting...\n");
501 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
502 MSG_WriteString(&cls.netcon->message, "new");
507 // netquake uses reconnect on level changes (silly)
508 if (Cmd_Argc(cmd) != 1)
510 Con_Print("reconnect : wait for signon messages again\n");
515 Con_Print("reconnect: no signon, ignoring reconnect\n");
518 cls.signon = 0; // need new connection messages
523 =====================
526 User command to connect to server
527 =====================
529 static void Host_Connect_f(cmd_state_t *cmd)
531 if (Cmd_Argc(cmd) < 2)
533 Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
536 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
537 if(rcon_secure.integer <= 0)
538 Cvar_SetQuick(&rcon_password, "");
539 CL_EstablishConnection(Cmd_Argv(cmd, 1), 2);
544 ===============================================================================
548 ===============================================================================
551 #define SAVEGAME_VERSION 5
553 void Host_Savegame_to(prvm_prog_t *prog, const char *name)
556 int i, k, l, numbuffers, lightstyles = 64;
557 char comment[SAVEGAME_COMMENT_LENGTH+1];
558 char line[MAX_INPUTLINE];
562 // first we have to figure out if this can be saved in 64 lightstyles
563 // (for Quake compatibility)
564 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
565 if (sv.lightstyles[i][0])
568 isserver = prog == SVVM_prog;
570 Con_Printf("Saving game to %s...\n", name);
571 f = FS_OpenRealFile(name, "wb", false);
574 Con_Print("ERROR: couldn't open.\n");
578 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
580 memset(comment, 0, sizeof(comment));
582 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));
584 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", prog->name);
585 // convert space to _ to make stdio happy
586 // LadyHavoc: convert control characters to _ as well
587 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
588 if (ISWHITESPACEORCONTROL(comment[i]))
590 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
592 FS_Printf(f, "%s\n", comment);
595 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
596 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
597 FS_Printf(f, "%d\n", current_skill);
598 FS_Printf(f, "%s\n", sv.name);
599 FS_Printf(f, "%f\n",sv.time);
603 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
604 FS_Printf(f, "(dummy)\n");
605 FS_Printf(f, "%d\n", 0);
606 FS_Printf(f, "%s\n", "(dummy)");
607 FS_Printf(f, "%f\n", realtime);
610 // write the light styles
611 for (i=0 ; i<lightstyles ; i++)
613 if (isserver && sv.lightstyles[i][0])
614 FS_Printf(f, "%s\n", sv.lightstyles[i]);
619 PRVM_ED_WriteGlobals (prog, f);
620 for (i=0 ; i<prog->num_edicts ; i++)
622 FS_Printf(f,"// edict %d\n", i);
623 //Con_Printf("edict %d...\n", i);
624 PRVM_ED_Write (prog, f, PRVM_EDICT_NUM(i));
629 FS_Printf(f,"// DarkPlaces extended savegame\n");
630 // darkplaces extension - extra lightstyles, support for color lightstyles
631 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
632 if (isserver && sv.lightstyles[i][0])
633 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
635 // darkplaces extension - model precaches
636 for (i=1 ; i<MAX_MODELS ; i++)
637 if (sv.model_precache[i][0])
638 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
640 // darkplaces extension - sound precaches
641 for (i=1 ; i<MAX_SOUNDS ; i++)
642 if (sv.sound_precache[i][0])
643 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
645 // darkplaces extension - save buffers
646 numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
647 for (i = 0; i < numbuffers; i++)
649 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
650 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
652 FS_Printf(f,"sv.buffer %i %i \"string\"\n", i, stringbuffer->flags & STRINGBUFFER_QCFLAGS);
653 for(k = 0; k < stringbuffer->num_strings; k++)
655 if (!stringbuffer->strings[k])
657 // Parse the string a bit to turn special characters
658 // (like newline, specifically) into escape codes
659 s = stringbuffer->strings[k];
660 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
687 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
695 Con_Print("done.\n");
703 static void Host_Savegame_f(cmd_state_t *cmd)
705 prvm_prog_t *prog = SVVM_prog;
706 char name[MAX_QPATH];
707 qboolean deadflag = false;
711 Con_Print("Can't save - no server running.\n");
715 deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
719 // singleplayer checks
722 Con_Print("Can't save in intermission.\n");
728 Con_Print("Can't savegame with a dead player\n");
733 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");
735 if (Cmd_Argc(cmd) != 2)
737 Con_Print("save <savename> : save a game\n");
741 if (strstr(Cmd_Argv(cmd, 1), ".."))
743 Con_Print("Relative pathnames are not allowed.\n");
747 strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
748 FS_DefaultExtension (name, ".sav", sizeof (name));
750 Host_Savegame_to(prog, name);
760 static void Host_Loadgame_f(cmd_state_t *cmd)
762 prvm_prog_t *prog = SVVM_prog;
763 char filename[MAX_QPATH];
764 char mapname[MAX_QPATH];
771 int i, k, numbuffers;
774 float spawn_parms[NUM_SPAWN_PARMS];
775 prvm_stringbuffer_t *stringbuffer;
777 if (Cmd_Argc(cmd) != 2)
779 Con_Print("load <savename> : load a game\n");
783 strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename));
784 FS_DefaultExtension (filename, ".sav", sizeof (filename));
786 Con_Printf("Loading game from %s...\n", filename);
788 // stop playing demos
789 if (cls.demoplayback)
794 if (key_dest == key_menu || key_dest == key_menu_grabbed)
799 cls.demonum = -1; // stop demo loop in case this fails
801 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
804 Con_Print("ERROR: couldn't open.\n");
808 if(developer_entityparsing.integer)
809 Con_Printf("Host_Loadgame_f: loading version\n");
812 COM_ParseToken_Simple(&t, false, false, true);
813 version = atoi(com_token);
814 if (version != SAVEGAME_VERSION)
817 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
821 if(developer_entityparsing.integer)
822 Con_Printf("Host_Loadgame_f: loading description\n");
825 COM_ParseToken_Simple(&t, false, false, true);
827 for (i = 0;i < NUM_SPAWN_PARMS;i++)
829 COM_ParseToken_Simple(&t, false, false, true);
830 spawn_parms[i] = atof(com_token);
833 COM_ParseToken_Simple(&t, false, false, true);
834 // this silliness is so we can load 1.06 save files, which have float skill values
835 current_skill = (int)(atof(com_token) + 0.5);
836 Cvar_SetValue (&cvars_all, "skill", (float)current_skill);
838 if(developer_entityparsing.integer)
839 Con_Printf("Host_Loadgame_f: loading mapname\n");
842 COM_ParseToken_Simple(&t, false, false, true);
843 strlcpy (mapname, com_token, sizeof(mapname));
845 if(developer_entityparsing.integer)
846 Con_Printf("Host_Loadgame_f: loading time\n");
849 COM_ParseToken_Simple(&t, false, false, true);
850 time = atof(com_token);
852 allowcheats = sv_cheats.integer != 0;
854 if(developer_entityparsing.integer)
855 Con_Printf("Host_Loadgame_f: spawning server\n");
857 SV_SpawnServer (mapname);
861 Con_Print("Couldn't load map\n");
864 sv.paused = true; // pause until all clients connect
867 if(developer_entityparsing.integer)
868 Con_Printf("Host_Loadgame_f: loading light styles\n");
870 // load the light styles
875 for (i = 0;i < MAX_LIGHTSTYLES;i++)
879 COM_ParseToken_Simple(&t, false, false, true);
880 // if this is a 64 lightstyle savegame produced by Quake, stop now
881 // we have to check this because darkplaces may save more than 64
882 if (com_token[0] == '{')
887 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
890 if(developer_entityparsing.integer)
891 Con_Printf("Host_Loadgame_f: skipping until globals\n");
893 // now skip everything before the first opening brace
894 // (this is for forward compatibility, so that older versions (at
895 // least ones with this fix) can load savegames with extra data before the
896 // first brace, as might be produced by a later engine version)
900 if (!COM_ParseToken_Simple(&t, false, false, true))
902 if (com_token[0] == '{')
909 // unlink all entities
910 World_UnlinkAll(&sv.world);
912 // load the edicts out of the savegame file
917 while (COM_ParseToken_Simple(&t, false, false, true))
918 if (!strcmp(com_token, "}"))
920 if (!COM_ParseToken_Simple(&start, false, false, true))
925 if (strcmp(com_token,"{"))
928 Host_Error ("First token isn't a brace");
933 if(developer_entityparsing.integer)
934 Con_Printf("Host_Loadgame_f: loading globals\n");
936 // parse the global vars
937 PRVM_ED_ParseGlobals (prog, start);
939 // restore the autocvar globals
940 Cvar_UpdateAllAutoCvars(prog->console_cmd->cvars);
945 if (entnum >= MAX_EDICTS)
948 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
950 while (entnum >= prog->max_edicts)
951 PRVM_MEM_IncreaseEdicts(prog);
952 ent = PRVM_EDICT_NUM(entnum);
953 memset(ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
954 ent->priv.server->free = false;
956 if(developer_entityparsing.integer)
957 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
959 PRVM_ED_ParseEdict (prog, start, ent);
961 // link it into the bsp tree
962 if (!ent->priv.server->free)
970 prog->num_edicts = entnum;
973 for (i = 0;i < NUM_SPAWN_PARMS;i++)
974 svs.clients[0].spawn_parms[i] = spawn_parms[i];
976 if(developer_entityparsing.integer)
977 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
979 // read extended data if present
980 // the extended data is stored inside a /* */ comment block, which the
981 // parser intentionally skips, so we have to check for it manually here
984 while (*end == '\r' || *end == '\n')
986 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
988 if(developer_entityparsing.integer)
989 Con_Printf("Host_Loadgame_f: loading extended data\n");
991 Con_Printf("Loading extended DarkPlaces savegame\n");
993 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
994 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
995 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
998 while (COM_ParseToken_Simple(&t, false, false, true))
1000 if (!strcmp(com_token, "sv.lightstyles"))
1002 COM_ParseToken_Simple(&t, false, false, true);
1003 i = atoi(com_token);
1004 COM_ParseToken_Simple(&t, false, false, true);
1005 if (i >= 0 && i < MAX_LIGHTSTYLES)
1006 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
1008 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
1010 else if (!strcmp(com_token, "sv.model_precache"))
1012 COM_ParseToken_Simple(&t, false, false, true);
1013 i = atoi(com_token);
1014 COM_ParseToken_Simple(&t, false, false, true);
1015 if (i >= 0 && i < MAX_MODELS)
1017 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
1018 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
1021 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
1023 else if (!strcmp(com_token, "sv.sound_precache"))
1025 COM_ParseToken_Simple(&t, false, false, true);
1026 i = atoi(com_token);
1027 COM_ParseToken_Simple(&t, false, false, true);
1028 if (i >= 0 && i < MAX_SOUNDS)
1029 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1031 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1033 else if (!strcmp(com_token, "sv.buffer"))
1035 if (COM_ParseToken_Simple(&t, false, false, true))
1037 i = atoi(com_token);
1040 k = STRINGBUFFER_SAVED;
1041 if (COM_ParseToken_Simple(&t, false, false, true))
1042 k |= atoi(com_token);
1043 if (!BufStr_FindCreateReplace(prog, i, k, "string"))
1044 Con_Printf("failed to create stringbuffer %i\n", i);
1047 Con_Printf("unsupported stringbuffer index %i \"%s\"\n", i, com_token);
1050 Con_Printf("unexpected end of line when parsing sv.buffer (expected buffer index)\n");
1052 else if (!strcmp(com_token, "sv.bufstr"))
1054 if (!COM_ParseToken_Simple(&t, false, false, true))
1055 Con_Printf("unexpected end of line when parsing sv.bufstr\n");
1058 i = atoi(com_token);
1059 stringbuffer = BufStr_FindCreateReplace(prog, i, STRINGBUFFER_SAVED, "string");
1062 if (COM_ParseToken_Simple(&t, false, false, true))
1064 k = atoi(com_token);
1065 if (COM_ParseToken_Simple(&t, false, false, true))
1066 BufStr_Set(prog, stringbuffer, k, com_token);
1068 Con_Printf("unexpected end of line when parsing sv.bufstr (expected string)\n");
1071 Con_Printf("unexpected end of line when parsing sv.bufstr (expected strindex)\n");
1074 Con_Printf("failed to create stringbuffer %i \"%s\"\n", i, com_token);
1077 // skip any trailing text or unrecognized commands
1078 while (COM_ParseToken_Simple(&t, true, false, true) && strcmp(com_token, "\n"))
1085 // remove all temporary flagged string buffers (ones created with BufStr_FindCreateReplace)
1086 numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
1087 for (i = 0; i < numbuffers; i++)
1089 if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
1090 if (stringbuffer->flags & STRINGBUFFER_TEMP)
1091 BufStr_Del(prog, stringbuffer);
1094 if(developer_entityparsing.integer)
1095 Con_Printf("Host_Loadgame_f: finished\n");
1097 // make sure we're connected to loopback
1098 if (sv.active && cls.state == ca_disconnected)
1099 CL_EstablishConnection("local:1", -2);
1102 //============================================================================
1105 ======================
1107 ======================
1109 cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1110 static void Host_Name_f(cmd_state_t *cmd)
1112 prvm_prog_t *prog = SVVM_prog;
1114 qboolean valid_colors;
1115 const char *newNameSource;
1116 char newName[sizeof(host_client->name)];
1118 if (Cmd_Argc (cmd) == 1)
1120 if (cmd->source == src_command)
1122 Con_Printf("name: %s\n", cl_name.string);
1127 if (Cmd_Argc (cmd) == 2)
1128 newNameSource = Cmd_Argv(cmd, 1);
1130 newNameSource = Cmd_Args(cmd);
1132 strlcpy(newName, newNameSource, sizeof(newName));
1134 if (cmd->source == src_command)
1136 Cvar_Set (&cvars_all, "_cl_name", newName);
1137 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1139 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1140 Con_Printf("name: %s\n", cl_name.string);
1145 if (realtime < host_client->nametime)
1147 SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
1151 host_client->nametime = realtime + max(0.0f, sv_namechangetimer.value);
1153 // point the string back at updateclient->name to keep it safe
1154 strlcpy (host_client->name, newName, sizeof (host_client->name));
1156 for (i = 0, j = 0;host_client->name[i];i++)
1157 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1158 host_client->name[j++] = host_client->name[i];
1159 host_client->name[j] = 0;
1161 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1162 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1164 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1165 host_client->name[sizeof(host_client->name) - 1] = 0;
1166 host_client->name[0] = STRING_COLOR_TAG;
1167 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1170 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1171 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1174 l = strlen(host_client->name);
1175 if(l < sizeof(host_client->name) - 1)
1177 // duplicate the color tag to escape it
1178 host_client->name[i] = STRING_COLOR_TAG;
1179 host_client->name[i+1] = 0;
1180 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1184 // remove the last character to fix the color code
1185 host_client->name[l-1] = 0;
1186 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1190 // find the last color tag offset and decide if we need to add a reset tag
1191 for (i = 0, j = -1;host_client->name[i];i++)
1193 if (host_client->name[i] == STRING_COLOR_TAG)
1195 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1198 // if this happens to be a reset tag then we don't need one
1199 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1204 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]))
1210 if (host_client->name[i+1] == STRING_COLOR_TAG)
1217 // does not end in the default color string, so add it
1218 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1219 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1221 PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
1222 if (strcmp(host_client->old_name, host_client->name))
1224 if (host_client->begun)
1225 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1226 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1227 // send notification to all clients
1228 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1229 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1230 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1231 SV_WriteNetnameIntoDemo(host_client);
1236 ======================
1238 ======================
1240 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)"};
1241 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1242 static void Host_Playermodel_f(cmd_state_t *cmd)
1244 prvm_prog_t *prog = SVVM_prog;
1246 char newPath[sizeof(host_client->playermodel)];
1248 if (Cmd_Argc (cmd) == 1)
1250 if (cmd->source == src_command)
1252 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1257 if (Cmd_Argc (cmd) == 2)
1258 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1260 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1262 for (i = 0, j = 0;newPath[i];i++)
1263 if (newPath[i] != '\r' && newPath[i] != '\n')
1264 newPath[j++] = newPath[i];
1267 if (cmd->source == src_command)
1269 Cvar_Set (&cvars_all, "_cl_playermodel", newPath);
1274 if (realtime < host_client->nametime)
1276 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1280 host_client->nametime = realtime + 5;
1283 // point the string back at updateclient->name to keep it safe
1284 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1285 PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
1286 if (strcmp(host_client->old_model, host_client->playermodel))
1288 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1289 /*// send notification to all clients
1290 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1291 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1292 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1297 ======================
1299 ======================
1301 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)"};
1302 static void Host_Playerskin_f(cmd_state_t *cmd)
1304 prvm_prog_t *prog = SVVM_prog;
1306 char newPath[sizeof(host_client->playerskin)];
1308 if (Cmd_Argc (cmd) == 1)
1310 if (cmd->source == src_command)
1312 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1317 if (Cmd_Argc (cmd) == 2)
1318 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1320 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1322 for (i = 0, j = 0;newPath[i];i++)
1323 if (newPath[i] != '\r' && newPath[i] != '\n')
1324 newPath[j++] = newPath[i];
1327 if (cmd->source == src_command)
1329 Cvar_Set (&cvars_all, "_cl_playerskin", newPath);
1334 if (realtime < host_client->nametime)
1336 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1340 host_client->nametime = realtime + 5;
1343 // point the string back at updateclient->name to keep it safe
1344 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1345 PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
1346 if (strcmp(host_client->old_skin, host_client->playerskin))
1348 //if (host_client->begun)
1349 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1350 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1351 /*// send notification to all clients
1352 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1353 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1354 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1358 static void Host_Version_f(cmd_state_t *cmd)
1360 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1363 static void Host_Say(cmd_state_t *cmd, qboolean teamonly)
1365 prvm_prog_t *prog = SVVM_prog;
1370 // LadyHavoc: long say messages
1372 qboolean fromServer = false;
1374 if (cmd->source == src_command)
1376 if (cls.state == ca_dedicated)
1383 Cmd_ForwardToServer_f(cmd);
1388 if (Cmd_Argc (cmd) < 2)
1391 if (!teamplay.integer)
1401 // note this uses the chat prefix \001
1402 if (!fromServer && !teamonly)
1403 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1404 else if (!fromServer && teamonly)
1405 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1406 else if(*(sv_adminnick.string))
1407 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1409 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1410 p2 = text + strlen(text);
1411 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1413 if (p2[-1] == '\"' && quoted)
1418 strlcat(text, "\n", sizeof(text));
1420 // note: save is not a valid edict if fromServer is true
1422 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1423 if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
1424 SV_ClientPrint(text);
1427 if (cls.state == ca_dedicated)
1428 Con_Print(&text[1]);
1432 static void Host_Say_f(cmd_state_t *cmd)
1434 Host_Say(cmd, false);
1438 static void Host_Say_Team_f(cmd_state_t *cmd)
1440 Host_Say(cmd, true);
1444 static void Host_Tell_f(cmd_state_t *cmd)
1446 const char *playername_start = NULL;
1447 size_t playername_length = 0;
1448 int playernumber = 0;
1451 const char *p1, *p2;
1452 char text[MAX_INPUTLINE]; // LadyHavoc: FIXME: temporary buffer overflow fix (was 64)
1453 qboolean fromServer = false;
1455 if (cmd->source == src_command)
1457 if (cls.state == ca_dedicated)
1461 Cmd_ForwardToServer_f(cmd);
1466 if (Cmd_Argc (cmd) < 2)
1469 // note this uses the chat prefix \001
1471 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1472 else if(*(sv_adminnick.string))
1473 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1475 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1478 p2 = p1 + strlen(p1);
1479 // remove the target name
1480 while (p1 < p2 && *p1 == ' ')
1485 while (p1 < p2 && *p1 == ' ')
1487 while (p1 < p2 && isdigit(*p1))
1489 playernumber = playernumber * 10 + (*p1 - '0');
1497 playername_start = p1;
1498 while (p1 < p2 && *p1 != '"')
1500 playername_length = p1 - playername_start;
1506 playername_start = p1;
1507 while (p1 < p2 && *p1 != ' ')
1509 playername_length = p1 - playername_start;
1511 while (p1 < p2 && *p1 == ' ')
1513 if(playername_start)
1515 // set playernumber to the right client
1517 if(playername_length >= sizeof(namebuf))
1520 Con_Print("Host_Tell: too long player name/ID\n");
1522 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1525 memcpy(namebuf, playername_start, playername_length);
1526 namebuf[playername_length] = 0;
1527 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1529 if (!svs.clients[playernumber].active)
1531 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1535 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1538 Con_Print("Host_Tell: invalid player name/ID\n");
1540 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1543 // remove trailing newlines
1544 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1546 // remove quotes if present
1552 else if (fromServer)
1553 Con_Print("Host_Tell: missing end quote\n");
1555 SV_ClientPrint("Host_Tell: missing end quote\n");
1557 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1560 return; // empty say
1561 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1567 host_client = svs.clients + playernumber;
1568 SV_ClientPrint(text);
1578 cvar_t cl_color = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1579 static void Host_Color(cmd_state_t *cmd, int changetop, int changebottom)
1581 prvm_prog_t *prog = SVVM_prog;
1582 int top, bottom, playercolor;
1584 // get top and bottom either from the provided values or the current values
1585 // (allows changing only top or bottom, or both at once)
1586 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1587 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1591 // LadyHavoc: allowing skin colormaps 14 and 15 by commenting this out
1597 playercolor = top*16 + bottom;
1599 if (cmd->source == src_command)
1601 Cvar_SetValueQuick(&cl_color, playercolor);
1605 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1608 if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
1610 Con_DPrint("Calling SV_ChangeTeam\n");
1611 prog->globals.fp[OFS_PARM0] = playercolor;
1612 PRVM_serverglobalfloat(time) = sv.time;
1613 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1614 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1618 if (host_client->edict)
1620 PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1621 PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1623 host_client->colors = playercolor;
1624 if (host_client->old_colors != host_client->colors)
1626 host_client->old_colors = host_client->colors;
1627 // send notification to all clients
1628 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1629 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1630 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1635 static void Host_Color_f(cmd_state_t *cmd)
1639 if (Cmd_Argc(cmd) == 1)
1641 if (cmd->source == src_command)
1643 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1644 Con_Print("color <0-15> [0-15]\n");
1649 if (Cmd_Argc(cmd) == 2)
1650 top = bottom = atoi(Cmd_Argv(cmd, 1));
1653 top = atoi(Cmd_Argv(cmd, 1));
1654 bottom = atoi(Cmd_Argv(cmd, 2));
1656 Host_Color(cmd, top, bottom);
1659 static void Host_TopColor_f(cmd_state_t *cmd)
1661 if (Cmd_Argc(cmd) == 1)
1663 if (cmd->source == src_command)
1665 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1666 Con_Print("topcolor <0-15>\n");
1671 Host_Color(cmd, atoi(Cmd_Argv(cmd, 1)), -1);
1674 static void Host_BottomColor_f(cmd_state_t *cmd)
1676 if (Cmd_Argc(cmd) == 1)
1678 if (cmd->source == src_command)
1680 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1681 Con_Print("bottomcolor <0-15>\n");
1686 Host_Color(cmd, -1, atoi(Cmd_Argv(cmd, 1)));
1689 cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1690 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)"};
1691 static void Host_Rate_f(cmd_state_t *cmd)
1695 if (Cmd_Argc(cmd) != 2)
1697 if (cmd->source == src_command)
1699 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1700 Con_Print("rate <bytespersecond>\n");
1705 rate = atoi(Cmd_Argv(cmd, 1));
1707 if (cmd->source == src_command)
1709 Cvar_SetValue (&cvars_all, "_cl_rate", max(NET_MINRATE, rate));
1713 host_client->rate = rate;
1715 static void Host_Rate_BurstSize_f(cmd_state_t *cmd)
1719 if (Cmd_Argc(cmd) != 2)
1721 Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
1722 Con_Print("rate_burstsize <bytes>\n");
1726 rate_burstsize = atoi(Cmd_Argv(cmd, 1));
1728 if (cmd->source == src_command)
1730 Cvar_SetValue (&cvars_all, "_cl_rate_burstsize", rate_burstsize);
1734 host_client->rate_burstsize = rate_burstsize;
1742 static void Host_Kill_f(cmd_state_t *cmd)
1744 prvm_prog_t *prog = SVVM_prog;
1745 if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
1747 SV_ClientPrint("Can't suicide -- already dead!\n");
1751 PRVM_serverglobalfloat(time) = sv.time;
1752 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1753 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
1762 static void Host_Pause_f(cmd_state_t *cmd)
1764 void (*print) (const char *fmt, ...);
1765 if (cmd->source == src_command)
1767 // if running a client, try to send over network so the pause is handled by the server
1768 if (cls.state == ca_connected)
1770 Cmd_ForwardToServer_f(cmd);
1776 print = SV_ClientPrintf;
1778 if (!pausable.integer)
1780 if (cmd->source == src_client)
1782 if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
1784 print("Pause not allowed.\n");
1791 if (cmd->source != src_command)
1792 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1793 else if(*(sv_adminnick.string))
1794 SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
1796 SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
1797 // send notification to all clients
1798 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1799 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1803 ======================
1805 LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1806 LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1807 ======================
1809 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)"};
1810 static void Host_PModel_f(cmd_state_t *cmd)
1812 prvm_prog_t *prog = SVVM_prog;
1815 if (Cmd_Argc (cmd) == 1)
1817 if (cmd->source == src_command)
1819 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1823 i = atoi(Cmd_Argv(cmd, 1));
1825 if (cmd->source == src_command)
1827 if (cl_pmodel.integer == i)
1829 Cvar_SetValue (&cvars_all, "_cl_pmodel", i);
1830 if (cls.state == ca_connected)
1831 Cmd_ForwardToServer_f(cmd);
1835 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1838 //===========================================================================
1846 static void Host_PreSpawn_f(cmd_state_t *cmd)
1848 if (host_client->prespawned)
1850 Con_Print("prespawn not valid -- already prespawned\n");
1853 host_client->prespawned = true;
1855 if (host_client->netconnection)
1857 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1858 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1859 MSG_WriteByte (&host_client->netconnection->message, 2);
1860 host_client->sendsignon = 0; // enable unlimited sends again
1863 // reset the name change timer because the client will send name soon
1864 host_client->nametime = 0;
1872 static void Host_Spawn_f(cmd_state_t *cmd)
1874 prvm_prog_t *prog = SVVM_prog;
1877 int stats[MAX_CL_STATS];
1879 if (!host_client->prespawned)
1881 Con_Print("Spawn not valid -- not yet prespawned\n");
1884 if (host_client->spawned)
1886 Con_Print("Spawn not valid -- already spawned\n");
1889 host_client->spawned = true;
1891 // reset name change timer again because they might want to change name
1892 // again in the first 5 seconds after connecting
1893 host_client->nametime = 0;
1895 // LadyHavoc: moved this above the QC calls at FrikaC's request
1896 // LadyHavoc: commented this out
1897 //if (host_client->netconnection)
1898 // SZ_Clear (&host_client->netconnection->message);
1900 // run the entrance script
1903 // loaded games are fully initialized already
1904 if (PRVM_serverfunction(RestoreGame))
1906 Con_DPrint("Calling RestoreGame\n");
1907 PRVM_serverglobalfloat(time) = sv.time;
1908 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1909 prog->ExecuteProgram(prog, PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1914 //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);
1916 // copy spawn parms out of the client_t
1917 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1918 (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
1920 // call the spawn function
1921 host_client->clientconnectcalled = true;
1922 PRVM_serverglobalfloat(time) = sv.time;
1923 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1924 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1926 if (cls.state == ca_dedicated)
1927 Con_Printf("%s connected\n", host_client->name);
1929 PRVM_serverglobalfloat(time) = sv.time;
1930 prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1933 if (!host_client->netconnection)
1936 // send time of update
1937 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1938 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1940 // send all current names, colors, and frag counts
1941 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1943 if (!client->active)
1945 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1946 MSG_WriteByte (&host_client->netconnection->message, i);
1947 MSG_WriteString (&host_client->netconnection->message, client->name);
1948 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1949 MSG_WriteByte (&host_client->netconnection->message, i);
1950 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1951 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1952 MSG_WriteByte (&host_client->netconnection->message, i);
1953 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1956 // send all current light styles
1957 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1959 if (sv.lightstyles[i][0])
1961 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1962 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1963 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1968 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1969 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1970 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1972 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1973 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1974 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1976 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1977 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1978 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1980 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1981 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1982 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
1985 // Never send a roll angle, because savegames can catch the server
1986 // in a state where it is expecting the client to correct the angle
1987 // and it won't happen if the game was just loaded, so you wind up
1988 // with a permanent head tilt
1991 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1992 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
1993 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
1994 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1998 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1999 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
2000 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
2001 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
2004 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
2006 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
2007 MSG_WriteByte (&host_client->netconnection->message, 3);
2015 static void Host_Begin_f(cmd_state_t *cmd)
2017 if (!host_client->spawned)
2019 Con_Print("Begin not valid -- not yet spawned\n");
2022 if (host_client->begun)
2024 Con_Print("Begin not valid -- already begun\n");
2027 host_client->begun = true;
2029 // LadyHavoc: note: this code also exists in SV_DropClient
2033 for (i = 0;i < svs.maxclients;i++)
2034 if (svs.clients[i].active && !svs.clients[i].spawned)
2036 if (i == svs.maxclients)
2038 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
2039 sv.paused = sv.loadgame = false; // we're basically done with loading now
2044 //===========================================================================
2051 Kicks a user off of the server
2054 static void Host_Kick_f(cmd_state_t *cmd)
2057 const char *message = NULL;
2060 qboolean byNumber = false;
2067 if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
2069 i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
2070 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
2076 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2078 if (!host_client->active)
2080 if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
2085 if (i < svs.maxclients)
2087 if (cmd->source == src_command)
2089 if (cls.state == ca_dedicated)
2092 who = cl_name.string;
2097 // can't kick yourself!
2098 if (host_client == save)
2101 if (Cmd_Argc(cmd) > 2)
2103 message = Cmd_Args(cmd);
2104 COM_ParseToken_Simple(&message, false, false, true);
2107 message++; // skip the #
2108 while (*message == ' ') // skip white space
2110 message += strlen(Cmd_Argv(cmd, 2)); // skip the number
2112 while (*message && *message == ' ')
2116 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2118 SV_ClientPrintf("Kicked by %s\n", who);
2119 SV_DropClient (false); // kicked
2126 ===============================================================================
2130 ===============================================================================
2138 static void Host_Give_f(cmd_state_t *cmd)
2140 prvm_prog_t *prog = SVVM_prog;
2146 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2150 t = Cmd_Argv(cmd, 1);
2151 v = atoi (Cmd_Argv(cmd, 2));
2165 // MED 01/04/97 added hipnotic give stuff
2166 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
2171 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2173 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2175 else if (t[0] == '9')
2176 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2177 else if (t[0] == '0')
2178 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2179 else if (t[0] >= '2')
2180 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2185 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2190 if (gamemode == GAME_ROGUE)
2191 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2193 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2196 if (gamemode == GAME_ROGUE)
2198 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2199 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2200 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2204 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2208 if (gamemode == GAME_ROGUE)
2210 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2211 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2212 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2216 if (gamemode == GAME_ROGUE)
2218 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2219 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2220 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2224 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2228 if (gamemode == GAME_ROGUE)
2230 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2231 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2232 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2236 PRVM_serveredictfloat(host_client->edict, health) = v;
2239 if (gamemode == GAME_ROGUE)
2241 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2242 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2243 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2247 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2251 if (gamemode == GAME_ROGUE)
2253 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2254 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2255 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2261 static prvm_edict_t *FindViewthing(prvm_prog_t *prog)
2266 for (i=0 ; i<prog->num_edicts ; i++)
2268 e = PRVM_EDICT_NUM(i);
2269 if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
2272 Con_Print("No viewthing on map\n");
2281 static void Host_Viewmodel_f(cmd_state_t *cmd)
2283 prvm_prog_t *prog = SVVM_prog;
2290 e = FindViewthing(prog);
2293 m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
2294 if (m && m->loaded && m->Draw)
2296 PRVM_serveredictfloat(e, frame) = 0;
2297 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2300 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
2309 static void Host_Viewframe_f(cmd_state_t *cmd)
2311 prvm_prog_t *prog = SVVM_prog;
2319 e = FindViewthing(prog);
2322 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2324 f = atoi(Cmd_Argv(cmd, 1));
2325 if (f >= m->numframes)
2328 PRVM_serveredictfloat(e, frame) = f;
2333 static void PrintFrameName (dp_model_t *m, int frame)
2336 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2338 Con_Printf("frame %i\n", frame);
2346 static void Host_Viewnext_f(cmd_state_t *cmd)
2348 prvm_prog_t *prog = SVVM_prog;
2355 e = FindViewthing(prog);
2358 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2360 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2361 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2362 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2364 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2373 static void Host_Viewprev_f(cmd_state_t *cmd)
2375 prvm_prog_t *prog = SVVM_prog;
2382 e = FindViewthing(prog);
2385 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2387 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2388 if (PRVM_serveredictfloat(e, frame) < 0)
2389 PRVM_serveredictfloat(e, frame) = 0;
2391 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2396 ===============================================================================
2400 ===============================================================================
2409 static void Host_Startdemos_f(cmd_state_t *cmd)
2413 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2416 c = Cmd_Argc(cmd) - 1;
2419 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2422 Con_DPrintf("%i demo(s) in loop\n", c);
2424 for (i=1 ; i<c+1 ; i++)
2425 strlcpy (cls.demos[i-1], Cmd_Argv(cmd, i), sizeof (cls.demos[i-1]));
2427 // LadyHavoc: clear the remaining slots
2428 for (;i <= MAX_DEMOS;i++)
2429 cls.demos[i-1][0] = 0;
2431 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2445 Return to looping demos
2448 static void Host_Demos_f(cmd_state_t *cmd)
2450 if (cls.state == ca_dedicated)
2452 if (cls.demonum == -1)
2454 CL_Disconnect_f (cmd);
2462 Return to looping demos
2465 static void Host_Stopdemo_f(cmd_state_t *cmd)
2467 if (!cls.demoplayback)
2470 Host_ShutdownServer ();
2473 static void Host_SendCvar_f(cmd_state_t *cmd)
2477 const char *cvarname;
2481 if(Cmd_Argc(cmd) != 2)
2483 cvarname = Cmd_Argv(cmd, 1);
2484 if (cls.state == ca_connected)
2486 c = Cvar_FindVar(&cvars_all, cvarname, CVAR_CLIENT | CVAR_SERVER);
2487 // LadyHavoc: if there is no such cvar or if it is private, send a
2488 // reply indicating that it has no value
2489 if(!c || (c->flags & CVAR_PRIVATE))
2490 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname));
2492 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string));
2495 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2499 if (cls.state != ca_dedicated)
2503 for(;i<svs.maxclients;i++)
2504 if(svs.clients[i].active && svs.clients[i].netconnection)
2506 host_client = &svs.clients[i];
2507 Host_ClientCommands("sendcvar %s\n", cvarname);
2512 static void MaxPlayers_f(cmd_state_t *cmd)
2516 if (Cmd_Argc(cmd) != 2)
2518 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2524 Con_Print("maxplayers can not be changed while a server is running.\n");
2525 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2528 n = atoi(Cmd_Argv(cmd, 1));
2529 n = bound(1, n, MAX_SCOREBOARD);
2530 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2532 svs.maxclients_next = n;
2534 Cvar_Set (&cvars_all, "deathmatch", "0");
2536 Cvar_Set (&cvars_all, "deathmatch", "1");
2540 =====================
2543 ProQuake rcon support
2544 =====================
2546 static void Host_PQRcon_f(cmd_state_t *cmd)
2550 lhnetsocket_t *mysocket;
2552 if (Cmd_Argc(cmd) == 1)
2554 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2558 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2560 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2564 e = strchr(rcon_password.string, ' ');
2565 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2568 cls.rcon_address = cls.netcon->peeraddress;
2571 if (!rcon_address.string[0])
2573 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2576 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2578 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2582 unsigned char bufdata[64];
2585 MSG_WriteLong(&buf, 0);
2586 MSG_WriteByte(&buf, CCREQ_RCON);
2587 SZ_Write(&buf, (const unsigned char*)rcon_password.string, n);
2588 MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string
2589 MSG_WriteString(&buf, Cmd_Args(cmd));
2590 StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK));
2591 NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address);
2596 //=============================================================================
2598 // QuakeWorld commands
2601 =====================
2604 Send the rest of the command line over as
2605 an unconnected command.
2606 =====================
2608 static void Host_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2612 lhnetsocket_t *mysocket;
2614 if (Cmd_Argc(cmd) == 1)
2616 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2620 if (!rcon_password.string || !rcon_password.string[0])
2622 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2626 e = strchr(rcon_password.string, ' ');
2627 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2630 cls.rcon_address = cls.netcon->peeraddress;
2633 if (!rcon_address.string[0])
2635 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2638 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2640 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2641 if (mysocket && Cmd_Args(cmd)[0])
2643 // simply put together the rcon packet and send it
2644 if(Cmd_Argv(cmd, 0)[0] == 's' || rcon_secure.integer > 1)
2646 if(cls.rcon_commands[cls.rcon_ringpos][0])
2649 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2650 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]);
2651 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2654 for (i = 0;i < MAX_RCONS;i++)
2655 if(cls.rcon_commands[i][0])
2656 if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i]))
2660 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later
2661 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2662 cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address;
2663 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2664 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2666 else if(rcon_secure.integer > 0)
2670 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args(cmd));
2671 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2672 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2675 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2676 NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address);
2682 memcpy(buf, "\377\377\377\377", 4);
2683 dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s", n, rcon_password.string, Cmd_Args(cmd));
2684 NetConn_WriteString(mysocket, buf, &cls.rcon_address);
2690 ====================
2693 user <name or userid>
2695 Dump userdata / masterdata for a user
2696 ====================
2698 static void Host_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2703 if (Cmd_Argc(cmd) != 2)
2705 Con_Printf ("Usage: user <username / userid>\n");
2709 uid = atoi(Cmd_Argv(cmd, 1));
2711 for (i = 0;i < cl.maxclients;i++)
2713 if (!cl.scores[i].name[0])
2715 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
2717 InfoString_Print(cl.scores[i].qw_userinfo);
2721 Con_Printf ("User not in server.\n");
2725 ====================
2728 Dump userids for all current players
2729 ====================
2731 static void Host_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2737 Con_Printf ("userid frags name\n");
2738 Con_Printf ("------ ----- ----\n");
2739 for (i = 0;i < cl.maxclients;i++)
2741 if (cl.scores[i].name[0])
2743 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2748 Con_Printf ("%i total users\n", c);
2753 Host_FullServerinfo_f
2755 Sent by server when serverinfo changes
2758 // TODO: shouldn't this be a cvar instead?
2759 static void Host_FullServerinfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2762 if (Cmd_Argc(cmd) != 2)
2764 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2768 strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo));
2769 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2770 cl.qw_teamplay = atoi(temp);
2777 Allow clients to change userinfo
2781 static void Host_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2787 if (Cmd_Argc(cmd) != 2)
2789 Con_Printf ("fullinfo <complete info string>\n");
2793 s = Cmd_Argv(cmd, 1);
2798 size_t len = strcspn(s, "\\");
2799 if (len >= sizeof(key)) {
2800 len = sizeof(key) - 1;
2802 strlcpy(key, s, len + 1);
2806 Con_Printf ("MISSING VALUE\n");
2809 ++s; // Skip over backslash.
2811 len = strcspn(s, "\\");
2812 if (len >= sizeof(value)) {
2813 len = sizeof(value) - 1;
2815 strlcpy(value, s, len + 1);
2817 CL_SetInfo(key, value, false, false, false, false);
2824 ++s; // Skip over backslash.
2832 Allow clients to change userinfo
2835 static void Host_SetInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2837 if (Cmd_Argc(cmd) == 1)
2839 InfoString_Print(cls.userinfo);
2842 if (Cmd_Argc(cmd) != 3)
2844 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2847 CL_SetInfo(Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), true, false, false, false);
2851 ====================
2854 packet <destination> <contents>
2856 Contents allows \n escape character
2857 ====================
2859 static void Host_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2865 lhnetaddress_t address;
2866 lhnetsocket_t *mysocket;
2868 if (Cmd_Argc(cmd) != 3)
2870 Con_Printf ("packet <destination> <contents>\n");
2874 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(cmd, 1), sv_netport.integer))
2876 Con_Printf ("Bad address\n");
2880 in = Cmd_Argv(cmd, 2);
2882 send[0] = send[1] = send[2] = send[3] = -1;
2884 l = (int)strlen (in);
2885 for (i=0 ; i<l ; i++)
2887 if (out >= send + sizeof(send) - 1)
2889 if (in[i] == '\\' && in[i+1] == 'n')
2894 else if (in[i] == '\\' && in[i+1] == '0')
2899 else if (in[i] == '\\' && in[i+1] == 't')
2904 else if (in[i] == '\\' && in[i+1] == 'r')
2909 else if (in[i] == '\\' && in[i+1] == '"')
2918 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2920 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2922 NetConn_Write(mysocket, send, out - send, &address);
2926 ====================
2929 Send back ping and packet loss update for all current players to this player
2930 ====================
2932 void Host_Pings_f(cmd_state_t *cmd)
2934 int i, j, ping, packetloss, movementloss;
2937 if (!host_client->netconnection)
2940 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2942 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2943 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2945 for (i = 0;i < svs.maxclients;i++)
2949 if (svs.clients[i].netconnection)
2951 for (j = 0;j < NETGRAPH_PACKETS;j++)
2952 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2954 for (j = 0;j < NETGRAPH_PACKETS;j++)
2955 if (svs.clients[i].movement_count[j] < 0)
2958 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2959 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2960 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2961 ping = bound(0, ping, 9999);
2962 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2964 // send qw_svc_updateping and qw_svc_updatepl messages
2965 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2966 MSG_WriteShort(&host_client->netconnection->message, ping);
2967 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2968 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2972 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2974 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2976 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2977 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2980 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2981 MSG_WriteString(&host_client->netconnection->message, "\n");
2984 static void Host_PingPLReport_f(cmd_state_t *cmd)
2988 int l = Cmd_Argc(cmd);
2989 if (l > cl.maxclients)
2991 for (i = 0;i < l;i++)
2993 cl.scores[i].qw_ping = atoi(Cmd_Argv(cmd, 1+i*2));
2994 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(cmd, 1+i*2+1), &errbyte, 0);
2995 if(errbyte && *errbyte == ',')
2996 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2998 cl.scores[i].qw_movementloss = 0;
3002 //=============================================================================
3009 void Host_InitCommands (void)
3011 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
3013 Cvar_RegisterVariable(&cl_name);
3014 Cvar_RegisterVariable(&cl_color);
3015 Cvar_RegisterVariable(&cl_rate);
3016 Cvar_RegisterVariable(&cl_rate_burstsize);
3017 Cvar_RegisterVariable(&cl_pmodel);
3018 Cvar_RegisterVariable(&cl_playermodel);
3019 Cvar_RegisterVariable(&cl_playerskin);
3020 Cvar_RegisterVariable(&rcon_password);
3021 Cvar_RegisterVariable(&rcon_address);
3022 Cvar_RegisterVariable(&rcon_secure);
3023 Cvar_RegisterVariable(&rcon_secure_challengetimeout);
3024 Cvar_RegisterVariable(&r_fixtrans_auto);
3025 Cvar_RegisterVariable(&team);
3026 Cvar_RegisterVariable(&skin);
3027 Cvar_RegisterVariable(&noaim);
3028 Cvar_RegisterVariable(&sv_cheats);
3029 Cvar_RegisterVariable(&sv_adminnick);
3030 Cvar_RegisterVariable(&sv_status_privacy);
3031 Cvar_RegisterVariable(&sv_status_show_qcstatus);
3032 Cvar_RegisterVariable(&sv_namechangetimer);
3034 // client commands - this includes server commands because the client can host a server, so they must exist
3035 Cmd_AddCommand(&cmd_client, "quit", Host_Quit_f, "quit the game");
3036 Cmd_AddCommand(&cmd_client, "status", Host_Status_f, "print server status information");
3037 Cmd_AddCommand(&cmd_client, "map", Host_Map_f, "kick everyone off the server and start a new level");
3038 Cmd_AddCommand(&cmd_client, "restart", Host_Restart_f, "restart current level");
3039 Cmd_AddCommand(&cmd_client, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3040 Cmd_AddCommand(&cmd_client, "version", Host_Version_f, "print engine version");
3041 Cmd_AddCommand(&cmd_client, "say", Host_Say_f, "send a chat message to everyone on the server");
3042 Cmd_AddCommand(&cmd_client, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3043 Cmd_AddCommand(&cmd_client, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3044 Cmd_AddCommand(&cmd_client, "kick", Host_Kick_f, "kick a player off the server by number or name");
3045 Cmd_AddCommand(&cmd_client, "ping", Host_Ping_f, "print ping times of all players on the server");
3046 Cmd_AddCommand(&cmd_client, "load", Host_Loadgame_f, "load a saved game file");
3047 Cmd_AddCommand(&cmd_client, "save", Host_Savegame_f, "save the game to a file");
3048 Cmd_AddCommand(&cmd_client, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3049 Cmd_AddCommand(&cmd_client, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3050 Cmd_AddCommand(&cmd_client, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3051 Cmd_AddCommand(&cmd_client, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3052 Cmd_AddCommand(&cmd_client, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3053 Cmd_AddCommand(&cmd_client, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3054 Cmd_AddCommand(&cmd_client, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3056 // dedicated server commands
3057 Cmd_AddCommand(&cmd_server, "quit", Host_Quit_f, "quit the game");
3058 Cmd_AddCommand(&cmd_server, "status", Host_Status_f, "print server status information");
3059 Cmd_AddCommand(&cmd_server, "map", Host_Map_f, "kick everyone off the server and start a new level");
3060 Cmd_AddCommand(&cmd_server, "restart", Host_Restart_f, "restart current level");
3061 Cmd_AddCommand(&cmd_server, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3062 Cmd_AddCommand(&cmd_server, "version", Host_Version_f, "print engine version");
3063 Cmd_AddCommand(&cmd_server, "say", Host_Say_f, "send a chat message to everyone on the server");
3064 Cmd_AddCommand(&cmd_server, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3065 Cmd_AddCommand(&cmd_server, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3066 Cmd_AddCommand(&cmd_server, "kick", Host_Kick_f, "kick a player off the server by number or name");
3067 Cmd_AddCommand(&cmd_server, "ping", Host_Ping_f, "print ping times of all players on the server");
3068 Cmd_AddCommand(&cmd_server, "load", Host_Loadgame_f, "load a saved game file");
3069 Cmd_AddCommand(&cmd_server, "save", Host_Savegame_f, "save the game to a file");
3070 Cmd_AddCommand(&cmd_server, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3071 Cmd_AddCommand(&cmd_server, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3072 Cmd_AddCommand(&cmd_server, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3073 Cmd_AddCommand(&cmd_server, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3074 Cmd_AddCommand(&cmd_server, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3075 Cmd_AddCommand(&cmd_server, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3076 Cmd_AddCommand(&cmd_server, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3078 // 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)
3079 Cmd_AddCommand(&cmd_serverfromclient, "prespawn", Host_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
3080 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)");
3081 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)");
3082 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)");
3084 Cmd_AddCommand(&cmd_serverfromclient, "status", Host_Status_f, "print server status information");
3085 Cmd_AddCommand(&cmd_serverfromclient, "god", Host_God_f, "god mode (invulnerability)");
3086 Cmd_AddCommand(&cmd_serverfromclient, "notarget", Host_Notarget_f, "notarget mode (monsters do not see you)");
3087 Cmd_AddCommand(&cmd_serverfromclient, "fly", Host_Fly_f, "fly mode (flight)");
3088 Cmd_AddCommand(&cmd_serverfromclient, "noclip", Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
3089 Cmd_AddCommand(&cmd_serverfromclient, "give", Host_Give_f, "alter inventory");
3090 Cmd_AddCommand(&cmd_serverfromclient, "say", Host_Say_f, "send a chat message to everyone on the server");
3091 Cmd_AddCommand(&cmd_serverfromclient, "say_team", Host_Say_Team_f, "send a chat message to your team on the server");
3092 Cmd_AddCommand(&cmd_serverfromclient, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3093 Cmd_AddCommand(&cmd_serverfromclient, "kill", Host_Kill_f, "die instantly");
3094 Cmd_AddCommand(&cmd_serverfromclient, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3095 Cmd_AddCommand(&cmd_serverfromclient, "ping", Host_Ping_f, "print ping times of all players on the server");
3096 Cmd_AddCommand(&cmd_serverfromclient, "name", Host_Name_f, "change your player name");
3097 Cmd_AddCommand(&cmd_serverfromclient, "color", Host_Color_f, "change your player shirt and pants colors");
3098 Cmd_AddCommand(&cmd_serverfromclient, "rate", Host_Rate_f, "change your network connection speed");
3099 Cmd_AddCommand(&cmd_serverfromclient, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3100 Cmd_AddCommand(&cmd_serverfromclient, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3101 Cmd_AddCommand(&cmd_serverfromclient, "playermodel", Host_Playermodel_f, "change your player model");
3102 Cmd_AddCommand(&cmd_serverfromclient, "playerskin", Host_Playerskin_f, "change your player skin number");
3104 // client commands that require a connection and are simply forwarded to server
3105 Cmd_AddCommand(&cmd_client, "god", Cmd_ForwardToServer_f, "god mode (invulnerability)");
3106 Cmd_AddCommand(&cmd_client, "notarget", Cmd_ForwardToServer_f, "notarget mode (monsters do not see you)");
3107 Cmd_AddCommand(&cmd_client, "fly", Cmd_ForwardToServer_f, "fly mode (flight)");
3108 Cmd_AddCommand(&cmd_client, "noclip", Cmd_ForwardToServer_f, "noclip mode (flight without collisions, move through walls)");
3109 Cmd_AddCommand(&cmd_client, "give", Cmd_ForwardToServer_f, "alter inventory");
3110 Cmd_AddCommand(&cmd_client, "say_team", Cmd_ForwardToServer_f, "send a chat message to your team on the server");
3111 Cmd_AddCommand(&cmd_client, "kill", Cmd_ForwardToServer_f, "die instantly");
3113 Cmd_AddCommand(&cmd_client, "connect", Host_Connect_f, "connect to a server by IP address or hostname");
3114 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)");
3115 Cmd_AddCommand(&cmd_client, "startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
3116 Cmd_AddCommand(&cmd_client, "demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
3117 Cmd_AddCommand(&cmd_client, "stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
3118 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");
3119 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");
3120 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");
3121 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)");
3122 Cmd_AddCommand(&cmd_client, "fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
3123 Cmd_AddCommand(&cmd_client, "setinfo", Host_SetInfo_f, "modifies your userinfo");
3124 Cmd_AddCommand(&cmd_client, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3125 Cmd_AddCommand(&cmd_client, "topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
3126 Cmd_AddCommand(&cmd_client, "bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
3127 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)");
3129 // client commands that also exist as cmd_serverfromclient and are often forwarded
3130 Cmd_AddCommand(&cmd_client, "name", Host_Name_f, "change your player name");
3131 Cmd_AddCommand(&cmd_client, "color", Host_Color_f, "change your player shirt and pants colors");
3132 Cmd_AddCommand(&cmd_client, "rate", Host_Rate_f, "change your network connection speed");
3133 Cmd_AddCommand(&cmd_client, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3134 Cmd_AddCommand(&cmd_client, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3135 Cmd_AddCommand(&cmd_client, "playermodel", Host_Playermodel_f, "change your player model");
3136 Cmd_AddCommand(&cmd_client, "playerskin", Host_Playerskin_f, "change your player skin number");
3138 // commands that are only sent by server to client for execution
3139 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)");
3140 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");
3143 void Host_NoOperation_f(cmd_state_t *cmd)