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 (1 = time based, 2 = challenge based); 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_netgraph[j].unreliablebytes == 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 %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
177 else if (in == 1) // extended layout
179 print ("%s%-21s %2i %4i %2i:%02i:%02i %4i #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, packetloss, ping, hours, minutes, seconds, frags, i+1, client->name);
181 else if (in == 2) // reduced layout
183 print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
187 if(cmd_source == src_command)
196 Sets client to godmode
199 void Host_God_f (void)
203 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
207 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
208 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
209 SV_ClientPrint("godmode OFF\n");
211 SV_ClientPrint("godmode ON\n");
214 void Host_Notarget_f (void)
218 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
222 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
223 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
224 SV_ClientPrint("notarget OFF\n");
226 SV_ClientPrint("notarget ON\n");
229 qboolean noclip_anglehack;
231 void Host_Noclip_f (void)
235 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
239 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
241 noclip_anglehack = true;
242 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
243 SV_ClientPrint("noclip ON\n");
247 noclip_anglehack = false;
248 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
249 SV_ClientPrint("noclip OFF\n");
257 Sets client to flymode
260 void Host_Fly_f (void)
264 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
268 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
270 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
271 SV_ClientPrint("flymode ON\n");
275 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
276 SV_ClientPrint("flymode OFF\n");
287 void Host_Pings_f (void); // called by Host_Ping_f
288 void Host_Ping_f (void)
292 void (*print) (const char *fmt, ...);
294 if (cmd_source == src_command)
296 // if running a client, try to send over network so the client's ping report parser will see the report
297 if (cls.state == ca_connected)
299 Cmd_ForwardToServer ();
305 print = SV_ClientPrintf;
310 print("Client ping times:\n");
311 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
315 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
318 // 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)
319 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
324 ===============================================================================
328 ===============================================================================
332 ======================
337 command from the console. Active clients are kicked off.
338 ======================
340 void Host_Map_f (void)
342 char level[MAX_QPATH];
346 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
350 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
351 if (gamemode == GAME_DELUXEQUAKE)
352 Cvar_Set("warpmark", "");
354 cls.demonum = -1; // stop demo loop in case this fails
357 Host_ShutdownServer();
359 if(svs.maxclients != svs.maxclients_next)
361 svs.maxclients = svs.maxclients_next;
363 Mem_Free(svs.clients);
364 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
370 svs.serverflags = 0; // haven't completed an episode yet
371 allowcheats = sv_cheats.integer != 0;
372 strlcpy(level, Cmd_Argv(1), sizeof(level));
373 SV_SpawnServer(level);
374 if (sv.active && cls.state == ca_disconnected)
375 CL_EstablishConnection("local:1");
382 Goes to a new map, taking all clients along
385 void Host_Changelevel_f (void)
387 char level[MAX_QPATH];
391 Con_Print("changelevel <levelname> : continue game on a new level\n");
404 SV_SaveSpawnparms ();
406 allowcheats = sv_cheats.integer != 0;
407 strlcpy(level, Cmd_Argv(1), sizeof(level));
408 SV_SpawnServer(level);
409 if (sv.active && cls.state == ca_disconnected)
410 CL_EstablishConnection("local:1");
417 Restarts the current server for a dead player
420 void Host_Restart_f (void)
422 char mapname[MAX_QPATH];
426 Con_Print("restart : restart current level\n");
431 Con_Print("Only the server may restart\n");
438 allowcheats = sv_cheats.integer != 0;
439 strlcpy(mapname, sv.name, sizeof(mapname));
440 SV_SpawnServer(mapname);
441 if (sv.active && cls.state == ca_disconnected)
442 CL_EstablishConnection("local:1");
449 This command causes the client to wait for the signon messages again.
450 This is sent just before a server changes levels
453 void Host_Reconnect_f (void)
456 // if not connected, reconnect to the most recent server
459 // if we have connected to a server recently, the userinfo
460 // will still contain its IP address, so get the address...
461 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
463 CL_EstablishConnection(temp);
465 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
468 // if connected, do something based on protocol
469 if (cls.protocol == PROTOCOL_QUAKEWORLD)
471 // quakeworld can just re-login
472 if (cls.qw_downloadmemory) // don't change when downloading
477 if (cls.state == ca_connected && cls.signon < SIGNONS)
479 Con_Printf("reconnecting...\n");
480 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
481 MSG_WriteString(&cls.netcon->message, "new");
486 // netquake uses reconnect on level changes (silly)
489 Con_Print("reconnect : wait for signon messages again\n");
494 Con_Print("reconnect: no signon, ignoring reconnect\n");
497 cls.signon = 0; // need new connection messages
502 =====================
505 User command to connect to server
506 =====================
508 void Host_Connect_f (void)
512 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
515 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
516 if(!rcon_secure.integer)
517 Cvar_SetQuick(&rcon_password, "");
518 CL_EstablishConnection(Cmd_Argv(1));
523 ===============================================================================
527 ===============================================================================
530 #define SAVEGAME_VERSION 5
532 void Host_Savegame_to (const char *name)
535 int i, lightstyles = 64;
536 char comment[SAVEGAME_COMMENT_LENGTH+1];
539 // first we have to figure out if this can be saved in 64 lightstyles
540 // (for Quake compatibility)
541 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
542 if (sv.lightstyles[i][0])
545 isserver = !strcmp(PRVM_NAME, "server");
547 Con_Printf("Saving game to %s...\n", name);
548 f = FS_OpenRealFile(name, "wb", false);
551 Con_Print("ERROR: couldn't open.\n");
555 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
557 memset(comment, 0, sizeof(comment));
559 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);
561 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
562 // convert space to _ to make stdio happy
563 // LordHavoc: convert control characters to _ as well
564 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
565 if (ISWHITESPACEORCONTROL(comment[i]))
567 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
569 FS_Printf(f, "%s\n", comment);
572 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
573 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
574 FS_Printf(f, "%d\n", current_skill);
575 FS_Printf(f, "%s\n", sv.name);
576 FS_Printf(f, "%f\n",sv.time);
580 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
581 FS_Printf(f, "(dummy)\n");
582 FS_Printf(f, "%d\n", 0);
583 FS_Printf(f, "%s\n", "(dummy)");
584 FS_Printf(f, "%f\n", realtime);
587 // write the light styles
588 for (i=0 ; i<lightstyles ; i++)
590 if (isserver && sv.lightstyles[i][0])
591 FS_Printf(f, "%s\n", sv.lightstyles[i]);
596 PRVM_ED_WriteGlobals (f);
597 for (i=0 ; i<prog->num_edicts ; i++)
599 FS_Printf(f,"// edict %d\n", i);
600 //Con_Printf("edict %d...\n", i);
601 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
606 FS_Printf(f,"// DarkPlaces extended savegame\n");
607 // darkplaces extension - extra lightstyles, support for color lightstyles
608 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
609 if (isserver && sv.lightstyles[i][0])
610 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
612 // darkplaces extension - model precaches
613 for (i=1 ; i<MAX_MODELS ; i++)
614 if (sv.model_precache[i][0])
615 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
617 // darkplaces extension - sound precaches
618 for (i=1 ; i<MAX_SOUNDS ; i++)
619 if (sv.sound_precache[i][0])
620 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
625 Con_Print("done.\n");
633 void Host_Savegame_f (void)
635 char name[MAX_QPATH];
639 Con_Print("Can't save - no server running.\n");
645 // singleplayer checks
648 Con_Print("Can't save in intermission.\n");
652 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
654 Con_Print("Can't savegame with a dead player\n");
659 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");
663 Con_Print("save <savename> : save a game\n");
667 if (strstr(Cmd_Argv(1), ".."))
669 Con_Print("Relative pathnames are not allowed.\n");
673 strlcpy (name, Cmd_Argv(1), sizeof (name));
674 FS_DefaultExtension (name, ".sav", sizeof (name));
677 Host_Savegame_to(name);
687 void Host_Loadgame_f (void)
689 char filename[MAX_QPATH];
690 char mapname[MAX_QPATH];
700 float spawn_parms[NUM_SPAWN_PARMS];
704 Con_Print("load <savename> : load a game\n");
708 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
709 FS_DefaultExtension (filename, ".sav", sizeof (filename));
711 Con_Printf("Loading game from %s...\n", filename);
713 // stop playing demos
714 if (cls.demoplayback)
720 cls.demonum = -1; // stop demo loop in case this fails
722 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
725 Con_Print("ERROR: couldn't open.\n");
729 if(developer_entityparsing.integer)
730 Con_Printf("Host_Loadgame_f: loading version\n");
733 COM_ParseToken_Simple(&t, false, false);
734 version = atoi(com_token);
735 if (version != SAVEGAME_VERSION)
738 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
742 if(developer_entityparsing.integer)
743 Con_Printf("Host_Loadgame_f: loading description\n");
746 COM_ParseToken_Simple(&t, false, false);
748 for (i = 0;i < NUM_SPAWN_PARMS;i++)
750 COM_ParseToken_Simple(&t, false, false);
751 spawn_parms[i] = atof(com_token);
754 COM_ParseToken_Simple(&t, false, false);
755 // this silliness is so we can load 1.06 save files, which have float skill values
756 current_skill = (int)(atof(com_token) + 0.5);
757 Cvar_SetValue ("skill", (float)current_skill);
759 if(developer_entityparsing.integer)
760 Con_Printf("Host_Loadgame_f: loading mapname\n");
763 COM_ParseToken_Simple(&t, false, false);
764 strlcpy (mapname, com_token, sizeof(mapname));
766 if(developer_entityparsing.integer)
767 Con_Printf("Host_Loadgame_f: loading time\n");
770 COM_ParseToken_Simple(&t, false, false);
771 time = atof(com_token);
773 allowcheats = sv_cheats.integer != 0;
775 if(developer_entityparsing.integer)
776 Con_Printf("Host_Loadgame_f: spawning server\n");
778 SV_SpawnServer (mapname);
782 Con_Print("Couldn't load map\n");
785 sv.paused = true; // pause until all clients connect
788 if(developer_entityparsing.integer)
789 Con_Printf("Host_Loadgame_f: loading light styles\n");
791 // load the light styles
797 for (i = 0;i < MAX_LIGHTSTYLES;i++)
801 COM_ParseToken_Simple(&t, false, false);
802 // if this is a 64 lightstyle savegame produced by Quake, stop now
803 // we have to check this because darkplaces may save more than 64
804 if (com_token[0] == '{')
809 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
812 if(developer_entityparsing.integer)
813 Con_Printf("Host_Loadgame_f: skipping until globals\n");
815 // now skip everything before the first opening brace
816 // (this is for forward compatibility, so that older versions (at
817 // least ones with this fix) can load savegames with extra data before the
818 // first brace, as might be produced by a later engine version)
822 if (!COM_ParseToken_Simple(&t, false, false))
824 if (com_token[0] == '{')
831 // load the edicts out of the savegame file
836 while (COM_ParseToken_Simple(&t, false, false))
837 if (!strcmp(com_token, "}"))
839 if (!COM_ParseToken_Simple(&start, false, false))
844 if (strcmp(com_token,"{"))
847 Host_Error ("First token isn't a brace");
852 if(developer_entityparsing.integer)
853 Con_Printf("Host_Loadgame_f: loading globals\n");
855 // parse the global vars
856 PRVM_ED_ParseGlobals (start);
861 if (entnum >= MAX_EDICTS)
864 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
866 while (entnum >= prog->max_edicts)
867 PRVM_MEM_IncreaseEdicts();
868 ent = PRVM_EDICT_NUM(entnum);
869 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
870 ent->priv.server->free = false;
872 if(developer_entityparsing.integer)
873 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
875 PRVM_ED_ParseEdict (start, ent);
877 // link it into the bsp tree
878 if (!ent->priv.server->free)
879 SV_LinkEdict (ent, false);
886 prog->num_edicts = entnum;
889 for (i = 0;i < NUM_SPAWN_PARMS;i++)
890 svs.clients[0].spawn_parms[i] = spawn_parms[i];
892 if(developer_entityparsing.integer)
893 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
895 // read extended data if present
896 // the extended data is stored inside a /* */ comment block, which the
897 // parser intentionally skips, so we have to check for it manually here
900 while (*end == '\r' || *end == '\n')
902 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
904 if(developer_entityparsing.integer)
905 Con_Printf("Host_Loadgame_f: loading extended data\n");
907 Con_Printf("Loading extended DarkPlaces savegame\n");
909 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
910 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
911 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
912 while (COM_ParseToken_Simple(&t, false, false))
914 if (!strcmp(com_token, "sv.lightstyles"))
916 COM_ParseToken_Simple(&t, false, false);
918 COM_ParseToken_Simple(&t, false, false);
919 if (i >= 0 && i < MAX_LIGHTSTYLES)
920 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
922 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
924 else if (!strcmp(com_token, "sv.model_precache"))
926 COM_ParseToken_Simple(&t, false, false);
928 COM_ParseToken_Simple(&t, false, false);
929 if (i >= 0 && i < MAX_MODELS)
931 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
932 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
935 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
937 else if (!strcmp(com_token, "sv.sound_precache"))
939 COM_ParseToken_Simple(&t, false, false);
941 COM_ParseToken_Simple(&t, false, false);
942 if (i >= 0 && i < MAX_SOUNDS)
943 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
945 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
947 // skip any trailing text or unrecognized commands
948 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
955 if(developer_entityparsing.integer)
956 Con_Printf("Host_Loadgame_f: finished\n");
960 // make sure we're connected to loopback
961 if (sv.active && cls.state == ca_disconnected)
962 CL_EstablishConnection("local:1");
965 //============================================================================
968 ======================
970 ======================
972 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
973 void Host_Name_f (void)
976 qboolean valid_colors;
977 const char *newNameSource;
978 char newName[sizeof(host_client->name)];
980 if (Cmd_Argc () == 1)
982 Con_Printf("name: %s\n", cl_name.string);
986 if (Cmd_Argc () == 2)
987 newNameSource = Cmd_Argv(1);
989 newNameSource = Cmd_Args();
991 strlcpy(newName, newNameSource, sizeof(newName));
993 if (cmd_source == src_command)
995 Cvar_Set ("_cl_name", newName);
996 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
998 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
999 Con_Printf("name: %s\n", cl_name.string);
1004 if (realtime < host_client->nametime)
1006 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1010 host_client->nametime = realtime + 5;
1012 // point the string back at updateclient->name to keep it safe
1013 strlcpy (host_client->name, newName, sizeof (host_client->name));
1015 for (i = 0, j = 0;host_client->name[i];i++)
1016 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1017 host_client->name[j++] = host_client->name[i];
1018 host_client->name[j] = 0;
1020 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1021 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1023 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1024 host_client->name[sizeof(host_client->name) - 1] = 0;
1025 host_client->name[0] = STRING_COLOR_TAG;
1026 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1029 COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1030 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1033 l = strlen(host_client->name);
1034 if(l < sizeof(host_client->name) - 1)
1036 // duplicate the color tag to escape it
1037 host_client->name[i] = STRING_COLOR_TAG;
1038 host_client->name[i+1] = 0;
1039 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1043 // remove the last character to fix the color code
1044 host_client->name[l-1] = 0;
1045 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1049 // find the last color tag offset and decide if we need to add a reset tag
1050 for (i = 0, j = -1;host_client->name[i];i++)
1052 if (host_client->name[i] == STRING_COLOR_TAG)
1054 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1057 // if this happens to be a reset tag then we don't need one
1058 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1063 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]))
1069 if (host_client->name[i+1] == STRING_COLOR_TAG)
1076 // does not end in the default color string, so add it
1077 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1078 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1080 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1081 if (strcmp(host_client->old_name, host_client->name))
1083 if (host_client->spawned)
1084 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1085 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1086 // send notification to all clients
1087 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1088 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1089 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1090 SV_WriteNetnameIntoDemo(host_client);
1095 ======================
1097 ======================
1099 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1100 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1101 void Host_Playermodel_f (void)
1104 char newPath[sizeof(host_client->playermodel)];
1106 if (Cmd_Argc () == 1)
1108 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1112 if (Cmd_Argc () == 2)
1113 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1115 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1117 for (i = 0, j = 0;newPath[i];i++)
1118 if (newPath[i] != '\r' && newPath[i] != '\n')
1119 newPath[j++] = newPath[i];
1122 if (cmd_source == src_command)
1124 Cvar_Set ("_cl_playermodel", newPath);
1129 if (realtime < host_client->nametime)
1131 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1135 host_client->nametime = realtime + 5;
1138 // point the string back at updateclient->name to keep it safe
1139 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1140 if( prog->fieldoffsets.playermodel >= 0 )
1141 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1142 if (strcmp(host_client->old_model, host_client->playermodel))
1144 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1145 /*// send notification to all clients
1146 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1147 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1148 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1153 ======================
1155 ======================
1157 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1158 void Host_Playerskin_f (void)
1161 char newPath[sizeof(host_client->playerskin)];
1163 if (Cmd_Argc () == 1)
1165 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1169 if (Cmd_Argc () == 2)
1170 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1172 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1174 for (i = 0, j = 0;newPath[i];i++)
1175 if (newPath[i] != '\r' && newPath[i] != '\n')
1176 newPath[j++] = newPath[i];
1179 if (cmd_source == src_command)
1181 Cvar_Set ("_cl_playerskin", newPath);
1186 if (realtime < host_client->nametime)
1188 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1192 host_client->nametime = realtime + 5;
1195 // point the string back at updateclient->name to keep it safe
1196 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1197 if( prog->fieldoffsets.playerskin >= 0 )
1198 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1199 if (strcmp(host_client->old_skin, host_client->playerskin))
1201 //if (host_client->spawned)
1202 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1203 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1204 /*// send notification to all clients
1205 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1206 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1207 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1211 void Host_Version_f (void)
1213 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1216 void Host_Say(qboolean teamonly)
1222 // LordHavoc: long say messages
1224 qboolean fromServer = false;
1226 if (cmd_source == src_command)
1228 if (cls.state == ca_dedicated)
1235 Cmd_ForwardToServer ();
1240 if (Cmd_Argc () < 2)
1243 if (!teamplay.integer)
1253 // note this uses the chat prefix \001
1254 if (!fromServer && !teamonly)
1255 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1256 else if (!fromServer && teamonly)
1257 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1258 else if(*(sv_adminnick.string))
1259 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1261 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1262 p2 = text + strlen(text);
1263 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1265 if (p2[-1] == '\"' && quoted)
1270 strlcat(text, "\n", sizeof(text));
1272 // note: save is not a valid edict if fromServer is true
1274 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1275 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1276 SV_ClientPrint(text);
1279 if (cls.state == ca_dedicated)
1280 Con_Print(&text[1]);
1284 void Host_Say_f(void)
1290 void Host_Say_Team_f(void)
1296 void Host_Tell_f(void)
1298 const char *playername_start = NULL;
1299 size_t playername_length = 0;
1300 int playernumber = 0;
1303 const char *p1, *p2;
1304 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1305 qboolean fromServer = false;
1307 if (cmd_source == src_command)
1309 if (cls.state == ca_dedicated)
1313 Cmd_ForwardToServer ();
1318 if (Cmd_Argc () < 2)
1321 // note this uses the chat prefix \001
1323 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1324 else if(*(sv_adminnick.string))
1325 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1327 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1330 p2 = p1 + strlen(p1);
1331 // remove the target name
1332 while (p1 < p2 && *p1 == ' ')
1337 while (p1 < p2 && *p1 == ' ')
1339 while (p1 < p2 && isdigit(*p1))
1341 playernumber = playernumber * 10 + (*p1 - '0');
1349 playername_start = p1;
1350 while (p1 < p2 && *p1 != '"')
1352 playername_length = p1 - playername_start;
1358 playername_start = p1;
1359 while (p1 < p2 && *p1 != ' ')
1361 playername_length = p1 - playername_start;
1363 while (p1 < p2 && *p1 == ' ')
1365 if(playername_start)
1367 // set playernumber to the right client
1369 if(playername_length >= sizeof(namebuf))
1372 Con_Print("Host_Tell: too long player name/ID\n");
1374 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1377 memcpy(namebuf, playername_start, playername_length);
1378 namebuf[playername_length] = 0;
1379 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1381 if (!svs.clients[playernumber].active)
1383 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1387 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1390 Con_Print("Host_Tell: invalid player name/ID\n");
1392 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1395 // remove trailing newlines
1396 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1398 // remove quotes if present
1404 else if (fromServer)
1405 Con_Print("Host_Tell: missing end quote\n");
1407 SV_ClientPrint("Host_Tell: missing end quote\n");
1409 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1412 return; // empty say
1413 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1419 host_client = svs.clients + playernumber;
1420 SV_ClientPrint(text);
1430 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1431 void Host_Color(int changetop, int changebottom)
1433 int top, bottom, playercolor;
1435 // get top and bottom either from the provided values or the current values
1436 // (allows changing only top or bottom, or both at once)
1437 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1438 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1442 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1448 playercolor = top*16 + bottom;
1450 if (cmd_source == src_command)
1452 Cvar_SetValueQuick(&cl_color, playercolor);
1456 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1459 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1461 Con_DPrint("Calling SV_ChangeTeam\n");
1462 prog->globals.server->time = sv.time;
1463 prog->globals.generic[OFS_PARM0] = playercolor;
1464 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1465 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1470 if (host_client->edict)
1472 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1473 val->_float = playercolor;
1474 host_client->edict->fields.server->team = bottom + 1;
1476 host_client->colors = playercolor;
1477 if (host_client->old_colors != host_client->colors)
1479 host_client->old_colors = host_client->colors;
1480 // send notification to all clients
1481 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1482 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1483 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1488 void Host_Color_f(void)
1492 if (Cmd_Argc() == 1)
1494 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1495 Con_Print("color <0-15> [0-15]\n");
1499 if (Cmd_Argc() == 2)
1500 top = bottom = atoi(Cmd_Argv(1));
1503 top = atoi(Cmd_Argv(1));
1504 bottom = atoi(Cmd_Argv(2));
1506 Host_Color(top, bottom);
1509 void Host_TopColor_f(void)
1511 if (Cmd_Argc() == 1)
1513 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1514 Con_Print("topcolor <0-15>\n");
1518 Host_Color(atoi(Cmd_Argv(1)), -1);
1521 void Host_BottomColor_f(void)
1523 if (Cmd_Argc() == 1)
1525 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1526 Con_Print("bottomcolor <0-15>\n");
1530 Host_Color(-1, atoi(Cmd_Argv(1)));
1533 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1534 void Host_Rate_f(void)
1538 if (Cmd_Argc() != 2)
1540 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1541 Con_Print("rate <bytespersecond>\n");
1545 rate = atoi(Cmd_Argv(1));
1547 if (cmd_source == src_command)
1549 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1553 host_client->rate = rate;
1561 void Host_Kill_f (void)
1563 if (host_client->edict->fields.server->health <= 0)
1565 SV_ClientPrint("Can't suicide -- already dead!\n");
1569 prog->globals.server->time = sv.time;
1570 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1571 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1580 void Host_Pause_f (void)
1582 if (!pausable.integer)
1583 SV_ClientPrint("Pause not allowed.\n");
1587 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1588 // send notification to all clients
1589 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1590 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1595 ======================
1597 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1598 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1599 ======================
1601 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)"};
1602 static void Host_PModel_f (void)
1607 if (Cmd_Argc () == 1)
1609 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1612 i = atoi(Cmd_Argv(1));
1614 if (cmd_source == src_command)
1616 if (cl_pmodel.integer == i)
1618 Cvar_SetValue ("_cl_pmodel", i);
1619 if (cls.state == ca_connected)
1620 Cmd_ForwardToServer ();
1624 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1628 //===========================================================================
1636 void Host_PreSpawn_f (void)
1638 if (host_client->spawned)
1640 Con_Print("prespawn not valid -- already spawned\n");
1644 if (host_client->netconnection)
1646 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1647 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1648 MSG_WriteByte (&host_client->netconnection->message, 2);
1649 host_client->sendsignon = 0; // enable unlimited sends again
1652 // reset the name change timer because the client will send name soon
1653 host_client->nametime = 0;
1661 void Host_Spawn_f (void)
1665 int stats[MAX_CL_STATS];
1667 if (host_client->spawned)
1669 Con_Print("Spawn not valid -- already spawned\n");
1673 // reset name change timer again because they might want to change name
1674 // again in the first 5 seconds after connecting
1675 host_client->nametime = 0;
1677 // LordHavoc: moved this above the QC calls at FrikaC's request
1678 // LordHavoc: commented this out
1679 //if (host_client->netconnection)
1680 // SZ_Clear (&host_client->netconnection->message);
1682 // run the entrance script
1685 // loaded games are fully initialized already
1686 if (prog->funcoffsets.RestoreGame)
1688 Con_DPrint("Calling RestoreGame\n");
1689 prog->globals.server->time = sv.time;
1690 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1691 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1696 //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);
1698 // copy spawn parms out of the client_t
1699 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1700 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1702 // call the spawn function
1703 host_client->clientconnectcalled = true;
1704 prog->globals.server->time = sv.time;
1705 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1706 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1708 if (cls.state == ca_dedicated)
1709 Con_Printf("%s connected\n", host_client->name);
1711 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1714 if (!host_client->netconnection)
1717 // send time of update
1718 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1719 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1721 // send all current names, colors, and frag counts
1722 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1724 if (!client->active)
1726 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1727 MSG_WriteByte (&host_client->netconnection->message, i);
1728 MSG_WriteString (&host_client->netconnection->message, client->name);
1729 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1730 MSG_WriteByte (&host_client->netconnection->message, i);
1731 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1732 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1733 MSG_WriteByte (&host_client->netconnection->message, i);
1734 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1737 // send all current light styles
1738 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1740 if (sv.lightstyles[i][0])
1742 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1743 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1744 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1749 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1750 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1751 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1753 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1754 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1755 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1757 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1758 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1759 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1761 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1762 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1763 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1766 // Never send a roll angle, because savegames can catch the server
1767 // in a state where it is expecting the client to correct the angle
1768 // and it won't happen if the game was just loaded, so you wind up
1769 // with a permanent head tilt
1772 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1773 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1774 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1775 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1779 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1780 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1781 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1782 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1785 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1787 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1788 MSG_WriteByte (&host_client->netconnection->message, 3);
1796 void Host_Begin_f (void)
1798 host_client->spawned = true;
1800 // LordHavoc: note: this code also exists in SV_DropClient
1804 for (i = 0;i < svs.maxclients;i++)
1805 if (svs.clients[i].active && !svs.clients[i].spawned)
1807 if (i == svs.maxclients)
1809 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1810 sv.paused = sv.loadgame = false; // we're basically done with loading now
1815 //===========================================================================
1822 Kicks a user off of the server
1825 void Host_Kick_f (void)
1828 const char *message = NULL;
1831 qboolean byNumber = false;
1839 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1841 i = (int)(atof(Cmd_Argv(2)) - 1);
1842 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1848 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1850 if (!host_client->active)
1852 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1857 if (i < svs.maxclients)
1859 if (cmd_source == src_command)
1861 if (cls.state == ca_dedicated)
1864 who = cl_name.string;
1869 // can't kick yourself!
1870 if (host_client == save)
1875 message = Cmd_Args();
1876 COM_ParseToken_Simple(&message, false, false);
1879 message++; // skip the #
1880 while (*message == ' ') // skip white space
1882 message += strlen(Cmd_Argv(2)); // skip the number
1884 while (*message && *message == ' ')
1888 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1890 SV_ClientPrintf("Kicked by %s\n", who);
1891 SV_DropClient (false); // kicked
1899 ===============================================================================
1903 ===============================================================================
1911 void Host_Give_f (void)
1919 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1924 v = atoi (Cmd_Argv(2));
1938 // MED 01/04/97 added hipnotic give stuff
1939 if (gamemode == GAME_HIPNOTIC)
1944 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1946 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1948 else if (t[0] == '9')
1949 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1950 else if (t[0] == '0')
1951 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1952 else if (t[0] >= '2')
1953 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1958 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1963 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1966 host_client->edict->fields.server->ammo_shells = v;
1969 if (gamemode == GAME_ROGUE)
1971 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1974 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1975 host_client->edict->fields.server->ammo_nails = v;
1980 host_client->edict->fields.server->ammo_nails = v;
1984 if (gamemode == GAME_ROGUE)
1986 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
1990 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1991 host_client->edict->fields.server->ammo_nails = v;
1996 if (gamemode == GAME_ROGUE)
1998 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2002 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2003 host_client->edict->fields.server->ammo_rockets = v;
2008 host_client->edict->fields.server->ammo_rockets = v;
2012 if (gamemode == GAME_ROGUE)
2014 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2018 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2019 host_client->edict->fields.server->ammo_rockets = v;
2024 host_client->edict->fields.server->health = v;
2027 if (gamemode == GAME_ROGUE)
2029 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2033 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2034 host_client->edict->fields.server->ammo_cells = v;
2039 host_client->edict->fields.server->ammo_cells = v;
2043 if (gamemode == GAME_ROGUE)
2045 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2049 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2050 host_client->edict->fields.server->ammo_cells = v;
2057 prvm_edict_t *FindViewthing (void)
2062 for (i=0 ; i<prog->num_edicts ; i++)
2064 e = PRVM_EDICT_NUM(i);
2065 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2068 Con_Print("No viewthing on map\n");
2077 void Host_Viewmodel_f (void)
2086 e = FindViewthing ();
2091 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2092 if (!m || !m->loaded || !m->Draw)
2094 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2098 e->fields.server->frame = 0;
2099 cl.model_precache[(int)e->fields.server->modelindex] = m;
2107 void Host_Viewframe_f (void)
2117 e = FindViewthing ();
2121 m = cl.model_precache[(int)e->fields.server->modelindex];
2123 f = atoi(Cmd_Argv(1));
2124 if (f >= m->numframes)
2127 e->fields.server->frame = f;
2131 void PrintFrameName (dp_model_t *m, int frame)
2134 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2136 Con_Printf("frame %i\n", frame);
2144 void Host_Viewnext_f (void)
2153 e = FindViewthing ();
2157 m = cl.model_precache[(int)e->fields.server->modelindex];
2159 e->fields.server->frame = e->fields.server->frame + 1;
2160 if (e->fields.server->frame >= m->numframes)
2161 e->fields.server->frame = m->numframes - 1;
2163 PrintFrameName (m, (int)e->fields.server->frame);
2171 void Host_Viewprev_f (void)
2180 e = FindViewthing ();
2185 m = cl.model_precache[(int)e->fields.server->modelindex];
2187 e->fields.server->frame = e->fields.server->frame - 1;
2188 if (e->fields.server->frame < 0)
2189 e->fields.server->frame = 0;
2191 PrintFrameName (m, (int)e->fields.server->frame);
2195 ===============================================================================
2199 ===============================================================================
2208 void Host_Startdemos_f (void)
2212 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2218 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2221 Con_DPrintf("%i demo(s) in loop\n", c);
2223 for (i=1 ; i<c+1 ; i++)
2224 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2226 // LordHavoc: clear the remaining slots
2227 for (;i <= MAX_DEMOS;i++)
2228 cls.demos[i-1][0] = 0;
2230 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2244 Return to looping demos
2247 void Host_Demos_f (void)
2249 if (cls.state == ca_dedicated)
2251 if (cls.demonum == -1)
2261 Return to looping demos
2264 void Host_Stopdemo_f (void)
2266 if (!cls.demoplayback)
2269 Host_ShutdownServer ();
2272 void Host_SendCvar_f (void)
2276 const char *cvarname;
2281 cvarname = Cmd_Argv(1);
2282 if (cls.state == ca_connected)
2284 c = Cvar_FindVar(cvarname);
2285 // LordHavoc: if there is no such cvar or if it is private, send a
2286 // reply indicating that it has no value
2287 if(!c || (c->flags & CVAR_PRIVATE))
2288 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2290 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2293 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2297 if (cls.state != ca_dedicated)
2301 for(;i<svs.maxclients;i++)
2302 if(svs.clients[i].active && svs.clients[i].netconnection)
2304 host_client = &svs.clients[i];
2305 Host_ClientCommands("sendcvar %s\n", cvarname);
2310 static void MaxPlayers_f(void)
2314 if (Cmd_Argc() != 2)
2316 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2322 Con_Print("maxplayers can not be changed while a server is running.\n");
2323 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2326 n = atoi(Cmd_Argv(1));
2327 n = bound(1, n, MAX_SCOREBOARD);
2328 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2330 svs.maxclients_next = n;
2332 Cvar_Set ("deathmatch", "0");
2334 Cvar_Set ("deathmatch", "1");
2338 =====================
2341 ProQuake rcon support
2342 =====================
2344 void Host_PQRcon_f (void)
2348 lhnetsocket_t *mysocket;
2349 char peer_address[64];
2351 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer)
2353 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2357 for (i = 0;rcon_password.string[i];i++)
2359 if (ISWHITESPACE(rcon_password.string[i]))
2361 Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2368 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2372 if (!rcon_address.string[0])
2374 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2377 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2379 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2380 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2383 SZ_Clear(&net_message);
2384 MSG_WriteLong (&net_message, 0);
2385 MSG_WriteByte (&net_message, CCREQ_RCON);
2386 MSG_WriteString (&net_message, rcon_password.string);
2387 MSG_WriteString (&net_message, Cmd_Args());
2388 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2389 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2390 SZ_Clear (&net_message);
2394 //=============================================================================
2396 // QuakeWorld commands
2399 =====================
2402 Send the rest of the command line over as
2403 an unconnected command.
2404 =====================
2406 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2410 lhnetsocket_t *mysocket;
2412 if (!rcon_password.string || !rcon_password.string[0])
2414 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2418 for (i = 0;rcon_password.string[i];i++)
2420 if (ISWHITESPACE(rcon_password.string[i]))
2422 Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2428 to = cls.netcon->peeraddress;
2431 if (!rcon_address.string[0])
2433 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2436 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2438 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2439 if (mysocket && Cmd_Args()[0])
2441 // simply put together the rcon packet and send it
2442 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2444 if(!cls.rcon_commands[cls.rcon_ringpos][0])
2446 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2447 cls.rcon_addresses[cls.rcon_ringpos] = to;
2448 cls.rcon_ringpos = (cls.rcon_ringpos) % 64;
2449 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to);
2451 else if(rcon_secure.integer)
2455 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2456 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2457 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
2460 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2461 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2466 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
2472 ====================
2475 user <name or userid>
2477 Dump userdata / masterdata for a user
2478 ====================
2480 void Host_User_f (void) // credit: taken from QuakeWorld
2485 if (Cmd_Argc() != 2)
2487 Con_Printf ("Usage: user <username / userid>\n");
2491 uid = atoi(Cmd_Argv(1));
2493 for (i = 0;i < cl.maxclients;i++)
2495 if (!cl.scores[i].name[0])
2497 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2499 InfoString_Print(cl.scores[i].qw_userinfo);
2503 Con_Printf ("User not in server.\n");
2507 ====================
2510 Dump userids for all current players
2511 ====================
2513 void Host_Users_f (void) // credit: taken from QuakeWorld
2519 Con_Printf ("userid frags name\n");
2520 Con_Printf ("------ ----- ----\n");
2521 for (i = 0;i < cl.maxclients;i++)
2523 if (cl.scores[i].name[0])
2525 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2530 Con_Printf ("%i total users\n", c);
2535 Host_FullServerinfo_f
2537 Sent by server when serverinfo changes
2540 // TODO: shouldn't this be a cvar instead?
2541 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2544 if (Cmd_Argc() != 2)
2546 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2550 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2551 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2552 cl.qw_teamplay = atoi(temp);
2559 Allow clients to change userinfo
2563 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2570 if (Cmd_Argc() != 2)
2572 Con_Printf ("fullinfo <complete info string>\n");
2582 while (*s && *s != '\\')
2588 Con_Printf ("MISSING VALUE\n");
2594 while (*s && *s != '\\')
2601 CL_SetInfo(key, value, false, false, false, false);
2609 Allow clients to change userinfo
2612 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2614 if (Cmd_Argc() == 1)
2616 InfoString_Print(cls.userinfo);
2619 if (Cmd_Argc() != 3)
2621 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2624 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2628 ====================
2631 packet <destination> <contents>
2633 Contents allows \n escape character
2634 ====================
2636 void Host_Packet_f (void) // credit: taken from QuakeWorld
2642 lhnetaddress_t address;
2643 lhnetsocket_t *mysocket;
2645 if (Cmd_Argc() != 3)
2647 Con_Printf ("packet <destination> <contents>\n");
2651 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2653 Con_Printf ("Bad address\n");
2659 send[0] = send[1] = send[2] = send[3] = -1;
2661 l = (int)strlen (in);
2662 for (i=0 ; i<l ; i++)
2664 if (out >= send + sizeof(send) - 1)
2666 if (in[i] == '\\' && in[i+1] == 'n')
2671 else if (in[i] == '\\' && in[i+1] == '0')
2676 else if (in[i] == '\\' && in[i+1] == 't')
2681 else if (in[i] == '\\' && in[i+1] == 'r')
2686 else if (in[i] == '\\' && in[i+1] == '"')
2695 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2697 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2699 NetConn_Write(mysocket, send, out - send, &address);
2703 ====================
2706 Send back ping and packet loss update for all current players to this player
2707 ====================
2709 void Host_Pings_f (void)
2711 int i, j, ping, packetloss;
2714 if (!host_client->netconnection)
2717 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2719 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2720 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2722 for (i = 0;i < svs.maxclients;i++)
2725 if (svs.clients[i].netconnection)
2726 for (j = 0;j < NETGRAPH_PACKETS;j++)
2727 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2729 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
2730 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2731 ping = bound(0, ping, 9999);
2732 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2734 // send qw_svc_updateping and qw_svc_updatepl messages
2735 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2736 MSG_WriteShort(&host_client->netconnection->message, ping);
2737 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2738 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2742 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2743 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2744 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2747 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2748 MSG_WriteString(&host_client->netconnection->message, "\n");
2751 void Host_PingPLReport_f(void)
2755 if (l > cl.maxclients)
2757 for (i = 0;i < l;i++)
2759 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2760 cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
2764 //=============================================================================
2771 void Host_InitCommands (void)
2773 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2775 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2776 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2777 if (gamemode == GAME_NEHAHRA)
2779 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2780 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2781 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2782 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2783 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2787 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2788 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2789 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2790 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2791 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2793 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2794 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2795 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2796 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2797 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)");
2798 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2799 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2800 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2801 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2802 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2803 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2804 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2805 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2806 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2807 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2809 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2810 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2811 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2813 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2814 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2815 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2816 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2818 Cvar_RegisterVariable (&cl_name);
2819 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2820 Cvar_RegisterVariable (&cl_color);
2821 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2822 Cvar_RegisterVariable (&cl_rate);
2823 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2824 if (gamemode == GAME_NEHAHRA)
2826 Cvar_RegisterVariable (&cl_pmodel);
2827 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2830 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2831 Cvar_RegisterVariable (&cl_playermodel);
2832 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2833 Cvar_RegisterVariable (&cl_playerskin);
2834 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2836 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2837 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2838 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)");
2839 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2841 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2843 Cvar_RegisterVariable (&rcon_password);
2844 Cvar_RegisterVariable (&rcon_address);
2845 Cvar_RegisterVariable (&rcon_secure);
2846 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); note: if rcon_secure is set, client and server clocks must be synced e.g. via NTP");
2847 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; note: client and server clocks must be synced e.g. via NTP");
2848 Cmd_AddCommand ("pqrcon", Host_PQRcon_f, "sends a command to a proquake server console (if your rcon_password matches the server's rcon_password), or to the address specified by rcon_address when not connected (again rcon_password must match the server's)");
2849 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2850 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2851 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2852 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2853 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2854 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2855 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2856 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2858 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)");
2859 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)");
2861 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)");
2862 Cvar_RegisterVariable (&r_fixtrans_auto);
2864 Cvar_RegisterVariable (&team);
2865 Cvar_RegisterVariable (&skin);
2866 Cvar_RegisterVariable (&noaim);
2868 Cvar_RegisterVariable(&sv_cheats);
2869 Cvar_RegisterVariable(&sv_adminnick);
2870 Cvar_RegisterVariable(&sv_status_privacy);
2871 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2874 void Host_NoOperation_f(void)