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_secure_challengetimeout = {0, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"};
38 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
39 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
40 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
41 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
42 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)"};
43 qboolean allowcheats = false;
45 extern qboolean host_shuttingdown;
46 extern cvar_t developer_entityparsing;
54 void Host_Quit_f (void)
57 Con_Printf("shutting down already!\n");
67 void Host_Status_f (void)
71 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
72 void (*print) (const char *fmt, ...);
76 if (cmd_source == src_command)
78 // if running a client, try to send over network so the client's status report parser will see the report
79 if (cls.state == ca_connected)
81 Cmd_ForwardToServer ();
87 print = SV_ClientPrintf;
92 if(cmd_source == src_command)
98 if (strcmp(Cmd_Argv(1), "1") == 0)
100 else if (strcmp(Cmd_Argv(1), "2") == 0)
104 for (players = 0, i = 0;i < svs.maxclients;i++)
105 if (svs.clients[i].active)
107 print ("host: %s\n", Cvar_VariableString ("hostname"));
108 print ("version: %s build %s\n", gamename, buildstring);
109 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
110 print ("map: %s\n", sv.name);
111 print ("timing: %s\n", Host_TimingReport());
112 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
115 print ("^2IP %%pl ping time frags no name\n");
117 print ("^5IP no name\n");
119 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
126 if (in == 0 || in == 1)
128 seconds = (int)(realtime - client->connecttime);
129 minutes = seconds / 60;
132 seconds -= (minutes * 60);
133 hours = minutes / 60;
135 minutes -= (hours * 60);
141 if (client->netconnection)
142 for (j = 0;j < NETGRAPH_PACKETS;j++)
143 if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
145 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
146 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
149 if(sv_status_privacy.integer && cmd_source != src_command)
150 strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
152 strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
154 frags = client->frags;
156 if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
158 const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
164 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
165 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
169 frags = atoi(qcstatus);
173 if (in == 0) // default layout
175 print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
178 else if (in == 1) // extended layout
180 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);
182 else if (in == 2) // reduced layout
184 print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
188 if(cmd_source == src_command)
197 Sets client to godmode
200 void Host_God_f (void)
204 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
208 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
209 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
210 SV_ClientPrint("godmode OFF\n");
212 SV_ClientPrint("godmode ON\n");
215 void Host_Notarget_f (void)
219 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
223 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
224 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
225 SV_ClientPrint("notarget OFF\n");
227 SV_ClientPrint("notarget ON\n");
230 qboolean noclip_anglehack;
232 void Host_Noclip_f (void)
236 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
240 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
242 noclip_anglehack = true;
243 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
244 SV_ClientPrint("noclip ON\n");
248 noclip_anglehack = false;
249 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
250 SV_ClientPrint("noclip OFF\n");
258 Sets client to flymode
261 void Host_Fly_f (void)
265 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
269 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
271 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
272 SV_ClientPrint("flymode ON\n");
276 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
277 SV_ClientPrint("flymode OFF\n");
288 void Host_Pings_f (void); // called by Host_Ping_f
289 void Host_Ping_f (void)
293 void (*print) (const char *fmt, ...);
295 if (cmd_source == src_command)
297 // if running a client, try to send over network so the client's ping report parser will see the report
298 if (cls.state == ca_connected)
300 Cmd_ForwardToServer ();
306 print = SV_ClientPrintf;
311 print("Client ping times:\n");
312 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
316 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
319 // 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)
320 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
325 ===============================================================================
329 ===============================================================================
333 ======================
338 command from the console. Active clients are kicked off.
339 ======================
341 void Host_Map_f (void)
343 char level[MAX_QPATH];
347 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
351 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
352 if (gamemode == GAME_DELUXEQUAKE)
353 Cvar_Set("warpmark", "");
355 cls.demonum = -1; // stop demo loop in case this fails
358 Host_ShutdownServer();
360 if(svs.maxclients != svs.maxclients_next)
362 svs.maxclients = svs.maxclients_next;
364 Mem_Free(svs.clients);
365 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
371 svs.serverflags = 0; // haven't completed an episode yet
372 allowcheats = sv_cheats.integer != 0;
373 strlcpy(level, Cmd_Argv(1), sizeof(level));
374 SV_SpawnServer(level);
375 if (sv.active && cls.state == ca_disconnected)
376 CL_EstablishConnection("local:1");
383 Goes to a new map, taking all clients along
386 void Host_Changelevel_f (void)
388 char level[MAX_QPATH];
392 Con_Print("changelevel <levelname> : continue game on a new level\n");
405 SV_SaveSpawnparms ();
407 allowcheats = sv_cheats.integer != 0;
408 strlcpy(level, Cmd_Argv(1), sizeof(level));
409 SV_SpawnServer(level);
410 if (sv.active && cls.state == ca_disconnected)
411 CL_EstablishConnection("local:1");
418 Restarts the current server for a dead player
421 void Host_Restart_f (void)
423 char mapname[MAX_QPATH];
427 Con_Print("restart : restart current level\n");
432 Con_Print("Only the server may restart\n");
439 allowcheats = sv_cheats.integer != 0;
440 strlcpy(mapname, sv.name, sizeof(mapname));
441 SV_SpawnServer(mapname);
442 if (sv.active && cls.state == ca_disconnected)
443 CL_EstablishConnection("local:1");
450 This command causes the client to wait for the signon messages again.
451 This is sent just before a server changes levels
454 void Host_Reconnect_f (void)
457 // if not connected, reconnect to the most recent server
460 // if we have connected to a server recently, the userinfo
461 // will still contain its IP address, so get the address...
462 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
464 CL_EstablishConnection(temp);
466 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
469 // if connected, do something based on protocol
470 if (cls.protocol == PROTOCOL_QUAKEWORLD)
472 // quakeworld can just re-login
473 if (cls.qw_downloadmemory) // don't change when downloading
478 if (cls.state == ca_connected && cls.signon < SIGNONS)
480 Con_Printf("reconnecting...\n");
481 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
482 MSG_WriteString(&cls.netcon->message, "new");
487 // netquake uses reconnect on level changes (silly)
490 Con_Print("reconnect : wait for signon messages again\n");
495 Con_Print("reconnect: no signon, ignoring reconnect\n");
498 cls.signon = 0; // need new connection messages
503 =====================
506 User command to connect to server
507 =====================
509 void Host_Connect_f (void)
513 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
516 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
517 if(!rcon_secure.integer)
518 Cvar_SetQuick(&rcon_password, "");
519 CL_EstablishConnection(Cmd_Argv(1));
524 ===============================================================================
528 ===============================================================================
531 #define SAVEGAME_VERSION 5
533 void Host_Savegame_to (const char *name)
536 int i, lightstyles = 64;
537 char comment[SAVEGAME_COMMENT_LENGTH+1];
540 // first we have to figure out if this can be saved in 64 lightstyles
541 // (for Quake compatibility)
542 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
543 if (sv.lightstyles[i][0])
546 isserver = !strcmp(PRVM_NAME, "server");
548 Con_Printf("Saving game to %s...\n", name);
549 f = FS_OpenRealFile(name, "wb", false);
552 Con_Print("ERROR: couldn't open.\n");
556 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
558 memset(comment, 0, sizeof(comment));
560 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);
562 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
563 // convert space to _ to make stdio happy
564 // LordHavoc: convert control characters to _ as well
565 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
566 if (ISWHITESPACEORCONTROL(comment[i]))
568 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
570 FS_Printf(f, "%s\n", comment);
573 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
574 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
575 FS_Printf(f, "%d\n", current_skill);
576 FS_Printf(f, "%s\n", sv.name);
577 FS_Printf(f, "%f\n",sv.time);
581 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
582 FS_Printf(f, "(dummy)\n");
583 FS_Printf(f, "%d\n", 0);
584 FS_Printf(f, "%s\n", "(dummy)");
585 FS_Printf(f, "%f\n", realtime);
588 // write the light styles
589 for (i=0 ; i<lightstyles ; i++)
591 if (isserver && sv.lightstyles[i][0])
592 FS_Printf(f, "%s\n", sv.lightstyles[i]);
597 PRVM_ED_WriteGlobals (f);
598 for (i=0 ; i<prog->num_edicts ; i++)
600 FS_Printf(f,"// edict %d\n", i);
601 //Con_Printf("edict %d...\n", i);
602 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
607 FS_Printf(f,"// DarkPlaces extended savegame\n");
608 // darkplaces extension - extra lightstyles, support for color lightstyles
609 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
610 if (isserver && sv.lightstyles[i][0])
611 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
613 // darkplaces extension - model precaches
614 for (i=1 ; i<MAX_MODELS ; i++)
615 if (sv.model_precache[i][0])
616 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
618 // darkplaces extension - sound precaches
619 for (i=1 ; i<MAX_SOUNDS ; i++)
620 if (sv.sound_precache[i][0])
621 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
626 Con_Print("done.\n");
634 void Host_Savegame_f (void)
636 char name[MAX_QPATH];
640 Con_Print("Can't save - no server running.\n");
646 // singleplayer checks
649 Con_Print("Can't save in intermission.\n");
653 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
655 Con_Print("Can't savegame with a dead player\n");
660 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");
664 Con_Print("save <savename> : save a game\n");
668 if (strstr(Cmd_Argv(1), ".."))
670 Con_Print("Relative pathnames are not allowed.\n");
674 strlcpy (name, Cmd_Argv(1), sizeof (name));
675 FS_DefaultExtension (name, ".sav", sizeof (name));
678 Host_Savegame_to(name);
688 void Host_Loadgame_f (void)
690 char filename[MAX_QPATH];
691 char mapname[MAX_QPATH];
701 float spawn_parms[NUM_SPAWN_PARMS];
705 Con_Print("load <savename> : load a game\n");
709 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
710 FS_DefaultExtension (filename, ".sav", sizeof (filename));
712 Con_Printf("Loading game from %s...\n", filename);
714 // stop playing demos
715 if (cls.demoplayback)
721 cls.demonum = -1; // stop demo loop in case this fails
723 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
726 Con_Print("ERROR: couldn't open.\n");
730 if(developer_entityparsing.integer)
731 Con_Printf("Host_Loadgame_f: loading version\n");
734 COM_ParseToken_Simple(&t, false, false);
735 version = atoi(com_token);
736 if (version != SAVEGAME_VERSION)
739 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
743 if(developer_entityparsing.integer)
744 Con_Printf("Host_Loadgame_f: loading description\n");
747 COM_ParseToken_Simple(&t, false, false);
749 for (i = 0;i < NUM_SPAWN_PARMS;i++)
751 COM_ParseToken_Simple(&t, false, false);
752 spawn_parms[i] = atof(com_token);
755 COM_ParseToken_Simple(&t, false, false);
756 // this silliness is so we can load 1.06 save files, which have float skill values
757 current_skill = (int)(atof(com_token) + 0.5);
758 Cvar_SetValue ("skill", (float)current_skill);
760 if(developer_entityparsing.integer)
761 Con_Printf("Host_Loadgame_f: loading mapname\n");
764 COM_ParseToken_Simple(&t, false, false);
765 strlcpy (mapname, com_token, sizeof(mapname));
767 if(developer_entityparsing.integer)
768 Con_Printf("Host_Loadgame_f: loading time\n");
771 COM_ParseToken_Simple(&t, false, false);
772 time = atof(com_token);
774 allowcheats = sv_cheats.integer != 0;
776 if(developer_entityparsing.integer)
777 Con_Printf("Host_Loadgame_f: spawning server\n");
779 SV_SpawnServer (mapname);
783 Con_Print("Couldn't load map\n");
786 sv.paused = true; // pause until all clients connect
789 if(developer_entityparsing.integer)
790 Con_Printf("Host_Loadgame_f: loading light styles\n");
792 // load the light styles
798 for (i = 0;i < MAX_LIGHTSTYLES;i++)
802 COM_ParseToken_Simple(&t, false, false);
803 // if this is a 64 lightstyle savegame produced by Quake, stop now
804 // we have to check this because darkplaces may save more than 64
805 if (com_token[0] == '{')
810 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
813 if(developer_entityparsing.integer)
814 Con_Printf("Host_Loadgame_f: skipping until globals\n");
816 // now skip everything before the first opening brace
817 // (this is for forward compatibility, so that older versions (at
818 // least ones with this fix) can load savegames with extra data before the
819 // first brace, as might be produced by a later engine version)
823 if (!COM_ParseToken_Simple(&t, false, false))
825 if (com_token[0] == '{')
832 // load the edicts out of the savegame file
837 while (COM_ParseToken_Simple(&t, false, false))
838 if (!strcmp(com_token, "}"))
840 if (!COM_ParseToken_Simple(&start, false, false))
845 if (strcmp(com_token,"{"))
848 Host_Error ("First token isn't a brace");
853 if(developer_entityparsing.integer)
854 Con_Printf("Host_Loadgame_f: loading globals\n");
856 // parse the global vars
857 PRVM_ED_ParseGlobals (start);
862 if (entnum >= MAX_EDICTS)
865 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
867 while (entnum >= prog->max_edicts)
868 PRVM_MEM_IncreaseEdicts();
869 ent = PRVM_EDICT_NUM(entnum);
870 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
871 ent->priv.server->free = false;
873 if(developer_entityparsing.integer)
874 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
876 PRVM_ED_ParseEdict (start, ent);
878 // link it into the bsp tree
879 if (!ent->priv.server->free)
887 prog->num_edicts = entnum;
890 for (i = 0;i < NUM_SPAWN_PARMS;i++)
891 svs.clients[0].spawn_parms[i] = spawn_parms[i];
893 if(developer_entityparsing.integer)
894 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
896 // read extended data if present
897 // the extended data is stored inside a /* */ comment block, which the
898 // parser intentionally skips, so we have to check for it manually here
901 while (*end == '\r' || *end == '\n')
903 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
905 if(developer_entityparsing.integer)
906 Con_Printf("Host_Loadgame_f: loading extended data\n");
908 Con_Printf("Loading extended DarkPlaces savegame\n");
910 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
911 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
912 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
913 while (COM_ParseToken_Simple(&t, false, false))
915 if (!strcmp(com_token, "sv.lightstyles"))
917 COM_ParseToken_Simple(&t, false, false);
919 COM_ParseToken_Simple(&t, false, false);
920 if (i >= 0 && i < MAX_LIGHTSTYLES)
921 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
923 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
925 else if (!strcmp(com_token, "sv.model_precache"))
927 COM_ParseToken_Simple(&t, false, false);
929 COM_ParseToken_Simple(&t, false, false);
930 if (i >= 0 && i < MAX_MODELS)
932 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
933 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
936 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
938 else if (!strcmp(com_token, "sv.sound_precache"))
940 COM_ParseToken_Simple(&t, false, false);
942 COM_ParseToken_Simple(&t, false, false);
943 if (i >= 0 && i < MAX_SOUNDS)
944 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
946 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
948 // skip any trailing text or unrecognized commands
949 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
956 if(developer_entityparsing.integer)
957 Con_Printf("Host_Loadgame_f: finished\n");
961 // make sure we're connected to loopback
962 if (sv.active && cls.state == ca_disconnected)
963 CL_EstablishConnection("local:1");
966 //============================================================================
969 ======================
971 ======================
973 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
974 void Host_Name_f (void)
977 qboolean valid_colors;
978 const char *newNameSource;
979 char newName[sizeof(host_client->name)];
981 if (Cmd_Argc () == 1)
983 Con_Printf("name: %s\n", cl_name.string);
987 if (Cmd_Argc () == 2)
988 newNameSource = Cmd_Argv(1);
990 newNameSource = Cmd_Args();
992 strlcpy(newName, newNameSource, sizeof(newName));
994 if (cmd_source == src_command)
996 Cvar_Set ("_cl_name", newName);
997 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
999 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1000 Con_Printf("name: %s\n", cl_name.string);
1005 if (realtime < host_client->nametime)
1007 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1011 host_client->nametime = realtime + 5;
1013 // point the string back at updateclient->name to keep it safe
1014 strlcpy (host_client->name, newName, sizeof (host_client->name));
1016 for (i = 0, j = 0;host_client->name[i];i++)
1017 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1018 host_client->name[j++] = host_client->name[i];
1019 host_client->name[j] = 0;
1021 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1022 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1024 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1025 host_client->name[sizeof(host_client->name) - 1] = 0;
1026 host_client->name[0] = STRING_COLOR_TAG;
1027 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1030 COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1031 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1034 l = strlen(host_client->name);
1035 if(l < sizeof(host_client->name) - 1)
1037 // duplicate the color tag to escape it
1038 host_client->name[i] = STRING_COLOR_TAG;
1039 host_client->name[i+1] = 0;
1040 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1044 // remove the last character to fix the color code
1045 host_client->name[l-1] = 0;
1046 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1050 // find the last color tag offset and decide if we need to add a reset tag
1051 for (i = 0, j = -1;host_client->name[i];i++)
1053 if (host_client->name[i] == STRING_COLOR_TAG)
1055 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1058 // if this happens to be a reset tag then we don't need one
1059 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1064 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]))
1070 if (host_client->name[i+1] == STRING_COLOR_TAG)
1077 // does not end in the default color string, so add it
1078 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1079 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1081 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1082 if (strcmp(host_client->old_name, host_client->name))
1084 if (host_client->spawned)
1085 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1086 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1087 // send notification to all clients
1088 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1089 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1090 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1091 SV_WriteNetnameIntoDemo(host_client);
1096 ======================
1098 ======================
1100 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1101 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1102 void Host_Playermodel_f (void)
1105 char newPath[sizeof(host_client->playermodel)];
1107 if (Cmd_Argc () == 1)
1109 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1113 if (Cmd_Argc () == 2)
1114 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1116 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1118 for (i = 0, j = 0;newPath[i];i++)
1119 if (newPath[i] != '\r' && newPath[i] != '\n')
1120 newPath[j++] = newPath[i];
1123 if (cmd_source == src_command)
1125 Cvar_Set ("_cl_playermodel", newPath);
1130 if (realtime < host_client->nametime)
1132 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1136 host_client->nametime = realtime + 5;
1139 // point the string back at updateclient->name to keep it safe
1140 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1141 if( prog->fieldoffsets.playermodel >= 0 )
1142 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1143 if (strcmp(host_client->old_model, host_client->playermodel))
1145 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1146 /*// send notification to all clients
1147 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1148 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1149 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1154 ======================
1156 ======================
1158 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1159 void Host_Playerskin_f (void)
1162 char newPath[sizeof(host_client->playerskin)];
1164 if (Cmd_Argc () == 1)
1166 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1170 if (Cmd_Argc () == 2)
1171 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1173 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1175 for (i = 0, j = 0;newPath[i];i++)
1176 if (newPath[i] != '\r' && newPath[i] != '\n')
1177 newPath[j++] = newPath[i];
1180 if (cmd_source == src_command)
1182 Cvar_Set ("_cl_playerskin", newPath);
1187 if (realtime < host_client->nametime)
1189 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1193 host_client->nametime = realtime + 5;
1196 // point the string back at updateclient->name to keep it safe
1197 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1198 if( prog->fieldoffsets.playerskin >= 0 )
1199 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1200 if (strcmp(host_client->old_skin, host_client->playerskin))
1202 //if (host_client->spawned)
1203 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1204 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1205 /*// send notification to all clients
1206 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1207 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1208 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1212 void Host_Version_f (void)
1214 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1217 void Host_Say(qboolean teamonly)
1223 // LordHavoc: long say messages
1225 qboolean fromServer = false;
1227 if (cmd_source == src_command)
1229 if (cls.state == ca_dedicated)
1236 Cmd_ForwardToServer ();
1241 if (Cmd_Argc () < 2)
1244 if (!teamplay.integer)
1254 // note this uses the chat prefix \001
1255 if (!fromServer && !teamonly)
1256 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1257 else if (!fromServer && teamonly)
1258 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1259 else if(*(sv_adminnick.string))
1260 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1262 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1263 p2 = text + strlen(text);
1264 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1266 if (p2[-1] == '\"' && quoted)
1271 strlcat(text, "\n", sizeof(text));
1273 // note: save is not a valid edict if fromServer is true
1275 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1276 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1277 SV_ClientPrint(text);
1280 if (cls.state == ca_dedicated)
1281 Con_Print(&text[1]);
1285 void Host_Say_f(void)
1291 void Host_Say_Team_f(void)
1297 void Host_Tell_f(void)
1299 const char *playername_start = NULL;
1300 size_t playername_length = 0;
1301 int playernumber = 0;
1304 const char *p1, *p2;
1305 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1306 qboolean fromServer = false;
1308 if (cmd_source == src_command)
1310 if (cls.state == ca_dedicated)
1314 Cmd_ForwardToServer ();
1319 if (Cmd_Argc () < 2)
1322 // note this uses the chat prefix \001
1324 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1325 else if(*(sv_adminnick.string))
1326 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1328 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1331 p2 = p1 + strlen(p1);
1332 // remove the target name
1333 while (p1 < p2 && *p1 == ' ')
1338 while (p1 < p2 && *p1 == ' ')
1340 while (p1 < p2 && isdigit(*p1))
1342 playernumber = playernumber * 10 + (*p1 - '0');
1350 playername_start = p1;
1351 while (p1 < p2 && *p1 != '"')
1353 playername_length = p1 - playername_start;
1359 playername_start = p1;
1360 while (p1 < p2 && *p1 != ' ')
1362 playername_length = p1 - playername_start;
1364 while (p1 < p2 && *p1 == ' ')
1366 if(playername_start)
1368 // set playernumber to the right client
1370 if(playername_length >= sizeof(namebuf))
1373 Con_Print("Host_Tell: too long player name/ID\n");
1375 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1378 memcpy(namebuf, playername_start, playername_length);
1379 namebuf[playername_length] = 0;
1380 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1382 if (!svs.clients[playernumber].active)
1384 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1388 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1391 Con_Print("Host_Tell: invalid player name/ID\n");
1393 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1396 // remove trailing newlines
1397 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1399 // remove quotes if present
1405 else if (fromServer)
1406 Con_Print("Host_Tell: missing end quote\n");
1408 SV_ClientPrint("Host_Tell: missing end quote\n");
1410 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1413 return; // empty say
1414 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1420 host_client = svs.clients + playernumber;
1421 SV_ClientPrint(text);
1431 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1432 void Host_Color(int changetop, int changebottom)
1434 int top, bottom, playercolor;
1436 // get top and bottom either from the provided values or the current values
1437 // (allows changing only top or bottom, or both at once)
1438 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1439 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1443 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1449 playercolor = top*16 + bottom;
1451 if (cmd_source == src_command)
1453 Cvar_SetValueQuick(&cl_color, playercolor);
1457 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1460 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1462 Con_DPrint("Calling SV_ChangeTeam\n");
1463 prog->globals.server->time = sv.time;
1464 prog->globals.generic[OFS_PARM0] = playercolor;
1465 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1466 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1471 if (host_client->edict)
1473 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1474 val->_float = playercolor;
1475 host_client->edict->fields.server->team = bottom + 1;
1477 host_client->colors = playercolor;
1478 if (host_client->old_colors != host_client->colors)
1480 host_client->old_colors = host_client->colors;
1481 // send notification to all clients
1482 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1483 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1484 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1489 void Host_Color_f(void)
1493 if (Cmd_Argc() == 1)
1495 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1496 Con_Print("color <0-15> [0-15]\n");
1500 if (Cmd_Argc() == 2)
1501 top = bottom = atoi(Cmd_Argv(1));
1504 top = atoi(Cmd_Argv(1));
1505 bottom = atoi(Cmd_Argv(2));
1507 Host_Color(top, bottom);
1510 void Host_TopColor_f(void)
1512 if (Cmd_Argc() == 1)
1514 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1515 Con_Print("topcolor <0-15>\n");
1519 Host_Color(atoi(Cmd_Argv(1)), -1);
1522 void Host_BottomColor_f(void)
1524 if (Cmd_Argc() == 1)
1526 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1527 Con_Print("bottomcolor <0-15>\n");
1531 Host_Color(-1, atoi(Cmd_Argv(1)));
1534 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1535 void Host_Rate_f(void)
1539 if (Cmd_Argc() != 2)
1541 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1542 Con_Print("rate <bytespersecond>\n");
1546 rate = atoi(Cmd_Argv(1));
1548 if (cmd_source == src_command)
1550 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1554 host_client->rate = rate;
1562 void Host_Kill_f (void)
1564 if (host_client->edict->fields.server->health <= 0)
1566 SV_ClientPrint("Can't suicide -- already dead!\n");
1570 prog->globals.server->time = sv.time;
1571 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1572 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1581 void Host_Pause_f (void)
1583 if (!pausable.integer)
1584 SV_ClientPrint("Pause not allowed.\n");
1588 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1589 // send notification to all clients
1590 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1591 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1596 ======================
1598 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1599 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1600 ======================
1602 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)"};
1603 static void Host_PModel_f (void)
1608 if (Cmd_Argc () == 1)
1610 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1613 i = atoi(Cmd_Argv(1));
1615 if (cmd_source == src_command)
1617 if (cl_pmodel.integer == i)
1619 Cvar_SetValue ("_cl_pmodel", i);
1620 if (cls.state == ca_connected)
1621 Cmd_ForwardToServer ();
1625 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1629 //===========================================================================
1637 void Host_PreSpawn_f (void)
1639 if (host_client->spawned)
1641 Con_Print("prespawn not valid -- already spawned\n");
1645 if (host_client->netconnection)
1647 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1648 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1649 MSG_WriteByte (&host_client->netconnection->message, 2);
1650 host_client->sendsignon = 0; // enable unlimited sends again
1653 // reset the name change timer because the client will send name soon
1654 host_client->nametime = 0;
1662 void Host_Spawn_f (void)
1666 int stats[MAX_CL_STATS];
1668 if (host_client->spawned)
1670 Con_Print("Spawn not valid -- already spawned\n");
1674 // reset name change timer again because they might want to change name
1675 // again in the first 5 seconds after connecting
1676 host_client->nametime = 0;
1678 // LordHavoc: moved this above the QC calls at FrikaC's request
1679 // LordHavoc: commented this out
1680 //if (host_client->netconnection)
1681 // SZ_Clear (&host_client->netconnection->message);
1683 // run the entrance script
1686 // loaded games are fully initialized already
1687 if (prog->funcoffsets.RestoreGame)
1689 Con_DPrint("Calling RestoreGame\n");
1690 prog->globals.server->time = sv.time;
1691 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1692 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1697 //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);
1699 // copy spawn parms out of the client_t
1700 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1701 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1703 // call the spawn function
1704 host_client->clientconnectcalled = true;
1705 prog->globals.server->time = sv.time;
1706 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1707 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1709 if (cls.state == ca_dedicated)
1710 Con_Printf("%s connected\n", host_client->name);
1712 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1715 if (!host_client->netconnection)
1718 // send time of update
1719 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1720 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1722 // send all current names, colors, and frag counts
1723 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1725 if (!client->active)
1727 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1728 MSG_WriteByte (&host_client->netconnection->message, i);
1729 MSG_WriteString (&host_client->netconnection->message, client->name);
1730 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1731 MSG_WriteByte (&host_client->netconnection->message, i);
1732 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1733 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1734 MSG_WriteByte (&host_client->netconnection->message, i);
1735 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1738 // send all current light styles
1739 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1741 if (sv.lightstyles[i][0])
1743 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1744 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1745 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1750 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1751 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1752 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1754 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1755 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1756 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1758 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1759 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1760 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1762 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1763 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1764 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1767 // Never send a roll angle, because savegames can catch the server
1768 // in a state where it is expecting the client to correct the angle
1769 // and it won't happen if the game was just loaded, so you wind up
1770 // with a permanent head tilt
1773 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1774 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1775 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1776 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1780 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1781 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1782 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1783 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1786 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1788 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1789 MSG_WriteByte (&host_client->netconnection->message, 3);
1797 void Host_Begin_f (void)
1799 host_client->spawned = true;
1801 // LordHavoc: note: this code also exists in SV_DropClient
1805 for (i = 0;i < svs.maxclients;i++)
1806 if (svs.clients[i].active && !svs.clients[i].spawned)
1808 if (i == svs.maxclients)
1810 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1811 sv.paused = sv.loadgame = false; // we're basically done with loading now
1816 //===========================================================================
1823 Kicks a user off of the server
1826 void Host_Kick_f (void)
1829 const char *message = NULL;
1832 qboolean byNumber = false;
1840 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1842 i = (int)(atof(Cmd_Argv(2)) - 1);
1843 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1849 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1851 if (!host_client->active)
1853 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1858 if (i < svs.maxclients)
1860 if (cmd_source == src_command)
1862 if (cls.state == ca_dedicated)
1865 who = cl_name.string;
1870 // can't kick yourself!
1871 if (host_client == save)
1876 message = Cmd_Args();
1877 COM_ParseToken_Simple(&message, false, false);
1880 message++; // skip the #
1881 while (*message == ' ') // skip white space
1883 message += strlen(Cmd_Argv(2)); // skip the number
1885 while (*message && *message == ' ')
1889 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1891 SV_ClientPrintf("Kicked by %s\n", who);
1892 SV_DropClient (false); // kicked
1900 ===============================================================================
1904 ===============================================================================
1912 void Host_Give_f (void)
1920 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1925 v = atoi (Cmd_Argv(2));
1939 // MED 01/04/97 added hipnotic give stuff
1940 if (gamemode == GAME_HIPNOTIC)
1945 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1947 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1949 else if (t[0] == '9')
1950 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1951 else if (t[0] == '0')
1952 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1953 else if (t[0] >= '2')
1954 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1959 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1964 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1967 host_client->edict->fields.server->ammo_shells = v;
1970 if (gamemode == GAME_ROGUE)
1972 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1975 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1976 host_client->edict->fields.server->ammo_nails = v;
1981 host_client->edict->fields.server->ammo_nails = v;
1985 if (gamemode == GAME_ROGUE)
1987 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
1991 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1992 host_client->edict->fields.server->ammo_nails = v;
1997 if (gamemode == GAME_ROGUE)
1999 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2003 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2004 host_client->edict->fields.server->ammo_rockets = v;
2009 host_client->edict->fields.server->ammo_rockets = v;
2013 if (gamemode == GAME_ROGUE)
2015 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2019 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2020 host_client->edict->fields.server->ammo_rockets = v;
2025 host_client->edict->fields.server->health = v;
2028 if (gamemode == GAME_ROGUE)
2030 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2034 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2035 host_client->edict->fields.server->ammo_cells = v;
2040 host_client->edict->fields.server->ammo_cells = v;
2044 if (gamemode == GAME_ROGUE)
2046 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2050 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2051 host_client->edict->fields.server->ammo_cells = v;
2058 prvm_edict_t *FindViewthing (void)
2063 for (i=0 ; i<prog->num_edicts ; i++)
2065 e = PRVM_EDICT_NUM(i);
2066 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2069 Con_Print("No viewthing on map\n");
2078 void Host_Viewmodel_f (void)
2087 e = FindViewthing ();
2092 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2093 if (!m || !m->loaded || !m->Draw)
2095 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2099 e->fields.server->frame = 0;
2100 cl.model_precache[(int)e->fields.server->modelindex] = m;
2108 void Host_Viewframe_f (void)
2118 e = FindViewthing ();
2122 m = cl.model_precache[(int)e->fields.server->modelindex];
2124 f = atoi(Cmd_Argv(1));
2125 if (f >= m->numframes)
2128 e->fields.server->frame = f;
2132 void PrintFrameName (dp_model_t *m, int frame)
2135 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2137 Con_Printf("frame %i\n", frame);
2145 void Host_Viewnext_f (void)
2154 e = FindViewthing ();
2158 m = cl.model_precache[(int)e->fields.server->modelindex];
2160 e->fields.server->frame = e->fields.server->frame + 1;
2161 if (e->fields.server->frame >= m->numframes)
2162 e->fields.server->frame = m->numframes - 1;
2164 PrintFrameName (m, (int)e->fields.server->frame);
2172 void Host_Viewprev_f (void)
2181 e = FindViewthing ();
2186 m = cl.model_precache[(int)e->fields.server->modelindex];
2188 e->fields.server->frame = e->fields.server->frame - 1;
2189 if (e->fields.server->frame < 0)
2190 e->fields.server->frame = 0;
2192 PrintFrameName (m, (int)e->fields.server->frame);
2196 ===============================================================================
2200 ===============================================================================
2209 void Host_Startdemos_f (void)
2213 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2219 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2222 Con_DPrintf("%i demo(s) in loop\n", c);
2224 for (i=1 ; i<c+1 ; i++)
2225 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2227 // LordHavoc: clear the remaining slots
2228 for (;i <= MAX_DEMOS;i++)
2229 cls.demos[i-1][0] = 0;
2231 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2245 Return to looping demos
2248 void Host_Demos_f (void)
2250 if (cls.state == ca_dedicated)
2252 if (cls.demonum == -1)
2262 Return to looping demos
2265 void Host_Stopdemo_f (void)
2267 if (!cls.demoplayback)
2270 Host_ShutdownServer ();
2273 void Host_SendCvar_f (void)
2277 const char *cvarname;
2282 cvarname = Cmd_Argv(1);
2283 if (cls.state == ca_connected)
2285 c = Cvar_FindVar(cvarname);
2286 // LordHavoc: if there is no such cvar or if it is private, send a
2287 // reply indicating that it has no value
2288 if(!c || (c->flags & CVAR_PRIVATE))
2289 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2291 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2294 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2298 if (cls.state != ca_dedicated)
2302 for(;i<svs.maxclients;i++)
2303 if(svs.clients[i].active && svs.clients[i].netconnection)
2305 host_client = &svs.clients[i];
2306 Host_ClientCommands("sendcvar %s\n", cvarname);
2311 static void MaxPlayers_f(void)
2315 if (Cmd_Argc() != 2)
2317 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2323 Con_Print("maxplayers can not be changed while a server is running.\n");
2324 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2327 n = atoi(Cmd_Argv(1));
2328 n = bound(1, n, MAX_SCOREBOARD);
2329 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2331 svs.maxclients_next = n;
2333 Cvar_Set ("deathmatch", "0");
2335 Cvar_Set ("deathmatch", "1");
2339 =====================
2342 ProQuake rcon support
2343 =====================
2345 void Host_PQRcon_f (void)
2349 lhnetsocket_t *mysocket;
2350 char peer_address[64];
2352 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer)
2354 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2358 for (i = 0;rcon_password.string[i];i++)
2360 if (ISWHITESPACE(rcon_password.string[i]))
2362 Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2369 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2373 if (!rcon_address.string[0])
2375 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2378 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2380 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2381 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2384 SZ_Clear(&net_message);
2385 MSG_WriteLong (&net_message, 0);
2386 MSG_WriteByte (&net_message, CCREQ_RCON);
2387 MSG_WriteString (&net_message, rcon_password.string);
2388 MSG_WriteString (&net_message, Cmd_Args());
2389 *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2390 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2391 SZ_Clear (&net_message);
2395 //=============================================================================
2397 // QuakeWorld commands
2400 =====================
2403 Send the rest of the command line over as
2404 an unconnected command.
2405 =====================
2407 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2411 lhnetsocket_t *mysocket;
2413 if (!rcon_password.string || !rcon_password.string[0])
2415 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2419 for (i = 0;rcon_password.string[i];i++)
2421 if (ISWHITESPACE(rcon_password.string[i]))
2423 Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2429 to = cls.netcon->peeraddress;
2432 if (!rcon_address.string[0])
2434 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2437 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2439 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2440 if (mysocket && Cmd_Args()[0])
2442 // simply put together the rcon packet and send it
2443 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2445 if(cls.rcon_commands[cls.rcon_ringpos][0])
2448 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2449 Con_Printf("rcon to %s (for command %s) failed: too many buffered commands (possibly increase MAX_RCONS)\n", s, cls.rcon_commands[cls.rcon_ringpos]);
2450 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2453 for (i = 0;i < MAX_RCONS;i++)
2454 if(cls.rcon_commands[i][0])
2455 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2459 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2460 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2461 cls.rcon_addresses[cls.rcon_ringpos] = to;
2462 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2463 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2465 else if(rcon_secure.integer)
2469 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2470 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2471 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
2474 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2475 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2480 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
2486 ====================
2489 user <name or userid>
2491 Dump userdata / masterdata for a user
2492 ====================
2494 void Host_User_f (void) // credit: taken from QuakeWorld
2499 if (Cmd_Argc() != 2)
2501 Con_Printf ("Usage: user <username / userid>\n");
2505 uid = atoi(Cmd_Argv(1));
2507 for (i = 0;i < cl.maxclients;i++)
2509 if (!cl.scores[i].name[0])
2511 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2513 InfoString_Print(cl.scores[i].qw_userinfo);
2517 Con_Printf ("User not in server.\n");
2521 ====================
2524 Dump userids for all current players
2525 ====================
2527 void Host_Users_f (void) // credit: taken from QuakeWorld
2533 Con_Printf ("userid frags name\n");
2534 Con_Printf ("------ ----- ----\n");
2535 for (i = 0;i < cl.maxclients;i++)
2537 if (cl.scores[i].name[0])
2539 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2544 Con_Printf ("%i total users\n", c);
2549 Host_FullServerinfo_f
2551 Sent by server when serverinfo changes
2554 // TODO: shouldn't this be a cvar instead?
2555 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2558 if (Cmd_Argc() != 2)
2560 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2564 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2565 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2566 cl.qw_teamplay = atoi(temp);
2573 Allow clients to change userinfo
2577 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2584 if (Cmd_Argc() != 2)
2586 Con_Printf ("fullinfo <complete info string>\n");
2596 while (*s && *s != '\\')
2602 Con_Printf ("MISSING VALUE\n");
2608 while (*s && *s != '\\')
2615 CL_SetInfo(key, value, false, false, false, false);
2623 Allow clients to change userinfo
2626 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2628 if (Cmd_Argc() == 1)
2630 InfoString_Print(cls.userinfo);
2633 if (Cmd_Argc() != 3)
2635 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2638 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2642 ====================
2645 packet <destination> <contents>
2647 Contents allows \n escape character
2648 ====================
2650 void Host_Packet_f (void) // credit: taken from QuakeWorld
2656 lhnetaddress_t address;
2657 lhnetsocket_t *mysocket;
2659 if (Cmd_Argc() != 3)
2661 Con_Printf ("packet <destination> <contents>\n");
2665 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2667 Con_Printf ("Bad address\n");
2673 send[0] = send[1] = send[2] = send[3] = -1;
2675 l = (int)strlen (in);
2676 for (i=0 ; i<l ; i++)
2678 if (out >= send + sizeof(send) - 1)
2680 if (in[i] == '\\' && in[i+1] == 'n')
2685 else if (in[i] == '\\' && in[i+1] == '0')
2690 else if (in[i] == '\\' && in[i+1] == 't')
2695 else if (in[i] == '\\' && in[i+1] == 'r')
2700 else if (in[i] == '\\' && in[i+1] == '"')
2709 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2711 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2713 NetConn_Write(mysocket, send, out - send, &address);
2717 ====================
2720 Send back ping and packet loss update for all current players to this player
2721 ====================
2723 void Host_Pings_f (void)
2725 int i, j, ping, packetloss;
2728 if (!host_client->netconnection)
2731 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2733 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2734 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2736 for (i = 0;i < svs.maxclients;i++)
2739 if (svs.clients[i].netconnection)
2740 for (j = 0;j < NETGRAPH_PACKETS;j++)
2741 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2743 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
2744 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2745 ping = bound(0, ping, 9999);
2746 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2748 // send qw_svc_updateping and qw_svc_updatepl messages
2749 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2750 MSG_WriteShort(&host_client->netconnection->message, ping);
2751 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2752 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2756 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2757 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2758 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2761 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2762 MSG_WriteString(&host_client->netconnection->message, "\n");
2765 void Host_PingPLReport_f(void)
2769 if (l > cl.maxclients)
2771 for (i = 0;i < l;i++)
2773 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2774 cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
2778 //=============================================================================
2785 void Host_InitCommands (void)
2787 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2789 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2790 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2791 if (gamemode == GAME_NEHAHRA)
2793 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2794 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2795 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2796 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2797 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2801 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2802 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2803 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2804 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2805 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2807 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2808 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2809 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2810 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2811 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)");
2812 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2813 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2814 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2815 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2816 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2817 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2818 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2819 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2820 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2821 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2823 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2824 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2825 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2827 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2828 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2829 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2830 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2832 Cvar_RegisterVariable (&cl_name);
2833 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2834 Cvar_RegisterVariable (&cl_color);
2835 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2836 Cvar_RegisterVariable (&cl_rate);
2837 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2838 if (gamemode == GAME_NEHAHRA)
2840 Cvar_RegisterVariable (&cl_pmodel);
2841 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2844 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2845 Cvar_RegisterVariable (&cl_playermodel);
2846 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2847 Cvar_RegisterVariable (&cl_playerskin);
2848 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2850 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2851 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2852 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)");
2853 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2855 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2857 Cvar_RegisterVariable (&rcon_password);
2858 Cvar_RegisterVariable (&rcon_address);
2859 Cvar_RegisterVariable (&rcon_secure);
2860 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2861 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");
2862 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");
2863 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)");
2864 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2865 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2866 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2867 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2868 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2869 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2870 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2871 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2873 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)");
2874 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)");
2876 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)");
2877 Cvar_RegisterVariable (&r_fixtrans_auto);
2879 Cvar_RegisterVariable (&team);
2880 Cvar_RegisterVariable (&skin);
2881 Cvar_RegisterVariable (&noaim);
2883 Cvar_RegisterVariable(&sv_cheats);
2884 Cvar_RegisterVariable(&sv_adminnick);
2885 Cvar_RegisterVariable(&sv_status_privacy);
2886 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2889 void Host_NoOperation_f(void)