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.
27 // for secure rcon authentication
33 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
34 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
35 cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
36 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."};
37 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; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
38 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"};
39 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"};
40 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
41 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
42 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
43 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
44 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)"};
45 qboolean allowcheats = false;
47 extern qboolean host_shuttingdown;
48 extern cvar_t developer_entityparsing;
56 void Host_Quit_f (void)
59 Con_Printf("shutting down already!\n");
69 void Host_Status_f (void)
73 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
74 void (*print) (const char *fmt, ...);
78 if (cmd_source == src_command)
80 // if running a client, try to send over network so the client's status report parser will see the report
81 if (cls.state == ca_connected)
83 Cmd_ForwardToServer ();
89 print = SV_ClientPrintf;
94 if(cmd_source == src_command)
100 if (strcmp(Cmd_Argv(1), "1") == 0)
102 else if (strcmp(Cmd_Argv(1), "2") == 0)
106 for (players = 0, i = 0;i < svs.maxclients;i++)
107 if (svs.clients[i].active)
109 print ("host: %s\n", Cvar_VariableString ("hostname"));
110 print ("version: %s build %s\n", gamename, buildstring);
111 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
112 print ("map: %s\n", sv.name);
113 print ("timing: %s\n", Host_TimingReport());
114 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
117 print ("^2IP %%pl ping time frags no name\n");
119 print ("^5IP no name\n");
121 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
128 if (in == 0 || in == 1)
130 seconds = (int)(realtime - client->connecttime);
131 minutes = seconds / 60;
134 seconds -= (minutes * 60);
135 hours = minutes / 60;
137 minutes -= (hours * 60);
143 if (client->netconnection)
144 for (j = 0;j < NETGRAPH_PACKETS;j++)
145 if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
147 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
148 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
151 if(sv_status_privacy.integer && cmd_source != src_command)
152 strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
154 strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
156 frags = client->frags;
158 if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
160 const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
166 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
167 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
171 frags = atoi(qcstatus);
175 if (in == 0) // default layout
177 print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
180 else if (in == 1) // extended layout
182 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);
184 else if (in == 2) // reduced layout
186 print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
190 if(cmd_source == src_command)
199 Sets client to godmode
202 void Host_God_f (void)
206 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
210 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
211 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
212 SV_ClientPrint("godmode OFF\n");
214 SV_ClientPrint("godmode ON\n");
217 void Host_Notarget_f (void)
221 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
225 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
226 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
227 SV_ClientPrint("notarget OFF\n");
229 SV_ClientPrint("notarget ON\n");
232 qboolean noclip_anglehack;
234 void Host_Noclip_f (void)
238 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
242 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
244 noclip_anglehack = true;
245 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
246 SV_ClientPrint("noclip ON\n");
250 noclip_anglehack = false;
251 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
252 SV_ClientPrint("noclip OFF\n");
260 Sets client to flymode
263 void Host_Fly_f (void)
267 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
271 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
273 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
274 SV_ClientPrint("flymode ON\n");
278 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
279 SV_ClientPrint("flymode OFF\n");
290 void Host_Pings_f (void); // called by Host_Ping_f
291 void Host_Ping_f (void)
295 void (*print) (const char *fmt, ...);
297 if (cmd_source == src_command)
299 // if running a client, try to send over network so the client's ping report parser will see the report
300 if (cls.state == ca_connected)
302 Cmd_ForwardToServer ();
308 print = SV_ClientPrintf;
313 print("Client ping times:\n");
314 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
318 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
321 // 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)
322 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
327 ===============================================================================
331 ===============================================================================
335 ======================
340 command from the console. Active clients are kicked off.
341 ======================
343 void Host_Map_f (void)
345 char level[MAX_QPATH];
349 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
353 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
354 if (gamemode == GAME_DELUXEQUAKE)
355 Cvar_Set("warpmark", "");
357 cls.demonum = -1; // stop demo loop in case this fails
360 Host_ShutdownServer();
362 if(svs.maxclients != svs.maxclients_next)
364 svs.maxclients = svs.maxclients_next;
366 Mem_Free(svs.clients);
367 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
373 svs.serverflags = 0; // haven't completed an episode yet
374 allowcheats = sv_cheats.integer != 0;
375 strlcpy(level, Cmd_Argv(1), sizeof(level));
376 SV_SpawnServer(level);
377 if (sv.active && cls.state == ca_disconnected)
378 CL_EstablishConnection("local:1");
385 Goes to a new map, taking all clients along
388 void Host_Changelevel_f (void)
390 char level[MAX_QPATH];
394 Con_Print("changelevel <levelname> : continue game on a new level\n");
407 SV_SaveSpawnparms ();
409 allowcheats = sv_cheats.integer != 0;
410 strlcpy(level, Cmd_Argv(1), sizeof(level));
411 SV_SpawnServer(level);
412 if (sv.active && cls.state == ca_disconnected)
413 CL_EstablishConnection("local:1");
420 Restarts the current server for a dead player
423 void Host_Restart_f (void)
425 char mapname[MAX_QPATH];
429 Con_Print("restart : restart current level\n");
434 Con_Print("Only the server may restart\n");
441 allowcheats = sv_cheats.integer != 0;
442 strlcpy(mapname, sv.name, sizeof(mapname));
443 SV_SpawnServer(mapname);
444 if (sv.active && cls.state == ca_disconnected)
445 CL_EstablishConnection("local:1");
452 This command causes the client to wait for the signon messages again.
453 This is sent just before a server changes levels
456 void Host_Reconnect_f (void)
459 // if not connected, reconnect to the most recent server
462 // if we have connected to a server recently, the userinfo
463 // will still contain its IP address, so get the address...
464 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
466 CL_EstablishConnection(temp);
468 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
471 // if connected, do something based on protocol
472 if (cls.protocol == PROTOCOL_QUAKEWORLD)
474 // quakeworld can just re-login
475 if (cls.qw_downloadmemory) // don't change when downloading
480 if (cls.state == ca_connected && cls.signon < SIGNONS)
482 Con_Printf("reconnecting...\n");
483 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
484 MSG_WriteString(&cls.netcon->message, "new");
489 // netquake uses reconnect on level changes (silly)
492 Con_Print("reconnect : wait for signon messages again\n");
497 Con_Print("reconnect: no signon, ignoring reconnect\n");
500 cls.signon = 0; // need new connection messages
505 =====================
508 User command to connect to server
509 =====================
511 void Host_Connect_f (void)
515 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
518 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
519 if(rcon_secure.integer <= 0)
520 Cvar_SetQuick(&rcon_password, "");
521 CL_EstablishConnection(Cmd_Argv(1));
526 ===============================================================================
530 ===============================================================================
533 #define SAVEGAME_VERSION 5
535 void Host_Savegame_to (const char *name)
538 int i, lightstyles = 64;
539 char comment[SAVEGAME_COMMENT_LENGTH+1];
542 // first we have to figure out if this can be saved in 64 lightstyles
543 // (for Quake compatibility)
544 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
545 if (sv.lightstyles[i][0])
548 isserver = !strcmp(PRVM_NAME, "server");
550 Con_Printf("Saving game to %s...\n", name);
551 f = FS_OpenRealFile(name, "wb", false);
554 Con_Print("ERROR: couldn't open.\n");
558 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
560 memset(comment, 0, sizeof(comment));
562 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);
564 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
565 // convert space to _ to make stdio happy
566 // LordHavoc: convert control characters to _ as well
567 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
568 if (ISWHITESPACEORCONTROL(comment[i]))
570 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
572 FS_Printf(f, "%s\n", comment);
575 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
576 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
577 FS_Printf(f, "%d\n", current_skill);
578 FS_Printf(f, "%s\n", sv.name);
579 FS_Printf(f, "%f\n",sv.time);
583 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
584 FS_Printf(f, "(dummy)\n");
585 FS_Printf(f, "%d\n", 0);
586 FS_Printf(f, "%s\n", "(dummy)");
587 FS_Printf(f, "%f\n", realtime);
590 // write the light styles
591 for (i=0 ; i<lightstyles ; i++)
593 if (isserver && sv.lightstyles[i][0])
594 FS_Printf(f, "%s\n", sv.lightstyles[i]);
599 PRVM_ED_WriteGlobals (f);
600 for (i=0 ; i<prog->num_edicts ; i++)
602 FS_Printf(f,"// edict %d\n", i);
603 //Con_Printf("edict %d...\n", i);
604 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
609 FS_Printf(f,"// DarkPlaces extended savegame\n");
610 // darkplaces extension - extra lightstyles, support for color lightstyles
611 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
612 if (isserver && sv.lightstyles[i][0])
613 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
615 // darkplaces extension - model precaches
616 for (i=1 ; i<MAX_MODELS ; i++)
617 if (sv.model_precache[i][0])
618 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
620 // darkplaces extension - sound precaches
621 for (i=1 ; i<MAX_SOUNDS ; i++)
622 if (sv.sound_precache[i][0])
623 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
628 Con_Print("done.\n");
636 void Host_Savegame_f (void)
638 char name[MAX_QPATH];
642 Con_Print("Can't save - no server running.\n");
648 // singleplayer checks
651 Con_Print("Can't save in intermission.\n");
655 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
657 Con_Print("Can't savegame with a dead player\n");
662 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");
666 Con_Print("save <savename> : save a game\n");
670 if (strstr(Cmd_Argv(1), ".."))
672 Con_Print("Relative pathnames are not allowed.\n");
676 strlcpy (name, Cmd_Argv(1), sizeof (name));
677 FS_DefaultExtension (name, ".sav", sizeof (name));
680 Host_Savegame_to(name);
690 void Host_Loadgame_f (void)
692 char filename[MAX_QPATH];
693 char mapname[MAX_QPATH];
703 float spawn_parms[NUM_SPAWN_PARMS];
707 Con_Print("load <savename> : load a game\n");
711 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
712 FS_DefaultExtension (filename, ".sav", sizeof (filename));
714 Con_Printf("Loading game from %s...\n", filename);
716 // stop playing demos
717 if (cls.demoplayback)
723 cls.demonum = -1; // stop demo loop in case this fails
725 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
728 Con_Print("ERROR: couldn't open.\n");
732 if(developer_entityparsing.integer)
733 Con_Printf("Host_Loadgame_f: loading version\n");
736 COM_ParseToken_Simple(&t, false, false);
737 version = atoi(com_token);
738 if (version != SAVEGAME_VERSION)
741 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
745 if(developer_entityparsing.integer)
746 Con_Printf("Host_Loadgame_f: loading description\n");
749 COM_ParseToken_Simple(&t, false, false);
751 for (i = 0;i < NUM_SPAWN_PARMS;i++)
753 COM_ParseToken_Simple(&t, false, false);
754 spawn_parms[i] = atof(com_token);
757 COM_ParseToken_Simple(&t, false, false);
758 // this silliness is so we can load 1.06 save files, which have float skill values
759 current_skill = (int)(atof(com_token) + 0.5);
760 Cvar_SetValue ("skill", (float)current_skill);
762 if(developer_entityparsing.integer)
763 Con_Printf("Host_Loadgame_f: loading mapname\n");
766 COM_ParseToken_Simple(&t, false, false);
767 strlcpy (mapname, com_token, sizeof(mapname));
769 if(developer_entityparsing.integer)
770 Con_Printf("Host_Loadgame_f: loading time\n");
773 COM_ParseToken_Simple(&t, false, false);
774 time = atof(com_token);
776 allowcheats = sv_cheats.integer != 0;
778 if(developer_entityparsing.integer)
779 Con_Printf("Host_Loadgame_f: spawning server\n");
781 SV_SpawnServer (mapname);
785 Con_Print("Couldn't load map\n");
788 sv.paused = true; // pause until all clients connect
791 if(developer_entityparsing.integer)
792 Con_Printf("Host_Loadgame_f: loading light styles\n");
794 // load the light styles
800 for (i = 0;i < MAX_LIGHTSTYLES;i++)
804 COM_ParseToken_Simple(&t, false, false);
805 // if this is a 64 lightstyle savegame produced by Quake, stop now
806 // we have to check this because darkplaces may save more than 64
807 if (com_token[0] == '{')
812 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
815 if(developer_entityparsing.integer)
816 Con_Printf("Host_Loadgame_f: skipping until globals\n");
818 // now skip everything before the first opening brace
819 // (this is for forward compatibility, so that older versions (at
820 // least ones with this fix) can load savegames with extra data before the
821 // first brace, as might be produced by a later engine version)
825 if (!COM_ParseToken_Simple(&t, false, false))
827 if (com_token[0] == '{')
834 // load the edicts out of the savegame file
839 while (COM_ParseToken_Simple(&t, false, false))
840 if (!strcmp(com_token, "}"))
842 if (!COM_ParseToken_Simple(&start, false, false))
847 if (strcmp(com_token,"{"))
850 Host_Error ("First token isn't a brace");
855 if(developer_entityparsing.integer)
856 Con_Printf("Host_Loadgame_f: loading globals\n");
858 // parse the global vars
859 PRVM_ED_ParseGlobals (start);
864 if (entnum >= MAX_EDICTS)
867 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
869 while (entnum >= prog->max_edicts)
870 PRVM_MEM_IncreaseEdicts();
871 ent = PRVM_EDICT_NUM(entnum);
872 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
873 ent->priv.server->free = false;
875 if(developer_entityparsing.integer)
876 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
878 PRVM_ED_ParseEdict (start, ent);
880 // link it into the bsp tree
881 if (!ent->priv.server->free)
889 prog->num_edicts = entnum;
892 for (i = 0;i < NUM_SPAWN_PARMS;i++)
893 svs.clients[0].spawn_parms[i] = spawn_parms[i];
895 if(developer_entityparsing.integer)
896 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
898 // read extended data if present
899 // the extended data is stored inside a /* */ comment block, which the
900 // parser intentionally skips, so we have to check for it manually here
903 while (*end == '\r' || *end == '\n')
905 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
907 if(developer_entityparsing.integer)
908 Con_Printf("Host_Loadgame_f: loading extended data\n");
910 Con_Printf("Loading extended DarkPlaces savegame\n");
912 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
913 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
914 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
915 while (COM_ParseToken_Simple(&t, false, false))
917 if (!strcmp(com_token, "sv.lightstyles"))
919 COM_ParseToken_Simple(&t, false, false);
921 COM_ParseToken_Simple(&t, false, false);
922 if (i >= 0 && i < MAX_LIGHTSTYLES)
923 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
925 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
927 else if (!strcmp(com_token, "sv.model_precache"))
929 COM_ParseToken_Simple(&t, false, false);
931 COM_ParseToken_Simple(&t, false, false);
932 if (i >= 0 && i < MAX_MODELS)
934 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
935 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
938 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
940 else if (!strcmp(com_token, "sv.sound_precache"))
942 COM_ParseToken_Simple(&t, false, false);
944 COM_ParseToken_Simple(&t, false, false);
945 if (i >= 0 && i < MAX_SOUNDS)
946 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
948 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
950 // skip any trailing text or unrecognized commands
951 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
958 if(developer_entityparsing.integer)
959 Con_Printf("Host_Loadgame_f: finished\n");
963 // make sure we're connected to loopback
964 if (sv.active && cls.state == ca_disconnected)
965 CL_EstablishConnection("local:1");
968 //============================================================================
971 ======================
973 ======================
975 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
976 void Host_Name_f (void)
979 qboolean valid_colors;
980 const char *newNameSource;
981 char newName[sizeof(host_client->name)];
983 if (Cmd_Argc () == 1)
985 Con_Printf("name: %s\n", cl_name.string);
989 if (Cmd_Argc () == 2)
990 newNameSource = Cmd_Argv(1);
992 newNameSource = Cmd_Args();
994 strlcpy(newName, newNameSource, sizeof(newName));
996 if (cmd_source == src_command)
998 Cvar_Set ("_cl_name", newName);
999 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1001 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1002 Con_Printf("name: %s\n", cl_name.string);
1007 if (realtime < host_client->nametime)
1009 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1013 host_client->nametime = realtime + 5;
1015 // point the string back at updateclient->name to keep it safe
1016 strlcpy (host_client->name, newName, sizeof (host_client->name));
1018 for (i = 0, j = 0;host_client->name[i];i++)
1019 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1020 host_client->name[j++] = host_client->name[i];
1021 host_client->name[j] = 0;
1023 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1024 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1026 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1027 host_client->name[sizeof(host_client->name) - 1] = 0;
1028 host_client->name[0] = STRING_COLOR_TAG;
1029 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1032 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1033 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1036 l = strlen(host_client->name);
1037 if(l < sizeof(host_client->name) - 1)
1039 // duplicate the color tag to escape it
1040 host_client->name[i] = STRING_COLOR_TAG;
1041 host_client->name[i+1] = 0;
1042 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1046 // remove the last character to fix the color code
1047 host_client->name[l-1] = 0;
1048 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1052 // find the last color tag offset and decide if we need to add a reset tag
1053 for (i = 0, j = -1;host_client->name[i];i++)
1055 if (host_client->name[i] == STRING_COLOR_TAG)
1057 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1060 // if this happens to be a reset tag then we don't need one
1061 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1066 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]))
1072 if (host_client->name[i+1] == STRING_COLOR_TAG)
1079 // does not end in the default color string, so add it
1080 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1081 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1083 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1084 if (strcmp(host_client->old_name, host_client->name))
1086 if (host_client->spawned)
1087 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1088 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1089 // send notification to all clients
1090 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1091 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1092 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1093 SV_WriteNetnameIntoDemo(host_client);
1098 ======================
1100 ======================
1102 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1103 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1104 void Host_Playermodel_f (void)
1107 char newPath[sizeof(host_client->playermodel)];
1109 if (Cmd_Argc () == 1)
1111 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1115 if (Cmd_Argc () == 2)
1116 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1118 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1120 for (i = 0, j = 0;newPath[i];i++)
1121 if (newPath[i] != '\r' && newPath[i] != '\n')
1122 newPath[j++] = newPath[i];
1125 if (cmd_source == src_command)
1127 Cvar_Set ("_cl_playermodel", newPath);
1132 if (realtime < host_client->nametime)
1134 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1138 host_client->nametime = realtime + 5;
1141 // point the string back at updateclient->name to keep it safe
1142 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1143 if( prog->fieldoffsets.playermodel >= 0 )
1144 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1145 if (strcmp(host_client->old_model, host_client->playermodel))
1147 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1148 /*// send notification to all clients
1149 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1150 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1151 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1156 ======================
1158 ======================
1160 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1161 void Host_Playerskin_f (void)
1164 char newPath[sizeof(host_client->playerskin)];
1166 if (Cmd_Argc () == 1)
1168 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1172 if (Cmd_Argc () == 2)
1173 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1175 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1177 for (i = 0, j = 0;newPath[i];i++)
1178 if (newPath[i] != '\r' && newPath[i] != '\n')
1179 newPath[j++] = newPath[i];
1182 if (cmd_source == src_command)
1184 Cvar_Set ("_cl_playerskin", newPath);
1189 if (realtime < host_client->nametime)
1191 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1195 host_client->nametime = realtime + 5;
1198 // point the string back at updateclient->name to keep it safe
1199 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1200 if( prog->fieldoffsets.playerskin >= 0 )
1201 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1202 if (strcmp(host_client->old_skin, host_client->playerskin))
1204 //if (host_client->spawned)
1205 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1206 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1207 /*// send notification to all clients
1208 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1209 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1210 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1214 void Host_Version_f (void)
1216 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1219 void Host_Say(qboolean teamonly)
1225 // LordHavoc: long say messages
1227 qboolean fromServer = false;
1229 if (cmd_source == src_command)
1231 if (cls.state == ca_dedicated)
1238 Cmd_ForwardToServer ();
1243 if (Cmd_Argc () < 2)
1246 if (!teamplay.integer)
1256 // note this uses the chat prefix \001
1257 if (!fromServer && !teamonly)
1258 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1259 else if (!fromServer && teamonly)
1260 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1261 else if(*(sv_adminnick.string))
1262 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1264 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1265 p2 = text + strlen(text);
1266 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1268 if (p2[-1] == '\"' && quoted)
1273 strlcat(text, "\n", sizeof(text));
1275 // note: save is not a valid edict if fromServer is true
1277 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1278 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1279 SV_ClientPrint(text);
1282 if (cls.state == ca_dedicated)
1283 Con_Print(&text[1]);
1287 void Host_Say_f(void)
1293 void Host_Say_Team_f(void)
1299 void Host_Tell_f(void)
1301 const char *playername_start = NULL;
1302 size_t playername_length = 0;
1303 int playernumber = 0;
1306 const char *p1, *p2;
1307 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1308 qboolean fromServer = false;
1310 if (cmd_source == src_command)
1312 if (cls.state == ca_dedicated)
1316 Cmd_ForwardToServer ();
1321 if (Cmd_Argc () < 2)
1324 // note this uses the chat prefix \001
1326 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1327 else if(*(sv_adminnick.string))
1328 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1330 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1333 p2 = p1 + strlen(p1);
1334 // remove the target name
1335 while (p1 < p2 && *p1 == ' ')
1340 while (p1 < p2 && *p1 == ' ')
1342 while (p1 < p2 && isdigit(*p1))
1344 playernumber = playernumber * 10 + (*p1 - '0');
1352 playername_start = p1;
1353 while (p1 < p2 && *p1 != '"')
1355 playername_length = p1 - playername_start;
1361 playername_start = p1;
1362 while (p1 < p2 && *p1 != ' ')
1364 playername_length = p1 - playername_start;
1366 while (p1 < p2 && *p1 == ' ')
1368 if(playername_start)
1370 // set playernumber to the right client
1372 if(playername_length >= sizeof(namebuf))
1375 Con_Print("Host_Tell: too long player name/ID\n");
1377 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1380 memcpy(namebuf, playername_start, playername_length);
1381 namebuf[playername_length] = 0;
1382 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1384 if (!svs.clients[playernumber].active)
1386 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1390 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1393 Con_Print("Host_Tell: invalid player name/ID\n");
1395 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1398 // remove trailing newlines
1399 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1401 // remove quotes if present
1407 else if (fromServer)
1408 Con_Print("Host_Tell: missing end quote\n");
1410 SV_ClientPrint("Host_Tell: missing end quote\n");
1412 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1415 return; // empty say
1416 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1422 host_client = svs.clients + playernumber;
1423 SV_ClientPrint(text);
1433 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1434 void Host_Color(int changetop, int changebottom)
1436 int top, bottom, playercolor;
1438 // get top and bottom either from the provided values or the current values
1439 // (allows changing only top or bottom, or both at once)
1440 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1441 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1445 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1451 playercolor = top*16 + bottom;
1453 if (cmd_source == src_command)
1455 Cvar_SetValueQuick(&cl_color, playercolor);
1459 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1462 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1464 Con_DPrint("Calling SV_ChangeTeam\n");
1465 prog->globals.server->time = sv.time;
1466 prog->globals.generic[OFS_PARM0] = playercolor;
1467 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1468 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1473 if (host_client->edict)
1475 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1476 val->_float = playercolor;
1477 host_client->edict->fields.server->team = bottom + 1;
1479 host_client->colors = playercolor;
1480 if (host_client->old_colors != host_client->colors)
1482 host_client->old_colors = host_client->colors;
1483 // send notification to all clients
1484 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1485 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1486 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1491 void Host_Color_f(void)
1495 if (Cmd_Argc() == 1)
1497 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1498 Con_Print("color <0-15> [0-15]\n");
1502 if (Cmd_Argc() == 2)
1503 top = bottom = atoi(Cmd_Argv(1));
1506 top = atoi(Cmd_Argv(1));
1507 bottom = atoi(Cmd_Argv(2));
1509 Host_Color(top, bottom);
1512 void Host_TopColor_f(void)
1514 if (Cmd_Argc() == 1)
1516 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1517 Con_Print("topcolor <0-15>\n");
1521 Host_Color(atoi(Cmd_Argv(1)), -1);
1524 void Host_BottomColor_f(void)
1526 if (Cmd_Argc() == 1)
1528 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1529 Con_Print("bottomcolor <0-15>\n");
1533 Host_Color(-1, atoi(Cmd_Argv(1)));
1536 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1537 void Host_Rate_f(void)
1541 if (Cmd_Argc() != 2)
1543 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1544 Con_Print("rate <bytespersecond>\n");
1548 rate = atoi(Cmd_Argv(1));
1550 if (cmd_source == src_command)
1552 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1556 host_client->rate = rate;
1564 void Host_Kill_f (void)
1566 if (host_client->edict->fields.server->health <= 0)
1568 SV_ClientPrint("Can't suicide -- already dead!\n");
1572 prog->globals.server->time = sv.time;
1573 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1574 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1583 void Host_Pause_f (void)
1585 if (!pausable.integer)
1586 SV_ClientPrint("Pause not allowed.\n");
1590 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1591 // send notification to all clients
1592 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1593 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1598 ======================
1600 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1601 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1602 ======================
1604 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)"};
1605 static void Host_PModel_f (void)
1610 if (Cmd_Argc () == 1)
1612 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1615 i = atoi(Cmd_Argv(1));
1617 if (cmd_source == src_command)
1619 if (cl_pmodel.integer == i)
1621 Cvar_SetValue ("_cl_pmodel", i);
1622 if (cls.state == ca_connected)
1623 Cmd_ForwardToServer ();
1627 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1631 //===========================================================================
1639 void Host_PreSpawn_f (void)
1641 if (host_client->spawned)
1643 Con_Print("prespawn not valid -- already spawned\n");
1647 if (host_client->netconnection)
1649 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1650 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1651 MSG_WriteByte (&host_client->netconnection->message, 2);
1652 host_client->sendsignon = 0; // enable unlimited sends again
1655 // reset the name change timer because the client will send name soon
1656 host_client->nametime = 0;
1664 void Host_Spawn_f (void)
1668 int stats[MAX_CL_STATS];
1670 if (host_client->spawned)
1672 Con_Print("Spawn not valid -- already spawned\n");
1676 // reset name change timer again because they might want to change name
1677 // again in the first 5 seconds after connecting
1678 host_client->nametime = 0;
1680 // LordHavoc: moved this above the QC calls at FrikaC's request
1681 // LordHavoc: commented this out
1682 //if (host_client->netconnection)
1683 // SZ_Clear (&host_client->netconnection->message);
1685 // run the entrance script
1688 // loaded games are fully initialized already
1689 if (prog->funcoffsets.RestoreGame)
1691 Con_DPrint("Calling RestoreGame\n");
1692 prog->globals.server->time = sv.time;
1693 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1694 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1699 //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);
1701 // copy spawn parms out of the client_t
1702 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1703 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1705 // call the spawn function
1706 host_client->clientconnectcalled = true;
1707 prog->globals.server->time = sv.time;
1708 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1709 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1711 if (cls.state == ca_dedicated)
1712 Con_Printf("%s connected\n", host_client->name);
1714 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1717 if (!host_client->netconnection)
1720 // send time of update
1721 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1722 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1724 // send all current names, colors, and frag counts
1725 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1727 if (!client->active)
1729 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1730 MSG_WriteByte (&host_client->netconnection->message, i);
1731 MSG_WriteString (&host_client->netconnection->message, client->name);
1732 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1733 MSG_WriteByte (&host_client->netconnection->message, i);
1734 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1735 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1736 MSG_WriteByte (&host_client->netconnection->message, i);
1737 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1740 // send all current light styles
1741 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1743 if (sv.lightstyles[i][0])
1745 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1746 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1747 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1752 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1753 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1754 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1756 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1757 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1758 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1760 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1761 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1762 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1764 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1765 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1766 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1769 // Never send a roll angle, because savegames can catch the server
1770 // in a state where it is expecting the client to correct the angle
1771 // and it won't happen if the game was just loaded, so you wind up
1772 // with a permanent head tilt
1775 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1776 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1777 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1778 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1782 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1783 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1784 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1785 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1788 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1790 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1791 MSG_WriteByte (&host_client->netconnection->message, 3);
1799 void Host_Begin_f (void)
1801 host_client->spawned = true;
1803 // LordHavoc: note: this code also exists in SV_DropClient
1807 for (i = 0;i < svs.maxclients;i++)
1808 if (svs.clients[i].active && !svs.clients[i].spawned)
1810 if (i == svs.maxclients)
1812 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1813 sv.paused = sv.loadgame = false; // we're basically done with loading now
1818 //===========================================================================
1825 Kicks a user off of the server
1828 void Host_Kick_f (void)
1831 const char *message = NULL;
1834 qboolean byNumber = false;
1842 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1844 i = (int)(atof(Cmd_Argv(2)) - 1);
1845 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1851 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1853 if (!host_client->active)
1855 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1860 if (i < svs.maxclients)
1862 if (cmd_source == src_command)
1864 if (cls.state == ca_dedicated)
1867 who = cl_name.string;
1872 // can't kick yourself!
1873 if (host_client == save)
1878 message = Cmd_Args();
1879 COM_ParseToken_Simple(&message, false, false);
1882 message++; // skip the #
1883 while (*message == ' ') // skip white space
1885 message += strlen(Cmd_Argv(2)); // skip the number
1887 while (*message && *message == ' ')
1891 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1893 SV_ClientPrintf("Kicked by %s\n", who);
1894 SV_DropClient (false); // kicked
1902 ===============================================================================
1906 ===============================================================================
1914 void Host_Give_f (void)
1922 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1927 v = atoi (Cmd_Argv(2));
1941 // MED 01/04/97 added hipnotic give stuff
1942 if (gamemode == GAME_HIPNOTIC)
1947 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1949 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1951 else if (t[0] == '9')
1952 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1953 else if (t[0] == '0')
1954 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1955 else if (t[0] >= '2')
1956 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1961 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1966 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1969 host_client->edict->fields.server->ammo_shells = v;
1972 if (gamemode == GAME_ROGUE)
1974 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1977 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1978 host_client->edict->fields.server->ammo_nails = v;
1983 host_client->edict->fields.server->ammo_nails = v;
1987 if (gamemode == GAME_ROGUE)
1989 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
1993 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1994 host_client->edict->fields.server->ammo_nails = v;
1999 if (gamemode == GAME_ROGUE)
2001 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2005 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2006 host_client->edict->fields.server->ammo_rockets = v;
2011 host_client->edict->fields.server->ammo_rockets = v;
2015 if (gamemode == GAME_ROGUE)
2017 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2021 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2022 host_client->edict->fields.server->ammo_rockets = v;
2027 host_client->edict->fields.server->health = v;
2030 if (gamemode == GAME_ROGUE)
2032 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2036 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2037 host_client->edict->fields.server->ammo_cells = v;
2042 host_client->edict->fields.server->ammo_cells = v;
2046 if (gamemode == GAME_ROGUE)
2048 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2052 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2053 host_client->edict->fields.server->ammo_cells = v;
2060 prvm_edict_t *FindViewthing (void)
2065 for (i=0 ; i<prog->num_edicts ; i++)
2067 e = PRVM_EDICT_NUM(i);
2068 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2071 Con_Print("No viewthing on map\n");
2080 void Host_Viewmodel_f (void)
2089 e = FindViewthing ();
2094 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2095 if (!m || !m->loaded || !m->Draw)
2097 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2101 e->fields.server->frame = 0;
2102 cl.model_precache[(int)e->fields.server->modelindex] = m;
2110 void Host_Viewframe_f (void)
2120 e = FindViewthing ();
2124 m = cl.model_precache[(int)e->fields.server->modelindex];
2126 f = atoi(Cmd_Argv(1));
2127 if (f >= m->numframes)
2130 e->fields.server->frame = f;
2134 void PrintFrameName (dp_model_t *m, int frame)
2137 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2139 Con_Printf("frame %i\n", frame);
2147 void Host_Viewnext_f (void)
2156 e = FindViewthing ();
2160 m = cl.model_precache[(int)e->fields.server->modelindex];
2162 e->fields.server->frame = e->fields.server->frame + 1;
2163 if (e->fields.server->frame >= m->numframes)
2164 e->fields.server->frame = m->numframes - 1;
2166 PrintFrameName (m, (int)e->fields.server->frame);
2174 void Host_Viewprev_f (void)
2183 e = FindViewthing ();
2188 m = cl.model_precache[(int)e->fields.server->modelindex];
2190 e->fields.server->frame = e->fields.server->frame - 1;
2191 if (e->fields.server->frame < 0)
2192 e->fields.server->frame = 0;
2194 PrintFrameName (m, (int)e->fields.server->frame);
2198 ===============================================================================
2202 ===============================================================================
2211 void Host_Startdemos_f (void)
2215 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2221 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2224 Con_DPrintf("%i demo(s) in loop\n", c);
2226 for (i=1 ; i<c+1 ; i++)
2227 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2229 // LordHavoc: clear the remaining slots
2230 for (;i <= MAX_DEMOS;i++)
2231 cls.demos[i-1][0] = 0;
2233 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2247 Return to looping demos
2250 void Host_Demos_f (void)
2252 if (cls.state == ca_dedicated)
2254 if (cls.demonum == -1)
2264 Return to looping demos
2267 void Host_Stopdemo_f (void)
2269 if (!cls.demoplayback)
2272 Host_ShutdownServer ();
2275 void Host_SendCvar_f (void)
2279 const char *cvarname;
2284 cvarname = Cmd_Argv(1);
2285 if (cls.state == ca_connected)
2287 c = Cvar_FindVar(cvarname);
2288 // LordHavoc: if there is no such cvar or if it is private, send a
2289 // reply indicating that it has no value
2290 if(!c || (c->flags & CVAR_PRIVATE))
2291 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2293 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2296 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2300 if (cls.state != ca_dedicated)
2304 for(;i<svs.maxclients;i++)
2305 if(svs.clients[i].active && svs.clients[i].netconnection)
2307 host_client = &svs.clients[i];
2308 Host_ClientCommands("sendcvar %s\n", cvarname);
2313 static void MaxPlayers_f(void)
2317 if (Cmd_Argc() != 2)
2319 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2325 Con_Print("maxplayers can not be changed while a server is running.\n");
2326 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2329 n = atoi(Cmd_Argv(1));
2330 n = bound(1, n, MAX_SCOREBOARD);
2331 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2333 svs.maxclients_next = n;
2335 Cvar_Set ("deathmatch", "0");
2337 Cvar_Set ("deathmatch", "1");
2341 =====================
2344 ProQuake rcon support
2345 =====================
2347 void Host_PQRcon_f (void)
2352 lhnetsocket_t *mysocket;
2353 char peer_address[64];
2355 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2357 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2361 e = strchr(rcon_password.string, ' ');
2362 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2366 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2370 if (!rcon_address.string[0])
2372 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2375 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2377 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2378 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2381 SZ_Clear(&net_message);
2382 MSG_WriteLong (&net_message, 0);
2383 MSG_WriteByte (&net_message, CCREQ_RCON);
2384 SZ_Write(&net_message, (void*)rcon_password.string, n);
2385 MSG_WriteString (&net_message, Cmd_Args());
2386 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2387 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2388 SZ_Clear (&net_message);
2392 //=============================================================================
2394 // QuakeWorld commands
2397 =====================
2400 Send the rest of the command line over as
2401 an unconnected command.
2402 =====================
2404 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2409 lhnetsocket_t *mysocket;
2411 if (!rcon_password.string || !rcon_password.string[0])
2413 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2417 e = strchr(rcon_password.string, ' ');
2418 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2421 to = cls.netcon->peeraddress;
2424 if (!rcon_address.string[0])
2426 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2429 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2431 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2432 if (mysocket && Cmd_Args()[0])
2434 // simply put together the rcon packet and send it
2435 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2437 if(cls.rcon_commands[cls.rcon_ringpos][0])
2440 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2441 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]);
2442 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2445 for (i = 0;i < MAX_RCONS;i++)
2446 if(cls.rcon_commands[i][0])
2447 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2451 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2452 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2453 cls.rcon_addresses[cls.rcon_ringpos] = to;
2454 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2455 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2457 else if(rcon_secure.integer > 0)
2461 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2462 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2463 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2466 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2467 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2472 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2478 ====================
2481 user <name or userid>
2483 Dump userdata / masterdata for a user
2484 ====================
2486 void Host_User_f (void) // credit: taken from QuakeWorld
2491 if (Cmd_Argc() != 2)
2493 Con_Printf ("Usage: user <username / userid>\n");
2497 uid = atoi(Cmd_Argv(1));
2499 for (i = 0;i < cl.maxclients;i++)
2501 if (!cl.scores[i].name[0])
2503 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2505 InfoString_Print(cl.scores[i].qw_userinfo);
2509 Con_Printf ("User not in server.\n");
2513 ====================
2516 Dump userids for all current players
2517 ====================
2519 void Host_Users_f (void) // credit: taken from QuakeWorld
2525 Con_Printf ("userid frags name\n");
2526 Con_Printf ("------ ----- ----\n");
2527 for (i = 0;i < cl.maxclients;i++)
2529 if (cl.scores[i].name[0])
2531 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2536 Con_Printf ("%i total users\n", c);
2541 Host_FullServerinfo_f
2543 Sent by server when serverinfo changes
2546 // TODO: shouldn't this be a cvar instead?
2547 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2550 if (Cmd_Argc() != 2)
2552 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2556 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2557 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2558 cl.qw_teamplay = atoi(temp);
2565 Allow clients to change userinfo
2569 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2576 if (Cmd_Argc() != 2)
2578 Con_Printf ("fullinfo <complete info string>\n");
2588 while (*s && *s != '\\')
2594 Con_Printf ("MISSING VALUE\n");
2600 while (*s && *s != '\\')
2607 CL_SetInfo(key, value, false, false, false, false);
2615 Allow clients to change userinfo
2618 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2620 if (Cmd_Argc() == 1)
2622 InfoString_Print(cls.userinfo);
2625 if (Cmd_Argc() != 3)
2627 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2630 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2634 ====================
2637 packet <destination> <contents>
2639 Contents allows \n escape character
2640 ====================
2642 void Host_Packet_f (void) // credit: taken from QuakeWorld
2648 lhnetaddress_t address;
2649 lhnetsocket_t *mysocket;
2651 if (Cmd_Argc() != 3)
2653 Con_Printf ("packet <destination> <contents>\n");
2657 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2659 Con_Printf ("Bad address\n");
2665 send[0] = send[1] = send[2] = send[3] = -1;
2667 l = (int)strlen (in);
2668 for (i=0 ; i<l ; i++)
2670 if (out >= send + sizeof(send) - 1)
2672 if (in[i] == '\\' && in[i+1] == 'n')
2677 else if (in[i] == '\\' && in[i+1] == '0')
2682 else if (in[i] == '\\' && in[i+1] == 't')
2687 else if (in[i] == '\\' && in[i+1] == 'r')
2692 else if (in[i] == '\\' && in[i+1] == '"')
2701 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2703 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2705 NetConn_Write(mysocket, send, out - send, &address);
2709 ====================
2712 Send back ping and packet loss update for all current players to this player
2713 ====================
2715 void Host_Pings_f (void)
2717 int i, j, ping, packetloss, movementloss;
2720 if (!host_client->netconnection)
2723 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2725 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2726 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2728 for (i = 0;i < svs.maxclients;i++)
2732 if (svs.clients[i].netconnection)
2734 for (j = 0;j < NETGRAPH_PACKETS;j++)
2735 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2737 for (j = 0;j < NETGRAPH_PACKETS;j++)
2738 if (svs.clients[i].movement_count[j] < 0)
2741 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2742 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2743 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2744 ping = bound(0, ping, 9999);
2745 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2747 // send qw_svc_updateping and qw_svc_updatepl messages
2748 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2749 MSG_WriteShort(&host_client->netconnection->message, ping);
2750 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2751 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2755 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2757 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2759 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2760 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2763 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2764 MSG_WriteString(&host_client->netconnection->message, "\n");
2767 void Host_PingPLReport_f(void)
2772 if (l > cl.maxclients)
2774 for (i = 0;i < l;i++)
2776 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2777 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2778 if(errbyte && *errbyte == ',')
2779 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2781 cl.scores[i].qw_movementloss = 0;
2785 //=============================================================================
2792 void Host_InitCommands (void)
2794 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2796 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2797 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2798 if (gamemode == GAME_NEHAHRA)
2800 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2801 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2802 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2803 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2804 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2808 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2809 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2810 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2811 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2812 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2814 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2815 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2816 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2817 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2818 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)");
2819 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2820 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2821 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2822 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2823 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2824 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2825 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2826 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2827 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2828 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2830 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2831 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2832 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2834 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2835 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2836 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2837 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2839 Cvar_RegisterVariable (&cl_name);
2840 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2841 Cvar_RegisterVariable (&cl_color);
2842 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2843 Cvar_RegisterVariable (&cl_rate);
2844 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2845 if (gamemode == GAME_NEHAHRA)
2847 Cvar_RegisterVariable (&cl_pmodel);
2848 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2851 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2852 Cvar_RegisterVariable (&cl_playermodel);
2853 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2854 Cvar_RegisterVariable (&cl_playerskin);
2855 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2857 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2858 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2859 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)");
2860 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2862 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2864 Cvar_RegisterVariable (&rcon_password);
2865 Cvar_RegisterVariable (&rcon_address);
2866 Cvar_RegisterVariable (&rcon_secure);
2867 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2868 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");
2869 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");
2870 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)");
2871 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2872 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2873 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2874 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2875 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2876 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2877 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2878 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2880 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)");
2881 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)");
2883 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)");
2884 Cvar_RegisterVariable (&r_fixtrans_auto);
2886 Cvar_RegisterVariable (&team);
2887 Cvar_RegisterVariable (&skin);
2888 Cvar_RegisterVariable (&noaim);
2890 Cvar_RegisterVariable(&sv_cheats);
2891 Cvar_RegisterVariable(&sv_adminnick);
2892 Cvar_RegisterVariable(&sv_status_privacy);
2893 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2896 void Host_NoOperation_f(void)