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 // unlink all entities
835 World_UnlinkAll(&sv.world);
837 // load the edicts out of the savegame file
842 while (COM_ParseToken_Simple(&t, false, false))
843 if (!strcmp(com_token, "}"))
845 if (!COM_ParseToken_Simple(&start, false, false))
850 if (strcmp(com_token,"{"))
853 Host_Error ("First token isn't a brace");
858 if(developer_entityparsing.integer)
859 Con_Printf("Host_Loadgame_f: loading globals\n");
861 // parse the global vars
862 PRVM_ED_ParseGlobals (start);
867 if (entnum >= MAX_EDICTS)
870 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
872 while (entnum >= prog->max_edicts)
873 PRVM_MEM_IncreaseEdicts();
874 ent = PRVM_EDICT_NUM(entnum);
875 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
876 ent->priv.server->free = false;
878 if(developer_entityparsing.integer)
879 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
881 PRVM_ED_ParseEdict (start, ent);
883 // link it into the bsp tree
884 if (!ent->priv.server->free)
892 prog->num_edicts = entnum;
895 for (i = 0;i < NUM_SPAWN_PARMS;i++)
896 svs.clients[0].spawn_parms[i] = spawn_parms[i];
898 if(developer_entityparsing.integer)
899 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
901 // read extended data if present
902 // the extended data is stored inside a /* */ comment block, which the
903 // parser intentionally skips, so we have to check for it manually here
906 while (*end == '\r' || *end == '\n')
908 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
910 if(developer_entityparsing.integer)
911 Con_Printf("Host_Loadgame_f: loading extended data\n");
913 Con_Printf("Loading extended DarkPlaces savegame\n");
915 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
916 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
917 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
918 while (COM_ParseToken_Simple(&t, false, false))
920 if (!strcmp(com_token, "sv.lightstyles"))
922 COM_ParseToken_Simple(&t, false, false);
924 COM_ParseToken_Simple(&t, false, false);
925 if (i >= 0 && i < MAX_LIGHTSTYLES)
926 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
928 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
930 else if (!strcmp(com_token, "sv.model_precache"))
932 COM_ParseToken_Simple(&t, false, false);
934 COM_ParseToken_Simple(&t, false, false);
935 if (i >= 0 && i < MAX_MODELS)
937 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
938 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
941 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
943 else if (!strcmp(com_token, "sv.sound_precache"))
945 COM_ParseToken_Simple(&t, false, false);
947 COM_ParseToken_Simple(&t, false, false);
948 if (i >= 0 && i < MAX_SOUNDS)
949 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
951 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
953 // skip any trailing text or unrecognized commands
954 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
961 if(developer_entityparsing.integer)
962 Con_Printf("Host_Loadgame_f: finished\n");
966 // make sure we're connected to loopback
967 if (sv.active && cls.state == ca_disconnected)
968 CL_EstablishConnection("local:1");
971 //============================================================================
974 ======================
976 ======================
978 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
979 void Host_Name_f (void)
982 qboolean valid_colors;
983 const char *newNameSource;
984 char newName[sizeof(host_client->name)];
986 if (Cmd_Argc () == 1)
988 Con_Printf("name: %s\n", cl_name.string);
992 if (Cmd_Argc () == 2)
993 newNameSource = Cmd_Argv(1);
995 newNameSource = Cmd_Args();
997 strlcpy(newName, newNameSource, sizeof(newName));
999 if (cmd_source == src_command)
1001 Cvar_Set ("_cl_name", newName);
1002 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1004 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1005 Con_Printf("name: %s\n", cl_name.string);
1010 if (realtime < host_client->nametime)
1012 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1016 host_client->nametime = realtime + 5;
1018 // point the string back at updateclient->name to keep it safe
1019 strlcpy (host_client->name, newName, sizeof (host_client->name));
1021 for (i = 0, j = 0;host_client->name[i];i++)
1022 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1023 host_client->name[j++] = host_client->name[i];
1024 host_client->name[j] = 0;
1026 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1027 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1029 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1030 host_client->name[sizeof(host_client->name) - 1] = 0;
1031 host_client->name[0] = STRING_COLOR_TAG;
1032 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1035 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1036 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1039 l = strlen(host_client->name);
1040 if(l < sizeof(host_client->name) - 1)
1042 // duplicate the color tag to escape it
1043 host_client->name[i] = STRING_COLOR_TAG;
1044 host_client->name[i+1] = 0;
1045 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1049 // remove the last character to fix the color code
1050 host_client->name[l-1] = 0;
1051 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1055 // find the last color tag offset and decide if we need to add a reset tag
1056 for (i = 0, j = -1;host_client->name[i];i++)
1058 if (host_client->name[i] == STRING_COLOR_TAG)
1060 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1063 // if this happens to be a reset tag then we don't need one
1064 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1069 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]))
1075 if (host_client->name[i+1] == STRING_COLOR_TAG)
1082 // does not end in the default color string, so add it
1083 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1084 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1086 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1087 if (strcmp(host_client->old_name, host_client->name))
1089 if (host_client->spawned)
1090 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1091 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1092 // send notification to all clients
1093 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1094 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1095 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1096 SV_WriteNetnameIntoDemo(host_client);
1101 ======================
1103 ======================
1105 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1106 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1107 void Host_Playermodel_f (void)
1110 char newPath[sizeof(host_client->playermodel)];
1112 if (Cmd_Argc () == 1)
1114 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1118 if (Cmd_Argc () == 2)
1119 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1121 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1123 for (i = 0, j = 0;newPath[i];i++)
1124 if (newPath[i] != '\r' && newPath[i] != '\n')
1125 newPath[j++] = newPath[i];
1128 if (cmd_source == src_command)
1130 Cvar_Set ("_cl_playermodel", newPath);
1135 if (realtime < host_client->nametime)
1137 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1141 host_client->nametime = realtime + 5;
1144 // point the string back at updateclient->name to keep it safe
1145 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1146 if( prog->fieldoffsets.playermodel >= 0 )
1147 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1148 if (strcmp(host_client->old_model, host_client->playermodel))
1150 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1151 /*// send notification to all clients
1152 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1153 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1154 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1159 ======================
1161 ======================
1163 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1164 void Host_Playerskin_f (void)
1167 char newPath[sizeof(host_client->playerskin)];
1169 if (Cmd_Argc () == 1)
1171 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1175 if (Cmd_Argc () == 2)
1176 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1178 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1180 for (i = 0, j = 0;newPath[i];i++)
1181 if (newPath[i] != '\r' && newPath[i] != '\n')
1182 newPath[j++] = newPath[i];
1185 if (cmd_source == src_command)
1187 Cvar_Set ("_cl_playerskin", newPath);
1192 if (realtime < host_client->nametime)
1194 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1198 host_client->nametime = realtime + 5;
1201 // point the string back at updateclient->name to keep it safe
1202 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1203 if( prog->fieldoffsets.playerskin >= 0 )
1204 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1205 if (strcmp(host_client->old_skin, host_client->playerskin))
1207 //if (host_client->spawned)
1208 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1209 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1210 /*// send notification to all clients
1211 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1212 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1213 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1217 void Host_Version_f (void)
1219 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1222 void Host_Say(qboolean teamonly)
1228 // LordHavoc: long say messages
1230 qboolean fromServer = false;
1232 if (cmd_source == src_command)
1234 if (cls.state == ca_dedicated)
1241 Cmd_ForwardToServer ();
1246 if (Cmd_Argc () < 2)
1249 if (!teamplay.integer)
1259 // note this uses the chat prefix \001
1260 if (!fromServer && !teamonly)
1261 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1262 else if (!fromServer && teamonly)
1263 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1264 else if(*(sv_adminnick.string))
1265 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1267 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1268 p2 = text + strlen(text);
1269 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1271 if (p2[-1] == '\"' && quoted)
1276 strlcat(text, "\n", sizeof(text));
1278 // note: save is not a valid edict if fromServer is true
1280 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1281 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1282 SV_ClientPrint(text);
1285 if (cls.state == ca_dedicated)
1286 Con_Print(&text[1]);
1290 void Host_Say_f(void)
1296 void Host_Say_Team_f(void)
1302 void Host_Tell_f(void)
1304 const char *playername_start = NULL;
1305 size_t playername_length = 0;
1306 int playernumber = 0;
1309 const char *p1, *p2;
1310 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1311 qboolean fromServer = false;
1313 if (cmd_source == src_command)
1315 if (cls.state == ca_dedicated)
1319 Cmd_ForwardToServer ();
1324 if (Cmd_Argc () < 2)
1327 // note this uses the chat prefix \001
1329 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1330 else if(*(sv_adminnick.string))
1331 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1333 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1336 p2 = p1 + strlen(p1);
1337 // remove the target name
1338 while (p1 < p2 && *p1 == ' ')
1343 while (p1 < p2 && *p1 == ' ')
1345 while (p1 < p2 && isdigit(*p1))
1347 playernumber = playernumber * 10 + (*p1 - '0');
1355 playername_start = p1;
1356 while (p1 < p2 && *p1 != '"')
1358 playername_length = p1 - playername_start;
1364 playername_start = p1;
1365 while (p1 < p2 && *p1 != ' ')
1367 playername_length = p1 - playername_start;
1369 while (p1 < p2 && *p1 == ' ')
1371 if(playername_start)
1373 // set playernumber to the right client
1375 if(playername_length >= sizeof(namebuf))
1378 Con_Print("Host_Tell: too long player name/ID\n");
1380 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1383 memcpy(namebuf, playername_start, playername_length);
1384 namebuf[playername_length] = 0;
1385 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1387 if (!svs.clients[playernumber].active)
1389 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1393 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1396 Con_Print("Host_Tell: invalid player name/ID\n");
1398 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1401 // remove trailing newlines
1402 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1404 // remove quotes if present
1410 else if (fromServer)
1411 Con_Print("Host_Tell: missing end quote\n");
1413 SV_ClientPrint("Host_Tell: missing end quote\n");
1415 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1418 return; // empty say
1419 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1425 host_client = svs.clients + playernumber;
1426 SV_ClientPrint(text);
1436 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1437 void Host_Color(int changetop, int changebottom)
1439 int top, bottom, playercolor;
1441 // get top and bottom either from the provided values or the current values
1442 // (allows changing only top or bottom, or both at once)
1443 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1444 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1448 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1454 playercolor = top*16 + bottom;
1456 if (cmd_source == src_command)
1458 Cvar_SetValueQuick(&cl_color, playercolor);
1462 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1465 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1467 Con_DPrint("Calling SV_ChangeTeam\n");
1468 prog->globals.server->time = sv.time;
1469 prog->globals.generic[OFS_PARM0] = playercolor;
1470 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1471 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1476 if (host_client->edict)
1478 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1479 val->_float = playercolor;
1480 host_client->edict->fields.server->team = bottom + 1;
1482 host_client->colors = playercolor;
1483 if (host_client->old_colors != host_client->colors)
1485 host_client->old_colors = host_client->colors;
1486 // send notification to all clients
1487 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1488 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1489 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1494 void Host_Color_f(void)
1498 if (Cmd_Argc() == 1)
1500 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1501 Con_Print("color <0-15> [0-15]\n");
1505 if (Cmd_Argc() == 2)
1506 top = bottom = atoi(Cmd_Argv(1));
1509 top = atoi(Cmd_Argv(1));
1510 bottom = atoi(Cmd_Argv(2));
1512 Host_Color(top, bottom);
1515 void Host_TopColor_f(void)
1517 if (Cmd_Argc() == 1)
1519 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1520 Con_Print("topcolor <0-15>\n");
1524 Host_Color(atoi(Cmd_Argv(1)), -1);
1527 void Host_BottomColor_f(void)
1529 if (Cmd_Argc() == 1)
1531 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1532 Con_Print("bottomcolor <0-15>\n");
1536 Host_Color(-1, atoi(Cmd_Argv(1)));
1539 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1540 void Host_Rate_f(void)
1544 if (Cmd_Argc() != 2)
1546 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1547 Con_Print("rate <bytespersecond>\n");
1551 rate = atoi(Cmd_Argv(1));
1553 if (cmd_source == src_command)
1555 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1559 host_client->rate = rate;
1567 void Host_Kill_f (void)
1569 if (host_client->edict->fields.server->health <= 0)
1571 SV_ClientPrint("Can't suicide -- already dead!\n");
1575 prog->globals.server->time = sv.time;
1576 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1577 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1586 void Host_Pause_f (void)
1588 if (!pausable.integer)
1589 SV_ClientPrint("Pause not allowed.\n");
1593 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1594 // send notification to all clients
1595 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1596 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1601 ======================
1603 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1604 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1605 ======================
1607 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)"};
1608 static void Host_PModel_f (void)
1613 if (Cmd_Argc () == 1)
1615 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1618 i = atoi(Cmd_Argv(1));
1620 if (cmd_source == src_command)
1622 if (cl_pmodel.integer == i)
1624 Cvar_SetValue ("_cl_pmodel", i);
1625 if (cls.state == ca_connected)
1626 Cmd_ForwardToServer ();
1630 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1634 //===========================================================================
1642 void Host_PreSpawn_f (void)
1644 if (host_client->spawned)
1646 Con_Print("prespawn not valid -- already spawned\n");
1650 if (host_client->netconnection)
1652 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1653 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1654 MSG_WriteByte (&host_client->netconnection->message, 2);
1655 host_client->sendsignon = 0; // enable unlimited sends again
1658 // reset the name change timer because the client will send name soon
1659 host_client->nametime = 0;
1667 void Host_Spawn_f (void)
1671 int stats[MAX_CL_STATS];
1673 if (host_client->spawned)
1675 Con_Print("Spawn not valid -- already spawned\n");
1679 // reset name change timer again because they might want to change name
1680 // again in the first 5 seconds after connecting
1681 host_client->nametime = 0;
1683 // LordHavoc: moved this above the QC calls at FrikaC's request
1684 // LordHavoc: commented this out
1685 //if (host_client->netconnection)
1686 // SZ_Clear (&host_client->netconnection->message);
1688 // run the entrance script
1691 // loaded games are fully initialized already
1692 if (prog->funcoffsets.RestoreGame)
1694 Con_DPrint("Calling RestoreGame\n");
1695 prog->globals.server->time = sv.time;
1696 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1697 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1702 //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);
1704 // copy spawn parms out of the client_t
1705 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1706 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1708 // call the spawn function
1709 host_client->clientconnectcalled = true;
1710 prog->globals.server->time = sv.time;
1711 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1712 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1714 if (cls.state == ca_dedicated)
1715 Con_Printf("%s connected\n", host_client->name);
1717 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1720 if (!host_client->netconnection)
1723 // send time of update
1724 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1725 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1727 // send all current names, colors, and frag counts
1728 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1730 if (!client->active)
1732 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1733 MSG_WriteByte (&host_client->netconnection->message, i);
1734 MSG_WriteString (&host_client->netconnection->message, client->name);
1735 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1736 MSG_WriteByte (&host_client->netconnection->message, i);
1737 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1738 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1739 MSG_WriteByte (&host_client->netconnection->message, i);
1740 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1743 // send all current light styles
1744 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1746 if (sv.lightstyles[i][0])
1748 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1749 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1750 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1755 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1756 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1757 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1759 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1760 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1761 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1763 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1764 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1765 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1767 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1768 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1769 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1772 // Never send a roll angle, because savegames can catch the server
1773 // in a state where it is expecting the client to correct the angle
1774 // and it won't happen if the game was just loaded, so you wind up
1775 // with a permanent head tilt
1778 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1779 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1780 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1781 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1785 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1786 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1787 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1788 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1791 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1793 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1794 MSG_WriteByte (&host_client->netconnection->message, 3);
1802 void Host_Begin_f (void)
1804 host_client->spawned = true;
1806 // LordHavoc: note: this code also exists in SV_DropClient
1810 for (i = 0;i < svs.maxclients;i++)
1811 if (svs.clients[i].active && !svs.clients[i].spawned)
1813 if (i == svs.maxclients)
1815 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1816 sv.paused = sv.loadgame = false; // we're basically done with loading now
1821 //===========================================================================
1828 Kicks a user off of the server
1831 void Host_Kick_f (void)
1834 const char *message = NULL;
1837 qboolean byNumber = false;
1845 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1847 i = (int)(atof(Cmd_Argv(2)) - 1);
1848 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1854 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1856 if (!host_client->active)
1858 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1863 if (i < svs.maxclients)
1865 if (cmd_source == src_command)
1867 if (cls.state == ca_dedicated)
1870 who = cl_name.string;
1875 // can't kick yourself!
1876 if (host_client == save)
1881 message = Cmd_Args();
1882 COM_ParseToken_Simple(&message, false, false);
1885 message++; // skip the #
1886 while (*message == ' ') // skip white space
1888 message += strlen(Cmd_Argv(2)); // skip the number
1890 while (*message && *message == ' ')
1894 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1896 SV_ClientPrintf("Kicked by %s\n", who);
1897 SV_DropClient (false); // kicked
1905 ===============================================================================
1909 ===============================================================================
1917 void Host_Give_f (void)
1925 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1930 v = atoi (Cmd_Argv(2));
1944 // MED 01/04/97 added hipnotic give stuff
1945 if (gamemode == GAME_HIPNOTIC)
1950 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1952 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1954 else if (t[0] == '9')
1955 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1956 else if (t[0] == '0')
1957 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1958 else if (t[0] >= '2')
1959 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1964 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1969 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1972 host_client->edict->fields.server->ammo_shells = v;
1975 if (gamemode == GAME_ROGUE)
1977 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1980 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1981 host_client->edict->fields.server->ammo_nails = v;
1986 host_client->edict->fields.server->ammo_nails = v;
1990 if (gamemode == GAME_ROGUE)
1992 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
1996 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1997 host_client->edict->fields.server->ammo_nails = v;
2002 if (gamemode == GAME_ROGUE)
2004 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2008 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2009 host_client->edict->fields.server->ammo_rockets = v;
2014 host_client->edict->fields.server->ammo_rockets = v;
2018 if (gamemode == GAME_ROGUE)
2020 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2024 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2025 host_client->edict->fields.server->ammo_rockets = v;
2030 host_client->edict->fields.server->health = v;
2033 if (gamemode == GAME_ROGUE)
2035 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2039 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2040 host_client->edict->fields.server->ammo_cells = v;
2045 host_client->edict->fields.server->ammo_cells = v;
2049 if (gamemode == GAME_ROGUE)
2051 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2055 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2056 host_client->edict->fields.server->ammo_cells = v;
2063 prvm_edict_t *FindViewthing (void)
2068 for (i=0 ; i<prog->num_edicts ; i++)
2070 e = PRVM_EDICT_NUM(i);
2071 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2074 Con_Print("No viewthing on map\n");
2083 void Host_Viewmodel_f (void)
2092 e = FindViewthing ();
2097 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2098 if (!m || !m->loaded || !m->Draw)
2100 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2104 e->fields.server->frame = 0;
2105 cl.model_precache[(int)e->fields.server->modelindex] = m;
2113 void Host_Viewframe_f (void)
2123 e = FindViewthing ();
2127 m = cl.model_precache[(int)e->fields.server->modelindex];
2129 f = atoi(Cmd_Argv(1));
2130 if (f >= m->numframes)
2133 e->fields.server->frame = f;
2137 void PrintFrameName (dp_model_t *m, int frame)
2140 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2142 Con_Printf("frame %i\n", frame);
2150 void Host_Viewnext_f (void)
2159 e = FindViewthing ();
2163 m = cl.model_precache[(int)e->fields.server->modelindex];
2165 e->fields.server->frame = e->fields.server->frame + 1;
2166 if (e->fields.server->frame >= m->numframes)
2167 e->fields.server->frame = m->numframes - 1;
2169 PrintFrameName (m, (int)e->fields.server->frame);
2177 void Host_Viewprev_f (void)
2186 e = FindViewthing ();
2191 m = cl.model_precache[(int)e->fields.server->modelindex];
2193 e->fields.server->frame = e->fields.server->frame - 1;
2194 if (e->fields.server->frame < 0)
2195 e->fields.server->frame = 0;
2197 PrintFrameName (m, (int)e->fields.server->frame);
2201 ===============================================================================
2205 ===============================================================================
2214 void Host_Startdemos_f (void)
2218 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2224 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2227 Con_DPrintf("%i demo(s) in loop\n", c);
2229 for (i=1 ; i<c+1 ; i++)
2230 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2232 // LordHavoc: clear the remaining slots
2233 for (;i <= MAX_DEMOS;i++)
2234 cls.demos[i-1][0] = 0;
2236 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2250 Return to looping demos
2253 void Host_Demos_f (void)
2255 if (cls.state == ca_dedicated)
2257 if (cls.demonum == -1)
2267 Return to looping demos
2270 void Host_Stopdemo_f (void)
2272 if (!cls.demoplayback)
2275 Host_ShutdownServer ();
2278 void Host_SendCvar_f (void)
2282 const char *cvarname;
2287 cvarname = Cmd_Argv(1);
2288 if (cls.state == ca_connected)
2290 c = Cvar_FindVar(cvarname);
2291 // LordHavoc: if there is no such cvar or if it is private, send a
2292 // reply indicating that it has no value
2293 if(!c || (c->flags & CVAR_PRIVATE))
2294 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2296 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2299 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2303 if (cls.state != ca_dedicated)
2307 for(;i<svs.maxclients;i++)
2308 if(svs.clients[i].active && svs.clients[i].netconnection)
2310 host_client = &svs.clients[i];
2311 Host_ClientCommands("sendcvar %s\n", cvarname);
2316 static void MaxPlayers_f(void)
2320 if (Cmd_Argc() != 2)
2322 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2328 Con_Print("maxplayers can not be changed while a server is running.\n");
2329 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2332 n = atoi(Cmd_Argv(1));
2333 n = bound(1, n, MAX_SCOREBOARD);
2334 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2336 svs.maxclients_next = n;
2338 Cvar_Set ("deathmatch", "0");
2340 Cvar_Set ("deathmatch", "1");
2344 =====================
2347 ProQuake rcon support
2348 =====================
2350 void Host_PQRcon_f (void)
2355 lhnetsocket_t *mysocket;
2356 char peer_address[64];
2358 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2360 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2364 e = strchr(rcon_password.string, ' ');
2365 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
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 SZ_Write(&net_message, (void*)rcon_password.string, n);
2388 MSG_WriteString (&net_message, Cmd_Args());
2389 StoreBigLong(net_message.data, 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
2412 lhnetsocket_t *mysocket;
2414 if (!rcon_password.string || !rcon_password.string[0])
2416 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2420 e = strchr(rcon_password.string, ' ');
2421 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2424 to = cls.netcon->peeraddress;
2427 if (!rcon_address.string[0])
2429 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2432 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2434 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2435 if (mysocket && Cmd_Args()[0])
2437 // simply put together the rcon packet and send it
2438 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2440 if(cls.rcon_commands[cls.rcon_ringpos][0])
2443 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2444 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]);
2445 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2448 for (i = 0;i < MAX_RCONS;i++)
2449 if(cls.rcon_commands[i][0])
2450 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2454 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2455 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2456 cls.rcon_addresses[cls.rcon_ringpos] = to;
2457 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2458 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2460 else if(rcon_secure.integer > 0)
2464 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2465 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2466 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2469 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2470 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2475 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2481 ====================
2484 user <name or userid>
2486 Dump userdata / masterdata for a user
2487 ====================
2489 void Host_User_f (void) // credit: taken from QuakeWorld
2494 if (Cmd_Argc() != 2)
2496 Con_Printf ("Usage: user <username / userid>\n");
2500 uid = atoi(Cmd_Argv(1));
2502 for (i = 0;i < cl.maxclients;i++)
2504 if (!cl.scores[i].name[0])
2506 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2508 InfoString_Print(cl.scores[i].qw_userinfo);
2512 Con_Printf ("User not in server.\n");
2516 ====================
2519 Dump userids for all current players
2520 ====================
2522 void Host_Users_f (void) // credit: taken from QuakeWorld
2528 Con_Printf ("userid frags name\n");
2529 Con_Printf ("------ ----- ----\n");
2530 for (i = 0;i < cl.maxclients;i++)
2532 if (cl.scores[i].name[0])
2534 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2539 Con_Printf ("%i total users\n", c);
2544 Host_FullServerinfo_f
2546 Sent by server when serverinfo changes
2549 // TODO: shouldn't this be a cvar instead?
2550 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2553 if (Cmd_Argc() != 2)
2555 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2559 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2560 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2561 cl.qw_teamplay = atoi(temp);
2568 Allow clients to change userinfo
2572 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2579 if (Cmd_Argc() != 2)
2581 Con_Printf ("fullinfo <complete info string>\n");
2591 while (*s && *s != '\\')
2597 Con_Printf ("MISSING VALUE\n");
2603 while (*s && *s != '\\')
2610 CL_SetInfo(key, value, false, false, false, false);
2618 Allow clients to change userinfo
2621 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2623 if (Cmd_Argc() == 1)
2625 InfoString_Print(cls.userinfo);
2628 if (Cmd_Argc() != 3)
2630 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2633 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2637 ====================
2640 packet <destination> <contents>
2642 Contents allows \n escape character
2643 ====================
2645 void Host_Packet_f (void) // credit: taken from QuakeWorld
2651 lhnetaddress_t address;
2652 lhnetsocket_t *mysocket;
2654 if (Cmd_Argc() != 3)
2656 Con_Printf ("packet <destination> <contents>\n");
2660 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2662 Con_Printf ("Bad address\n");
2668 send[0] = send[1] = send[2] = send[3] = -1;
2670 l = (int)strlen (in);
2671 for (i=0 ; i<l ; i++)
2673 if (out >= send + sizeof(send) - 1)
2675 if (in[i] == '\\' && in[i+1] == 'n')
2680 else if (in[i] == '\\' && in[i+1] == '0')
2685 else if (in[i] == '\\' && in[i+1] == 't')
2690 else if (in[i] == '\\' && in[i+1] == 'r')
2695 else if (in[i] == '\\' && in[i+1] == '"')
2704 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2706 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2708 NetConn_Write(mysocket, send, out - send, &address);
2712 ====================
2715 Send back ping and packet loss update for all current players to this player
2716 ====================
2718 void Host_Pings_f (void)
2720 int i, j, ping, packetloss, movementloss;
2723 if (!host_client->netconnection)
2726 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2728 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2729 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2731 for (i = 0;i < svs.maxclients;i++)
2735 if (svs.clients[i].netconnection)
2737 for (j = 0;j < NETGRAPH_PACKETS;j++)
2738 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2740 for (j = 0;j < NETGRAPH_PACKETS;j++)
2741 if (svs.clients[i].movement_count[j] < 0)
2744 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2745 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2746 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2747 ping = bound(0, ping, 9999);
2748 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2750 // send qw_svc_updateping and qw_svc_updatepl messages
2751 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2752 MSG_WriteShort(&host_client->netconnection->message, ping);
2753 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2754 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2758 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2760 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2762 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2763 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2766 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2767 MSG_WriteString(&host_client->netconnection->message, "\n");
2770 void Host_PingPLReport_f(void)
2775 if (l > cl.maxclients)
2777 for (i = 0;i < l;i++)
2779 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2780 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2781 if(errbyte && *errbyte == ',')
2782 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2784 cl.scores[i].qw_movementloss = 0;
2788 //=============================================================================
2795 void Host_InitCommands (void)
2797 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2799 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2800 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2801 if (gamemode == GAME_NEHAHRA)
2803 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2804 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2805 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2806 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2807 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2811 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2812 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2813 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2814 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2815 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2817 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2818 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2819 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2820 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2821 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)");
2822 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2823 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2824 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2825 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2826 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2827 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2828 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2829 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2830 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2831 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2833 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2834 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2835 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2837 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2838 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2839 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2840 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2842 Cvar_RegisterVariable (&cl_name);
2843 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2844 Cvar_RegisterVariable (&cl_color);
2845 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2846 Cvar_RegisterVariable (&cl_rate);
2847 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2848 if (gamemode == GAME_NEHAHRA)
2850 Cvar_RegisterVariable (&cl_pmodel);
2851 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2854 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2855 Cvar_RegisterVariable (&cl_playermodel);
2856 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2857 Cvar_RegisterVariable (&cl_playerskin);
2858 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2860 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2861 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2862 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)");
2863 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2865 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2867 Cvar_RegisterVariable (&rcon_password);
2868 Cvar_RegisterVariable (&rcon_address);
2869 Cvar_RegisterVariable (&rcon_secure);
2870 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2871 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");
2872 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");
2873 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)");
2874 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2875 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2876 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2877 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2878 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2879 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2880 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2881 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2883 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)");
2884 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)");
2886 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)");
2887 Cvar_RegisterVariable (&r_fixtrans_auto);
2889 Cvar_RegisterVariable (&team);
2890 Cvar_RegisterVariable (&skin);
2891 Cvar_RegisterVariable (&noaim);
2893 Cvar_RegisterVariable(&sv_cheats);
2894 Cvar_RegisterVariable(&sv_adminnick);
2895 Cvar_RegisterVariable(&sv_status_privacy);
2896 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2899 void Host_NoOperation_f(void)