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 // LordHavoc: we must use multiple prints for ProQuake compatibility
178 print ("#%-3u ", i+1);
179 print ("%-16.16s ", client->name);
180 print ("%4i ", frags);
181 print ("%2i:%02i:%02i\n ", hours, minutes, seconds);
183 // print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
184 // print (" %s\n", ip);
186 else if (in == 1) // extended layout
188 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);
190 else if (in == 2) // reduced layout
192 print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
196 if(cmd_source == src_command)
205 Sets client to godmode
208 void Host_God_f (void)
212 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
216 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
217 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
218 SV_ClientPrint("godmode OFF\n");
220 SV_ClientPrint("godmode ON\n");
223 void Host_Notarget_f (void)
227 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
231 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
232 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
233 SV_ClientPrint("notarget OFF\n");
235 SV_ClientPrint("notarget ON\n");
238 qboolean noclip_anglehack;
240 void Host_Noclip_f (void)
244 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
248 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
250 noclip_anglehack = true;
251 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
252 SV_ClientPrint("noclip ON\n");
256 noclip_anglehack = false;
257 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
258 SV_ClientPrint("noclip OFF\n");
266 Sets client to flymode
269 void Host_Fly_f (void)
273 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
277 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
279 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
280 SV_ClientPrint("flymode ON\n");
284 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
285 SV_ClientPrint("flymode OFF\n");
296 void Host_Pings_f (void); // called by Host_Ping_f
297 void Host_Ping_f (void)
301 void (*print) (const char *fmt, ...);
303 if (cmd_source == src_command)
305 // if running a client, try to send over network so the client's ping report parser will see the report
306 if (cls.state == ca_connected)
308 Cmd_ForwardToServer ();
314 print = SV_ClientPrintf;
319 print("Client ping times:\n");
320 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
324 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
327 // 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)
328 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
333 ===============================================================================
337 ===============================================================================
341 ======================
346 command from the console. Active clients are kicked off.
347 ======================
349 void Host_Map_f (void)
351 char level[MAX_QPATH];
355 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
359 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
360 if (gamemode == GAME_DELUXEQUAKE)
361 Cvar_Set("warpmark", "");
363 cls.demonum = -1; // stop demo loop in case this fails
366 Host_ShutdownServer();
368 if(svs.maxclients != svs.maxclients_next)
370 svs.maxclients = svs.maxclients_next;
372 Mem_Free(svs.clients);
373 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
379 svs.serverflags = 0; // haven't completed an episode yet
380 allowcheats = sv_cheats.integer != 0;
381 strlcpy(level, Cmd_Argv(1), sizeof(level));
382 SV_SpawnServer(level);
383 if (sv.active && cls.state == ca_disconnected)
384 CL_EstablishConnection("local:1");
391 Goes to a new map, taking all clients along
394 void Host_Changelevel_f (void)
396 char level[MAX_QPATH];
400 Con_Print("changelevel <levelname> : continue game on a new level\n");
413 SV_SaveSpawnparms ();
415 allowcheats = sv_cheats.integer != 0;
416 strlcpy(level, Cmd_Argv(1), sizeof(level));
417 SV_SpawnServer(level);
418 if (sv.active && cls.state == ca_disconnected)
419 CL_EstablishConnection("local:1");
426 Restarts the current server for a dead player
429 void Host_Restart_f (void)
431 char mapname[MAX_QPATH];
435 Con_Print("restart : restart current level\n");
440 Con_Print("Only the server may restart\n");
447 allowcheats = sv_cheats.integer != 0;
448 strlcpy(mapname, sv.name, sizeof(mapname));
449 SV_SpawnServer(mapname);
450 if (sv.active && cls.state == ca_disconnected)
451 CL_EstablishConnection("local:1");
458 This command causes the client to wait for the signon messages again.
459 This is sent just before a server changes levels
462 void Host_Reconnect_f (void)
465 // if not connected, reconnect to the most recent server
468 // if we have connected to a server recently, the userinfo
469 // will still contain its IP address, so get the address...
470 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
472 CL_EstablishConnection(temp);
474 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
477 // if connected, do something based on protocol
478 if (cls.protocol == PROTOCOL_QUAKEWORLD)
480 // quakeworld can just re-login
481 if (cls.qw_downloadmemory) // don't change when downloading
486 if (cls.state == ca_connected && cls.signon < SIGNONS)
488 Con_Printf("reconnecting...\n");
489 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
490 MSG_WriteString(&cls.netcon->message, "new");
495 // netquake uses reconnect on level changes (silly)
498 Con_Print("reconnect : wait for signon messages again\n");
503 Con_Print("reconnect: no signon, ignoring reconnect\n");
506 cls.signon = 0; // need new connection messages
511 =====================
514 User command to connect to server
515 =====================
517 void Host_Connect_f (void)
521 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
524 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
525 if(rcon_secure.integer <= 0)
526 Cvar_SetQuick(&rcon_password, "");
527 CL_EstablishConnection(Cmd_Argv(1));
532 ===============================================================================
536 ===============================================================================
539 #define SAVEGAME_VERSION 5
541 void Host_Savegame_to (const char *name)
544 int i, k, l, lightstyles = 64;
545 char comment[SAVEGAME_COMMENT_LENGTH+1];
546 char line[MAX_INPUTLINE];
550 // first we have to figure out if this can be saved in 64 lightstyles
551 // (for Quake compatibility)
552 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
553 if (sv.lightstyles[i][0])
556 isserver = !strcmp(PRVM_NAME, "server");
558 Con_Printf("Saving game to %s...\n", name);
559 f = FS_OpenRealFile(name, "wb", false);
562 Con_Print("ERROR: couldn't open.\n");
566 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
568 memset(comment, 0, sizeof(comment));
570 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);
572 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
573 // convert space to _ to make stdio happy
574 // LordHavoc: convert control characters to _ as well
575 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
576 if (ISWHITESPACEORCONTROL(comment[i]))
578 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
580 FS_Printf(f, "%s\n", comment);
583 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
584 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
585 FS_Printf(f, "%d\n", current_skill);
586 FS_Printf(f, "%s\n", sv.name);
587 FS_Printf(f, "%f\n",sv.time);
591 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
592 FS_Printf(f, "(dummy)\n");
593 FS_Printf(f, "%d\n", 0);
594 FS_Printf(f, "%s\n", "(dummy)");
595 FS_Printf(f, "%f\n", realtime);
598 // write the light styles
599 for (i=0 ; i<lightstyles ; i++)
601 if (isserver && sv.lightstyles[i][0])
602 FS_Printf(f, "%s\n", sv.lightstyles[i]);
607 PRVM_ED_WriteGlobals (f);
608 for (i=0 ; i<prog->num_edicts ; i++)
610 FS_Printf(f,"// edict %d\n", i);
611 //Con_Printf("edict %d...\n", i);
612 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
617 FS_Printf(f,"// DarkPlaces extended savegame\n");
618 // darkplaces extension - extra lightstyles, support for color lightstyles
619 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
620 if (isserver && sv.lightstyles[i][0])
621 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
623 // darkplaces extension - model precaches
624 for (i=1 ; i<MAX_MODELS ; i++)
625 if (sv.model_precache[i][0])
626 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
628 // darkplaces extension - sound precaches
629 for (i=1 ; i<MAX_SOUNDS ; i++)
630 if (sv.sound_precache[i][0])
631 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
633 // darkplaces extension - save buffers
634 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); i++)
636 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
637 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
639 for(k = 0; k < stringbuffer->num_strings; k++)
641 if (!stringbuffer->strings[k])
643 // Parse the string a bit to turn special characters
644 // (like newline, specifically) into escape codes
645 s = stringbuffer->strings[k];
646 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
673 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
681 Con_Print("done.\n");
689 void Host_Savegame_f (void)
691 char name[MAX_QPATH];
695 Con_Print("Can't save - no server running.\n");
701 // singleplayer checks
704 Con_Print("Can't save in intermission.\n");
708 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
710 Con_Print("Can't savegame with a dead player\n");
715 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");
719 Con_Print("save <savename> : save a game\n");
723 if (strstr(Cmd_Argv(1), ".."))
725 Con_Print("Relative pathnames are not allowed.\n");
729 strlcpy (name, Cmd_Argv(1), sizeof (name));
730 FS_DefaultExtension (name, ".sav", sizeof (name));
733 Host_Savegame_to(name);
744 void Host_Loadgame_f (void)
746 char filename[MAX_QPATH];
747 char mapname[MAX_QPATH];
757 float spawn_parms[NUM_SPAWN_PARMS];
758 prvm_stringbuffer_t *stringbuffer;
763 Con_Print("load <savename> : load a game\n");
767 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
768 FS_DefaultExtension (filename, ".sav", sizeof (filename));
770 Con_Printf("Loading game from %s...\n", filename);
772 // stop playing demos
773 if (cls.demoplayback)
779 cls.demonum = -1; // stop demo loop in case this fails
781 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
784 Con_Print("ERROR: couldn't open.\n");
788 if(developer_entityparsing.integer)
789 Con_Printf("Host_Loadgame_f: loading version\n");
792 COM_ParseToken_Simple(&t, false, false);
793 version = atoi(com_token);
794 if (version != SAVEGAME_VERSION)
797 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
801 if(developer_entityparsing.integer)
802 Con_Printf("Host_Loadgame_f: loading description\n");
805 COM_ParseToken_Simple(&t, false, false);
807 for (i = 0;i < NUM_SPAWN_PARMS;i++)
809 COM_ParseToken_Simple(&t, false, false);
810 spawn_parms[i] = atof(com_token);
813 COM_ParseToken_Simple(&t, false, false);
814 // this silliness is so we can load 1.06 save files, which have float skill values
815 current_skill = (int)(atof(com_token) + 0.5);
816 Cvar_SetValue ("skill", (float)current_skill);
818 if(developer_entityparsing.integer)
819 Con_Printf("Host_Loadgame_f: loading mapname\n");
822 COM_ParseToken_Simple(&t, false, false);
823 strlcpy (mapname, com_token, sizeof(mapname));
825 if(developer_entityparsing.integer)
826 Con_Printf("Host_Loadgame_f: loading time\n");
829 COM_ParseToken_Simple(&t, false, false);
830 time = atof(com_token);
832 allowcheats = sv_cheats.integer != 0;
834 if(developer_entityparsing.integer)
835 Con_Printf("Host_Loadgame_f: spawning server\n");
837 SV_SpawnServer (mapname);
841 Con_Print("Couldn't load map\n");
844 sv.paused = true; // pause until all clients connect
847 if(developer_entityparsing.integer)
848 Con_Printf("Host_Loadgame_f: loading light styles\n");
850 // load the light styles
856 for (i = 0;i < MAX_LIGHTSTYLES;i++)
860 COM_ParseToken_Simple(&t, false, false);
861 // if this is a 64 lightstyle savegame produced by Quake, stop now
862 // we have to check this because darkplaces may save more than 64
863 if (com_token[0] == '{')
868 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
871 if(developer_entityparsing.integer)
872 Con_Printf("Host_Loadgame_f: skipping until globals\n");
874 // now skip everything before the first opening brace
875 // (this is for forward compatibility, so that older versions (at
876 // least ones with this fix) can load savegames with extra data before the
877 // first brace, as might be produced by a later engine version)
881 if (!COM_ParseToken_Simple(&t, false, false))
883 if (com_token[0] == '{')
890 // unlink all entities
891 World_UnlinkAll(&sv.world);
893 // load the edicts out of the savegame file
898 while (COM_ParseToken_Simple(&t, false, false))
899 if (!strcmp(com_token, "}"))
901 if (!COM_ParseToken_Simple(&start, false, false))
906 if (strcmp(com_token,"{"))
909 Host_Error ("First token isn't a brace");
914 if(developer_entityparsing.integer)
915 Con_Printf("Host_Loadgame_f: loading globals\n");
917 // parse the global vars
918 PRVM_ED_ParseGlobals (start);
923 if (entnum >= MAX_EDICTS)
926 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
928 while (entnum >= prog->max_edicts)
929 PRVM_MEM_IncreaseEdicts();
930 ent = PRVM_EDICT_NUM(entnum);
931 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
932 ent->priv.server->free = false;
934 if(developer_entityparsing.integer)
935 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
937 PRVM_ED_ParseEdict (start, ent);
939 // link it into the bsp tree
940 if (!ent->priv.server->free)
948 prog->num_edicts = entnum;
951 for (i = 0;i < NUM_SPAWN_PARMS;i++)
952 svs.clients[0].spawn_parms[i] = spawn_parms[i];
954 if(developer_entityparsing.integer)
955 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
957 // read extended data if present
958 // the extended data is stored inside a /* */ comment block, which the
959 // parser intentionally skips, so we have to check for it manually here
962 while (*end == '\r' || *end == '\n')
964 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
966 if(developer_entityparsing.integer)
967 Con_Printf("Host_Loadgame_f: loading extended data\n");
969 Con_Printf("Loading extended DarkPlaces savegame\n");
971 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
972 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
973 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
974 while (COM_ParseToken_Simple(&t, false, false))
976 if (!strcmp(com_token, "sv.lightstyles"))
978 COM_ParseToken_Simple(&t, false, false);
980 COM_ParseToken_Simple(&t, false, false);
981 if (i >= 0 && i < MAX_LIGHTSTYLES)
982 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
984 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
986 else if (!strcmp(com_token, "sv.model_precache"))
988 COM_ParseToken_Simple(&t, false, false);
990 COM_ParseToken_Simple(&t, false, false);
991 if (i >= 0 && i < MAX_MODELS)
993 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
994 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
997 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
999 else if (!strcmp(com_token, "sv.sound_precache"))
1001 COM_ParseToken_Simple(&t, false, false);
1002 i = atoi(com_token);
1003 COM_ParseToken_Simple(&t, false, false);
1004 if (i >= 0 && i < MAX_SOUNDS)
1005 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1007 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1009 else if (!strcmp(com_token, "sv.bufstr"))
1011 COM_ParseToken_Simple(&t, false, false);
1012 i = atoi(com_token);
1013 COM_ParseToken_Simple(&t, false, false);
1014 k = atoi(com_token);
1015 COM_ParseToken_Simple(&t, false, false);
1016 stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
1017 // VorteX: nasty code, cleanup required
1018 // create buffer at this index
1020 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
1022 Con_Printf("cant write string %i into buffer %i\n", k, i);
1025 // code copied from VM_bufstr_set
1027 if (stringbuffer->max_strings <= i)
1029 char **oldstrings = stringbuffer->strings;
1030 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
1031 while (stringbuffer->max_strings <= i)
1032 stringbuffer->max_strings *= 2;
1033 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
1034 if (stringbuffer->num_strings > 0)
1035 memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
1037 Mem_Free(oldstrings);
1040 stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
1041 if(stringbuffer->strings[k])
1042 Mem_Free(stringbuffer->strings[k]);
1043 stringbuffer->strings[k] = NULL;
1044 alloclen = strlen(com_token) + 1;
1045 stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
1046 memcpy(stringbuffer->strings[k], com_token, alloclen);
1049 // skip any trailing text or unrecognized commands
1050 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
1057 if(developer_entityparsing.integer)
1058 Con_Printf("Host_Loadgame_f: finished\n");
1062 // make sure we're connected to loopback
1063 if (sv.active && cls.state == ca_disconnected)
1064 CL_EstablishConnection("local:1");
1067 //============================================================================
1070 ======================
1072 ======================
1074 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1075 void Host_Name_f (void)
1078 qboolean valid_colors;
1079 const char *newNameSource;
1080 char newName[sizeof(host_client->name)];
1082 if (Cmd_Argc () == 1)
1084 Con_Printf("name: %s\n", cl_name.string);
1088 if (Cmd_Argc () == 2)
1089 newNameSource = Cmd_Argv(1);
1091 newNameSource = Cmd_Args();
1093 strlcpy(newName, newNameSource, sizeof(newName));
1095 if (cmd_source == src_command)
1097 Cvar_Set ("_cl_name", newName);
1098 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1100 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1101 Con_Printf("name: %s\n", cl_name.string);
1106 if (realtime < host_client->nametime)
1108 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1112 host_client->nametime = realtime + 5;
1114 // point the string back at updateclient->name to keep it safe
1115 strlcpy (host_client->name, newName, sizeof (host_client->name));
1117 for (i = 0, j = 0;host_client->name[i];i++)
1118 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1119 host_client->name[j++] = host_client->name[i];
1120 host_client->name[j] = 0;
1122 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1123 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1125 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1126 host_client->name[sizeof(host_client->name) - 1] = 0;
1127 host_client->name[0] = STRING_COLOR_TAG;
1128 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1131 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1132 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1135 l = strlen(host_client->name);
1136 if(l < sizeof(host_client->name) - 1)
1138 // duplicate the color tag to escape it
1139 host_client->name[i] = STRING_COLOR_TAG;
1140 host_client->name[i+1] = 0;
1141 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1145 // remove the last character to fix the color code
1146 host_client->name[l-1] = 0;
1147 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1151 // find the last color tag offset and decide if we need to add a reset tag
1152 for (i = 0, j = -1;host_client->name[i];i++)
1154 if (host_client->name[i] == STRING_COLOR_TAG)
1156 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1159 // if this happens to be a reset tag then we don't need one
1160 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1165 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]))
1171 if (host_client->name[i+1] == STRING_COLOR_TAG)
1178 // does not end in the default color string, so add it
1179 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1180 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1182 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1183 if (strcmp(host_client->old_name, host_client->name))
1185 if (host_client->spawned)
1186 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1187 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1188 // send notification to all clients
1189 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1190 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1191 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1192 SV_WriteNetnameIntoDemo(host_client);
1197 ======================
1199 ======================
1201 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1202 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1203 void Host_Playermodel_f (void)
1206 char newPath[sizeof(host_client->playermodel)];
1208 if (Cmd_Argc () == 1)
1210 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1214 if (Cmd_Argc () == 2)
1215 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1217 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1219 for (i = 0, j = 0;newPath[i];i++)
1220 if (newPath[i] != '\r' && newPath[i] != '\n')
1221 newPath[j++] = newPath[i];
1224 if (cmd_source == src_command)
1226 Cvar_Set ("_cl_playermodel", newPath);
1231 if (realtime < host_client->nametime)
1233 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1237 host_client->nametime = realtime + 5;
1240 // point the string back at updateclient->name to keep it safe
1241 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1242 if( prog->fieldoffsets.playermodel >= 0 )
1243 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1244 if (strcmp(host_client->old_model, host_client->playermodel))
1246 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1247 /*// send notification to all clients
1248 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1249 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1250 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1255 ======================
1257 ======================
1259 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1260 void Host_Playerskin_f (void)
1263 char newPath[sizeof(host_client->playerskin)];
1265 if (Cmd_Argc () == 1)
1267 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1271 if (Cmd_Argc () == 2)
1272 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1274 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1276 for (i = 0, j = 0;newPath[i];i++)
1277 if (newPath[i] != '\r' && newPath[i] != '\n')
1278 newPath[j++] = newPath[i];
1281 if (cmd_source == src_command)
1283 Cvar_Set ("_cl_playerskin", newPath);
1288 if (realtime < host_client->nametime)
1290 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1294 host_client->nametime = realtime + 5;
1297 // point the string back at updateclient->name to keep it safe
1298 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1299 if( prog->fieldoffsets.playerskin >= 0 )
1300 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1301 if (strcmp(host_client->old_skin, host_client->playerskin))
1303 //if (host_client->spawned)
1304 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1305 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1306 /*// send notification to all clients
1307 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1308 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1309 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1313 void Host_Version_f (void)
1315 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1318 void Host_Say(qboolean teamonly)
1324 // LordHavoc: long say messages
1326 qboolean fromServer = false;
1328 if (cmd_source == src_command)
1330 if (cls.state == ca_dedicated)
1337 Cmd_ForwardToServer ();
1342 if (Cmd_Argc () < 2)
1345 if (!teamplay.integer)
1355 // note this uses the chat prefix \001
1356 if (!fromServer && !teamonly)
1357 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1358 else if (!fromServer && teamonly)
1359 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1360 else if(*(sv_adminnick.string))
1361 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1363 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1364 p2 = text + strlen(text);
1365 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1367 if (p2[-1] == '\"' && quoted)
1372 strlcat(text, "\n", sizeof(text));
1374 // note: save is not a valid edict if fromServer is true
1376 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1377 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1378 SV_ClientPrint(text);
1381 if (cls.state == ca_dedicated)
1382 Con_Print(&text[1]);
1386 void Host_Say_f(void)
1392 void Host_Say_Team_f(void)
1398 void Host_Tell_f(void)
1400 const char *playername_start = NULL;
1401 size_t playername_length = 0;
1402 int playernumber = 0;
1405 const char *p1, *p2;
1406 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1407 qboolean fromServer = false;
1409 if (cmd_source == src_command)
1411 if (cls.state == ca_dedicated)
1415 Cmd_ForwardToServer ();
1420 if (Cmd_Argc () < 2)
1423 // note this uses the chat prefix \001
1425 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1426 else if(*(sv_adminnick.string))
1427 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1429 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1432 p2 = p1 + strlen(p1);
1433 // remove the target name
1434 while (p1 < p2 && *p1 == ' ')
1439 while (p1 < p2 && *p1 == ' ')
1441 while (p1 < p2 && isdigit(*p1))
1443 playernumber = playernumber * 10 + (*p1 - '0');
1451 playername_start = p1;
1452 while (p1 < p2 && *p1 != '"')
1454 playername_length = p1 - playername_start;
1460 playername_start = p1;
1461 while (p1 < p2 && *p1 != ' ')
1463 playername_length = p1 - playername_start;
1465 while (p1 < p2 && *p1 == ' ')
1467 if(playername_start)
1469 // set playernumber to the right client
1471 if(playername_length >= sizeof(namebuf))
1474 Con_Print("Host_Tell: too long player name/ID\n");
1476 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1479 memcpy(namebuf, playername_start, playername_length);
1480 namebuf[playername_length] = 0;
1481 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1483 if (!svs.clients[playernumber].active)
1485 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1489 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1492 Con_Print("Host_Tell: invalid player name/ID\n");
1494 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1497 // remove trailing newlines
1498 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1500 // remove quotes if present
1506 else if (fromServer)
1507 Con_Print("Host_Tell: missing end quote\n");
1509 SV_ClientPrint("Host_Tell: missing end quote\n");
1511 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1514 return; // empty say
1515 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1521 host_client = svs.clients + playernumber;
1522 SV_ClientPrint(text);
1532 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1533 void Host_Color(int changetop, int changebottom)
1535 int top, bottom, playercolor;
1537 // get top and bottom either from the provided values or the current values
1538 // (allows changing only top or bottom, or both at once)
1539 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1540 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1544 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1550 playercolor = top*16 + bottom;
1552 if (cmd_source == src_command)
1554 Cvar_SetValueQuick(&cl_color, playercolor);
1558 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1561 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1563 Con_DPrint("Calling SV_ChangeTeam\n");
1564 prog->globals.server->time = sv.time;
1565 prog->globals.generic[OFS_PARM0] = playercolor;
1566 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1567 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1572 if (host_client->edict)
1574 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1575 val->_float = playercolor;
1576 host_client->edict->fields.server->team = bottom + 1;
1578 host_client->colors = playercolor;
1579 if (host_client->old_colors != host_client->colors)
1581 host_client->old_colors = host_client->colors;
1582 // send notification to all clients
1583 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1584 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1585 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1590 void Host_Color_f(void)
1594 if (Cmd_Argc() == 1)
1596 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1597 Con_Print("color <0-15> [0-15]\n");
1601 if (Cmd_Argc() == 2)
1602 top = bottom = atoi(Cmd_Argv(1));
1605 top = atoi(Cmd_Argv(1));
1606 bottom = atoi(Cmd_Argv(2));
1608 Host_Color(top, bottom);
1611 void Host_TopColor_f(void)
1613 if (Cmd_Argc() == 1)
1615 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1616 Con_Print("topcolor <0-15>\n");
1620 Host_Color(atoi(Cmd_Argv(1)), -1);
1623 void Host_BottomColor_f(void)
1625 if (Cmd_Argc() == 1)
1627 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1628 Con_Print("bottomcolor <0-15>\n");
1632 Host_Color(-1, atoi(Cmd_Argv(1)));
1635 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1636 void Host_Rate_f(void)
1640 if (Cmd_Argc() != 2)
1642 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1643 Con_Print("rate <bytespersecond>\n");
1647 rate = atoi(Cmd_Argv(1));
1649 if (cmd_source == src_command)
1651 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1655 host_client->rate = rate;
1663 void Host_Kill_f (void)
1665 if (host_client->edict->fields.server->health <= 0)
1667 SV_ClientPrint("Can't suicide -- already dead!\n");
1671 prog->globals.server->time = sv.time;
1672 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1673 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1682 void Host_Pause_f (void)
1684 if (!pausable.integer)
1685 SV_ClientPrint("Pause not allowed.\n");
1689 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1690 // send notification to all clients
1691 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1692 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1697 ======================
1699 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1700 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1701 ======================
1703 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)"};
1704 static void Host_PModel_f (void)
1709 if (Cmd_Argc () == 1)
1711 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1714 i = atoi(Cmd_Argv(1));
1716 if (cmd_source == src_command)
1718 if (cl_pmodel.integer == i)
1720 Cvar_SetValue ("_cl_pmodel", i);
1721 if (cls.state == ca_connected)
1722 Cmd_ForwardToServer ();
1726 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1730 //===========================================================================
1738 void Host_PreSpawn_f (void)
1740 if (host_client->spawned)
1742 Con_Print("prespawn not valid -- already spawned\n");
1746 if (host_client->netconnection)
1748 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1749 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1750 MSG_WriteByte (&host_client->netconnection->message, 2);
1751 host_client->sendsignon = 0; // enable unlimited sends again
1754 // reset the name change timer because the client will send name soon
1755 host_client->nametime = 0;
1763 void Host_Spawn_f (void)
1767 int stats[MAX_CL_STATS];
1769 if (host_client->spawned)
1771 Con_Print("Spawn not valid -- already spawned\n");
1775 // reset name change timer again because they might want to change name
1776 // again in the first 5 seconds after connecting
1777 host_client->nametime = 0;
1779 // LordHavoc: moved this above the QC calls at FrikaC's request
1780 // LordHavoc: commented this out
1781 //if (host_client->netconnection)
1782 // SZ_Clear (&host_client->netconnection->message);
1784 // run the entrance script
1787 // loaded games are fully initialized already
1788 if (prog->funcoffsets.RestoreGame)
1790 Con_DPrint("Calling RestoreGame\n");
1791 prog->globals.server->time = sv.time;
1792 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1793 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1798 //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);
1800 // copy spawn parms out of the client_t
1801 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1802 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1804 // call the spawn function
1805 host_client->clientconnectcalled = true;
1806 prog->globals.server->time = sv.time;
1807 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1808 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1810 if (cls.state == ca_dedicated)
1811 Con_Printf("%s connected\n", host_client->name);
1813 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1816 if (!host_client->netconnection)
1819 // send time of update
1820 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1821 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1823 // send all current names, colors, and frag counts
1824 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1826 if (!client->active)
1828 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1829 MSG_WriteByte (&host_client->netconnection->message, i);
1830 MSG_WriteString (&host_client->netconnection->message, client->name);
1831 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1832 MSG_WriteByte (&host_client->netconnection->message, i);
1833 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1834 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1835 MSG_WriteByte (&host_client->netconnection->message, i);
1836 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1839 // send all current light styles
1840 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1842 if (sv.lightstyles[i][0])
1844 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1845 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1846 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1851 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1852 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1853 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1855 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1856 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1857 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1859 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1860 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1861 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1863 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1864 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1865 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1868 // Never send a roll angle, because savegames can catch the server
1869 // in a state where it is expecting the client to correct the angle
1870 // and it won't happen if the game was just loaded, so you wind up
1871 // with a permanent head tilt
1874 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1875 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1876 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1877 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1881 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1882 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1883 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1884 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1887 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1889 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1890 MSG_WriteByte (&host_client->netconnection->message, 3);
1898 void Host_Begin_f (void)
1900 host_client->spawned = true;
1902 // LordHavoc: note: this code also exists in SV_DropClient
1906 for (i = 0;i < svs.maxclients;i++)
1907 if (svs.clients[i].active && !svs.clients[i].spawned)
1909 if (i == svs.maxclients)
1911 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1912 sv.paused = sv.loadgame = false; // we're basically done with loading now
1917 //===========================================================================
1924 Kicks a user off of the server
1927 void Host_Kick_f (void)
1930 const char *message = NULL;
1933 qboolean byNumber = false;
1941 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1943 i = (int)(atof(Cmd_Argv(2)) - 1);
1944 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1950 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1952 if (!host_client->active)
1954 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1959 if (i < svs.maxclients)
1961 if (cmd_source == src_command)
1963 if (cls.state == ca_dedicated)
1966 who = cl_name.string;
1971 // can't kick yourself!
1972 if (host_client == save)
1977 message = Cmd_Args();
1978 COM_ParseToken_Simple(&message, false, false);
1981 message++; // skip the #
1982 while (*message == ' ') // skip white space
1984 message += strlen(Cmd_Argv(2)); // skip the number
1986 while (*message && *message == ' ')
1990 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1992 SV_ClientPrintf("Kicked by %s\n", who);
1993 SV_DropClient (false); // kicked
2001 ===============================================================================
2005 ===============================================================================
2013 void Host_Give_f (void)
2021 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2026 v = atoi (Cmd_Argv(2));
2040 // MED 01/04/97 added hipnotic give stuff
2041 if (gamemode == GAME_HIPNOTIC)
2046 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
2048 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
2050 else if (t[0] == '9')
2051 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
2052 else if (t[0] == '0')
2053 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
2054 else if (t[0] >= '2')
2055 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2060 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2065 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
2068 host_client->edict->fields.server->ammo_shells = v;
2071 if (gamemode == GAME_ROGUE)
2073 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
2076 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2077 host_client->edict->fields.server->ammo_nails = v;
2082 host_client->edict->fields.server->ammo_nails = v;
2086 if (gamemode == GAME_ROGUE)
2088 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
2092 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2093 host_client->edict->fields.server->ammo_nails = v;
2098 if (gamemode == GAME_ROGUE)
2100 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2104 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2105 host_client->edict->fields.server->ammo_rockets = v;
2110 host_client->edict->fields.server->ammo_rockets = v;
2114 if (gamemode == GAME_ROGUE)
2116 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2120 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2121 host_client->edict->fields.server->ammo_rockets = v;
2126 host_client->edict->fields.server->health = v;
2129 if (gamemode == GAME_ROGUE)
2131 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2135 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2136 host_client->edict->fields.server->ammo_cells = v;
2141 host_client->edict->fields.server->ammo_cells = v;
2145 if (gamemode == GAME_ROGUE)
2147 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2151 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2152 host_client->edict->fields.server->ammo_cells = v;
2159 prvm_edict_t *FindViewthing (void)
2164 for (i=0 ; i<prog->num_edicts ; i++)
2166 e = PRVM_EDICT_NUM(i);
2167 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2170 Con_Print("No viewthing on map\n");
2179 void Host_Viewmodel_f (void)
2188 e = FindViewthing ();
2193 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2194 if (!m || !m->loaded || !m->Draw)
2196 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2200 e->fields.server->frame = 0;
2201 cl.model_precache[(int)e->fields.server->modelindex] = m;
2209 void Host_Viewframe_f (void)
2219 e = FindViewthing ();
2223 m = cl.model_precache[(int)e->fields.server->modelindex];
2225 f = atoi(Cmd_Argv(1));
2226 if (f >= m->numframes)
2229 e->fields.server->frame = f;
2233 void PrintFrameName (dp_model_t *m, int frame)
2236 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2238 Con_Printf("frame %i\n", frame);
2246 void Host_Viewnext_f (void)
2255 e = FindViewthing ();
2259 m = cl.model_precache[(int)e->fields.server->modelindex];
2261 e->fields.server->frame = e->fields.server->frame + 1;
2262 if (e->fields.server->frame >= m->numframes)
2263 e->fields.server->frame = m->numframes - 1;
2265 PrintFrameName (m, (int)e->fields.server->frame);
2273 void Host_Viewprev_f (void)
2282 e = FindViewthing ();
2287 m = cl.model_precache[(int)e->fields.server->modelindex];
2289 e->fields.server->frame = e->fields.server->frame - 1;
2290 if (e->fields.server->frame < 0)
2291 e->fields.server->frame = 0;
2293 PrintFrameName (m, (int)e->fields.server->frame);
2297 ===============================================================================
2301 ===============================================================================
2310 void Host_Startdemos_f (void)
2314 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2320 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2323 Con_DPrintf("%i demo(s) in loop\n", c);
2325 for (i=1 ; i<c+1 ; i++)
2326 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2328 // LordHavoc: clear the remaining slots
2329 for (;i <= MAX_DEMOS;i++)
2330 cls.demos[i-1][0] = 0;
2332 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2346 Return to looping demos
2349 void Host_Demos_f (void)
2351 if (cls.state == ca_dedicated)
2353 if (cls.demonum == -1)
2363 Return to looping demos
2366 void Host_Stopdemo_f (void)
2368 if (!cls.demoplayback)
2371 Host_ShutdownServer ();
2374 void Host_SendCvar_f (void)
2378 const char *cvarname;
2383 cvarname = Cmd_Argv(1);
2384 if (cls.state == ca_connected)
2386 c = Cvar_FindVar(cvarname);
2387 // LordHavoc: if there is no such cvar or if it is private, send a
2388 // reply indicating that it has no value
2389 if(!c || (c->flags & CVAR_PRIVATE))
2390 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2392 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2395 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2399 if (cls.state != ca_dedicated)
2403 for(;i<svs.maxclients;i++)
2404 if(svs.clients[i].active && svs.clients[i].netconnection)
2406 host_client = &svs.clients[i];
2407 Host_ClientCommands("sendcvar %s\n", cvarname);
2412 static void MaxPlayers_f(void)
2416 if (Cmd_Argc() != 2)
2418 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2424 Con_Print("maxplayers can not be changed while a server is running.\n");
2425 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2428 n = atoi(Cmd_Argv(1));
2429 n = bound(1, n, MAX_SCOREBOARD);
2430 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2432 svs.maxclients_next = n;
2434 Cvar_Set ("deathmatch", "0");
2436 Cvar_Set ("deathmatch", "1");
2440 =====================
2443 ProQuake rcon support
2444 =====================
2446 void Host_PQRcon_f (void)
2451 lhnetsocket_t *mysocket;
2452 char peer_address[64];
2454 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2456 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2460 e = strchr(rcon_password.string, ' ');
2461 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2465 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2469 if (!rcon_address.string[0])
2471 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2474 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2476 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2477 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2480 SZ_Clear(&net_message);
2481 MSG_WriteLong (&net_message, 0);
2482 MSG_WriteByte (&net_message, CCREQ_RCON);
2483 SZ_Write(&net_message, (void*)rcon_password.string, n);
2484 MSG_WriteString (&net_message, Cmd_Args());
2485 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2486 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2487 SZ_Clear (&net_message);
2491 //=============================================================================
2493 // QuakeWorld commands
2496 =====================
2499 Send the rest of the command line over as
2500 an unconnected command.
2501 =====================
2503 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2508 lhnetsocket_t *mysocket;
2510 if (!rcon_password.string || !rcon_password.string[0])
2512 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2516 e = strchr(rcon_password.string, ' ');
2517 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2520 to = cls.netcon->peeraddress;
2523 if (!rcon_address.string[0])
2525 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2528 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2530 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2531 if (mysocket && Cmd_Args()[0])
2533 // simply put together the rcon packet and send it
2534 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2536 if(cls.rcon_commands[cls.rcon_ringpos][0])
2539 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2540 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]);
2541 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2544 for (i = 0;i < MAX_RCONS;i++)
2545 if(cls.rcon_commands[i][0])
2546 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2550 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2551 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2552 cls.rcon_addresses[cls.rcon_ringpos] = to;
2553 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2554 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2556 else if(rcon_secure.integer > 0)
2560 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2561 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2562 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2565 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2566 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2571 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2577 ====================
2580 user <name or userid>
2582 Dump userdata / masterdata for a user
2583 ====================
2585 void Host_User_f (void) // credit: taken from QuakeWorld
2590 if (Cmd_Argc() != 2)
2592 Con_Printf ("Usage: user <username / userid>\n");
2596 uid = atoi(Cmd_Argv(1));
2598 for (i = 0;i < cl.maxclients;i++)
2600 if (!cl.scores[i].name[0])
2602 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2604 InfoString_Print(cl.scores[i].qw_userinfo);
2608 Con_Printf ("User not in server.\n");
2612 ====================
2615 Dump userids for all current players
2616 ====================
2618 void Host_Users_f (void) // credit: taken from QuakeWorld
2624 Con_Printf ("userid frags name\n");
2625 Con_Printf ("------ ----- ----\n");
2626 for (i = 0;i < cl.maxclients;i++)
2628 if (cl.scores[i].name[0])
2630 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2635 Con_Printf ("%i total users\n", c);
2640 Host_FullServerinfo_f
2642 Sent by server when serverinfo changes
2645 // TODO: shouldn't this be a cvar instead?
2646 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2649 if (Cmd_Argc() != 2)
2651 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2655 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2656 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2657 cl.qw_teamplay = atoi(temp);
2664 Allow clients to change userinfo
2668 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2675 if (Cmd_Argc() != 2)
2677 Con_Printf ("fullinfo <complete info string>\n");
2687 while (*s && *s != '\\')
2693 Con_Printf ("MISSING VALUE\n");
2699 while (*s && *s != '\\')
2706 CL_SetInfo(key, value, false, false, false, false);
2714 Allow clients to change userinfo
2717 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2719 if (Cmd_Argc() == 1)
2721 InfoString_Print(cls.userinfo);
2724 if (Cmd_Argc() != 3)
2726 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2729 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2733 ====================
2736 packet <destination> <contents>
2738 Contents allows \n escape character
2739 ====================
2741 void Host_Packet_f (void) // credit: taken from QuakeWorld
2747 lhnetaddress_t address;
2748 lhnetsocket_t *mysocket;
2750 if (Cmd_Argc() != 3)
2752 Con_Printf ("packet <destination> <contents>\n");
2756 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2758 Con_Printf ("Bad address\n");
2764 send[0] = send[1] = send[2] = send[3] = -1;
2766 l = (int)strlen (in);
2767 for (i=0 ; i<l ; i++)
2769 if (out >= send + sizeof(send) - 1)
2771 if (in[i] == '\\' && in[i+1] == 'n')
2776 else if (in[i] == '\\' && in[i+1] == '0')
2781 else if (in[i] == '\\' && in[i+1] == 't')
2786 else if (in[i] == '\\' && in[i+1] == 'r')
2791 else if (in[i] == '\\' && in[i+1] == '"')
2800 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2802 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2804 NetConn_Write(mysocket, send, out - send, &address);
2808 ====================
2811 Send back ping and packet loss update for all current players to this player
2812 ====================
2814 void Host_Pings_f (void)
2816 int i, j, ping, packetloss, movementloss;
2819 if (!host_client->netconnection)
2822 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2824 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2825 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2827 for (i = 0;i < svs.maxclients;i++)
2831 if (svs.clients[i].netconnection)
2833 for (j = 0;j < NETGRAPH_PACKETS;j++)
2834 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2836 for (j = 0;j < NETGRAPH_PACKETS;j++)
2837 if (svs.clients[i].movement_count[j] < 0)
2840 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2841 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2842 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2843 ping = bound(0, ping, 9999);
2844 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2846 // send qw_svc_updateping and qw_svc_updatepl messages
2847 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2848 MSG_WriteShort(&host_client->netconnection->message, ping);
2849 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2850 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2854 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2856 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2858 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2859 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2862 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2863 MSG_WriteString(&host_client->netconnection->message, "\n");
2866 void Host_PingPLReport_f(void)
2871 if (l > cl.maxclients)
2873 for (i = 0;i < l;i++)
2875 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2876 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2877 if(errbyte && *errbyte == ',')
2878 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2880 cl.scores[i].qw_movementloss = 0;
2884 //=============================================================================
2891 void Host_InitCommands (void)
2893 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2895 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2896 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2897 if (gamemode == GAME_NEHAHRA)
2899 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2900 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2901 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2902 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2903 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2907 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2908 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2909 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2910 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2911 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2913 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2914 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2915 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2916 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2917 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)");
2918 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2919 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2920 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2921 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2922 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2923 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2924 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2925 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2926 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2927 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2929 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2930 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2931 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2933 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2934 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2935 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2936 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2938 Cvar_RegisterVariable (&cl_name);
2939 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2940 Cvar_RegisterVariable (&cl_color);
2941 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2942 Cvar_RegisterVariable (&cl_rate);
2943 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2944 if (gamemode == GAME_NEHAHRA)
2946 Cvar_RegisterVariable (&cl_pmodel);
2947 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2950 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2951 Cvar_RegisterVariable (&cl_playermodel);
2952 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2953 Cvar_RegisterVariable (&cl_playerskin);
2954 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2956 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2957 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2958 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)");
2959 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2961 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2963 Cvar_RegisterVariable (&rcon_password);
2964 Cvar_RegisterVariable (&rcon_address);
2965 Cvar_RegisterVariable (&rcon_secure);
2966 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2967 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");
2968 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");
2969 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)");
2970 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2971 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2972 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2973 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2974 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2975 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2976 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2977 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2979 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)");
2980 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)");
2982 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)");
2983 Cvar_RegisterVariable (&r_fixtrans_auto);
2985 Cvar_RegisterVariable (&team);
2986 Cvar_RegisterVariable (&skin);
2987 Cvar_RegisterVariable (&noaim);
2989 Cvar_RegisterVariable(&sv_cheats);
2990 Cvar_RegisterVariable(&sv_adminnick);
2991 Cvar_RegisterVariable(&sv_status_privacy);
2992 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2995 void Host_NoOperation_f(void)