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 // optional entity parameter for self (EXT_ENTITYPARAM)
1607 PRVM_G_INT(OFS_PARM1) = PRVM_EDICT_TO_PROG(host_client->edict);
1608 PRVM_serverglobalfloat(time) = sv.time;
1609 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1610 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1614 if (host_client->edict)
1616 PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1617 PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1619 host_client->colors = playercolor;
1620 if (host_client->old_colors != host_client->colors)
1622 host_client->old_colors = host_client->colors;
1623 // send notification to all clients
1624 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1625 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1626 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1631 static void Host_Color_f(cmd_state_t *cmd)
1635 if (Cmd_Argc(cmd) == 1)
1637 if (cmd->source == src_command)
1639 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1640 Con_Print("color <0-15> [0-15]\n");
1645 if (Cmd_Argc(cmd) == 2)
1646 top = bottom = atoi(Cmd_Argv(cmd, 1));
1649 top = atoi(Cmd_Argv(cmd, 1));
1650 bottom = atoi(Cmd_Argv(cmd, 2));
1652 Host_Color(cmd, top, bottom);
1655 static void Host_TopColor_f(cmd_state_t *cmd)
1657 if (Cmd_Argc(cmd) == 1)
1659 if (cmd->source == src_command)
1661 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1662 Con_Print("topcolor <0-15>\n");
1667 Host_Color(cmd, atoi(Cmd_Argv(cmd, 1)), -1);
1670 static void Host_BottomColor_f(cmd_state_t *cmd)
1672 if (Cmd_Argc(cmd) == 1)
1674 if (cmd->source == src_command)
1676 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1677 Con_Print("bottomcolor <0-15>\n");
1682 Host_Color(cmd, -1, atoi(Cmd_Argv(cmd, 1)));
1685 cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1686 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)"};
1687 static void Host_Rate_f(cmd_state_t *cmd)
1691 if (Cmd_Argc(cmd) != 2)
1693 if (cmd->source == src_command)
1695 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1696 Con_Print("rate <bytespersecond>\n");
1701 rate = atoi(Cmd_Argv(cmd, 1));
1703 if (cmd->source == src_command)
1705 Cvar_SetValue (&cvars_all, "_cl_rate", max(NET_MINRATE, rate));
1709 host_client->rate = rate;
1711 static void Host_Rate_BurstSize_f(cmd_state_t *cmd)
1715 if (Cmd_Argc(cmd) != 2)
1717 Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
1718 Con_Print("rate_burstsize <bytes>\n");
1722 rate_burstsize = atoi(Cmd_Argv(cmd, 1));
1724 if (cmd->source == src_command)
1726 Cvar_SetValue (&cvars_all, "_cl_rate_burstsize", rate_burstsize);
1730 host_client->rate_burstsize = rate_burstsize;
1738 static void Host_Kill_f(cmd_state_t *cmd)
1740 prvm_prog_t *prog = SVVM_prog;
1741 if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
1743 SV_ClientPrint("Can't suicide -- already dead!\n");
1747 PRVM_serverglobalfloat(time) = sv.time;
1748 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1749 // optional entity parameter for self (EXT_ENTITYPARAM)
1750 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(host_client->edict);
1751 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
1760 static void Host_Pause_f(cmd_state_t *cmd)
1762 void (*print) (const char *fmt, ...);
1763 if (cmd->source == src_command)
1765 // if running a client, try to send over network so the pause is handled by the server
1766 if (cls.state == ca_connected)
1768 Cmd_ForwardToServer_f(cmd);
1774 print = SV_ClientPrintf;
1776 if (!pausable.integer)
1778 if (cmd->source == src_client)
1780 if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
1782 print("Pause not allowed.\n");
1789 if (cmd->source != src_command)
1790 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1791 else if(*(sv_adminnick.string))
1792 SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
1794 SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
1795 // send notification to all clients
1796 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1797 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1801 ======================
1803 LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1804 LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1805 ======================
1807 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)"};
1808 static void Host_PModel_f(cmd_state_t *cmd)
1810 prvm_prog_t *prog = SVVM_prog;
1813 if (Cmd_Argc (cmd) == 1)
1815 if (cmd->source == src_command)
1817 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1821 i = atoi(Cmd_Argv(cmd, 1));
1823 if (cmd->source == src_command)
1825 if (cl_pmodel.integer == i)
1827 Cvar_SetValue (&cvars_all, "_cl_pmodel", i);
1828 if (cls.state == ca_connected)
1829 Cmd_ForwardToServer_f(cmd);
1833 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1836 //===========================================================================
1844 static void Host_PreSpawn_f(cmd_state_t *cmd)
1846 if (host_client->prespawned)
1848 Con_Print("prespawn not valid -- already prespawned\n");
1851 host_client->prespawned = true;
1853 if (host_client->netconnection)
1855 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1856 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1857 MSG_WriteByte (&host_client->netconnection->message, 2);
1858 host_client->sendsignon = 0; // enable unlimited sends again
1861 // reset the name change timer because the client will send name soon
1862 host_client->nametime = 0;
1870 static void Host_Spawn_f(cmd_state_t *cmd)
1872 prvm_prog_t *prog = SVVM_prog;
1875 int stats[MAX_CL_STATS];
1877 if (!host_client->prespawned)
1879 Con_Print("Spawn not valid -- not yet prespawned\n");
1882 if (host_client->spawned)
1884 Con_Print("Spawn not valid -- already spawned\n");
1887 host_client->spawned = true;
1889 // reset name change timer again because they might want to change name
1890 // again in the first 5 seconds after connecting
1891 host_client->nametime = 0;
1893 // LadyHavoc: moved this above the QC calls at FrikaC's request
1894 // LadyHavoc: commented this out
1895 //if (host_client->netconnection)
1896 // SZ_Clear (&host_client->netconnection->message);
1898 // run the entrance script
1901 // loaded games are fully initialized already
1902 if (PRVM_serverfunction(RestoreGame))
1904 Con_DPrint("Calling RestoreGame\n");
1905 PRVM_serverglobalfloat(time) = sv.time;
1906 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1907 // optional entity parameter for self (EXT_ENTITYPARAM)
1908 PRVM_G_INT(OFS_PARM0) = 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 // optional entity parameter for self (EXT_ENTITYPARAM)
1925 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(host_client->edict);
1926 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1928 if (cls.state == ca_dedicated)
1929 Con_Printf("%s connected\n", host_client->name);
1931 PRVM_serverglobalfloat(time) = sv.time;
1932 // optional entity parameter for self (EXT_ENTITYPARAM)
1933 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(host_client->edict);
1934 prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1937 if (!host_client->netconnection)
1940 // send time of update
1941 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1942 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1944 // send all current names, colors, and frag counts
1945 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1947 if (!client->active)
1949 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1950 MSG_WriteByte (&host_client->netconnection->message, i);
1951 MSG_WriteString (&host_client->netconnection->message, client->name);
1952 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1953 MSG_WriteByte (&host_client->netconnection->message, i);
1954 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1955 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1956 MSG_WriteByte (&host_client->netconnection->message, i);
1957 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1960 // send all current light styles
1961 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1963 if (sv.lightstyles[i][0])
1965 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1966 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1967 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1972 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1973 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1974 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1976 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1977 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1978 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1980 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1981 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1982 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1984 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1985 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1986 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
1989 // Never send a roll angle, because savegames can catch the server
1990 // in a state where it is expecting the client to correct the angle
1991 // and it won't happen if the game was just loaded, so you wind up
1992 // with a permanent head tilt
1995 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1996 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
1997 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
1998 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
2002 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
2003 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
2004 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
2005 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
2008 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
2010 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
2011 MSG_WriteByte (&host_client->netconnection->message, 3);
2019 static void Host_Begin_f(cmd_state_t *cmd)
2021 if (!host_client->spawned)
2023 Con_Print("Begin not valid -- not yet spawned\n");
2026 if (host_client->begun)
2028 Con_Print("Begin not valid -- already begun\n");
2031 host_client->begun = true;
2033 // LadyHavoc: note: this code also exists in SV_DropClient
2037 for (i = 0;i < svs.maxclients;i++)
2038 if (svs.clients[i].active && !svs.clients[i].spawned)
2040 if (i == svs.maxclients)
2042 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
2043 sv.paused = sv.loadgame = false; // we're basically done with loading now
2048 //===========================================================================
2055 Kicks a user off of the server
2058 static void Host_Kick_f(cmd_state_t *cmd)
2061 const char *message = NULL;
2064 qboolean byNumber = false;
2071 if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
2073 i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
2074 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
2080 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2082 if (!host_client->active)
2084 if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
2089 if (i < svs.maxclients)
2091 if (cmd->source == src_command)
2093 if (cls.state == ca_dedicated)
2096 who = cl_name.string;
2101 // can't kick yourself!
2102 if (host_client == save)
2105 if (Cmd_Argc(cmd) > 2)
2107 message = Cmd_Args(cmd);
2108 COM_ParseToken_Simple(&message, false, false, true);
2111 message++; // skip the #
2112 while (*message == ' ') // skip white space
2114 message += strlen(Cmd_Argv(cmd, 2)); // skip the number
2116 while (*message && *message == ' ')
2120 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2122 SV_ClientPrintf("Kicked by %s\n", who);
2123 SV_DropClient (false); // kicked
2130 ===============================================================================
2134 ===============================================================================
2142 static void Host_Give_f(cmd_state_t *cmd)
2144 prvm_prog_t *prog = SVVM_prog;
2148 if (!sv_cheats.integer)
2150 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2154 t = Cmd_Argv(cmd, 1);
2155 v = atoi (Cmd_Argv(cmd, 2));
2169 // MED 01/04/97 added hipnotic give stuff
2170 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
2175 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2177 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2179 else if (t[0] == '9')
2180 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2181 else if (t[0] == '0')
2182 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2183 else if (t[0] >= '2')
2184 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2189 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2194 if (gamemode == GAME_ROGUE)
2195 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2197 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2200 if (gamemode == GAME_ROGUE)
2202 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2203 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2204 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2208 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2212 if (gamemode == GAME_ROGUE)
2214 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2215 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2216 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2220 if (gamemode == GAME_ROGUE)
2222 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2223 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2224 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2228 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2232 if (gamemode == GAME_ROGUE)
2234 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2235 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2236 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2240 PRVM_serveredictfloat(host_client->edict, health) = v;
2243 if (gamemode == GAME_ROGUE)
2245 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2246 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2247 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2251 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2255 if (gamemode == GAME_ROGUE)
2257 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2258 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2259 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2265 static prvm_edict_t *FindViewthing(prvm_prog_t *prog)
2270 for (i=0 ; i<prog->num_edicts ; i++)
2272 e = PRVM_EDICT_NUM(i);
2273 if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
2276 Con_Print("No viewthing on map\n");
2285 static void Host_Viewmodel_f(cmd_state_t *cmd)
2287 prvm_prog_t *prog = SVVM_prog;
2294 e = FindViewthing(prog);
2297 m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
2298 if (m && m->loaded && m->Draw)
2300 PRVM_serveredictfloat(e, frame) = 0;
2301 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2304 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
2313 static void Host_Viewframe_f(cmd_state_t *cmd)
2315 prvm_prog_t *prog = SVVM_prog;
2323 e = FindViewthing(prog);
2326 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2328 f = atoi(Cmd_Argv(cmd, 1));
2329 if (f >= m->numframes)
2332 PRVM_serveredictfloat(e, frame) = f;
2337 static void PrintFrameName (dp_model_t *m, int frame)
2340 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2342 Con_Printf("frame %i\n", frame);
2350 static void Host_Viewnext_f(cmd_state_t *cmd)
2352 prvm_prog_t *prog = SVVM_prog;
2359 e = FindViewthing(prog);
2362 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2364 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2365 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2366 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2368 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2377 static void Host_Viewprev_f(cmd_state_t *cmd)
2379 prvm_prog_t *prog = SVVM_prog;
2386 e = FindViewthing(prog);
2389 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2391 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2392 if (PRVM_serveredictfloat(e, frame) < 0)
2393 PRVM_serveredictfloat(e, frame) = 0;
2395 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2400 ===============================================================================
2404 ===============================================================================
2413 static void Host_Startdemos_f(cmd_state_t *cmd)
2417 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2420 c = Cmd_Argc(cmd) - 1;
2423 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2426 Con_DPrintf("%i demo(s) in loop\n", c);
2428 for (i=1 ; i<c+1 ; i++)
2429 strlcpy (cls.demos[i-1], Cmd_Argv(cmd, i), sizeof (cls.demos[i-1]));
2431 // LadyHavoc: clear the remaining slots
2432 for (;i <= MAX_DEMOS;i++)
2433 cls.demos[i-1][0] = 0;
2435 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2449 Return to looping demos
2452 static void Host_Demos_f(cmd_state_t *cmd)
2454 if (cls.state == ca_dedicated)
2456 if (cls.demonum == -1)
2458 CL_Disconnect_f (cmd);
2466 Return to looping demos
2469 static void Host_Stopdemo_f(cmd_state_t *cmd)
2471 if (!cls.demoplayback)
2474 Host_ShutdownServer ();
2477 static void Host_SendCvar_f(cmd_state_t *cmd)
2481 const char *cvarname;
2485 if(Cmd_Argc(cmd) != 2)
2487 cvarname = Cmd_Argv(cmd, 1);
2488 if (cls.state == ca_connected)
2490 c = Cvar_FindVar(&cvars_all, cvarname, CVAR_CLIENT | CVAR_SERVER);
2491 // LadyHavoc: if there is no such cvar or if it is private, send a
2492 // reply indicating that it has no value
2493 if(!c || (c->flags & CVAR_PRIVATE))
2494 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname));
2496 Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string));
2499 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2503 if (cls.state != ca_dedicated)
2507 for(;i<svs.maxclients;i++)
2508 if(svs.clients[i].active && svs.clients[i].netconnection)
2510 host_client = &svs.clients[i];
2511 Host_ClientCommands("sendcvar %s\n", cvarname);
2516 static void MaxPlayers_f(cmd_state_t *cmd)
2520 if (Cmd_Argc(cmd) != 2)
2522 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2528 Con_Print("maxplayers can not be changed while a server is running.\n");
2529 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2532 n = atoi(Cmd_Argv(cmd, 1));
2533 n = bound(1, n, MAX_SCOREBOARD);
2534 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2536 svs.maxclients_next = n;
2538 Cvar_Set (&cvars_all, "deathmatch", "0");
2540 Cvar_Set (&cvars_all, "deathmatch", "1");
2544 =====================
2547 ProQuake rcon support
2548 =====================
2550 static void Host_PQRcon_f(cmd_state_t *cmd)
2554 lhnetsocket_t *mysocket;
2556 if (Cmd_Argc(cmd) == 1)
2558 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2562 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2564 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2568 e = strchr(rcon_password.string, ' ');
2569 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2572 cls.rcon_address = cls.netcon->peeraddress;
2575 if (!rcon_address.string[0])
2577 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2580 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2582 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2586 unsigned char bufdata[64];
2589 MSG_WriteLong(&buf, 0);
2590 MSG_WriteByte(&buf, CCREQ_RCON);
2591 SZ_Write(&buf, (const unsigned char*)rcon_password.string, n);
2592 MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string
2593 MSG_WriteString(&buf, Cmd_Args(cmd));
2594 StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK));
2595 NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address);
2600 //=============================================================================
2602 // QuakeWorld commands
2605 =====================
2608 Send the rest of the command line over as
2609 an unconnected command.
2610 =====================
2612 static void Host_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2616 lhnetsocket_t *mysocket;
2618 if (Cmd_Argc(cmd) == 1)
2620 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2624 if (!rcon_password.string || !rcon_password.string[0])
2626 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2630 e = strchr(rcon_password.string, ' ');
2631 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2634 cls.rcon_address = cls.netcon->peeraddress;
2637 if (!rcon_address.string[0])
2639 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2642 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2644 mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2645 if (mysocket && Cmd_Args(cmd)[0])
2647 // simply put together the rcon packet and send it
2648 if(Cmd_Argv(cmd, 0)[0] == 's' || rcon_secure.integer > 1)
2650 if(cls.rcon_commands[cls.rcon_ringpos][0])
2653 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2654 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]);
2655 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2658 for (i = 0;i < MAX_RCONS;i++)
2659 if(cls.rcon_commands[i][0])
2660 if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i]))
2664 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later
2665 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2666 cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address;
2667 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2668 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2670 else if(rcon_secure.integer > 0)
2674 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args(cmd));
2675 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2676 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2679 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2680 NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address);
2686 memcpy(buf, "\377\377\377\377", 4);
2687 dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s", n, rcon_password.string, Cmd_Args(cmd));
2688 NetConn_WriteString(mysocket, buf, &cls.rcon_address);
2694 ====================
2697 user <name or userid>
2699 Dump userdata / masterdata for a user
2700 ====================
2702 static void Host_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2707 if (Cmd_Argc(cmd) != 2)
2709 Con_Printf ("Usage: user <username / userid>\n");
2713 uid = atoi(Cmd_Argv(cmd, 1));
2715 for (i = 0;i < cl.maxclients;i++)
2717 if (!cl.scores[i].name[0])
2719 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
2721 InfoString_Print(cl.scores[i].qw_userinfo);
2725 Con_Printf ("User not in server.\n");
2729 ====================
2732 Dump userids for all current players
2733 ====================
2735 static void Host_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2741 Con_Printf ("userid frags name\n");
2742 Con_Printf ("------ ----- ----\n");
2743 for (i = 0;i < cl.maxclients;i++)
2745 if (cl.scores[i].name[0])
2747 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2752 Con_Printf ("%i total users\n", c);
2757 Host_FullServerinfo_f
2759 Sent by server when serverinfo changes
2762 // TODO: shouldn't this be a cvar instead?
2763 static void Host_FullServerinfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2766 if (Cmd_Argc(cmd) != 2)
2768 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2772 strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo));
2773 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2774 cl.qw_teamplay = atoi(temp);
2781 Allow clients to change userinfo
2785 static void Host_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2791 if (Cmd_Argc(cmd) != 2)
2793 Con_Printf ("fullinfo <complete info string>\n");
2797 s = Cmd_Argv(cmd, 1);
2802 size_t len = strcspn(s, "\\");
2803 if (len >= sizeof(key)) {
2804 len = sizeof(key) - 1;
2806 strlcpy(key, s, len + 1);
2810 Con_Printf ("MISSING VALUE\n");
2813 ++s; // Skip over backslash.
2815 len = strcspn(s, "\\");
2816 if (len >= sizeof(value)) {
2817 len = sizeof(value) - 1;
2819 strlcpy(value, s, len + 1);
2821 CL_SetInfo(key, value, false, false, false, false);
2828 ++s; // Skip over backslash.
2836 Allow clients to change userinfo
2839 static void Host_SetInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2841 if (Cmd_Argc(cmd) == 1)
2843 InfoString_Print(cls.userinfo);
2846 if (Cmd_Argc(cmd) != 3)
2848 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2851 CL_SetInfo(Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), true, false, false, false);
2855 ====================
2858 packet <destination> <contents>
2860 Contents allows \n escape character
2861 ====================
2863 static void Host_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2869 lhnetaddress_t address;
2870 lhnetsocket_t *mysocket;
2872 if (Cmd_Argc(cmd) != 3)
2874 Con_Printf ("packet <destination> <contents>\n");
2878 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(cmd, 1), sv_netport.integer))
2880 Con_Printf ("Bad address\n");
2884 in = Cmd_Argv(cmd, 2);
2886 send[0] = send[1] = send[2] = send[3] = -1;
2888 l = (int)strlen (in);
2889 for (i=0 ; i<l ; i++)
2891 if (out >= send + sizeof(send) - 1)
2893 if (in[i] == '\\' && in[i+1] == 'n')
2898 else if (in[i] == '\\' && in[i+1] == '0')
2903 else if (in[i] == '\\' && in[i+1] == 't')
2908 else if (in[i] == '\\' && in[i+1] == 'r')
2913 else if (in[i] == '\\' && in[i+1] == '"')
2922 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2924 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2926 NetConn_Write(mysocket, send, out - send, &address);
2930 ====================
2933 Send back ping and packet loss update for all current players to this player
2934 ====================
2936 static void Host_Pings_f(cmd_state_t *cmd)
2938 int i, j, ping, packetloss, movementloss;
2941 if (!host_client->netconnection)
2944 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2946 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2947 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2949 for (i = 0;i < svs.maxclients;i++)
2953 if (svs.clients[i].netconnection)
2955 for (j = 0;j < NETGRAPH_PACKETS;j++)
2956 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2958 for (j = 0;j < NETGRAPH_PACKETS;j++)
2959 if (svs.clients[i].movement_count[j] < 0)
2962 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2963 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2964 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2965 ping = bound(0, ping, 9999);
2966 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2968 // send qw_svc_updateping and qw_svc_updatepl messages
2969 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2970 MSG_WriteShort(&host_client->netconnection->message, ping);
2971 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2972 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2976 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2978 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2980 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2981 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2984 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2985 MSG_WriteString(&host_client->netconnection->message, "\n");
2988 static void Host_PingPLReport_f(cmd_state_t *cmd)
2992 int l = Cmd_Argc(cmd);
2993 if (l > cl.maxclients)
2995 for (i = 0;i < l;i++)
2997 cl.scores[i].qw_ping = atoi(Cmd_Argv(cmd, 1+i*2));
2998 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(cmd, 1+i*2+1), &errbyte, 0);
2999 if(errbyte && *errbyte == ',')
3000 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
3002 cl.scores[i].qw_movementloss = 0;
3006 //=============================================================================
3013 void Host_InitCommands (void)
3015 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
3017 Cvar_RegisterVariable(&cl_name);
3018 Cvar_RegisterVariable(&cl_color);
3019 Cvar_RegisterVariable(&cl_rate);
3020 Cvar_RegisterVariable(&cl_rate_burstsize);
3021 Cvar_RegisterVariable(&cl_pmodel);
3022 Cvar_RegisterVariable(&cl_playermodel);
3023 Cvar_RegisterVariable(&cl_playerskin);
3024 Cvar_RegisterVariable(&rcon_password);
3025 Cvar_RegisterVariable(&rcon_address);
3026 Cvar_RegisterVariable(&rcon_secure);
3027 Cvar_RegisterVariable(&rcon_secure_challengetimeout);
3028 Cvar_RegisterVariable(&r_fixtrans_auto);
3029 Cvar_RegisterVariable(&team);
3030 Cvar_RegisterVariable(&skin);
3031 Cvar_RegisterVariable(&noaim);
3032 Cvar_RegisterVariable(&sv_cheats);
3033 Cvar_RegisterVariable(&sv_adminnick);
3034 Cvar_RegisterVariable(&sv_status_privacy);
3035 Cvar_RegisterVariable(&sv_status_show_qcstatus);
3036 Cvar_RegisterVariable(&sv_namechangetimer);
3038 // client commands - this includes server commands because the client can host a server, so they must exist
3039 Cmd_AddCommand(&cmd_client, "quit", Host_Quit_f, "quit the game");
3040 Cmd_AddCommand(&cmd_client, "status", Host_Status_f, "print server status information");
3041 Cmd_AddCommand(&cmd_client, "map", Host_Map_f, "kick everyone off the server and start a new level");
3042 Cmd_AddCommand(&cmd_client, "restart", Host_Restart_f, "restart current level");
3043 Cmd_AddCommand(&cmd_client, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3044 Cmd_AddCommand(&cmd_client, "version", Host_Version_f, "print engine version");
3045 Cmd_AddCommand(&cmd_client, "say", Host_Say_f, "send a chat message to everyone on the server");
3046 Cmd_AddCommand(&cmd_client, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3047 Cmd_AddCommand(&cmd_client, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3048 Cmd_AddCommand(&cmd_client, "kick", Host_Kick_f, "kick a player off the server by number or name");
3049 Cmd_AddCommand(&cmd_client, "ping", Host_Ping_f, "print ping times of all players on the server");
3050 Cmd_AddCommand(&cmd_client, "load", Host_Loadgame_f, "load a saved game file");
3051 Cmd_AddCommand(&cmd_client, "save", Host_Savegame_f, "save the game to a file");
3052 Cmd_AddCommand(&cmd_client, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3053 Cmd_AddCommand(&cmd_client, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3054 Cmd_AddCommand(&cmd_client, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3055 Cmd_AddCommand(&cmd_client, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3056 Cmd_AddCommand(&cmd_client, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3057 Cmd_AddCommand(&cmd_client, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3058 Cmd_AddCommand(&cmd_client, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3060 // dedicated server commands
3061 Cmd_AddCommand(&cmd_server, "quit", Host_Quit_f, "quit the game");
3062 Cmd_AddCommand(&cmd_server, "status", Host_Status_f, "print server status information");
3063 Cmd_AddCommand(&cmd_server, "map", Host_Map_f, "kick everyone off the server and start a new level");
3064 Cmd_AddCommand(&cmd_server, "restart", Host_Restart_f, "restart current level");
3065 Cmd_AddCommand(&cmd_server, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3066 Cmd_AddCommand(&cmd_server, "version", Host_Version_f, "print engine version");
3067 Cmd_AddCommand(&cmd_server, "say", Host_Say_f, "send a chat message to everyone on the server");
3068 Cmd_AddCommand(&cmd_server, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3069 Cmd_AddCommand(&cmd_server, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3070 Cmd_AddCommand(&cmd_server, "kick", Host_Kick_f, "kick a player off the server by number or name");
3071 Cmd_AddCommand(&cmd_server, "ping", Host_Ping_f, "print ping times of all players on the server");
3072 Cmd_AddCommand(&cmd_server, "load", Host_Loadgame_f, "load a saved game file");
3073 Cmd_AddCommand(&cmd_server, "save", Host_Savegame_f, "save the game to a file");
3074 Cmd_AddCommand(&cmd_server, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3075 Cmd_AddCommand(&cmd_server, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3076 Cmd_AddCommand(&cmd_server, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3077 Cmd_AddCommand(&cmd_server, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3078 Cmd_AddCommand(&cmd_server, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3079 Cmd_AddCommand(&cmd_server, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3080 Cmd_AddCommand(&cmd_server, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3082 // 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)
3083 Cmd_AddCommand(&cmd_serverfromclient, "prespawn", Host_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
3084 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)");
3085 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)");
3086 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)");
3088 Cmd_AddCommand(&cmd_serverfromclient, "status", Host_Status_f, "print server status information");
3089 Cmd_AddCommand(&cmd_serverfromclient, "god", Host_God_f, "god mode (invulnerability)");
3090 Cmd_AddCommand(&cmd_serverfromclient, "notarget", Host_Notarget_f, "notarget mode (monsters do not see you)");
3091 Cmd_AddCommand(&cmd_serverfromclient, "fly", Host_Fly_f, "fly mode (flight)");
3092 Cmd_AddCommand(&cmd_serverfromclient, "noclip", Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
3093 Cmd_AddCommand(&cmd_serverfromclient, "give", Host_Give_f, "alter inventory");
3094 Cmd_AddCommand(&cmd_serverfromclient, "say", Host_Say_f, "send a chat message to everyone on the server");
3095 Cmd_AddCommand(&cmd_serverfromclient, "say_team", Host_Say_Team_f, "send a chat message to your team on the server");
3096 Cmd_AddCommand(&cmd_serverfromclient, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3097 Cmd_AddCommand(&cmd_serverfromclient, "kill", Host_Kill_f, "die instantly");
3098 Cmd_AddCommand(&cmd_serverfromclient, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3099 Cmd_AddCommand(&cmd_serverfromclient, "ping", Host_Ping_f, "print ping times of all players on the server");
3100 Cmd_AddCommand(&cmd_serverfromclient, "name", Host_Name_f, "change your player name");
3101 Cmd_AddCommand(&cmd_serverfromclient, "color", Host_Color_f, "change your player shirt and pants colors");
3102 Cmd_AddCommand(&cmd_serverfromclient, "rate", Host_Rate_f, "change your network connection speed");
3103 Cmd_AddCommand(&cmd_serverfromclient, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3104 Cmd_AddCommand(&cmd_serverfromclient, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3105 Cmd_AddCommand(&cmd_serverfromclient, "playermodel", Host_Playermodel_f, "change your player model");
3106 Cmd_AddCommand(&cmd_serverfromclient, "playerskin", Host_Playerskin_f, "change your player skin number");
3108 // client commands that require a connection and are simply forwarded to server
3109 Cmd_AddCommand(&cmd_client, "god", Cmd_ForwardToServer_f, "god mode (invulnerability)");
3110 Cmd_AddCommand(&cmd_client, "notarget", Cmd_ForwardToServer_f, "notarget mode (monsters do not see you)");
3111 Cmd_AddCommand(&cmd_client, "fly", Cmd_ForwardToServer_f, "fly mode (flight)");
3112 Cmd_AddCommand(&cmd_client, "noclip", Cmd_ForwardToServer_f, "noclip mode (flight without collisions, move through walls)");
3113 Cmd_AddCommand(&cmd_client, "give", Cmd_ForwardToServer_f, "alter inventory");
3114 Cmd_AddCommand(&cmd_client, "say_team", Cmd_ForwardToServer_f, "send a chat message to your team on the server");
3115 Cmd_AddCommand(&cmd_client, "kill", Cmd_ForwardToServer_f, "die instantly");
3117 Cmd_AddCommand(&cmd_client, "connect", Host_Connect_f, "connect to a server by IP address or hostname");
3118 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)");
3119 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)");
3120 Cmd_AddCommand(&cmd_client, "startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
3121 Cmd_AddCommand(&cmd_client, "demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
3122 Cmd_AddCommand(&cmd_client, "stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
3123 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");
3124 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");
3125 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");
3126 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)");
3127 Cmd_AddCommand(&cmd_client, "fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
3128 Cmd_AddCommand(&cmd_client, "setinfo", Host_SetInfo_f, "modifies your userinfo");
3129 Cmd_AddCommand(&cmd_client, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3130 Cmd_AddCommand(&cmd_clientfromserver, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3131 Cmd_AddCommand(&cmd_client, "topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
3132 Cmd_AddCommand(&cmd_client, "bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
3133 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)");
3135 // client commands that also exist as cmd_serverfromclient and are often forwarded
3136 Cmd_AddCommand(&cmd_client, "name", Host_Name_f, "change your player name");
3137 Cmd_AddCommand(&cmd_client, "color", Host_Color_f, "change your player shirt and pants colors");
3138 Cmd_AddCommand(&cmd_client, "rate", Host_Rate_f, "change your network connection speed");
3139 Cmd_AddCommand(&cmd_client, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3140 Cmd_AddCommand(&cmd_client, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3141 Cmd_AddCommand(&cmd_client, "playermodel", Host_Playermodel_f, "change your player model");
3142 Cmd_AddCommand(&cmd_client, "playerskin", Host_Playerskin_f, "change your player skin number");
3144 // commands that are only sent by server to client for execution
3145 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)");
3146 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");
3149 void Host_NoOperation_f(cmd_state_t *cmd)