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(1);
1002 newNameSource = Cmd_Args();
1004 strlcpy(newName, newNameSource, sizeof(newName));
1006 if (cmd_source == src_command)
1008 Cvar_Set ("_cl_name", newName);
1009 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1011 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1012 Con_Printf("name: %s\n", cl_name.string);
1017 if (realtime < host_client->nametime)
1019 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1023 host_client->nametime = realtime + 5;
1025 // point the string back at updateclient->name to keep it safe
1026 strlcpy (host_client->name, newName, sizeof (host_client->name));
1028 for (i = 0, j = 0;host_client->name[i];i++)
1029 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1030 host_client->name[j++] = host_client->name[i];
1031 host_client->name[j] = 0;
1033 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1034 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1036 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1037 host_client->name[sizeof(host_client->name) - 1] = 0;
1038 host_client->name[0] = STRING_COLOR_TAG;
1039 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1042 COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1043 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1046 l = strlen(host_client->name);
1047 if(l < sizeof(host_client->name) - 1)
1049 // duplicate the color tag to escape it
1050 host_client->name[i] = STRING_COLOR_TAG;
1051 host_client->name[i+1] = 0;
1052 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1056 // remove the last character to fix the color code
1057 host_client->name[l-1] = 0;
1058 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1062 // find the last color tag offset and decide if we need to add a reset tag
1063 for (i = 0, j = -1;host_client->name[i];i++)
1065 if (host_client->name[i] == STRING_COLOR_TAG)
1067 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1070 // if this happens to be a reset tag then we don't need one
1071 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1076 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]))
1082 if (host_client->name[i+1] == STRING_COLOR_TAG)
1089 // does not end in the default color string, so add it
1090 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1091 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1093 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1094 if (strcmp(host_client->old_name, host_client->name))
1096 if (host_client->spawned)
1097 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1098 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1099 // send notification to all clients
1100 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1101 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1102 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1103 SV_WriteNetnameIntoDemo(host_client);
1108 ======================
1110 ======================
1112 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1113 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1114 void Host_Playermodel_f (void)
1117 char newPath[sizeof(host_client->playermodel)];
1119 if (Cmd_Argc () == 1)
1121 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1125 if (Cmd_Argc () == 2)
1126 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1128 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1130 for (i = 0, j = 0;newPath[i];i++)
1131 if (newPath[i] != '\r' && newPath[i] != '\n')
1132 newPath[j++] = newPath[i];
1135 if (cmd_source == src_command)
1137 Cvar_Set ("_cl_playermodel", newPath);
1142 if (realtime < host_client->nametime)
1144 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1148 host_client->nametime = realtime + 5;
1151 // point the string back at updateclient->name to keep it safe
1152 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1153 if( prog->fieldoffsets.playermodel >= 0 )
1154 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1155 if (strcmp(host_client->old_model, host_client->playermodel))
1157 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1158 /*// send notification to all clients
1159 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1160 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1161 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1166 ======================
1168 ======================
1170 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1171 void Host_Playerskin_f (void)
1174 char newPath[sizeof(host_client->playerskin)];
1176 if (Cmd_Argc () == 1)
1178 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1182 if (Cmd_Argc () == 2)
1183 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1185 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1187 for (i = 0, j = 0;newPath[i];i++)
1188 if (newPath[i] != '\r' && newPath[i] != '\n')
1189 newPath[j++] = newPath[i];
1192 if (cmd_source == src_command)
1194 Cvar_Set ("_cl_playerskin", newPath);
1199 if (realtime < host_client->nametime)
1201 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1205 host_client->nametime = realtime + 5;
1208 // point the string back at updateclient->name to keep it safe
1209 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1210 if( prog->fieldoffsets.playerskin >= 0 )
1211 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1212 if (strcmp(host_client->old_skin, host_client->playerskin))
1214 //if (host_client->spawned)
1215 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1216 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1217 /*// send notification to all clients
1218 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1219 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1220 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1224 void Host_Version_f (void)
1226 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1229 void Host_Say(qboolean teamonly)
1235 // LordHavoc: long say messages
1237 qboolean fromServer = false;
1239 if (cmd_source == src_command)
1241 if (cls.state == ca_dedicated)
1248 Cmd_ForwardToServer ();
1253 if (Cmd_Argc () < 2)
1256 if (!teamplay.integer)
1266 // note this uses the chat prefix \001
1267 if (!fromServer && !teamonly)
1268 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1269 else if (!fromServer && teamonly)
1270 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1271 else if(*(sv_adminnick.string))
1272 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1274 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1275 p2 = text + strlen(text);
1276 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1278 if (p2[-1] == '\"' && quoted)
1283 strlcat(text, "\n", sizeof(text));
1285 // note: save is not a valid edict if fromServer is true
1287 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1288 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1289 SV_ClientPrint(text);
1292 if (cls.state == ca_dedicated)
1293 Con_Print(&text[1]);
1297 void Host_Say_f(void)
1303 void Host_Say_Team_f(void)
1309 void Host_Tell_f(void)
1311 const char *playername_start = NULL;
1312 size_t playername_length = 0;
1313 int playernumber = 0;
1316 const char *p1, *p2;
1317 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1318 qboolean fromServer = false;
1320 if (cmd_source == src_command)
1322 if (cls.state == ca_dedicated)
1326 Cmd_ForwardToServer ();
1331 if (Cmd_Argc () < 2)
1334 // note this uses the chat prefix \001
1336 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1337 else if(*(sv_adminnick.string))
1338 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1340 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1343 p2 = p1 + strlen(p1);
1344 // remove the target name
1345 while (p1 < p2 && *p1 == ' ')
1350 while (p1 < p2 && *p1 == ' ')
1352 while (p1 < p2 && isdigit(*p1))
1354 playernumber = playernumber * 10 + (*p1 - '0');
1362 playername_start = p1;
1363 while (p1 < p2 && *p1 != '"')
1365 playername_length = p1 - playername_start;
1371 playername_start = p1;
1372 while (p1 < p2 && *p1 != ' ')
1374 playername_length = p1 - playername_start;
1376 while (p1 < p2 && *p1 == ' ')
1378 if(playername_start)
1380 // set playernumber to the right client
1382 if(playername_length >= sizeof(namebuf))
1385 Con_Print("Host_Tell: too long player name/ID\n");
1387 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1390 memcpy(namebuf, playername_start, playername_length);
1391 namebuf[playername_length] = 0;
1392 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1394 if (!svs.clients[playernumber].active)
1396 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1400 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1403 Con_Print("Host_Tell: invalid player name/ID\n");
1405 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1408 // remove trailing newlines
1409 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1411 // remove quotes if present
1417 else if (fromServer)
1418 Con_Print("Host_Tell: missing end quote\n");
1420 SV_ClientPrint("Host_Tell: missing end quote\n");
1422 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1425 return; // empty say
1426 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1432 host_client = svs.clients + playernumber;
1433 SV_ClientPrint(text);
1443 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1444 void Host_Color(int changetop, int changebottom)
1446 int top, bottom, playercolor;
1448 // get top and bottom either from the provided values or the current values
1449 // (allows changing only top or bottom, or both at once)
1450 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1451 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1455 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1461 playercolor = top*16 + bottom;
1463 if (cmd_source == src_command)
1465 Cvar_SetValueQuick(&cl_color, playercolor);
1469 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1472 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1474 Con_DPrint("Calling SV_ChangeTeam\n");
1475 prog->globals.server->time = sv.time;
1476 prog->globals.generic[OFS_PARM0] = playercolor;
1477 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1478 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1483 if (host_client->edict)
1485 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1486 val->_float = playercolor;
1487 host_client->edict->fields.server->team = bottom + 1;
1489 host_client->colors = playercolor;
1490 if (host_client->old_colors != host_client->colors)
1492 host_client->old_colors = host_client->colors;
1493 // send notification to all clients
1494 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1495 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1496 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1501 void Host_Color_f(void)
1505 if (Cmd_Argc() == 1)
1507 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1508 Con_Print("color <0-15> [0-15]\n");
1512 if (Cmd_Argc() == 2)
1513 top = bottom = atoi(Cmd_Argv(1));
1516 top = atoi(Cmd_Argv(1));
1517 bottom = atoi(Cmd_Argv(2));
1519 Host_Color(top, bottom);
1522 void Host_TopColor_f(void)
1524 if (Cmd_Argc() == 1)
1526 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1527 Con_Print("topcolor <0-15>\n");
1531 Host_Color(atoi(Cmd_Argv(1)), -1);
1534 void Host_BottomColor_f(void)
1536 if (Cmd_Argc() == 1)
1538 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1539 Con_Print("bottomcolor <0-15>\n");
1543 Host_Color(-1, atoi(Cmd_Argv(1)));
1546 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1547 void Host_Rate_f(void)
1551 if (Cmd_Argc() != 2)
1553 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1554 Con_Print("rate <bytespersecond>\n");
1558 rate = atoi(Cmd_Argv(1));
1560 if (cmd_source == src_command)
1562 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1566 host_client->rate = rate;
1574 void Host_Kill_f (void)
1576 if (host_client->edict->fields.server->health <= 0)
1578 SV_ClientPrint("Can't suicide -- already dead!\n");
1582 prog->globals.server->time = sv.time;
1583 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1584 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1593 void Host_Pause_f (void)
1595 if (!pausable.integer)
1596 SV_ClientPrint("Pause not allowed.\n");
1600 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1601 // send notification to all clients
1602 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1603 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1608 ======================
1610 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1611 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1612 ======================
1614 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)"};
1615 static void Host_PModel_f (void)
1620 if (Cmd_Argc () == 1)
1622 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1625 i = atoi(Cmd_Argv(1));
1627 if (cmd_source == src_command)
1629 if (cl_pmodel.integer == i)
1631 Cvar_SetValue ("_cl_pmodel", i);
1632 if (cls.state == ca_connected)
1633 Cmd_ForwardToServer ();
1637 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1641 //===========================================================================
1649 void Host_PreSpawn_f (void)
1651 if (host_client->spawned)
1653 Con_Print("prespawn not valid -- already spawned\n");
1657 if (host_client->netconnection)
1659 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1660 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1661 MSG_WriteByte (&host_client->netconnection->message, 2);
1662 host_client->sendsignon = 0; // enable unlimited sends again
1665 // reset the name change timer because the client will send name soon
1666 host_client->nametime = 0;
1674 void Host_Spawn_f (void)
1678 int stats[MAX_CL_STATS];
1680 if (host_client->spawned)
1682 Con_Print("Spawn not valid -- already spawned\n");
1686 // reset name change timer again because they might want to change name
1687 // again in the first 5 seconds after connecting
1688 host_client->nametime = 0;
1690 // LordHavoc: moved this above the QC calls at FrikaC's request
1691 // LordHavoc: commented this out
1692 //if (host_client->netconnection)
1693 // SZ_Clear (&host_client->netconnection->message);
1695 // run the entrance script
1698 // loaded games are fully initialized already
1699 if (prog->funcoffsets.RestoreGame)
1701 Con_DPrint("Calling RestoreGame\n");
1702 prog->globals.server->time = sv.time;
1703 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1704 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1709 //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);
1711 // copy spawn parms out of the client_t
1712 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1713 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1715 // call the spawn function
1716 host_client->clientconnectcalled = true;
1717 prog->globals.server->time = sv.time;
1718 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1719 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1721 if (cls.state == ca_dedicated)
1722 Con_Printf("%s connected\n", host_client->name);
1724 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1727 if (!host_client->netconnection)
1730 // send time of update
1731 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1732 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1734 // send all current names, colors, and frag counts
1735 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1737 if (!client->active)
1739 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1740 MSG_WriteByte (&host_client->netconnection->message, i);
1741 MSG_WriteString (&host_client->netconnection->message, client->name);
1742 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1743 MSG_WriteByte (&host_client->netconnection->message, i);
1744 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1745 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1746 MSG_WriteByte (&host_client->netconnection->message, i);
1747 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1750 // send all current light styles
1751 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1753 if (sv.lightstyles[i][0])
1755 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1756 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1757 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1762 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1763 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1764 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1766 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1767 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1768 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1770 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1771 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1772 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1774 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1775 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1776 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1779 // Never send a roll angle, because savegames can catch the server
1780 // in a state where it is expecting the client to correct the angle
1781 // and it won't happen if the game was just loaded, so you wind up
1782 // with a permanent head tilt
1785 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1786 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1787 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1788 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1792 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1793 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1794 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1795 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1798 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1800 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1801 MSG_WriteByte (&host_client->netconnection->message, 3);
1809 void Host_Begin_f (void)
1811 host_client->spawned = true;
1813 // LordHavoc: note: this code also exists in SV_DropClient
1817 for (i = 0;i < svs.maxclients;i++)
1818 if (svs.clients[i].active && !svs.clients[i].spawned)
1820 if (i == svs.maxclients)
1822 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1823 sv.paused = sv.loadgame = false; // we're basically done with loading now
1828 //===========================================================================
1835 Kicks a user off of the server
1838 void Host_Kick_f (void)
1841 const char *message = NULL;
1844 qboolean byNumber = false;
1852 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1854 i = (int)(atof(Cmd_Argv(2)) - 1);
1855 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1861 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1863 if (!host_client->active)
1865 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1870 if (i < svs.maxclients)
1872 if (cmd_source == src_command)
1874 if (cls.state == ca_dedicated)
1877 who = cl_name.string;
1882 // can't kick yourself!
1883 if (host_client == save)
1888 message = Cmd_Args();
1889 COM_ParseToken_Simple(&message, false, false);
1892 message++; // skip the #
1893 while (*message == ' ') // skip white space
1895 message += strlen(Cmd_Argv(2)); // skip the number
1897 while (*message && *message == ' ')
1901 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1903 SV_ClientPrintf("Kicked by %s\n", who);
1904 SV_DropClient (false); // kicked
1912 ===============================================================================
1916 ===============================================================================
1924 void Host_Give_f (void)
1932 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1937 v = atoi (Cmd_Argv(2));
1951 // MED 01/04/97 added hipnotic give stuff
1952 if (gamemode == GAME_HIPNOTIC)
1957 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1959 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1961 else if (t[0] == '9')
1962 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1963 else if (t[0] == '0')
1964 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1965 else if (t[0] >= '2')
1966 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1971 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1976 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1979 host_client->edict->fields.server->ammo_shells = v;
1982 if (gamemode == GAME_ROGUE)
1984 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1987 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1988 host_client->edict->fields.server->ammo_nails = v;
1993 host_client->edict->fields.server->ammo_nails = v;
1997 if (gamemode == GAME_ROGUE)
1999 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
2003 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2004 host_client->edict->fields.server->ammo_nails = v;
2009 if (gamemode == GAME_ROGUE)
2011 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2015 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2016 host_client->edict->fields.server->ammo_rockets = v;
2021 host_client->edict->fields.server->ammo_rockets = v;
2025 if (gamemode == GAME_ROGUE)
2027 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2031 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2032 host_client->edict->fields.server->ammo_rockets = v;
2037 host_client->edict->fields.server->health = v;
2040 if (gamemode == GAME_ROGUE)
2042 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2046 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2047 host_client->edict->fields.server->ammo_cells = v;
2052 host_client->edict->fields.server->ammo_cells = v;
2056 if (gamemode == GAME_ROGUE)
2058 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2062 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2063 host_client->edict->fields.server->ammo_cells = v;
2070 prvm_edict_t *FindViewthing (void)
2075 for (i=0 ; i<prog->num_edicts ; i++)
2077 e = PRVM_EDICT_NUM(i);
2078 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2081 Con_Print("No viewthing on map\n");
2090 void Host_Viewmodel_f (void)
2099 e = FindViewthing ();
2104 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2105 if (!m || !m->loaded || !m->Draw)
2107 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2111 e->fields.server->frame = 0;
2112 cl.model_precache[(int)e->fields.server->modelindex] = m;
2120 void Host_Viewframe_f (void)
2130 e = FindViewthing ();
2134 m = cl.model_precache[(int)e->fields.server->modelindex];
2136 f = atoi(Cmd_Argv(1));
2137 if (f >= m->numframes)
2140 e->fields.server->frame = f;
2144 void PrintFrameName (dp_model_t *m, int frame)
2147 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2149 Con_Printf("frame %i\n", frame);
2157 void Host_Viewnext_f (void)
2166 e = FindViewthing ();
2170 m = cl.model_precache[(int)e->fields.server->modelindex];
2172 e->fields.server->frame = e->fields.server->frame + 1;
2173 if (e->fields.server->frame >= m->numframes)
2174 e->fields.server->frame = m->numframes - 1;
2176 PrintFrameName (m, (int)e->fields.server->frame);
2184 void Host_Viewprev_f (void)
2193 e = FindViewthing ();
2198 m = cl.model_precache[(int)e->fields.server->modelindex];
2200 e->fields.server->frame = e->fields.server->frame - 1;
2201 if (e->fields.server->frame < 0)
2202 e->fields.server->frame = 0;
2204 PrintFrameName (m, (int)e->fields.server->frame);
2208 ===============================================================================
2212 ===============================================================================
2221 void Host_Startdemos_f (void)
2225 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2231 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2234 Con_DPrintf("%i demo(s) in loop\n", c);
2236 for (i=1 ; i<c+1 ; i++)
2237 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2239 // LordHavoc: clear the remaining slots
2240 for (;i <= MAX_DEMOS;i++)
2241 cls.demos[i-1][0] = 0;
2243 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2257 Return to looping demos
2260 void Host_Demos_f (void)
2262 if (cls.state == ca_dedicated)
2264 if (cls.demonum == -1)
2274 Return to looping demos
2277 void Host_Stopdemo_f (void)
2279 if (!cls.demoplayback)
2282 Host_ShutdownServer ();
2285 void Host_SendCvar_f (void)
2289 const char *cvarname;
2294 cvarname = Cmd_Argv(1);
2295 if (cls.state == ca_connected)
2297 c = Cvar_FindVar(cvarname);
2298 // LordHavoc: if there is no such cvar or if it is private, send a
2299 // reply indicating that it has no value
2300 if(!c || (c->flags & CVAR_PRIVATE))
2301 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2303 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2306 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2310 if (cls.state != ca_dedicated)
2314 for(;i<svs.maxclients;i++)
2315 if(svs.clients[i].active && svs.clients[i].netconnection)
2317 host_client = &svs.clients[i];
2318 Host_ClientCommands("sendcvar %s\n", cvarname);
2323 static void MaxPlayers_f(void)
2327 if (Cmd_Argc() != 2)
2329 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2335 Con_Print("maxplayers can not be changed while a server is running.\n");
2336 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2339 n = atoi(Cmd_Argv(1));
2340 n = bound(1, n, MAX_SCOREBOARD);
2341 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2343 svs.maxclients_next = n;
2345 Cvar_Set ("deathmatch", "0");
2347 Cvar_Set ("deathmatch", "1");
2350 //=============================================================================
2352 // QuakeWorld commands
2355 =====================
2358 Send the rest of the command line over as
2359 an unconnected command.
2360 =====================
2362 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2366 lhnetsocket_t *mysocket;
2368 if (!rcon_password.string || !rcon_password.string[0])
2370 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2374 for (i = 0;rcon_password.string[i];i++)
2376 if (ISWHITESPACE(rcon_password.string[i]))
2378 Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2384 to = cls.netcon->peeraddress;
2387 if (!rcon_address.string[0])
2389 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2392 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2394 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2397 // simply put together the rcon packet and send it
2398 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer)
2402 dpsnprintf(argbuf, sizeof(argbuf), "%ld %s", (long) time(NULL), Cmd_Args());
2403 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2404 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
2407 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2408 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2413 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
2419 ====================
2422 user <name or userid>
2424 Dump userdata / masterdata for a user
2425 ====================
2427 void Host_User_f (void) // credit: taken from QuakeWorld
2432 if (Cmd_Argc() != 2)
2434 Con_Printf ("Usage: user <username / userid>\n");
2438 uid = atoi(Cmd_Argv(1));
2440 for (i = 0;i < cl.maxclients;i++)
2442 if (!cl.scores[i].name[0])
2444 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2446 InfoString_Print(cl.scores[i].qw_userinfo);
2450 Con_Printf ("User not in server.\n");
2454 ====================
2457 Dump userids for all current players
2458 ====================
2460 void Host_Users_f (void) // credit: taken from QuakeWorld
2466 Con_Printf ("userid frags name\n");
2467 Con_Printf ("------ ----- ----\n");
2468 for (i = 0;i < cl.maxclients;i++)
2470 if (cl.scores[i].name[0])
2472 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2477 Con_Printf ("%i total users\n", c);
2482 Host_FullServerinfo_f
2484 Sent by server when serverinfo changes
2487 // TODO: shouldn't this be a cvar instead?
2488 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2491 if (Cmd_Argc() != 2)
2493 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2497 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2498 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2499 cl.qw_teamplay = atoi(temp);
2506 Allow clients to change userinfo
2510 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2517 if (Cmd_Argc() != 2)
2519 Con_Printf ("fullinfo <complete info string>\n");
2529 while (*s && *s != '\\')
2535 Con_Printf ("MISSING VALUE\n");
2541 while (*s && *s != '\\')
2548 CL_SetInfo(key, value, false, false, false, false);
2556 Allow clients to change userinfo
2559 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2561 if (Cmd_Argc() == 1)
2563 InfoString_Print(cls.userinfo);
2566 if (Cmd_Argc() != 3)
2568 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2571 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2575 ====================
2578 packet <destination> <contents>
2580 Contents allows \n escape character
2581 ====================
2583 void Host_Packet_f (void) // credit: taken from QuakeWorld
2589 lhnetaddress_t address;
2590 lhnetsocket_t *mysocket;
2592 if (Cmd_Argc() != 3)
2594 Con_Printf ("packet <destination> <contents>\n");
2598 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2600 Con_Printf ("Bad address\n");
2606 send[0] = send[1] = send[2] = send[3] = 0xff;
2608 l = (int)strlen (in);
2609 for (i=0 ; i<l ; i++)
2611 if (out >= send + sizeof(send) - 1)
2613 if (in[i] == '\\' && in[i+1] == 'n')
2618 else if (in[i] == '\\' && in[i+1] == '0')
2623 else if (in[i] == '\\' && in[i+1] == 't')
2628 else if (in[i] == '\\' && in[i+1] == 'r')
2633 else if (in[i] == '\\' && in[i+1] == '"')
2642 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2644 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2646 NetConn_Write(mysocket, send, out - send, &address);
2650 ====================
2653 Send back ping and packet loss update for all current players to this player
2654 ====================
2656 void Host_Pings_f (void)
2658 int i, j, ping, packetloss;
2661 if (!host_client->netconnection)
2664 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2666 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2667 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2669 for (i = 0;i < svs.maxclients;i++)
2672 if (svs.clients[i].netconnection)
2673 for (j = 0;j < NETGRAPH_PACKETS;j++)
2674 if (svs.clients[i].netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
2676 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
2677 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2678 ping = bound(0, ping, 9999);
2679 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2681 // send qw_svc_updateping and qw_svc_updatepl messages
2682 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2683 MSG_WriteShort(&host_client->netconnection->message, ping);
2684 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2685 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2689 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2690 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2691 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2694 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2695 MSG_WriteString(&host_client->netconnection->message, "\n");
2698 void Host_PingPLReport_f(void)
2702 if (l > cl.maxclients)
2704 for (i = 0;i < l;i++)
2706 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2707 cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
2711 //=============================================================================
2718 void Host_InitCommands (void)
2720 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2722 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2723 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2724 if (gamemode == GAME_NEHAHRA)
2726 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2727 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2728 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2729 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2730 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2734 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2735 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2736 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2737 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2738 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2740 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2741 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2742 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2743 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2744 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)");
2745 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2746 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2747 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2748 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2749 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2750 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2751 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2752 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2753 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2754 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2756 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2757 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2758 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2760 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2761 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2762 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2763 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2765 Cvar_RegisterVariable (&cl_name);
2766 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2767 Cvar_RegisterVariable (&cl_color);
2768 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2769 Cvar_RegisterVariable (&cl_rate);
2770 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2771 if (gamemode == GAME_NEHAHRA)
2773 Cvar_RegisterVariable (&cl_pmodel);
2774 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2777 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2778 Cvar_RegisterVariable (&cl_playermodel);
2779 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2780 Cvar_RegisterVariable (&cl_playerskin);
2781 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2783 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2784 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2785 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)");
2786 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2788 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2790 Cvar_RegisterVariable (&rcon_password);
2791 Cvar_RegisterVariable (&rcon_address);
2792 Cvar_RegisterVariable (&rcon_secure);
2793 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)");
2794 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");
2795 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2796 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2797 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2798 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2799 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2800 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2801 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2802 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2804 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)");
2805 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)");
2807 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)");
2808 Cvar_RegisterVariable (&r_fixtrans_auto);
2810 Cvar_RegisterVariable (&team);
2811 Cvar_RegisterVariable (&skin);
2812 Cvar_RegisterVariable (&noaim);
2814 Cvar_RegisterVariable(&sv_cheats);
2815 Cvar_RegisterVariable(&sv_adminnick);
2816 Cvar_RegisterVariable(&sv_status_privacy);
2817 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2820 void Host_NoOperation_f(void)