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);
334 ===============================================================================
338 ===============================================================================
342 ======================
347 command from the console. Active clients are kicked off.
348 ======================
350 static void Host_Map_f(cmd_state_t *cmd)
352 char level[MAX_QPATH];
354 if (Cmd_Argc(cmd) != 2)
356 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
360 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
361 if (gamemode == GAME_DELUXEQUAKE)
362 Cvar_Set(&cvars_all, "warpmark", "");
364 cls.demonum = -1; // stop demo loop in case this fails
367 Host_ShutdownServer();
369 if(svs.maxclients != svs.maxclients_next)
371 svs.maxclients = svs.maxclients_next;
373 Mem_Free(svs.clients);
374 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
379 if (key_dest == key_menu || key_dest == key_menu_grabbed)
384 svs.serverflags = 0; // haven't completed an episode yet
385 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
386 SV_SpawnServer(level);
387 if (sv.active && cls.state == ca_disconnected)
388 CL_EstablishConnection("local:1", -2);
395 Goes to a new map, taking all clients along
398 static void Host_Changelevel_f(cmd_state_t *cmd)
400 char level[MAX_QPATH];
402 if (Cmd_Argc(cmd) != 2)
404 Con_Print("changelevel <levelname> : continue game on a new level\n");
415 if (key_dest == key_menu || key_dest == key_menu_grabbed)
420 SV_SaveSpawnparms ();
421 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
422 SV_SpawnServer(level);
423 if (sv.active && cls.state == ca_disconnected)
424 CL_EstablishConnection("local:1", -2);
431 Restarts the current server for a dead player
434 static void Host_Restart_f(cmd_state_t *cmd)
436 char mapname[MAX_QPATH];
438 if (Cmd_Argc(cmd) != 1)
440 Con_Print("restart : restart current level\n");
445 Con_Print("Only the server may restart\n");
451 if (key_dest == key_menu || key_dest == key_menu_grabbed)
456 strlcpy(mapname, sv.name, sizeof(mapname));
457 SV_SpawnServer(mapname);
458 if (sv.active && cls.state == ca_disconnected)
459 CL_EstablishConnection("local:1", -2);
466 This command causes the client to wait for the signon messages again.
467 This is sent just before a server changes levels
470 void Host_Reconnect_f(cmd_state_t *cmd)
473 // if not connected, reconnect to the most recent server
476 // if we have connected to a server recently, the userinfo
477 // will still contain its IP address, so get the address...
478 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
480 CL_EstablishConnection(temp, -1);
482 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
485 // if connected, do something based on protocol
486 if (cls.protocol == PROTOCOL_QUAKEWORLD)
488 // quakeworld can just re-login
489 if (cls.qw_downloadmemory) // don't change when downloading
494 if (cls.state == ca_connected)
496 Con_Printf("Server is changing level...\n");
497 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
498 MSG_WriteString(&cls.netcon->message, "new");
503 // netquake uses reconnect on level changes (silly)
504 if (Cmd_Argc(cmd) != 1)
506 Con_Print("reconnect : wait for signon messages again\n");
511 Con_Print("reconnect: no signon, ignoring reconnect\n");
514 cls.signon = 0; // need new connection messages
519 =====================
522 User command to connect to server
523 =====================
525 static void Host_Connect_f(cmd_state_t *cmd)
527 if (Cmd_Argc(cmd) < 2)
529 Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
532 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
533 if(rcon_secure.integer <= 0)
534 Cvar_SetQuick(&rcon_password, "");
535 CL_EstablishConnection(Cmd_Argv(cmd, 1), 2);
540 ===============================================================================
544 ===============================================================================
547 #define SAVEGAME_VERSION 5
549 void Host_Savegame_to(prvm_prog_t *prog, const char *name)
552 int i, k, l, numbuffers, lightstyles = 64;
553 char comment[SAVEGAME_COMMENT_LENGTH+1];
554 char line[MAX_INPUTLINE];
558 // first we have to figure out if this can be saved in 64 lightstyles
559 // (for Quake compatibility)
560 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
561 if (sv.lightstyles[i][0])
564 isserver = prog == SVVM_prog;
566 Con_Printf("Saving game to %s...\n", name);
567 f = FS_OpenRealFile(name, "wb", false);
570 Con_Print("ERROR: couldn't open.\n");
574 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
576 memset(comment, 0, sizeof(comment));
578 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));
580 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", prog->name);
581 // convert space to _ to make stdio happy
582 // LadyHavoc: convert control characters to _ as well
583 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
584 if (ISWHITESPACEORCONTROL(comment[i]))
586 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
588 FS_Printf(f, "%s\n", comment);
591 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
592 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
593 FS_Printf(f, "%d\n", current_skill);
594 FS_Printf(f, "%s\n", sv.name);
595 FS_Printf(f, "%f\n",sv.time);
599 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
600 FS_Printf(f, "(dummy)\n");
601 FS_Printf(f, "%d\n", 0);
602 FS_Printf(f, "%s\n", "(dummy)");
603 FS_Printf(f, "%f\n", realtime);
606 // write the light styles
607 for (i=0 ; i<lightstyles ; i++)
609 if (isserver && sv.lightstyles[i][0])
610 FS_Printf(f, "%s\n", sv.lightstyles[i]);
615 PRVM_ED_WriteGlobals (prog, f);
616 for (i=0 ; i<prog->num_edicts ; i++)
618 FS_Printf(f,"// edict %d\n", i);
619 //Con_Printf("edict %d...\n", i);
620 PRVM_ED_Write (prog, f, PRVM_EDICT_NUM(i));
625 FS_Printf(f,"// DarkPlaces extended savegame\n");
626 // darkplaces extension - extra lightstyles, support for color lightstyles
627 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
628 if (isserver && sv.lightstyles[i][0])
629 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
631 // darkplaces extension - model precaches
632 for (i=1 ; i<MAX_MODELS ; i++)
633 if (sv.model_precache[i][0])
634 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
636 // darkplaces extension - sound precaches
637 for (i=1 ; i<MAX_SOUNDS ; i++)
638 if (sv.sound_precache[i][0])
639 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
641 // darkplaces extension - save buffers
642 numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
643 for (i = 0; i < numbuffers; i++)
645 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
646 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
648 FS_Printf(f,"sv.buffer %i %i \"string\"\n", i, stringbuffer->flags & STRINGBUFFER_QCFLAGS);
649 for(k = 0; k < stringbuffer->num_strings; k++)
651 if (!stringbuffer->strings[k])
653 // Parse the string a bit to turn special characters
654 // (like newline, specifically) into escape codes
655 s = stringbuffer->strings[k];
656 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
683 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
691 Con_Print("done.\n");
699 static void Host_Savegame_f(cmd_state_t *cmd)
701 prvm_prog_t *prog = SVVM_prog;
702 char name[MAX_QPATH];
703 qboolean deadflag = false;
707 Con_Print("Can't save - no server running.\n");
711 deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
715 // singleplayer checks
718 Con_Print("Can't save in intermission.\n");
724 Con_Print("Can't savegame with a dead player\n");
729 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");
731 if (Cmd_Argc(cmd) != 2)
733 Con_Print("save <savename> : save a game\n");
737 if (strstr(Cmd_Argv(cmd, 1), ".."))
739 Con_Print("Relative pathnames are not allowed.\n");
743 strlcpy (name, Cmd_Argv(cmd, 1), sizeof (name));
744 FS_DefaultExtension (name, ".sav", sizeof (name));
746 Host_Savegame_to(prog, name);
756 static void Host_Loadgame_f(cmd_state_t *cmd)
758 prvm_prog_t *prog = SVVM_prog;
759 char filename[MAX_QPATH];
760 char mapname[MAX_QPATH];
767 int i, k, numbuffers;
770 float spawn_parms[NUM_SPAWN_PARMS];
771 prvm_stringbuffer_t *stringbuffer;
773 if (Cmd_Argc(cmd) != 2)
775 Con_Print("load <savename> : load a game\n");
779 strlcpy (filename, Cmd_Argv(cmd, 1), sizeof(filename));
780 FS_DefaultExtension (filename, ".sav", sizeof (filename));
782 Con_Printf("Loading game from %s...\n", filename);
784 // stop playing demos
785 if (cls.demoplayback)
790 if (key_dest == key_menu || key_dest == key_menu_grabbed)
795 cls.demonum = -1; // stop demo loop in case this fails
797 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
800 Con_Print("ERROR: couldn't open.\n");
804 if(developer_entityparsing.integer)
805 Con_Printf("Host_Loadgame_f: loading version\n");
808 COM_ParseToken_Simple(&t, false, false, true);
809 version = atoi(com_token);
810 if (version != SAVEGAME_VERSION)
813 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
817 if(developer_entityparsing.integer)
818 Con_Printf("Host_Loadgame_f: loading description\n");
821 COM_ParseToken_Simple(&t, false, false, true);
823 for (i = 0;i < NUM_SPAWN_PARMS;i++)
825 COM_ParseToken_Simple(&t, false, false, true);
826 spawn_parms[i] = atof(com_token);
829 COM_ParseToken_Simple(&t, false, false, true);
830 // this silliness is so we can load 1.06 save files, which have float skill values
831 current_skill = (int)(atof(com_token) + 0.5);
832 Cvar_SetValue (&cvars_all, "skill", (float)current_skill);
834 if(developer_entityparsing.integer)
835 Con_Printf("Host_Loadgame_f: loading mapname\n");
838 COM_ParseToken_Simple(&t, false, false, true);
839 strlcpy (mapname, com_token, sizeof(mapname));
841 if(developer_entityparsing.integer)
842 Con_Printf("Host_Loadgame_f: loading time\n");
845 COM_ParseToken_Simple(&t, false, false, true);
846 time = atof(com_token);
848 if(developer_entityparsing.integer)
849 Con_Printf("Host_Loadgame_f: spawning server\n");
851 SV_SpawnServer (mapname);
855 Con_Print("Couldn't load map\n");
858 sv.paused = true; // pause until all clients connect
861 if(developer_entityparsing.integer)
862 Con_Printf("Host_Loadgame_f: loading light styles\n");
864 // load the light styles
869 for (i = 0;i < MAX_LIGHTSTYLES;i++)
873 COM_ParseToken_Simple(&t, false, false, true);
874 // if this is a 64 lightstyle savegame produced by Quake, stop now
875 // we have to check this because darkplaces may save more than 64
876 if (com_token[0] == '{')
881 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
884 if(developer_entityparsing.integer)
885 Con_Printf("Host_Loadgame_f: skipping until globals\n");
887 // now skip everything before the first opening brace
888 // (this is for forward compatibility, so that older versions (at
889 // least ones with this fix) can load savegames with extra data before the
890 // first brace, as might be produced by a later engine version)
894 if (!COM_ParseToken_Simple(&t, false, false, true))
896 if (com_token[0] == '{')
903 // unlink all entities
904 World_UnlinkAll(&sv.world);
906 // load the edicts out of the savegame file
911 while (COM_ParseToken_Simple(&t, false, false, true))
912 if (!strcmp(com_token, "}"))
914 if (!COM_ParseToken_Simple(&start, false, false, true))
919 if (strcmp(com_token,"{"))
922 Host_Error ("First token isn't a brace");
927 if(developer_entityparsing.integer)
928 Con_Printf("Host_Loadgame_f: loading globals\n");
930 // parse the global vars
931 PRVM_ED_ParseGlobals (prog, start);
933 // restore the autocvar globals
934 Cvar_UpdateAllAutoCvars(prog->console_cmd->cvars);
939 if (entnum >= MAX_EDICTS)
942 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
944 while (entnum >= prog->max_edicts)
945 PRVM_MEM_IncreaseEdicts(prog);
946 ent = PRVM_EDICT_NUM(entnum);
947 memset(ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
948 ent->priv.server->free = false;
950 if(developer_entityparsing.integer)
951 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
953 PRVM_ED_ParseEdict (prog, start, ent);
955 // link it into the bsp tree
956 if (!ent->priv.server->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
964 prog->num_edicts = entnum;
967 for (i = 0;i < NUM_SPAWN_PARMS;i++)
968 svs.clients[0].spawn_parms[i] = spawn_parms[i];
970 if(developer_entityparsing.integer)
971 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
973 // read extended data if present
974 // the extended data is stored inside a /* */ comment block, which the
975 // parser intentionally skips, so we have to check for it manually here
978 while (*end == '\r' || *end == '\n')
980 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
982 if(developer_entityparsing.integer)
983 Con_Printf("Host_Loadgame_f: loading extended data\n");
985 Con_Printf("Loading extended DarkPlaces savegame\n");
987 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
988 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
989 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
992 while (COM_ParseToken_Simple(&t, false, false, true))
994 if (!strcmp(com_token, "sv.lightstyles"))
996 COM_ParseToken_Simple(&t, false, false, true);
998 COM_ParseToken_Simple(&t, false, false, true);
999 if (i >= 0 && i < MAX_LIGHTSTYLES)
1000 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
1002 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
1004 else if (!strcmp(com_token, "sv.model_precache"))
1006 COM_ParseToken_Simple(&t, false, false, true);
1007 i = atoi(com_token);
1008 COM_ParseToken_Simple(&t, false, false, true);
1009 if (i >= 0 && i < MAX_MODELS)
1011 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
1012 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
1015 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
1017 else if (!strcmp(com_token, "sv.sound_precache"))
1019 COM_ParseToken_Simple(&t, false, false, true);
1020 i = atoi(com_token);
1021 COM_ParseToken_Simple(&t, false, false, true);
1022 if (i >= 0 && i < MAX_SOUNDS)
1023 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1025 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1027 else if (!strcmp(com_token, "sv.buffer"))
1029 if (COM_ParseToken_Simple(&t, false, false, true))
1031 i = atoi(com_token);
1034 k = STRINGBUFFER_SAVED;
1035 if (COM_ParseToken_Simple(&t, false, false, true))
1036 k |= atoi(com_token);
1037 if (!BufStr_FindCreateReplace(prog, i, k, "string"))
1038 Con_Errorf("failed to create stringbuffer %i\n", i);
1041 Con_Printf("unsupported stringbuffer index %i \"%s\"\n", i, com_token);
1044 Con_Printf("unexpected end of line when parsing sv.buffer (expected buffer index)\n");
1046 else if (!strcmp(com_token, "sv.bufstr"))
1048 if (!COM_ParseToken_Simple(&t, false, false, true))
1049 Con_Printf("unexpected end of line when parsing sv.bufstr\n");
1052 i = atoi(com_token);
1053 stringbuffer = BufStr_FindCreateReplace(prog, i, STRINGBUFFER_SAVED, "string");
1056 if (COM_ParseToken_Simple(&t, false, false, true))
1058 k = atoi(com_token);
1059 if (COM_ParseToken_Simple(&t, false, false, true))
1060 BufStr_Set(prog, stringbuffer, k, com_token);
1062 Con_Printf("unexpected end of line when parsing sv.bufstr (expected string)\n");
1065 Con_Printf("unexpected end of line when parsing sv.bufstr (expected strindex)\n");
1068 Con_Errorf("failed to create stringbuffer %i \"%s\"\n", i, com_token);
1071 // skip any trailing text or unrecognized commands
1072 while (COM_ParseToken_Simple(&t, true, false, true) && strcmp(com_token, "\n"))
1079 // remove all temporary flagged string buffers (ones created with BufStr_FindCreateReplace)
1080 numbuffers = (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray);
1081 for (i = 0; i < numbuffers; i++)
1083 if ( (stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i)) )
1084 if (stringbuffer->flags & STRINGBUFFER_TEMP)
1085 BufStr_Del(prog, stringbuffer);
1088 if(developer_entityparsing.integer)
1089 Con_Printf("Host_Loadgame_f: finished\n");
1091 // make sure we're connected to loopback
1092 if (sv.active && cls.state == ca_disconnected)
1093 CL_EstablishConnection("local:1", -2);
1096 //============================================================================
1099 ======================
1101 ======================
1103 cvar_t cl_name = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1104 static void Host_Name_f(cmd_state_t *cmd)
1106 prvm_prog_t *prog = SVVM_prog;
1108 qboolean valid_colors;
1109 const char *newNameSource;
1110 char newName[sizeof(host_client->name)];
1112 if (Cmd_Argc (cmd) == 1)
1114 if (cmd->source == src_command)
1116 Con_Printf("name: %s\n", cl_name.string);
1121 if (Cmd_Argc (cmd) == 2)
1122 newNameSource = Cmd_Argv(cmd, 1);
1124 newNameSource = Cmd_Args(cmd);
1126 strlcpy(newName, newNameSource, sizeof(newName));
1128 if (cmd->source == src_command)
1130 Cvar_Set (&cvars_all, "_cl_name", newName);
1131 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1133 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1134 Con_Printf("name: %s\n", cl_name.string);
1139 if (realtime < host_client->nametime)
1141 SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
1145 host_client->nametime = realtime + max(0.0f, sv_namechangetimer.value);
1147 // point the string back at updateclient->name to keep it safe
1148 strlcpy (host_client->name, newName, sizeof (host_client->name));
1150 for (i = 0, j = 0;host_client->name[i];i++)
1151 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1152 host_client->name[j++] = host_client->name[i];
1153 host_client->name[j] = 0;
1155 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1156 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1158 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1159 host_client->name[sizeof(host_client->name) - 1] = 0;
1160 host_client->name[0] = STRING_COLOR_TAG;
1161 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1164 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1165 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1168 l = strlen(host_client->name);
1169 if(l < sizeof(host_client->name) - 1)
1171 // duplicate the color tag to escape it
1172 host_client->name[i] = STRING_COLOR_TAG;
1173 host_client->name[i+1] = 0;
1174 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1178 // remove the last character to fix the color code
1179 host_client->name[l-1] = 0;
1180 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1184 // find the last color tag offset and decide if we need to add a reset tag
1185 for (i = 0, j = -1;host_client->name[i];i++)
1187 if (host_client->name[i] == STRING_COLOR_TAG)
1189 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1192 // if this happens to be a reset tag then we don't need one
1193 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1198 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]))
1204 if (host_client->name[i+1] == STRING_COLOR_TAG)
1211 // does not end in the default color string, so add it
1212 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1213 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1215 PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
1216 if (strcmp(host_client->old_name, host_client->name))
1218 if (host_client->begun)
1219 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1220 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1221 // send notification to all clients
1222 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1223 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1224 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1225 SV_WriteNetnameIntoDemo(host_client);
1230 ======================
1232 ======================
1234 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)"};
1235 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1236 static void Host_Playermodel_f(cmd_state_t *cmd)
1238 prvm_prog_t *prog = SVVM_prog;
1240 char newPath[sizeof(host_client->playermodel)];
1242 if (Cmd_Argc (cmd) == 1)
1244 if (cmd->source == src_command)
1246 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1251 if (Cmd_Argc (cmd) == 2)
1252 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1254 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1256 for (i = 0, j = 0;newPath[i];i++)
1257 if (newPath[i] != '\r' && newPath[i] != '\n')
1258 newPath[j++] = newPath[i];
1261 if (cmd->source == src_command)
1263 Cvar_Set (&cvars_all, "_cl_playermodel", newPath);
1268 if (realtime < host_client->nametime)
1270 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1274 host_client->nametime = realtime + 5;
1277 // point the string back at updateclient->name to keep it safe
1278 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1279 PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
1280 if (strcmp(host_client->old_model, host_client->playermodel))
1282 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1283 /*// send notification to all clients
1284 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1285 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1286 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1291 ======================
1293 ======================
1295 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)"};
1296 static void Host_Playerskin_f(cmd_state_t *cmd)
1298 prvm_prog_t *prog = SVVM_prog;
1300 char newPath[sizeof(host_client->playerskin)];
1302 if (Cmd_Argc (cmd) == 1)
1304 if (cmd->source == src_command)
1306 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1311 if (Cmd_Argc (cmd) == 2)
1312 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1314 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1316 for (i = 0, j = 0;newPath[i];i++)
1317 if (newPath[i] != '\r' && newPath[i] != '\n')
1318 newPath[j++] = newPath[i];
1321 if (cmd->source == src_command)
1323 Cvar_Set (&cvars_all, "_cl_playerskin", newPath);
1328 if (realtime < host_client->nametime)
1330 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1334 host_client->nametime = realtime + 5;
1337 // point the string back at updateclient->name to keep it safe
1338 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1339 PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
1340 if (strcmp(host_client->old_skin, host_client->playerskin))
1342 //if (host_client->begun)
1343 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1344 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1345 /*// send notification to all clients
1346 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1347 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1348 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1352 static void Host_Version_f(cmd_state_t *cmd)
1354 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1357 static void Host_Say(cmd_state_t *cmd, qboolean teamonly)
1359 prvm_prog_t *prog = SVVM_prog;
1364 // LadyHavoc: long say messages
1366 qboolean fromServer = false;
1368 if (cmd->source == src_command)
1370 if (cls.state == ca_dedicated)
1377 Cmd_ForwardToServer_f(cmd);
1382 if (Cmd_Argc (cmd) < 2)
1385 if (!teamplay.integer)
1395 // note this uses the chat prefix \001
1396 if (!fromServer && !teamonly)
1397 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1398 else if (!fromServer && teamonly)
1399 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1400 else if(*(sv_adminnick.string))
1401 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1403 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1404 p2 = text + strlen(text);
1405 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1407 if (p2[-1] == '\"' && quoted)
1412 strlcat(text, "\n", sizeof(text));
1414 // note: save is not a valid edict if fromServer is true
1416 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1417 if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
1418 SV_ClientPrint(text);
1421 if (cls.state == ca_dedicated)
1422 Con_Print(&text[1]);
1426 static void Host_Say_f(cmd_state_t *cmd)
1428 Host_Say(cmd, false);
1432 static void Host_Say_Team_f(cmd_state_t *cmd)
1434 Host_Say(cmd, true);
1438 static void Host_Tell_f(cmd_state_t *cmd)
1440 const char *playername_start = NULL;
1441 size_t playername_length = 0;
1442 int playernumber = 0;
1445 const char *p1, *p2;
1446 char text[MAX_INPUTLINE]; // LadyHavoc: FIXME: temporary buffer overflow fix (was 64)
1447 qboolean fromServer = false;
1449 if (cmd->source == src_command)
1451 if (cls.state == ca_dedicated)
1455 Cmd_ForwardToServer_f(cmd);
1460 if (Cmd_Argc (cmd) < 2)
1463 // note this uses the chat prefix \001
1465 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1466 else if(*(sv_adminnick.string))
1467 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1469 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1472 p2 = p1 + strlen(p1);
1473 // remove the target name
1474 while (p1 < p2 && *p1 == ' ')
1479 while (p1 < p2 && *p1 == ' ')
1481 while (p1 < p2 && isdigit(*p1))
1483 playernumber = playernumber * 10 + (*p1 - '0');
1491 playername_start = p1;
1492 while (p1 < p2 && *p1 != '"')
1494 playername_length = p1 - playername_start;
1500 playername_start = p1;
1501 while (p1 < p2 && *p1 != ' ')
1503 playername_length = p1 - playername_start;
1505 while (p1 < p2 && *p1 == ' ')
1507 if(playername_start)
1509 // set playernumber to the right client
1511 if(playername_length >= sizeof(namebuf))
1514 Con_Print("Host_Tell: too long player name/ID\n");
1516 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1519 memcpy(namebuf, playername_start, playername_length);
1520 namebuf[playername_length] = 0;
1521 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1523 if (!svs.clients[playernumber].active)
1525 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1529 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1532 Con_Print("Host_Tell: invalid player name/ID\n");
1534 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1537 // remove trailing newlines
1538 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1540 // remove quotes if present
1546 else if (fromServer)
1547 Con_Print("Host_Tell: missing end quote\n");
1549 SV_ClientPrint("Host_Tell: missing end quote\n");
1551 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1554 return; // empty say
1555 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1561 host_client = svs.clients + playernumber;
1562 SV_ClientPrint(text);
1572 cvar_t cl_color = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1573 static void Host_Color(cmd_state_t *cmd, int changetop, int changebottom)
1575 prvm_prog_t *prog = SVVM_prog;
1576 int top, bottom, playercolor;
1578 // get top and bottom either from the provided values or the current values
1579 // (allows changing only top or bottom, or both at once)
1580 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1581 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1585 // LadyHavoc: allowing skin colormaps 14 and 15 by commenting this out
1591 playercolor = top*16 + bottom;
1593 if (cmd->source == src_command)
1595 Cvar_SetValueQuick(&cl_color, playercolor);
1599 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1602 if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
1604 Con_DPrint("Calling SV_ChangeTeam\n");
1605 prog->globals.fp[OFS_PARM0] = playercolor;
1606 PRVM_serverglobalfloat(time) = sv.time;
1607 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1608 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1612 if (host_client->edict)
1614 PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1615 PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1617 host_client->colors = playercolor;
1618 if (host_client->old_colors != host_client->colors)
1620 host_client->old_colors = host_client->colors;
1621 // send notification to all clients
1622 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1623 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1624 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1629 static void Host_Color_f(cmd_state_t *cmd)
1633 if (Cmd_Argc(cmd) == 1)
1635 if (cmd->source == src_command)
1637 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1638 Con_Print("color <0-15> [0-15]\n");
1643 if (Cmd_Argc(cmd) == 2)
1644 top = bottom = atoi(Cmd_Argv(cmd, 1));
1647 top = atoi(Cmd_Argv(cmd, 1));
1648 bottom = atoi(Cmd_Argv(cmd, 2));
1650 Host_Color(cmd, top, bottom);
1653 static void Host_TopColor_f(cmd_state_t *cmd)
1655 if (Cmd_Argc(cmd) == 1)
1657 if (cmd->source == src_command)
1659 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1660 Con_Print("topcolor <0-15>\n");
1665 Host_Color(cmd, atoi(Cmd_Argv(cmd, 1)), -1);
1668 static void Host_BottomColor_f(cmd_state_t *cmd)
1670 if (Cmd_Argc(cmd) == 1)
1672 if (cmd->source == src_command)
1674 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1675 Con_Print("bottomcolor <0-15>\n");
1680 Host_Color(cmd, -1, atoi(Cmd_Argv(cmd, 1)));
1683 cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1684 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)"};
1685 static void Host_Rate_f(cmd_state_t *cmd)
1689 if (Cmd_Argc(cmd) != 2)
1691 if (cmd->source == src_command)
1693 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1694 Con_Print("rate <bytespersecond>\n");
1699 rate = atoi(Cmd_Argv(cmd, 1));
1701 if (cmd->source == src_command)
1703 Cvar_SetValue (&cvars_all, "_cl_rate", max(NET_MINRATE, rate));
1707 host_client->rate = rate;
1709 static void Host_Rate_BurstSize_f(cmd_state_t *cmd)
1713 if (Cmd_Argc(cmd) != 2)
1715 Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
1716 Con_Print("rate_burstsize <bytes>\n");
1720 rate_burstsize = atoi(Cmd_Argv(cmd, 1));
1722 if (cmd->source == src_command)
1724 Cvar_SetValue (&cvars_all, "_cl_rate_burstsize", rate_burstsize);
1728 host_client->rate_burstsize = rate_burstsize;
1736 static void Host_Kill_f(cmd_state_t *cmd)
1738 prvm_prog_t *prog = SVVM_prog;
1739 if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
1741 SV_ClientPrint("Can't suicide -- already dead!\n");
1745 PRVM_serverglobalfloat(time) = sv.time;
1746 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1747 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
1756 static void Host_Pause_f(cmd_state_t *cmd)
1758 void (*print) (const char *fmt, ...);
1759 if (cmd->source == src_command)
1761 // if running a client, try to send over network so the pause is handled by the server
1762 if (cls.state == ca_connected)
1764 Cmd_ForwardToServer_f(cmd);
1770 print = SV_ClientPrintf;
1772 if (!pausable.integer)
1774 if (cmd->source == src_client)
1776 if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
1778 print("Pause not allowed.\n");
1785 if (cmd->source != src_command)
1786 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1787 else if(*(sv_adminnick.string))
1788 SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
1790 SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
1791 // send notification to all clients
1792 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1793 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1797 ======================
1799 LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1800 LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1801 ======================
1803 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)"};
1804 static void Host_PModel_f(cmd_state_t *cmd)
1806 prvm_prog_t *prog = SVVM_prog;
1809 if (Cmd_Argc (cmd) == 1)
1811 if (cmd->source == src_command)
1813 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1817 i = atoi(Cmd_Argv(cmd, 1));
1819 if (cmd->source == src_command)
1821 if (cl_pmodel.integer == i)
1823 Cvar_SetValue (&cvars_all, "_cl_pmodel", i);
1824 if (cls.state == ca_connected)
1825 Cmd_ForwardToServer_f(cmd);
1829 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1832 //===========================================================================
1840 static void Host_PreSpawn_f(cmd_state_t *cmd)
1842 if (host_client->prespawned)
1844 Con_Print("prespawn not valid -- already prespawned\n");
1847 host_client->prespawned = true;
1849 if (host_client->netconnection)
1851 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1852 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1853 MSG_WriteByte (&host_client->netconnection->message, 2);
1854 host_client->sendsignon = 0; // enable unlimited sends again
1857 // reset the name change timer because the client will send name soon
1858 host_client->nametime = 0;
1866 static void Host_Spawn_f(cmd_state_t *cmd)
1868 prvm_prog_t *prog = SVVM_prog;
1871 int stats[MAX_CL_STATS];
1873 if (!host_client->prespawned)
1875 Con_Print("Spawn not valid -- not yet prespawned\n");
1878 if (host_client->spawned)
1880 Con_Print("Spawn not valid -- already spawned\n");
1883 host_client->spawned = true;
1885 // reset name change timer again because they might want to change name
1886 // again in the first 5 seconds after connecting
1887 host_client->nametime = 0;
1889 // LadyHavoc: moved this above the QC calls at FrikaC's request
1890 // LadyHavoc: commented this out
1891 //if (host_client->netconnection)
1892 // SZ_Clear (&host_client->netconnection->message);
1894 // run the entrance script
1897 // loaded games are fully initialized already
1898 if (PRVM_serverfunction(RestoreGame))
1900 Con_DPrint("Calling RestoreGame\n");
1901 PRVM_serverglobalfloat(time) = sv.time;
1902 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1903 prog->ExecuteProgram(prog, PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1908 //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);
1910 // copy spawn parms out of the client_t
1911 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1912 (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
1914 // call the spawn function
1915 host_client->clientconnectcalled = true;
1916 PRVM_serverglobalfloat(time) = sv.time;
1917 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1918 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1920 if (cls.state == ca_dedicated)
1921 Con_Printf("%s connected\n", host_client->name);
1923 PRVM_serverglobalfloat(time) = sv.time;
1924 prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1927 if (!host_client->netconnection)
1930 // send time of update
1931 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1932 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1934 // send all current names, colors, and frag counts
1935 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1937 if (!client->active)
1939 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1940 MSG_WriteByte (&host_client->netconnection->message, i);
1941 MSG_WriteString (&host_client->netconnection->message, client->name);
1942 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1943 MSG_WriteByte (&host_client->netconnection->message, i);
1944 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1945 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1946 MSG_WriteByte (&host_client->netconnection->message, i);
1947 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1950 // send all current light styles
1951 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1953 if (sv.lightstyles[i][0])
1955 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1956 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1957 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1962 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1963 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1964 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1966 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1967 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1968 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1970 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1971 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1972 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1974 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1975 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1976 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
1979 // Never send a roll angle, because savegames can catch the server
1980 // in a state where it is expecting the client to correct the angle
1981 // and it won't happen if the game was just loaded, so you wind up
1982 // with a permanent head tilt
1985 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1986 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
1987 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
1988 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1992 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1993 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
1994 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
1995 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1998 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
2000 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
2001 MSG_WriteByte (&host_client->netconnection->message, 3);
2009 static void Host_Begin_f(cmd_state_t *cmd)
2011 if (!host_client->spawned)
2013 Con_Print("Begin not valid -- not yet spawned\n");
2016 if (host_client->begun)
2018 Con_Print("Begin not valid -- already begun\n");
2021 host_client->begun = true;
2023 // LadyHavoc: note: this code also exists in SV_DropClient
2027 for (i = 0;i < svs.maxclients;i++)
2028 if (svs.clients[i].active && !svs.clients[i].spawned)
2030 if (i == svs.maxclients)
2032 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
2033 sv.paused = sv.loadgame = false; // we're basically done with loading now
2038 //===========================================================================
2045 Kicks a user off of the server
2048 static void Host_Kick_f(cmd_state_t *cmd)
2051 const char *message = NULL;
2054 qboolean byNumber = false;
2061 if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
2063 i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
2064 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
2070 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2072 if (!host_client->active)
2074 if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
2079 if (i < svs.maxclients)
2081 if (cmd->source == src_command)
2083 if (cls.state == ca_dedicated)
2086 who = cl_name.string;
2091 // can't kick yourself!
2092 if (host_client == save)
2095 if (Cmd_Argc(cmd) > 2)
2097 message = Cmd_Args(cmd);
2098 COM_ParseToken_Simple(&message, false, false, true);
2101 message++; // skip the #
2102 while (*message == ' ') // skip white space
2104 message += strlen(Cmd_Argv(cmd, 2)); // skip the number
2106 while (*message && *message == ' ')
2110 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2112 SV_ClientPrintf("Kicked by %s\n", who);
2113 SV_DropClient (false); // kicked
2120 ===============================================================================
2124 ===============================================================================
2132 static void Host_Give_f(cmd_state_t *cmd)
2134 prvm_prog_t *prog = SVVM_prog;
2138 if (!sv_cheats.integer)
2140 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2144 t = Cmd_Argv(cmd, 1);
2145 v = atoi (Cmd_Argv(cmd, 2));
2159 // MED 01/04/97 added hipnotic give stuff
2160 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
2165 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2167 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2169 else if (t[0] == '9')
2170 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2171 else if (t[0] == '0')
2172 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2173 else if (t[0] >= '2')
2174 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2179 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2184 if (gamemode == GAME_ROGUE)
2185 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2187 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2190 if (gamemode == GAME_ROGUE)
2192 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2193 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2194 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2198 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2202 if (gamemode == GAME_ROGUE)
2204 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2205 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2206 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2210 if (gamemode == GAME_ROGUE)
2212 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2213 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2214 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2218 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2222 if (gamemode == GAME_ROGUE)
2224 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2225 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2226 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2230 PRVM_serveredictfloat(host_client->edict, health) = v;
2233 if (gamemode == GAME_ROGUE)
2235 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2236 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2237 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2241 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2245 if (gamemode == GAME_ROGUE)
2247 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2248 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2249 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2255 static prvm_edict_t *FindViewthing(prvm_prog_t *prog)
2260 for (i=0 ; i<prog->num_edicts ; i++)
2262 e = PRVM_EDICT_NUM(i);
2263 if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
2266 Con_Print("No viewthing on map\n");
2275 static void Host_Viewmodel_f(cmd_state_t *cmd)
2277 prvm_prog_t *prog = SVVM_prog;
2284 e = FindViewthing(prog);
2287 m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
2288 if (m && m->loaded && m->Draw)
2290 PRVM_serveredictfloat(e, frame) = 0;
2291 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2294 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
2303 static void Host_Viewframe_f(cmd_state_t *cmd)
2305 prvm_prog_t *prog = SVVM_prog;
2313 e = FindViewthing(prog);
2316 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2318 f = atoi(Cmd_Argv(cmd, 1));
2319 if (f >= m->numframes)
2322 PRVM_serveredictfloat(e, frame) = f;
2327 static void PrintFrameName (dp_model_t *m, int frame)
2330 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2332 Con_Printf("frame %i\n", frame);
2340 static void Host_Viewnext_f(cmd_state_t *cmd)
2342 prvm_prog_t *prog = SVVM_prog;
2349 e = FindViewthing(prog);
2352 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2354 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2355 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2356 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2358 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2367 static void Host_Viewprev_f(cmd_state_t *cmd)
2369 prvm_prog_t *prog = SVVM_prog;
2376 e = FindViewthing(prog);
2379 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2381 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2382 if (PRVM_serveredictfloat(e, frame) < 0)
2383 PRVM_serveredictfloat(e, frame) = 0;
2385 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2390 ===============================================================================
2394 ===============================================================================
2403 static void Host_Startdemos_f(cmd_state_t *cmd)
2407 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2410 c = Cmd_Argc(cmd) - 1;
2413 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2416 Con_DPrintf("%i demo(s) in loop\n", c);
2418 for (i=1 ; i<c+1 ; i++)
2419 strlcpy (cls.demos[i-1], Cmd_Argv(cmd, i), sizeof (cls.demos[i-1]));
2421 // LadyHavoc: clear the remaining slots
2422 for (;i <= MAX_DEMOS;i++)
2423 cls.demos[i-1][0] = 0;
2425 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2439 Return to looping demos
2442 static void Host_Demos_f(cmd_state_t *cmd)
2444 if (cls.state == ca_dedicated)
2446 if (cls.demonum == -1)
2448 CL_Disconnect_f (cmd);
2456 Return to looping demos
2459 static void Host_Stopdemo_f(cmd_state_t *cmd)
2461 if (!cls.demoplayback)
2464 Host_ShutdownServer ();
2467 static void Host_SendCvar_f(cmd_state_t *cmd)
2471 const char *cvarname;
2475 if(Cmd_Argc(cmd) != 2)
2477 cvarname = Cmd_Argv(cmd, 1);
2478 if (cls.state == ca_connected)
2480 c = Cvar_FindVar(&cvars_all, cvarname, CVAR_CLIENT | CVAR_SERVER);
2481 // LadyHavoc: if there is no such cvar or if it is private, send a
2482 // reply indicating that it has no value
2483 if(!c || (c->flags & CVAR_PRIVATE))
2484 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname));
2486 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string));
2489 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2493 if (cls.state != ca_dedicated)
2497 for(;i<svs.maxclients;i++)
2498 if(svs.clients[i].active && svs.clients[i].netconnection)
2500 host_client = &svs.clients[i];
2501 Host_ClientCommands("sendcvar %s\n", cvarname);
2506 static void MaxPlayers_f(cmd_state_t *cmd)
2510 if (Cmd_Argc(cmd) != 2)
2512 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2518 Con_Print("maxplayers can not be changed while a server is running.\n");
2519 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2522 n = atoi(Cmd_Argv(cmd, 1));
2523 n = bound(1, n, MAX_SCOREBOARD);
2524 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2526 svs.maxclients_next = n;
2528 Cvar_Set (&cvars_all, "deathmatch", "0");
2530 Cvar_Set (&cvars_all, "deathmatch", "1");
2534 =====================
2537 ProQuake rcon support
2538 =====================
2540 static void Host_PQRcon_f(cmd_state_t *cmd)
2544 lhnetsocket_t *mysocket;
2546 if (Cmd_Argc(cmd) == 1)
2548 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2552 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2554 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2558 e = strchr(rcon_password.string, ' ');
2559 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2562 cls.rcon_address = cls.netcon->peeraddress;
2565 if (!rcon_address.string[0])
2567 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2570 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2572 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2576 unsigned char bufdata[64];
2579 MSG_WriteLong(&buf, 0);
2580 MSG_WriteByte(&buf, CCREQ_RCON);
2581 SZ_Write(&buf, (const unsigned char*)rcon_password.string, n);
2582 MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string
2583 MSG_WriteString(&buf, Cmd_Args(cmd));
2584 StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK));
2585 NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address);
2590 //=============================================================================
2592 // QuakeWorld commands
2595 =====================
2598 Send the rest of the command line over as
2599 an unconnected command.
2600 =====================
2602 static void Host_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2606 lhnetsocket_t *mysocket;
2608 if (Cmd_Argc(cmd) == 1)
2610 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2614 if (!rcon_password.string || !rcon_password.string[0])
2616 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2620 e = strchr(rcon_password.string, ' ');
2621 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2624 cls.rcon_address = cls.netcon->peeraddress;
2627 if (!rcon_address.string[0])
2629 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2632 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2634 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2635 if (mysocket && Cmd_Args(cmd)[0])
2637 // simply put together the rcon packet and send it
2638 if(Cmd_Argv(cmd, 0)[0] == 's' || rcon_secure.integer > 1)
2640 if(cls.rcon_commands[cls.rcon_ringpos][0])
2643 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2644 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]);
2645 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2648 for (i = 0;i < MAX_RCONS;i++)
2649 if(cls.rcon_commands[i][0])
2650 if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i]))
2654 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later
2655 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2656 cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address;
2657 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2658 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2660 else if(rcon_secure.integer > 0)
2664 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args(cmd));
2665 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2666 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2669 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2670 NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address);
2676 memcpy(buf, "\377\377\377\377", 4);
2677 dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s", n, rcon_password.string, Cmd_Args(cmd));
2678 NetConn_WriteString(mysocket, buf, &cls.rcon_address);
2684 ====================
2687 user <name or userid>
2689 Dump userdata / masterdata for a user
2690 ====================
2692 static void Host_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2697 if (Cmd_Argc(cmd) != 2)
2699 Con_Printf ("Usage: user <username / userid>\n");
2703 uid = atoi(Cmd_Argv(cmd, 1));
2705 for (i = 0;i < cl.maxclients;i++)
2707 if (!cl.scores[i].name[0])
2709 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
2711 InfoString_Print(cl.scores[i].qw_userinfo);
2715 Con_Printf ("User not in server.\n");
2719 ====================
2722 Dump userids for all current players
2723 ====================
2725 static void Host_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2731 Con_Printf ("userid frags name\n");
2732 Con_Printf ("------ ----- ----\n");
2733 for (i = 0;i < cl.maxclients;i++)
2735 if (cl.scores[i].name[0])
2737 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2742 Con_Printf ("%i total users\n", c);
2747 Host_FullServerinfo_f
2749 Sent by server when serverinfo changes
2752 // TODO: shouldn't this be a cvar instead?
2753 static void Host_FullServerinfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2756 if (Cmd_Argc(cmd) != 2)
2758 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2762 strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo));
2763 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2764 cl.qw_teamplay = atoi(temp);
2771 Allow clients to change userinfo
2775 static void Host_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2781 if (Cmd_Argc(cmd) != 2)
2783 Con_Printf ("fullinfo <complete info string>\n");
2787 s = Cmd_Argv(cmd, 1);
2792 size_t len = strcspn(s, "\\");
2793 if (len >= sizeof(key)) {
2794 len = sizeof(key) - 1;
2796 strlcpy(key, s, len + 1);
2800 Con_Printf ("MISSING VALUE\n");
2803 ++s; // Skip over backslash.
2805 len = strcspn(s, "\\");
2806 if (len >= sizeof(value)) {
2807 len = sizeof(value) - 1;
2809 strlcpy(value, s, len + 1);
2811 CL_SetInfo(key, value, false, false, false, false);
2818 ++s; // Skip over backslash.
2826 Allow clients to change userinfo
2829 static void Host_SetInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2831 if (Cmd_Argc(cmd) == 1)
2833 InfoString_Print(cls.userinfo);
2836 if (Cmd_Argc(cmd) != 3)
2838 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2841 CL_SetInfo(Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), true, false, false, false);
2845 ====================
2848 packet <destination> <contents>
2850 Contents allows \n escape character
2851 ====================
2853 static void Host_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2859 lhnetaddress_t address;
2860 lhnetsocket_t *mysocket;
2862 if (Cmd_Argc(cmd) != 3)
2864 Con_Printf ("packet <destination> <contents>\n");
2868 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(cmd, 1), sv_netport.integer))
2870 Con_Printf ("Bad address\n");
2874 in = Cmd_Argv(cmd, 2);
2876 send[0] = send[1] = send[2] = send[3] = -1;
2878 l = (int)strlen (in);
2879 for (i=0 ; i<l ; i++)
2881 if (out >= send + sizeof(send) - 1)
2883 if (in[i] == '\\' && in[i+1] == 'n')
2888 else if (in[i] == '\\' && in[i+1] == '0')
2893 else if (in[i] == '\\' && in[i+1] == 't')
2898 else if (in[i] == '\\' && in[i+1] == 'r')
2903 else if (in[i] == '\\' && in[i+1] == '"')
2912 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2914 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2916 NetConn_Write(mysocket, send, out - send, &address);
2920 ====================
2923 Send back ping and packet loss update for all current players to this player
2924 ====================
2926 static void Host_Pings_f(cmd_state_t *cmd)
2928 int i, j, ping, packetloss, movementloss;
2931 if (!host_client->netconnection)
2934 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2936 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2937 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2939 for (i = 0;i < svs.maxclients;i++)
2943 if (svs.clients[i].netconnection)
2945 for (j = 0;j < NETGRAPH_PACKETS;j++)
2946 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2948 for (j = 0;j < NETGRAPH_PACKETS;j++)
2949 if (svs.clients[i].movement_count[j] < 0)
2952 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2953 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2954 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2955 ping = bound(0, ping, 9999);
2956 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2958 // send qw_svc_updateping and qw_svc_updatepl messages
2959 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2960 MSG_WriteShort(&host_client->netconnection->message, ping);
2961 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2962 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2966 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2968 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2970 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2971 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2974 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2975 MSG_WriteString(&host_client->netconnection->message, "\n");
2978 static void Host_PingPLReport_f(cmd_state_t *cmd)
2982 int l = Cmd_Argc(cmd);
2983 if (l > cl.maxclients)
2985 for (i = 0;i < l;i++)
2987 cl.scores[i].qw_ping = atoi(Cmd_Argv(cmd, 1+i*2));
2988 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(cmd, 1+i*2+1), &errbyte, 0);
2989 if(errbyte && *errbyte == ',')
2990 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2992 cl.scores[i].qw_movementloss = 0;
2996 //=============================================================================
3003 void Host_InitCommands (void)
3005 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
3007 Cvar_RegisterVariable(&cl_name);
3008 Cvar_RegisterVariable(&cl_color);
3009 Cvar_RegisterVariable(&cl_rate);
3010 Cvar_RegisterVariable(&cl_rate_burstsize);
3011 Cvar_RegisterVariable(&cl_pmodel);
3012 Cvar_RegisterVariable(&cl_playermodel);
3013 Cvar_RegisterVariable(&cl_playerskin);
3014 Cvar_RegisterVariable(&rcon_password);
3015 Cvar_RegisterVariable(&rcon_address);
3016 Cvar_RegisterVariable(&rcon_secure);
3017 Cvar_RegisterVariable(&rcon_secure_challengetimeout);
3018 Cvar_RegisterVariable(&r_fixtrans_auto);
3019 Cvar_RegisterVariable(&team);
3020 Cvar_RegisterVariable(&skin);
3021 Cvar_RegisterVariable(&noaim);
3022 Cvar_RegisterVariable(&sv_cheats);
3023 Cvar_RegisterVariable(&sv_adminnick);
3024 Cvar_RegisterVariable(&sv_status_privacy);
3025 Cvar_RegisterVariable(&sv_status_show_qcstatus);
3026 Cvar_RegisterVariable(&sv_namechangetimer);
3028 // client commands - this includes server commands because the client can host a server, so they must exist
3029 Cmd_AddCommand(&cmd_client, "quit", Host_Quit_f, "quit the game");
3030 Cmd_AddCommand(&cmd_client, "status", Host_Status_f, "print server status information");
3031 Cmd_AddCommand(&cmd_client, "map", Host_Map_f, "kick everyone off the server and start a new level");
3032 Cmd_AddCommand(&cmd_client, "restart", Host_Restart_f, "restart current level");
3033 Cmd_AddCommand(&cmd_client, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3034 Cmd_AddCommand(&cmd_client, "version", Host_Version_f, "print engine version");
3035 Cmd_AddCommand(&cmd_client, "say", Host_Say_f, "send a chat message to everyone on the server");
3036 Cmd_AddCommand(&cmd_client, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3037 Cmd_AddCommand(&cmd_client, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3038 Cmd_AddCommand(&cmd_client, "kick", Host_Kick_f, "kick a player off the server by number or name");
3039 Cmd_AddCommand(&cmd_client, "ping", Host_Ping_f, "print ping times of all players on the server");
3040 Cmd_AddCommand(&cmd_client, "load", Host_Loadgame_f, "load a saved game file");
3041 Cmd_AddCommand(&cmd_client, "save", Host_Savegame_f, "save the game to a file");
3042 Cmd_AddCommand(&cmd_client, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3043 Cmd_AddCommand(&cmd_client, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3044 Cmd_AddCommand(&cmd_client, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3045 Cmd_AddCommand(&cmd_client, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3046 Cmd_AddCommand(&cmd_client, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3047 Cmd_AddCommand(&cmd_client, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3048 Cmd_AddCommand(&cmd_client, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3050 // dedicated server commands
3051 Cmd_AddCommand(&cmd_server, "quit", Host_Quit_f, "quit the game");
3052 Cmd_AddCommand(&cmd_server, "status", Host_Status_f, "print server status information");
3053 Cmd_AddCommand(&cmd_server, "map", Host_Map_f, "kick everyone off the server and start a new level");
3054 Cmd_AddCommand(&cmd_server, "restart", Host_Restart_f, "restart current level");
3055 Cmd_AddCommand(&cmd_server, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3056 Cmd_AddCommand(&cmd_server, "version", Host_Version_f, "print engine version");
3057 Cmd_AddCommand(&cmd_server, "say", Host_Say_f, "send a chat message to everyone on the server");
3058 Cmd_AddCommand(&cmd_server, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3059 Cmd_AddCommand(&cmd_server, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3060 Cmd_AddCommand(&cmd_server, "kick", Host_Kick_f, "kick a player off the server by number or name");
3061 Cmd_AddCommand(&cmd_server, "ping", Host_Ping_f, "print ping times of all players on the server");
3062 Cmd_AddCommand(&cmd_server, "load", Host_Loadgame_f, "load a saved game file");
3063 Cmd_AddCommand(&cmd_server, "save", Host_Savegame_f, "save the game to a file");
3064 Cmd_AddCommand(&cmd_server, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3065 Cmd_AddCommand(&cmd_server, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3066 Cmd_AddCommand(&cmd_server, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3067 Cmd_AddCommand(&cmd_server, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3068 Cmd_AddCommand(&cmd_server, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3069 Cmd_AddCommand(&cmd_server, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3070 Cmd_AddCommand(&cmd_server, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3072 // 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)
3073 Cmd_AddCommand(&cmd_serverfromclient, "prespawn", Host_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
3074 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)");
3075 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)");
3076 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)");
3078 Cmd_AddCommand(&cmd_serverfromclient, "status", Host_Status_f, "print server status information");
3079 Cmd_AddCommand(&cmd_serverfromclient, "god", Host_God_f, "god mode (invulnerability)");
3080 Cmd_AddCommand(&cmd_serverfromclient, "notarget", Host_Notarget_f, "notarget mode (monsters do not see you)");
3081 Cmd_AddCommand(&cmd_serverfromclient, "fly", Host_Fly_f, "fly mode (flight)");
3082 Cmd_AddCommand(&cmd_serverfromclient, "noclip", Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
3083 Cmd_AddCommand(&cmd_serverfromclient, "give", Host_Give_f, "alter inventory");
3084 Cmd_AddCommand(&cmd_serverfromclient, "say", Host_Say_f, "send a chat message to everyone on the server");
3085 Cmd_AddCommand(&cmd_serverfromclient, "say_team", Host_Say_Team_f, "send a chat message to your team on the server");
3086 Cmd_AddCommand(&cmd_serverfromclient, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3087 Cmd_AddCommand(&cmd_serverfromclient, "kill", Host_Kill_f, "die instantly");
3088 Cmd_AddCommand(&cmd_serverfromclient, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3089 Cmd_AddCommand(&cmd_serverfromclient, "ping", Host_Ping_f, "print ping times of all players on the server");
3090 Cmd_AddCommand(&cmd_serverfromclient, "name", Host_Name_f, "change your player name");
3091 Cmd_AddCommand(&cmd_serverfromclient, "color", Host_Color_f, "change your player shirt and pants colors");
3092 Cmd_AddCommand(&cmd_serverfromclient, "rate", Host_Rate_f, "change your network connection speed");
3093 Cmd_AddCommand(&cmd_serverfromclient, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3094 Cmd_AddCommand(&cmd_serverfromclient, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3095 Cmd_AddCommand(&cmd_serverfromclient, "playermodel", Host_Playermodel_f, "change your player model");
3096 Cmd_AddCommand(&cmd_serverfromclient, "playerskin", Host_Playerskin_f, "change your player skin number");
3098 // client commands that require a connection and are simply forwarded to server
3099 Cmd_AddCommand(&cmd_client, "god", Cmd_ForwardToServer_f, "god mode (invulnerability)");
3100 Cmd_AddCommand(&cmd_client, "notarget", Cmd_ForwardToServer_f, "notarget mode (monsters do not see you)");
3101 Cmd_AddCommand(&cmd_client, "fly", Cmd_ForwardToServer_f, "fly mode (flight)");
3102 Cmd_AddCommand(&cmd_client, "noclip", Cmd_ForwardToServer_f, "noclip mode (flight without collisions, move through walls)");
3103 Cmd_AddCommand(&cmd_client, "give", Cmd_ForwardToServer_f, "alter inventory");
3104 Cmd_AddCommand(&cmd_client, "say_team", Cmd_ForwardToServer_f, "send a chat message to your team on the server");
3105 Cmd_AddCommand(&cmd_client, "kill", Cmd_ForwardToServer_f, "die instantly");
3107 Cmd_AddCommand(&cmd_client, "connect", Host_Connect_f, "connect to a server by IP address or hostname");
3108 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)");
3109 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)");
3110 Cmd_AddCommand(&cmd_client, "startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
3111 Cmd_AddCommand(&cmd_client, "demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
3112 Cmd_AddCommand(&cmd_client, "stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
3113 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");
3114 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");
3115 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");
3116 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)");
3117 Cmd_AddCommand(&cmd_client, "fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
3118 Cmd_AddCommand(&cmd_client, "setinfo", Host_SetInfo_f, "modifies your userinfo");
3119 Cmd_AddCommand(&cmd_client, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3120 Cmd_AddCommand(&cmd_clientfromserver, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3121 Cmd_AddCommand(&cmd_client, "topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
3122 Cmd_AddCommand(&cmd_client, "bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
3123 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)");
3125 // client commands that also exist as cmd_serverfromclient and are often forwarded
3126 Cmd_AddCommand(&cmd_client, "name", Host_Name_f, "change your player name");
3127 Cmd_AddCommand(&cmd_client, "color", Host_Color_f, "change your player shirt and pants colors");
3128 Cmd_AddCommand(&cmd_client, "rate", Host_Rate_f, "change your network connection speed");
3129 Cmd_AddCommand(&cmd_client, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3130 Cmd_AddCommand(&cmd_client, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3131 Cmd_AddCommand(&cmd_client, "playermodel", Host_Playermodel_f, "change your player model");
3132 Cmd_AddCommand(&cmd_client, "playerskin", Host_Playerskin_f, "change your player skin number");
3134 // commands that are only sent by server to client for execution
3135 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)");
3136 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");
3139 void Host_NoOperation_f(cmd_state_t *cmd)