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 // for secure rcon authentication
31 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
32 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
33 cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
34 cvar_t sv_status_show_qcstatus = {CVAR_SAVE, "sv_status_show_qcstatus", "0", "show the 'qcstatus' field in status replies, not the 'frags' field. Turn this on if your mod uses this field, and the 'frags' field on the other hand has no meaningful value."};
35 cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
36 cvar_t rcon_secure = {CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
37 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
38 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
39 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
40 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
41 cvar_t r_fixtrans_auto = {0, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"};
42 qboolean allowcheats = false;
44 extern qboolean host_shuttingdown;
45 extern cvar_t developer_entityparsing;
53 void Host_Quit_f (void)
56 Con_Printf("shutting down already!\n");
66 void Host_Status_f (void)
70 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
71 void (*print) (const char *fmt, ...);
75 if (cmd_source == src_command)
77 // if running a client, try to send over network so the client's status report parser will see the report
78 if (cls.state == ca_connected)
80 Cmd_ForwardToServer ();
86 print = SV_ClientPrintf;
91 if(cmd_source == src_command)
97 if (strcmp(Cmd_Argv(1), "1") == 0)
99 else if (strcmp(Cmd_Argv(1), "2") == 0)
103 for (players = 0, i = 0;i < svs.maxclients;i++)
104 if (svs.clients[i].active)
106 print ("host: %s\n", Cvar_VariableString ("hostname"));
107 print ("version: %s build %s\n", gamename, buildstring);
108 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
109 print ("map: %s\n", sv.name);
110 print ("timing: %s\n", Host_TimingReport());
111 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
114 print ("^2IP %%pl ping time frags no name\n");
116 print ("^5IP no name\n");
118 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
125 if (in == 0 || in == 1)
127 seconds = (int)(realtime - client->connecttime);
128 minutes = seconds / 60;
131 seconds -= (minutes * 60);
132 hours = minutes / 60;
134 minutes -= (hours * 60);
140 if (client->netconnection)
141 for (j = 0;j < NETGRAPH_PACKETS;j++)
142 if (client->netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
144 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
145 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
148 if(sv_status_privacy.integer && cmd_source != src_command)
149 strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
151 strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
153 frags = client->frags;
155 if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
157 const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
163 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
164 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
168 frags = atoi(qcstatus);
172 if (in == 0) // default layout
174 print ("#%-3u ", i+1);
175 print ("%-16.16s ", client->name);
176 print ("%4i ", frags);
177 print ("%2i:%02i:%02i\n ", hours, minutes, seconds);
180 else if (in == 1) // extended layout
182 k%2 ? print("^3") : print("^7");
183 print ("%-21s ", ip);
184 print ("%2i ", packetloss);
185 print ("%4i ", ping);
186 print ("%2i:%02i:%02i ", hours, minutes, seconds);
187 print ("%4i ", frags);
188 print ("#%-3u ", i+1);
189 print ("^7%s\n", client->name);
191 else if (in == 2) // reduced layout
193 k%2 ? print("^3") : print("^7");
194 print ("%-21s ", ip);
195 print ("#%-3u ", i+1);
196 print ("^7%s\n", client->name);
200 if(cmd_source == src_command)
209 Sets client to godmode
212 void Host_God_f (void)
216 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
220 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
221 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
222 SV_ClientPrint("godmode OFF\n");
224 SV_ClientPrint("godmode ON\n");
227 void Host_Notarget_f (void)
231 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
235 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
236 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
237 SV_ClientPrint("notarget OFF\n");
239 SV_ClientPrint("notarget ON\n");
242 qboolean noclip_anglehack;
244 void Host_Noclip_f (void)
248 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
252 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
254 noclip_anglehack = true;
255 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
256 SV_ClientPrint("noclip ON\n");
260 noclip_anglehack = false;
261 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
262 SV_ClientPrint("noclip OFF\n");
270 Sets client to flymode
273 void Host_Fly_f (void)
277 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
281 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
283 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
284 SV_ClientPrint("flymode ON\n");
288 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
289 SV_ClientPrint("flymode OFF\n");
300 void Host_Pings_f (void); // called by Host_Ping_f
301 void Host_Ping_f (void)
305 void (*print) (const char *fmt, ...);
307 if (cmd_source == src_command)
309 // if running a client, try to send over network so the client's ping report parser will see the report
310 if (cls.state == ca_connected)
312 Cmd_ForwardToServer ();
318 print = SV_ClientPrintf;
323 print("Client ping times:\n");
324 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
328 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
331 // now call the Pings command also, which will send a report that contains packet loss for the scoreboard (as well as a simpler ping report)
332 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
337 ===============================================================================
341 ===============================================================================
345 ======================
350 command from the console. Active clients are kicked off.
351 ======================
353 void Host_Map_f (void)
355 char level[MAX_QPATH];
359 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
363 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
364 if (gamemode == GAME_DELUXEQUAKE)
365 Cvar_Set("warpmark", "");
367 cls.demonum = -1; // stop demo loop in case this fails
370 Host_ShutdownServer();
372 if(svs.maxclients != svs.maxclients_next)
374 svs.maxclients = svs.maxclients_next;
376 Mem_Free(svs.clients);
377 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
383 svs.serverflags = 0; // haven't completed an episode yet
384 allowcheats = sv_cheats.integer != 0;
385 strlcpy(level, Cmd_Argv(1), sizeof(level));
386 SV_SpawnServer(level);
387 if (sv.active && cls.state == ca_disconnected)
388 CL_EstablishConnection("local:1");
395 Goes to a new map, taking all clients along
398 void Host_Changelevel_f (void)
400 char level[MAX_QPATH];
404 Con_Print("changelevel <levelname> : continue game on a new level\n");
417 SV_SaveSpawnparms ();
419 allowcheats = sv_cheats.integer != 0;
420 strlcpy(level, Cmd_Argv(1), sizeof(level));
421 SV_SpawnServer(level);
422 if (sv.active && cls.state == ca_disconnected)
423 CL_EstablishConnection("local:1");
430 Restarts the current server for a dead player
433 void Host_Restart_f (void)
435 char mapname[MAX_QPATH];
439 Con_Print("restart : restart current level\n");
444 Con_Print("Only the server may restart\n");
451 allowcheats = sv_cheats.integer != 0;
452 strlcpy(mapname, sv.name, sizeof(mapname));
453 SV_SpawnServer(mapname);
454 if (sv.active && cls.state == ca_disconnected)
455 CL_EstablishConnection("local:1");
462 This command causes the client to wait for the signon messages again.
463 This is sent just before a server changes levels
466 void Host_Reconnect_f (void)
469 // if not connected, reconnect to the most recent server
472 // if we have connected to a server recently, the userinfo
473 // will still contain its IP address, so get the address...
474 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
476 CL_EstablishConnection(temp);
478 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
481 // if connected, do something based on protocol
482 if (cls.protocol == PROTOCOL_QUAKEWORLD)
484 // quakeworld can just re-login
485 if (cls.qw_downloadmemory) // don't change when downloading
490 if (cls.state == ca_connected && cls.signon < SIGNONS)
492 Con_Printf("reconnecting...\n");
493 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
494 MSG_WriteString(&cls.netcon->message, "new");
499 // netquake uses reconnect on level changes (silly)
502 Con_Print("reconnect : wait for signon messages again\n");
507 Con_Print("reconnect: no signon, ignoring reconnect\n");
510 cls.signon = 0; // need new connection messages
515 =====================
518 User command to connect to server
519 =====================
521 void Host_Connect_f (void)
525 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
528 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
529 if(!rcon_secure.integer)
530 Cvar_SetQuick(&rcon_password, "");
531 CL_EstablishConnection(Cmd_Argv(1));
536 ===============================================================================
540 ===============================================================================
543 #define SAVEGAME_VERSION 5
545 void Host_Savegame_to (const char *name)
548 int i, lightstyles = 64;
549 char comment[SAVEGAME_COMMENT_LENGTH+1];
552 // first we have to figure out if this can be saved in 64 lightstyles
553 // (for Quake compatibility)
554 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
555 if (sv.lightstyles[i][0])
558 isserver = !strcmp(PRVM_NAME, "server");
560 Con_Printf("Saving game to %s...\n", name);
561 f = FS_OpenRealFile(name, "wb", false);
564 Con_Print("ERROR: couldn't open.\n");
568 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
570 memset(comment, 0, sizeof(comment));
572 dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog->edicts->fields.server->message), (int)prog->globals.server->killed_monsters, (int)prog->globals.server->total_monsters);
574 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
575 // convert space to _ to make stdio happy
576 // LordHavoc: convert control characters to _ as well
577 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
578 if (ISWHITESPACEORCONTROL(comment[i]))
580 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
582 FS_Printf(f, "%s\n", comment);
585 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
586 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
587 FS_Printf(f, "%d\n", current_skill);
588 FS_Printf(f, "%s\n", sv.name);
589 FS_Printf(f, "%f\n",sv.time);
593 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
594 FS_Printf(f, "(dummy)\n");
595 FS_Printf(f, "%d\n", 0);
596 FS_Printf(f, "%s\n", "(dummy)");
597 FS_Printf(f, "%f\n", realtime);
600 // write the light styles
601 for (i=0 ; i<lightstyles ; i++)
603 if (isserver && sv.lightstyles[i][0])
604 FS_Printf(f, "%s\n", sv.lightstyles[i]);
609 PRVM_ED_WriteGlobals (f);
610 for (i=0 ; i<prog->num_edicts ; i++)
612 FS_Printf(f,"// edict %d\n", i);
613 //Con_Printf("edict %d...\n", i);
614 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
619 FS_Printf(f,"// DarkPlaces extended savegame\n");
620 // darkplaces extension - extra lightstyles, support for color lightstyles
621 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
622 if (isserver && sv.lightstyles[i][0])
623 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
625 // darkplaces extension - model precaches
626 for (i=1 ; i<MAX_MODELS ; i++)
627 if (sv.model_precache[i][0])
628 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
630 // darkplaces extension - sound precaches
631 for (i=1 ; i<MAX_SOUNDS ; i++)
632 if (sv.sound_precache[i][0])
633 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
638 Con_Print("done.\n");
646 void Host_Savegame_f (void)
648 char name[MAX_QPATH];
652 Con_Print("Can't save - no server running.\n");
658 // singleplayer checks
661 Con_Print("Can't save in intermission.\n");
665 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
667 Con_Print("Can't savegame with a dead player\n");
672 Con_Print("Warning: saving a multiplayer game may have strange results when restored (to properly resume, all players must join in the same player slots and then the game can be reloaded).\n");
676 Con_Print("save <savename> : save a game\n");
680 if (strstr(Cmd_Argv(1), ".."))
682 Con_Print("Relative pathnames are not allowed.\n");
686 strlcpy (name, Cmd_Argv(1), sizeof (name));
687 FS_DefaultExtension (name, ".sav", sizeof (name));
690 Host_Savegame_to(name);
700 void Host_Loadgame_f (void)
702 char filename[MAX_QPATH];
703 char mapname[MAX_QPATH];
713 float spawn_parms[NUM_SPAWN_PARMS];
717 Con_Print("load <savename> : load a game\n");
721 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
722 FS_DefaultExtension (filename, ".sav", sizeof (filename));
724 Con_Printf("Loading game from %s...\n", filename);
726 // stop playing demos
727 if (cls.demoplayback)
733 cls.demonum = -1; // stop demo loop in case this fails
735 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
738 Con_Print("ERROR: couldn't open.\n");
742 if(developer_entityparsing.integer)
743 Con_Printf("Host_Loadgame_f: loading version\n");
746 COM_ParseToken_Simple(&t, false, false);
747 version = atoi(com_token);
748 if (version != SAVEGAME_VERSION)
751 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
755 if(developer_entityparsing.integer)
756 Con_Printf("Host_Loadgame_f: loading description\n");
759 COM_ParseToken_Simple(&t, false, false);
761 for (i = 0;i < NUM_SPAWN_PARMS;i++)
763 COM_ParseToken_Simple(&t, false, false);
764 spawn_parms[i] = atof(com_token);
767 COM_ParseToken_Simple(&t, false, false);
768 // this silliness is so we can load 1.06 save files, which have float skill values
769 current_skill = (int)(atof(com_token) + 0.5);
770 Cvar_SetValue ("skill", (float)current_skill);
772 if(developer_entityparsing.integer)
773 Con_Printf("Host_Loadgame_f: loading mapname\n");
776 COM_ParseToken_Simple(&t, false, false);
777 strlcpy (mapname, com_token, sizeof(mapname));
779 if(developer_entityparsing.integer)
780 Con_Printf("Host_Loadgame_f: loading time\n");
783 COM_ParseToken_Simple(&t, false, false);
784 time = atof(com_token);
786 allowcheats = sv_cheats.integer != 0;
788 if(developer_entityparsing.integer)
789 Con_Printf("Host_Loadgame_f: spawning server\n");
791 SV_SpawnServer (mapname);
795 Con_Print("Couldn't load map\n");
798 sv.paused = true; // pause until all clients connect
801 if(developer_entityparsing.integer)
802 Con_Printf("Host_Loadgame_f: loading light styles\n");
804 // load the light styles
810 for (i = 0;i < MAX_LIGHTSTYLES;i++)
814 COM_ParseToken_Simple(&t, false, false);
815 // if this is a 64 lightstyle savegame produced by Quake, stop now
816 // we have to check this because darkplaces may save more than 64
817 if (com_token[0] == '{')
822 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
825 if(developer_entityparsing.integer)
826 Con_Printf("Host_Loadgame_f: skipping until globals\n");
828 // now skip everything before the first opening brace
829 // (this is for forward compatibility, so that older versions (at
830 // least ones with this fix) can load savegames with extra data before the
831 // first brace, as might be produced by a later engine version)
835 if (!COM_ParseToken_Simple(&t, false, false))
837 if (com_token[0] == '{')
844 // load the edicts out of the savegame file
849 while (COM_ParseToken_Simple(&t, false, false))
850 if (!strcmp(com_token, "}"))
852 if (!COM_ParseToken_Simple(&start, false, false))
857 if (strcmp(com_token,"{"))
860 Host_Error ("First token isn't a brace");
865 if(developer_entityparsing.integer)
866 Con_Printf("Host_Loadgame_f: loading globals\n");
868 // parse the global vars
869 PRVM_ED_ParseGlobals (start);
874 if (entnum >= MAX_EDICTS)
877 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
879 while (entnum >= prog->max_edicts)
880 PRVM_MEM_IncreaseEdicts();
881 ent = PRVM_EDICT_NUM(entnum);
882 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
883 ent->priv.server->free = false;
885 if(developer_entityparsing.integer)
886 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
888 PRVM_ED_ParseEdict (start, ent);
890 // link it into the bsp tree
891 if (!ent->priv.server->free)
892 SV_LinkEdict (ent, false);
899 prog->num_edicts = entnum;
902 for (i = 0;i < NUM_SPAWN_PARMS;i++)
903 svs.clients[0].spawn_parms[i] = spawn_parms[i];
905 if(developer_entityparsing.integer)
906 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
908 // read extended data if present
909 // the extended data is stored inside a /* */ comment block, which the
910 // parser intentionally skips, so we have to check for it manually here
913 while (*end == '\r' || *end == '\n')
915 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
917 if(developer_entityparsing.integer)
918 Con_Printf("Host_Loadgame_f: loading extended data\n");
920 Con_Printf("Loading extended DarkPlaces savegame\n");
922 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
923 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
924 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
925 while (COM_ParseToken_Simple(&t, false, false))
927 if (!strcmp(com_token, "sv.lightstyles"))
929 COM_ParseToken_Simple(&t, false, false);
931 COM_ParseToken_Simple(&t, false, false);
932 if (i >= 0 && i < MAX_LIGHTSTYLES)
933 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
935 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
937 else if (!strcmp(com_token, "sv.model_precache"))
939 COM_ParseToken_Simple(&t, false, false);
941 COM_ParseToken_Simple(&t, false, false);
942 if (i >= 0 && i < MAX_MODELS)
944 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
945 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
948 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
950 else if (!strcmp(com_token, "sv.sound_precache"))
952 COM_ParseToken_Simple(&t, false, false);
954 COM_ParseToken_Simple(&t, false, false);
955 if (i >= 0 && i < MAX_SOUNDS)
956 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
958 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
960 // skip any trailing text or unrecognized commands
961 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
968 if(developer_entityparsing.integer)
969 Con_Printf("Host_Loadgame_f: finished\n");
973 // make sure we're connected to loopback
974 if (sv.active && cls.state == ca_disconnected)
975 CL_EstablishConnection("local:1");
978 //============================================================================
981 ======================
983 ======================
985 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
986 void Host_Name_f (void)
989 qboolean valid_colors;
990 const char *newNameSource;
991 char newName[sizeof(host_client->name)];
993 if (Cmd_Argc () == 1)
995 Con_Printf("name: %s\n", cl_name.string);
999 if (Cmd_Argc () == 2)
1000 newNameSource = Cmd_Argv(i);
1002 newNameSource = Cmd_Args();
1004 if (cmd_source == src_command)
1006 strlcpy(newName, newNameSource, sizeof(newName));
1007 Cvar_Set ("_cl_name", newName);
1008 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1010 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", sizeof(newName) - 1);
1011 Con_Printf("name: %s\n", cl_name.string);
1016 if (realtime < host_client->nametime)
1018 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1022 host_client->nametime = realtime + 5;
1024 // point the string back at updateclient->name to keep it safe
1025 strlcpy (host_client->name, newName, sizeof (host_client->name));
1027 for (i = 0, j = 0;host_client->name[i];i++)
1028 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1029 host_client->name[j++] = host_client->name[i];
1030 host_client->name[j] = 0;
1032 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1033 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1035 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1036 host_client->name[sizeof(host_client->name) - 1] = 0;
1037 host_client->name[0] = STRING_COLOR_TAG;
1038 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1041 COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1042 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1045 l = strlen(host_client->name);
1046 if(l < sizeof(host_client->name) - 1)
1048 // duplicate the color tag to escape it
1049 host_client->name[i] = STRING_COLOR_TAG;
1050 host_client->name[i+1] = 0;
1051 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1055 // remove the last character to fix the color code
1056 host_client->name[l-1] = 0;
1057 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1061 // find the last color tag offset and decide if we need to add a reset tag
1062 for (i = 0, j = -1;host_client->name[i];i++)
1064 if (host_client->name[i] == STRING_COLOR_TAG)
1066 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1069 // if this happens to be a reset tag then we don't need one
1070 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1075 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]))
1081 if (host_client->name[i+1] == STRING_COLOR_TAG)
1088 // does not end in the default color string, so add it
1089 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1090 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1092 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1093 if (strcmp(host_client->old_name, host_client->name))
1095 if (host_client->spawned)
1096 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1097 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1098 // send notification to all clients
1099 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1100 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1101 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1102 SV_WriteNetnameIntoDemo(host_client);
1107 ======================
1109 ======================
1111 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1112 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1113 void Host_Playermodel_f (void)
1116 char newPath[sizeof(host_client->playermodel)];
1118 if (Cmd_Argc () == 1)
1120 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1124 if (Cmd_Argc () == 2)
1125 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1127 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1129 for (i = 0, j = 0;newPath[i];i++)
1130 if (newPath[i] != '\r' && newPath[i] != '\n')
1131 newPath[j++] = newPath[i];
1134 if (cmd_source == src_command)
1136 Cvar_Set ("_cl_playermodel", newPath);
1141 if (realtime < host_client->nametime)
1143 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1147 host_client->nametime = realtime + 5;
1150 // point the string back at updateclient->name to keep it safe
1151 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1152 if( prog->fieldoffsets.playermodel >= 0 )
1153 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1154 if (strcmp(host_client->old_model, host_client->playermodel))
1156 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1157 /*// send notification to all clients
1158 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1159 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1160 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1165 ======================
1167 ======================
1169 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1170 void Host_Playerskin_f (void)
1173 char newPath[sizeof(host_client->playerskin)];
1175 if (Cmd_Argc () == 1)
1177 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1181 if (Cmd_Argc () == 2)
1182 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1184 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1186 for (i = 0, j = 0;newPath[i];i++)
1187 if (newPath[i] != '\r' && newPath[i] != '\n')
1188 newPath[j++] = newPath[i];
1191 if (cmd_source == src_command)
1193 Cvar_Set ("_cl_playerskin", newPath);
1198 if (realtime < host_client->nametime)
1200 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1204 host_client->nametime = realtime + 5;
1207 // point the string back at updateclient->name to keep it safe
1208 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1209 if( prog->fieldoffsets.playerskin >= 0 )
1210 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1211 if (strcmp(host_client->old_skin, host_client->playerskin))
1213 //if (host_client->spawned)
1214 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1215 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1216 /*// send notification to all clients
1217 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1218 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1219 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1223 void Host_Version_f (void)
1225 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1228 void Host_Say(qboolean teamonly)
1234 // LordHavoc: long say messages
1236 qboolean fromServer = false;
1238 if (cmd_source == src_command)
1240 if (cls.state == ca_dedicated)
1247 Cmd_ForwardToServer ();
1252 if (Cmd_Argc () < 2)
1255 if (!teamplay.integer)
1265 // note this uses the chat prefix \001
1266 if (!fromServer && !teamonly)
1267 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1268 else if (!fromServer && teamonly)
1269 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1270 else if(*(sv_adminnick.string))
1271 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1273 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1274 p2 = text + strlen(text);
1275 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1277 if (p2[-1] == '\"' && quoted)
1282 strlcat(text, "\n", sizeof(text));
1284 // note: save is not a valid edict if fromServer is true
1286 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1287 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1288 SV_ClientPrint(text);
1291 if (cls.state == ca_dedicated)
1292 Con_Print(&text[1]);
1296 void Host_Say_f(void)
1302 void Host_Say_Team_f(void)
1308 void Host_Tell_f(void)
1310 const char *playername_start = NULL;
1311 size_t playername_length = 0;
1312 int playernumber = 0;
1315 const char *p1, *p2;
1316 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1317 qboolean fromServer = false;
1319 if (cmd_source == src_command)
1321 if (cls.state == ca_dedicated)
1325 Cmd_ForwardToServer ();
1330 if (Cmd_Argc () < 2)
1333 // note this uses the chat prefix \001
1335 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1336 else if(*(sv_adminnick.string))
1337 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1339 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1342 p2 = p1 + strlen(p1);
1343 // remove the target name
1344 while (p1 < p2 && *p1 == ' ')
1349 while (p1 < p2 && *p1 == ' ')
1351 while (p1 < p2 && isdigit(*p1))
1353 playernumber = playernumber * 10 + (*p1 - '0');
1361 playername_start = p1;
1362 while (p1 < p2 && *p1 != '"')
1364 playername_length = p1 - playername_start;
1370 playername_start = p1;
1371 while (p1 < p2 && *p1 != ' ')
1373 playername_length = p1 - playername_start;
1375 while (p1 < p2 && *p1 == ' ')
1377 if(playername_start)
1379 // set playernumber to the right client
1381 if(playername_length >= sizeof(namebuf))
1384 Con_Print("Host_Tell: too long player name/ID\n");
1386 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1389 memcpy(namebuf, playername_start, playername_length);
1390 namebuf[playername_length] = 0;
1391 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1393 if (!svs.clients[playernumber].active)
1395 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1399 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1402 Con_Print("Host_Tell: invalid player name/ID\n");
1404 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1407 // remove trailing newlines
1408 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1410 // remove quotes if present
1416 else if (fromServer)
1417 Con_Print("Host_Tell: missing end quote\n");
1419 SV_ClientPrint("Host_Tell: missing end quote\n");
1421 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1424 return; // empty say
1425 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1431 host_client = svs.clients + playernumber;
1432 SV_ClientPrint(text);
1442 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1443 void Host_Color(int changetop, int changebottom)
1445 int top, bottom, playercolor;
1447 // get top and bottom either from the provided values or the current values
1448 // (allows changing only top or bottom, or both at once)
1449 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1450 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1454 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1460 playercolor = top*16 + bottom;
1462 if (cmd_source == src_command)
1464 Cvar_SetValueQuick(&cl_color, playercolor);
1468 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1471 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1473 Con_DPrint("Calling SV_ChangeTeam\n");
1474 prog->globals.server->time = sv.time;
1475 prog->globals.generic[OFS_PARM0] = playercolor;
1476 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1477 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1482 if (host_client->edict)
1484 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1485 val->_float = playercolor;
1486 host_client->edict->fields.server->team = bottom + 1;
1488 host_client->colors = playercolor;
1489 if (host_client->old_colors != host_client->colors)
1491 host_client->old_colors = host_client->colors;
1492 // send notification to all clients
1493 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1494 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1495 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1500 void Host_Color_f(void)
1504 if (Cmd_Argc() == 1)
1506 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1507 Con_Print("color <0-15> [0-15]\n");
1511 if (Cmd_Argc() == 2)
1512 top = bottom = atoi(Cmd_Argv(1));
1515 top = atoi(Cmd_Argv(1));
1516 bottom = atoi(Cmd_Argv(2));
1518 Host_Color(top, bottom);
1521 void Host_TopColor_f(void)
1523 if (Cmd_Argc() == 1)
1525 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1526 Con_Print("topcolor <0-15>\n");
1530 Host_Color(atoi(Cmd_Argv(1)), -1);
1533 void Host_BottomColor_f(void)
1535 if (Cmd_Argc() == 1)
1537 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1538 Con_Print("bottomcolor <0-15>\n");
1542 Host_Color(-1, atoi(Cmd_Argv(1)));
1545 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1546 void Host_Rate_f(void)
1550 if (Cmd_Argc() != 2)
1552 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1553 Con_Print("rate <bytespersecond>\n");
1557 rate = atoi(Cmd_Argv(1));
1559 if (cmd_source == src_command)
1561 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1565 host_client->rate = rate;
1573 void Host_Kill_f (void)
1575 if (host_client->edict->fields.server->health <= 0)
1577 SV_ClientPrint("Can't suicide -- already dead!\n");
1581 prog->globals.server->time = sv.time;
1582 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1583 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1592 void Host_Pause_f (void)
1594 if (!pausable.integer)
1595 SV_ClientPrint("Pause not allowed.\n");
1599 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1600 // send notification to all clients
1601 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1602 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1607 ======================
1609 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1610 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1611 ======================
1613 cvar_t cl_pmodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_pmodel", "0", "internal storage cvar for current player model number in nehahra (changed by pmodel command)"};
1614 static void Host_PModel_f (void)
1619 if (Cmd_Argc () == 1)
1621 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1624 i = atoi(Cmd_Argv(1));
1626 if (cmd_source == src_command)
1628 if (cl_pmodel.integer == i)
1630 Cvar_SetValue ("_cl_pmodel", i);
1631 if (cls.state == ca_connected)
1632 Cmd_ForwardToServer ();
1636 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1640 //===========================================================================
1648 void Host_PreSpawn_f (void)
1650 if (host_client->spawned)
1652 Con_Print("prespawn not valid -- already spawned\n");
1656 if (host_client->netconnection)
1658 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1659 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1660 MSG_WriteByte (&host_client->netconnection->message, 2);
1661 host_client->sendsignon = 0; // enable unlimited sends again
1664 // reset the name change timer because the client will send name soon
1665 host_client->nametime = 0;
1673 void Host_Spawn_f (void)
1677 int stats[MAX_CL_STATS];
1679 if (host_client->spawned)
1681 Con_Print("Spawn not valid -- already spawned\n");
1685 // reset name change timer again because they might want to change name
1686 // again in the first 5 seconds after connecting
1687 host_client->nametime = 0;
1689 // LordHavoc: moved this above the QC calls at FrikaC's request
1690 // LordHavoc: commented this out
1691 //if (host_client->netconnection)
1692 // SZ_Clear (&host_client->netconnection->message);
1694 // run the entrance script
1697 // loaded games are fully initialized already
1698 if (prog->funcoffsets.RestoreGame)
1700 Con_DPrint("Calling RestoreGame\n");
1701 prog->globals.server->time = sv.time;
1702 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1703 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1708 //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(host_client->edict->fields.server->netname), PRVM_GetString(host_client->edict->fields.server->netname), host_client->name);
1710 // copy spawn parms out of the client_t
1711 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1712 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1714 // call the spawn function
1715 host_client->clientconnectcalled = true;
1716 prog->globals.server->time = sv.time;
1717 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1718 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1720 if (cls.state == ca_dedicated)
1721 Con_Printf("%s connected\n", host_client->name);
1723 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1726 if (!host_client->netconnection)
1729 // send time of update
1730 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1731 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1733 // send all current names, colors, and frag counts
1734 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1736 if (!client->active)
1738 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1739 MSG_WriteByte (&host_client->netconnection->message, i);
1740 MSG_WriteString (&host_client->netconnection->message, client->name);
1741 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1742 MSG_WriteByte (&host_client->netconnection->message, i);
1743 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1744 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1745 MSG_WriteByte (&host_client->netconnection->message, i);
1746 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1749 // send all current light styles
1750 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1752 if (sv.lightstyles[i][0])
1754 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1755 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1756 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1761 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1762 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1763 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1765 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1766 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1767 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1769 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1770 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1771 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1773 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1774 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1775 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1778 // Never send a roll angle, because savegames can catch the server
1779 // in a state where it is expecting the client to correct the angle
1780 // and it won't happen if the game was just loaded, so you wind up
1781 // with a permanent head tilt
1784 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1785 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1786 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1787 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1791 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1792 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1793 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1794 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1797 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1799 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1800 MSG_WriteByte (&host_client->netconnection->message, 3);
1808 void Host_Begin_f (void)
1810 host_client->spawned = true;
1812 // LordHavoc: note: this code also exists in SV_DropClient
1816 for (i = 0;i < svs.maxclients;i++)
1817 if (svs.clients[i].active && !svs.clients[i].spawned)
1819 if (i == svs.maxclients)
1821 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1822 sv.paused = sv.loadgame = false; // we're basically done with loading now
1827 //===========================================================================
1834 Kicks a user off of the server
1837 void Host_Kick_f (void)
1840 const char *message = NULL;
1843 qboolean byNumber = false;
1851 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1853 i = (int)(atof(Cmd_Argv(2)) - 1);
1854 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1860 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1862 if (!host_client->active)
1864 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1869 if (i < svs.maxclients)
1871 if (cmd_source == src_command)
1873 if (cls.state == ca_dedicated)
1876 who = cl_name.string;
1881 // can't kick yourself!
1882 if (host_client == save)
1887 message = Cmd_Args();
1888 COM_ParseToken_Simple(&message, false, false);
1891 message++; // skip the #
1892 while (*message == ' ') // skip white space
1894 message += strlen(Cmd_Argv(2)); // skip the number
1896 while (*message && *message == ' ')
1900 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1902 SV_ClientPrintf("Kicked by %s\n", who);
1903 SV_DropClient (false); // kicked
1911 ===============================================================================
1915 ===============================================================================
1923 void Host_Give_f (void)
1931 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1936 v = atoi (Cmd_Argv(2));
1950 // MED 01/04/97 added hipnotic give stuff
1951 if (gamemode == GAME_HIPNOTIC)
1956 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1958 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1960 else if (t[0] == '9')
1961 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1962 else if (t[0] == '0')
1963 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1964 else if (t[0] >= '2')
1965 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1970 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1975 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1978 host_client->edict->fields.server->ammo_shells = v;
1981 if (gamemode == GAME_ROGUE)
1983 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1986 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1987 host_client->edict->fields.server->ammo_nails = v;
1992 host_client->edict->fields.server->ammo_nails = v;
1996 if (gamemode == GAME_ROGUE)
1998 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
2002 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2003 host_client->edict->fields.server->ammo_nails = v;
2008 if (gamemode == GAME_ROGUE)
2010 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2014 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2015 host_client->edict->fields.server->ammo_rockets = v;
2020 host_client->edict->fields.server->ammo_rockets = v;
2024 if (gamemode == GAME_ROGUE)
2026 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2030 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2031 host_client->edict->fields.server->ammo_rockets = v;
2036 host_client->edict->fields.server->health = v;
2039 if (gamemode == GAME_ROGUE)
2041 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2045 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2046 host_client->edict->fields.server->ammo_cells = v;
2051 host_client->edict->fields.server->ammo_cells = v;
2055 if (gamemode == GAME_ROGUE)
2057 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2061 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2062 host_client->edict->fields.server->ammo_cells = v;
2069 prvm_edict_t *FindViewthing (void)
2074 for (i=0 ; i<prog->num_edicts ; i++)
2076 e = PRVM_EDICT_NUM(i);
2077 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2080 Con_Print("No viewthing on map\n");
2089 void Host_Viewmodel_f (void)
2098 e = FindViewthing ();
2103 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2104 if (!m || !m->loaded || !m->Draw)
2106 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2110 e->fields.server->frame = 0;
2111 cl.model_precache[(int)e->fields.server->modelindex] = m;
2119 void Host_Viewframe_f (void)
2129 e = FindViewthing ();
2133 m = cl.model_precache[(int)e->fields.server->modelindex];
2135 f = atoi(Cmd_Argv(1));
2136 if (f >= m->numframes)
2139 e->fields.server->frame = f;
2143 void PrintFrameName (dp_model_t *m, int frame)
2146 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2148 Con_Printf("frame %i\n", frame);
2156 void Host_Viewnext_f (void)
2165 e = FindViewthing ();
2169 m = cl.model_precache[(int)e->fields.server->modelindex];
2171 e->fields.server->frame = e->fields.server->frame + 1;
2172 if (e->fields.server->frame >= m->numframes)
2173 e->fields.server->frame = m->numframes - 1;
2175 PrintFrameName (m, (int)e->fields.server->frame);
2183 void Host_Viewprev_f (void)
2192 e = FindViewthing ();
2197 m = cl.model_precache[(int)e->fields.server->modelindex];
2199 e->fields.server->frame = e->fields.server->frame - 1;
2200 if (e->fields.server->frame < 0)
2201 e->fields.server->frame = 0;
2203 PrintFrameName (m, (int)e->fields.server->frame);
2207 ===============================================================================
2211 ===============================================================================
2220 void Host_Startdemos_f (void)
2224 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2230 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2233 Con_DPrintf("%i demo(s) in loop\n", c);
2235 for (i=1 ; i<c+1 ; i++)
2236 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2238 // LordHavoc: clear the remaining slots
2239 for (;i <= MAX_DEMOS;i++)
2240 cls.demos[i-1][0] = 0;
2242 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2256 Return to looping demos
2259 void Host_Demos_f (void)
2261 if (cls.state == ca_dedicated)
2263 if (cls.demonum == -1)
2273 Return to looping demos
2276 void Host_Stopdemo_f (void)
2278 if (!cls.demoplayback)
2281 Host_ShutdownServer ();
2284 void Host_SendCvar_f (void)
2288 const char *cvarname;
2293 cvarname = Cmd_Argv(1);
2294 if (cls.state == ca_connected)
2296 c = Cvar_FindVar(cvarname);
2297 // LordHavoc: if there is no such cvar or if it is private, send a
2298 // reply indicating that it has no value
2299 if(!c || (c->flags & CVAR_PRIVATE))
2300 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2302 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2305 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2309 if (cls.state != ca_dedicated)
2313 for(;i<svs.maxclients;i++)
2314 if(svs.clients[i].active && svs.clients[i].netconnection)
2316 host_client = &svs.clients[i];
2317 Host_ClientCommands("sendcvar %s\n", cvarname);
2322 static void MaxPlayers_f(void)
2326 if (Cmd_Argc() != 2)
2328 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2334 Con_Print("maxplayers can not be changed while a server is running.\n");
2335 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2338 n = atoi(Cmd_Argv(1));
2339 n = bound(1, n, MAX_SCOREBOARD);
2340 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2342 svs.maxclients_next = n;
2344 Cvar_Set ("deathmatch", "0");
2346 Cvar_Set ("deathmatch", "1");
2349 //=============================================================================
2351 // QuakeWorld commands
2354 =====================
2357 Send the rest of the command line over as
2358 an unconnected command.
2359 =====================
2361 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2365 lhnetsocket_t *mysocket;
2367 if (!rcon_password.string || !rcon_password.string[0])
2369 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2373 for (i = 0;rcon_password.string[i];i++)
2375 if (ISWHITESPACE(rcon_password.string[i]))
2377 Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2383 to = cls.netcon->peeraddress;
2386 if (!rcon_address.string[0])
2388 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2391 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2393 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2396 // simply put together the rcon packet and send it
2397 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer)
2401 dpsnprintf(argbuf, sizeof(argbuf), "%ld %s", (long) time(NULL), Cmd_Args());
2402 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2403 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
2406 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2407 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2412 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
2418 ====================
2421 user <name or userid>
2423 Dump userdata / masterdata for a user
2424 ====================
2426 void Host_User_f (void) // credit: taken from QuakeWorld
2431 if (Cmd_Argc() != 2)
2433 Con_Printf ("Usage: user <username / userid>\n");
2437 uid = atoi(Cmd_Argv(1));
2439 for (i = 0;i < cl.maxclients;i++)
2441 if (!cl.scores[i].name[0])
2443 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2445 InfoString_Print(cl.scores[i].qw_userinfo);
2449 Con_Printf ("User not in server.\n");
2453 ====================
2456 Dump userids for all current players
2457 ====================
2459 void Host_Users_f (void) // credit: taken from QuakeWorld
2465 Con_Printf ("userid frags name\n");
2466 Con_Printf ("------ ----- ----\n");
2467 for (i = 0;i < cl.maxclients;i++)
2469 if (cl.scores[i].name[0])
2471 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2476 Con_Printf ("%i total users\n", c);
2481 Host_FullServerinfo_f
2483 Sent by server when serverinfo changes
2486 // TODO: shouldn't this be a cvar instead?
2487 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2490 if (Cmd_Argc() != 2)
2492 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2496 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2497 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2498 cl.qw_teamplay = atoi(temp);
2505 Allow clients to change userinfo
2509 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2516 if (Cmd_Argc() != 2)
2518 Con_Printf ("fullinfo <complete info string>\n");
2528 while (*s && *s != '\\')
2534 Con_Printf ("MISSING VALUE\n");
2540 while (*s && *s != '\\')
2547 CL_SetInfo(key, value, false, false, false, false);
2555 Allow clients to change userinfo
2558 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2560 if (Cmd_Argc() == 1)
2562 InfoString_Print(cls.userinfo);
2565 if (Cmd_Argc() != 3)
2567 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2570 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2574 ====================
2577 packet <destination> <contents>
2579 Contents allows \n escape character
2580 ====================
2582 void Host_Packet_f (void) // credit: taken from QuakeWorld
2588 lhnetaddress_t address;
2589 lhnetsocket_t *mysocket;
2591 if (Cmd_Argc() != 3)
2593 Con_Printf ("packet <destination> <contents>\n");
2597 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2599 Con_Printf ("Bad address\n");
2605 send[0] = send[1] = send[2] = send[3] = 0xff;
2607 l = (int)strlen (in);
2608 for (i=0 ; i<l ; i++)
2610 if (out >= send + sizeof(send) - 1)
2612 if (in[i] == '\\' && in[i+1] == 'n')
2617 else if (in[i] == '\\' && in[i+1] == '0')
2622 else if (in[i] == '\\' && in[i+1] == 't')
2627 else if (in[i] == '\\' && in[i+1] == 'r')
2632 else if (in[i] == '\\' && in[i+1] == '"')
2641 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2643 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2645 NetConn_Write(mysocket, send, out - send, &address);
2649 ====================
2652 Send back ping and packet loss update for all current players to this player
2653 ====================
2655 void Host_Pings_f (void)
2657 int i, j, ping, packetloss;
2660 if (!host_client->netconnection)
2663 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2665 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2666 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2668 for (i = 0;i < svs.maxclients;i++)
2671 if (svs.clients[i].netconnection)
2672 for (j = 0;j < NETGRAPH_PACKETS;j++)
2673 if (svs.clients[i].netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
2675 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
2676 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2677 ping = bound(0, ping, 9999);
2678 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2680 // send qw_svc_updateping and qw_svc_updatepl messages
2681 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2682 MSG_WriteShort(&host_client->netconnection->message, ping);
2683 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2684 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2688 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2689 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2690 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2693 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2694 MSG_WriteString(&host_client->netconnection->message, "\n");
2697 void Host_PingPLReport_f(void)
2701 if (l > cl.maxclients)
2703 for (i = 0;i < l;i++)
2705 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2706 cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
2710 //=============================================================================
2717 void Host_InitCommands (void)
2719 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2721 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2722 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2723 if (gamemode == GAME_NEHAHRA)
2725 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2726 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2727 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2728 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2729 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2733 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2734 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2735 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2736 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2737 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2739 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2740 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2741 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2742 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2743 Cmd_AddCommand ("reconnect", Host_Reconnect_f, "reconnect to the last server you were on, or resets a quakeworld connection (do not use if currently playing on a netquake server)");
2744 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2745 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2746 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2747 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2748 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2749 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2750 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2751 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2752 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2753 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2755 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2756 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2757 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2759 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2760 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2761 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2762 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2764 Cvar_RegisterVariable (&cl_name);
2765 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2766 Cvar_RegisterVariable (&cl_color);
2767 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2768 Cvar_RegisterVariable (&cl_rate);
2769 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2770 if (gamemode == GAME_NEHAHRA)
2772 Cvar_RegisterVariable (&cl_pmodel);
2773 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2776 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2777 Cvar_RegisterVariable (&cl_playermodel);
2778 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2779 Cvar_RegisterVariable (&cl_playerskin);
2780 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2782 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2783 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2784 Cmd_AddCommand_WithClientCommand ("begin", NULL, Host_Begin_f, "signon 3 (client asks server to start sending entities, and will go to signon 4 (playing) when the first entity update is received)");
2785 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2787 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2789 Cvar_RegisterVariable (&rcon_password);
2790 Cvar_RegisterVariable (&rcon_address);
2791 Cvar_RegisterVariable (&rcon_secure);
2792 Cmd_AddCommand ("rcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
2793 Cmd_AddCommand ("srcon", Host_Rcon_f, "sends a command to the server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's); this always works as if rcon_secure is set");
2794 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2795 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2796 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2797 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2798 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2799 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2800 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2801 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2803 Cmd_AddCommand_WithClientCommand ("pings", NULL, Host_Pings_f, "command sent by clients to request updated ping and packetloss of players on scoreboard (originally from QW, but also used on NQ servers)");
2804 Cmd_AddCommand ("pingplreport", Host_PingPLReport_f, "command sent by server containing client ping and packet loss values for scoreboard, triggered by pings command from client (not used by QW servers)");
2806 Cmd_AddCommand ("fixtrans", Image_FixTransparentPixels_f, "change alpha-zero pixels in an image file to sensible values, and write out a new TGA (warning: SLOW)");
2807 Cvar_RegisterVariable (&r_fixtrans_auto);
2809 Cvar_RegisterVariable (&team);
2810 Cvar_RegisterVariable (&skin);
2811 Cvar_RegisterVariable (&noaim);
2813 Cvar_RegisterVariable(&sv_cheats);
2814 Cvar_RegisterVariable(&sv_adminnick);
2815 Cvar_RegisterVariable(&sv_status_privacy);
2816 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2819 void Host_NoOperation_f(void)