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, ...);
75 char ip[48]; // can contain a full length v6 address with [] and a port
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", 48);
154 strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 48);
156 frags = client->frags;
158 if(sv_status_show_qcstatus.integer)
160 prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
161 const char *str = PRVM_GetString(PRVM_serveredictstring(ed, clientstatus));
167 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
168 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
172 frags = atoi(qcstatus);
176 if (in == 0) // default layout
178 if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
180 // LordHavoc: this is very touchy because we must maintain ProQuake compatible status output
181 print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
186 // LordHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
187 print ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
191 else if (in == 1) // extended layout
193 print ("%s%-47s %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);
195 else if (in == 2) // reduced layout
197 print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
201 if(cmd_source == src_command)
210 Sets client to godmode
213 void Host_God_f (void)
217 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
221 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
222 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
223 SV_ClientPrint("godmode OFF\n");
225 SV_ClientPrint("godmode ON\n");
228 void Host_Notarget_f (void)
232 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
236 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
237 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
238 SV_ClientPrint("notarget OFF\n");
240 SV_ClientPrint("notarget ON\n");
243 qboolean noclip_anglehack;
245 void Host_Noclip_f (void)
249 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
253 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
255 noclip_anglehack = true;
256 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
257 SV_ClientPrint("noclip ON\n");
261 noclip_anglehack = false;
262 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
263 SV_ClientPrint("noclip OFF\n");
271 Sets client to flymode
274 void Host_Fly_f (void)
278 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
282 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
284 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
285 SV_ClientPrint("flymode ON\n");
289 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
290 SV_ClientPrint("flymode OFF\n");
301 void Host_Pings_f (void); // called by Host_Ping_f
302 void Host_Ping_f (void)
306 void (*print) (const char *fmt, ...);
308 if (cmd_source == src_command)
310 // if running a client, try to send over network so the client's ping report parser will see the report
311 if (cls.state == ca_connected)
313 Cmd_ForwardToServer ();
319 print = SV_ClientPrintf;
324 print("Client ping times:\n");
325 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
329 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
332 // 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)
333 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
338 ===============================================================================
342 ===============================================================================
346 ======================
351 command from the console. Active clients are kicked off.
352 ======================
354 void Host_Map_f (void)
356 char level[MAX_QPATH];
360 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
364 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
365 if (gamemode == GAME_DELUXEQUAKE)
366 Cvar_Set("warpmark", "");
368 cls.demonum = -1; // stop demo loop in case this fails
371 Host_ShutdownServer();
373 if(svs.maxclients != svs.maxclients_next)
375 svs.maxclients = svs.maxclients_next;
377 Mem_Free(svs.clients);
378 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
382 if (key_dest == key_menu || key_dest == key_menu_grabbed)
386 svs.serverflags = 0; // haven't completed an episode yet
387 allowcheats = sv_cheats.integer != 0;
388 strlcpy(level, Cmd_Argv(1), sizeof(level));
389 SV_SpawnServer(level);
390 if (sv.active && cls.state == ca_disconnected)
391 CL_EstablishConnection("local:1", -2);
398 Goes to a new map, taking all clients along
401 void Host_Changelevel_f (void)
403 char level[MAX_QPATH];
407 Con_Print("changelevel <levelname> : continue game on a new level\n");
417 if (key_dest == key_menu || key_dest == key_menu_grabbed)
422 SV_SaveSpawnparms ();
424 allowcheats = sv_cheats.integer != 0;
425 strlcpy(level, Cmd_Argv(1), sizeof(level));
426 SV_SpawnServer(level);
427 if (sv.active && cls.state == ca_disconnected)
428 CL_EstablishConnection("local:1", -2);
435 Restarts the current server for a dead player
438 void Host_Restart_f (void)
440 char mapname[MAX_QPATH];
444 Con_Print("restart : restart current level\n");
449 Con_Print("Only the server may restart\n");
454 if (key_dest == key_menu || key_dest == key_menu_grabbed)
458 allowcheats = sv_cheats.integer != 0;
459 strlcpy(mapname, sv.name, sizeof(mapname));
460 SV_SpawnServer(mapname);
461 if (sv.active && cls.state == ca_disconnected)
462 CL_EstablishConnection("local:1", -2);
469 This command causes the client to wait for the signon messages again.
470 This is sent just before a server changes levels
473 void Host_Reconnect_f (void)
476 // if not connected, reconnect to the most recent server
479 // if we have connected to a server recently, the userinfo
480 // will still contain its IP address, so get the address...
481 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
483 CL_EstablishConnection(temp, -1);
485 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
488 // if connected, do something based on protocol
489 if (cls.protocol == PROTOCOL_QUAKEWORLD)
491 // quakeworld can just re-login
492 if (cls.qw_downloadmemory) // don't change when downloading
497 if (cls.state == ca_connected && cls.signon < SIGNONS)
499 Con_Printf("reconnecting...\n");
500 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
501 MSG_WriteString(&cls.netcon->message, "new");
506 // netquake uses reconnect on level changes (silly)
509 Con_Print("reconnect : wait for signon messages again\n");
514 Con_Print("reconnect: no signon, ignoring reconnect\n");
517 cls.signon = 0; // need new connection messages
522 =====================
525 User command to connect to server
526 =====================
528 void Host_Connect_f (void)
532 Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
535 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
536 if(rcon_secure.integer <= 0)
537 Cvar_SetQuick(&rcon_password, "");
538 CL_EstablishConnection(Cmd_Argv(1), 2);
543 ===============================================================================
547 ===============================================================================
550 #define SAVEGAME_VERSION 5
552 void Host_Savegame_to (const char *name)
555 int i, k, l, lightstyles = 64;
556 char comment[SAVEGAME_COMMENT_LENGTH+1];
557 char line[MAX_INPUTLINE];
561 // first we have to figure out if this can be saved in 64 lightstyles
562 // (for Quake compatibility)
563 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
564 if (sv.lightstyles[i][0])
567 isserver = !strcmp(PRVM_NAME, "server");
569 Con_Printf("Saving game to %s...\n", name);
570 f = FS_OpenRealFile(name, "wb", false);
573 Con_Print("ERROR: couldn't open.\n");
577 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
579 memset(comment, 0, sizeof(comment));
581 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);
583 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
584 // convert space to _ to make stdio happy
585 // LordHavoc: convert control characters to _ as well
586 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
587 if (ISWHITESPACEORCONTROL(comment[i]))
589 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
591 FS_Printf(f, "%s\n", comment);
594 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
595 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
596 FS_Printf(f, "%d\n", current_skill);
597 FS_Printf(f, "%s\n", sv.name);
598 FS_Printf(f, "%f\n",sv.time);
602 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
603 FS_Printf(f, "(dummy)\n");
604 FS_Printf(f, "%d\n", 0);
605 FS_Printf(f, "%s\n", "(dummy)");
606 FS_Printf(f, "%f\n", realtime);
609 // write the light styles
610 for (i=0 ; i<lightstyles ; i++)
612 if (isserver && sv.lightstyles[i][0])
613 FS_Printf(f, "%s\n", sv.lightstyles[i]);
618 PRVM_ED_WriteGlobals (f);
619 for (i=0 ; i<prog->num_edicts ; i++)
621 FS_Printf(f,"// edict %d\n", i);
622 //Con_Printf("edict %d...\n", i);
623 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
628 FS_Printf(f,"// DarkPlaces extended savegame\n");
629 // darkplaces extension - extra lightstyles, support for color lightstyles
630 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
631 if (isserver && sv.lightstyles[i][0])
632 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
634 // darkplaces extension - model precaches
635 for (i=1 ; i<MAX_MODELS ; i++)
636 if (sv.model_precache[i][0])
637 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
639 // darkplaces extension - sound precaches
640 for (i=1 ; i<MAX_SOUNDS ; i++)
641 if (sv.sound_precache[i][0])
642 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
644 // darkplaces extension - save buffers
645 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); i++)
647 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
648 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
650 for(k = 0; k < stringbuffer->num_strings; k++)
652 if (!stringbuffer->strings[k])
654 // Parse the string a bit to turn special characters
655 // (like newline, specifically) into escape codes
656 s = stringbuffer->strings[k];
657 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
684 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
692 Con_Print("done.\n");
700 void Host_Savegame_f (void)
702 char name[MAX_QPATH];
706 Con_Print("Can't save - no server running.\n");
712 // singleplayer checks
715 Con_Print("Can't save in intermission.\n");
719 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
721 Con_Print("Can't savegame with a dead player\n");
726 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");
730 Con_Print("save <savename> : save a game\n");
734 if (strstr(Cmd_Argv(1), ".."))
736 Con_Print("Relative pathnames are not allowed.\n");
740 strlcpy (name, Cmd_Argv(1), sizeof (name));
741 FS_DefaultExtension (name, ".sav", sizeof (name));
744 Host_Savegame_to(name);
755 void Host_Loadgame_f (void)
757 char filename[MAX_QPATH];
758 char mapname[MAX_QPATH];
768 float spawn_parms[NUM_SPAWN_PARMS];
769 prvm_stringbuffer_t *stringbuffer;
774 Con_Print("load <savename> : load a game\n");
778 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
779 FS_DefaultExtension (filename, ".sav", sizeof (filename));
781 Con_Printf("Loading game from %s...\n", filename);
783 // stop playing demos
784 if (cls.demoplayback)
788 if (key_dest == key_menu || key_dest == key_menu_grabbed)
792 cls.demonum = -1; // stop demo loop in case this fails
794 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
797 Con_Print("ERROR: couldn't open.\n");
801 if(developer_entityparsing.integer)
802 Con_Printf("Host_Loadgame_f: loading version\n");
805 COM_ParseToken_Simple(&t, false, false);
806 version = atoi(com_token);
807 if (version != SAVEGAME_VERSION)
810 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
814 if(developer_entityparsing.integer)
815 Con_Printf("Host_Loadgame_f: loading description\n");
818 COM_ParseToken_Simple(&t, false, false);
820 for (i = 0;i < NUM_SPAWN_PARMS;i++)
822 COM_ParseToken_Simple(&t, false, false);
823 spawn_parms[i] = atof(com_token);
826 COM_ParseToken_Simple(&t, false, false);
827 // this silliness is so we can load 1.06 save files, which have float skill values
828 current_skill = (int)(atof(com_token) + 0.5);
829 Cvar_SetValue ("skill", (float)current_skill);
831 if(developer_entityparsing.integer)
832 Con_Printf("Host_Loadgame_f: loading mapname\n");
835 COM_ParseToken_Simple(&t, false, false);
836 strlcpy (mapname, com_token, sizeof(mapname));
838 if(developer_entityparsing.integer)
839 Con_Printf("Host_Loadgame_f: loading time\n");
842 COM_ParseToken_Simple(&t, false, false);
843 time = atof(com_token);
845 allowcheats = sv_cheats.integer != 0;
847 if(developer_entityparsing.integer)
848 Con_Printf("Host_Loadgame_f: spawning server\n");
850 SV_SpawnServer (mapname);
854 Con_Print("Couldn't load map\n");
857 sv.paused = true; // pause until all clients connect
860 if(developer_entityparsing.integer)
861 Con_Printf("Host_Loadgame_f: loading light styles\n");
863 // load the light styles
869 for (i = 0;i < MAX_LIGHTSTYLES;i++)
873 COM_ParseToken_Simple(&t, false, false);
874 // if this is a 64 lightstyle savegame produced by Quake, stop now
875 // we have to check this because darkplaces may save more than 64
876 if (com_token[0] == '{')
881 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
884 if(developer_entityparsing.integer)
885 Con_Printf("Host_Loadgame_f: skipping until globals\n");
887 // now skip everything before the first opening brace
888 // (this is for forward compatibility, so that older versions (at
889 // least ones with this fix) can load savegames with extra data before the
890 // first brace, as might be produced by a later engine version)
894 if (!COM_ParseToken_Simple(&t, false, false))
896 if (com_token[0] == '{')
903 // unlink all entities
904 World_UnlinkAll(&sv.world);
906 // load the edicts out of the savegame file
911 while (COM_ParseToken_Simple(&t, false, false))
912 if (!strcmp(com_token, "}"))
914 if (!COM_ParseToken_Simple(&start, false, false))
919 if (strcmp(com_token,"{"))
922 Host_Error ("First token isn't a brace");
927 if(developer_entityparsing.integer)
928 Con_Printf("Host_Loadgame_f: loading globals\n");
930 // parse the global vars
931 PRVM_ED_ParseGlobals (start);
933 // restore the autocvar globals
934 Cvar_UpdateAllAutoCvars();
939 if (entnum >= MAX_EDICTS)
942 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
944 while (entnum >= prog->max_edicts)
945 PRVM_MEM_IncreaseEdicts();
946 ent = PRVM_EDICT_NUM(entnum);
947 memset (ent->fields.server, 0, prog->entityfields * 4);
948 ent->priv.server->free = false;
950 if(developer_entityparsing.integer)
951 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
953 PRVM_ED_ParseEdict (start, ent);
955 // link it into the bsp tree
956 if (!ent->priv.server->free)
964 prog->num_edicts = entnum;
967 for (i = 0;i < NUM_SPAWN_PARMS;i++)
968 svs.clients[0].spawn_parms[i] = spawn_parms[i];
970 if(developer_entityparsing.integer)
971 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
973 // read extended data if present
974 // the extended data is stored inside a /* */ comment block, which the
975 // parser intentionally skips, so we have to check for it manually here
978 while (*end == '\r' || *end == '\n')
980 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
982 if(developer_entityparsing.integer)
983 Con_Printf("Host_Loadgame_f: loading extended data\n");
985 Con_Printf("Loading extended DarkPlaces savegame\n");
987 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
988 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
989 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
990 while (COM_ParseToken_Simple(&t, false, false))
992 if (!strcmp(com_token, "sv.lightstyles"))
994 COM_ParseToken_Simple(&t, false, false);
996 COM_ParseToken_Simple(&t, false, false);
997 if (i >= 0 && i < MAX_LIGHTSTYLES)
998 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
1000 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
1002 else if (!strcmp(com_token, "sv.model_precache"))
1004 COM_ParseToken_Simple(&t, false, false);
1005 i = atoi(com_token);
1006 COM_ParseToken_Simple(&t, false, false);
1007 if (i >= 0 && i < MAX_MODELS)
1009 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
1010 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
1013 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
1015 else if (!strcmp(com_token, "sv.sound_precache"))
1017 COM_ParseToken_Simple(&t, false, false);
1018 i = atoi(com_token);
1019 COM_ParseToken_Simple(&t, false, false);
1020 if (i >= 0 && i < MAX_SOUNDS)
1021 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1023 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1025 else if (!strcmp(com_token, "sv.bufstr"))
1027 COM_ParseToken_Simple(&t, false, false);
1028 i = atoi(com_token);
1029 COM_ParseToken_Simple(&t, false, false);
1030 k = atoi(com_token);
1031 COM_ParseToken_Simple(&t, false, false);
1032 stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
1033 // VorteX: nasty code, cleanup required
1034 // create buffer at this index
1036 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
1038 Con_Printf("cant write string %i into buffer %i\n", k, i);
1041 // code copied from VM_bufstr_set
1043 if (stringbuffer->max_strings <= i)
1045 char **oldstrings = stringbuffer->strings;
1046 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
1047 while (stringbuffer->max_strings <= i)
1048 stringbuffer->max_strings *= 2;
1049 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
1050 if (stringbuffer->num_strings > 0)
1051 memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
1053 Mem_Free(oldstrings);
1056 stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
1057 if(stringbuffer->strings[k])
1058 Mem_Free(stringbuffer->strings[k]);
1059 stringbuffer->strings[k] = NULL;
1060 alloclen = strlen(com_token) + 1;
1061 stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
1062 memcpy(stringbuffer->strings[k], com_token, alloclen);
1065 // skip any trailing text or unrecognized commands
1066 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
1073 if(developer_entityparsing.integer)
1074 Con_Printf("Host_Loadgame_f: finished\n");
1078 // make sure we're connected to loopback
1079 if (sv.active && cls.state == ca_disconnected)
1080 CL_EstablishConnection("local:1", -2);
1083 //============================================================================
1086 ======================
1088 ======================
1090 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1091 void Host_Name_f (void)
1094 qboolean valid_colors;
1095 const char *newNameSource;
1096 char newName[sizeof(host_client->name)];
1098 if (Cmd_Argc () == 1)
1100 Con_Printf("name: %s\n", cl_name.string);
1104 if (Cmd_Argc () == 2)
1105 newNameSource = Cmd_Argv(1);
1107 newNameSource = Cmd_Args();
1109 strlcpy(newName, newNameSource, sizeof(newName));
1111 if (cmd_source == src_command)
1113 Cvar_Set ("_cl_name", newName);
1114 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1116 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1117 Con_Printf("name: %s\n", cl_name.string);
1122 if (realtime < host_client->nametime)
1124 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1128 host_client->nametime = realtime + 5;
1130 // point the string back at updateclient->name to keep it safe
1131 strlcpy (host_client->name, newName, sizeof (host_client->name));
1133 for (i = 0, j = 0;host_client->name[i];i++)
1134 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1135 host_client->name[j++] = host_client->name[i];
1136 host_client->name[j] = 0;
1138 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1139 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1141 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1142 host_client->name[sizeof(host_client->name) - 1] = 0;
1143 host_client->name[0] = STRING_COLOR_TAG;
1144 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1147 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1148 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1151 l = strlen(host_client->name);
1152 if(l < sizeof(host_client->name) - 1)
1154 // duplicate the color tag to escape it
1155 host_client->name[i] = STRING_COLOR_TAG;
1156 host_client->name[i+1] = 0;
1157 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1161 // remove the last character to fix the color code
1162 host_client->name[l-1] = 0;
1163 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1167 // find the last color tag offset and decide if we need to add a reset tag
1168 for (i = 0, j = -1;host_client->name[i];i++)
1170 if (host_client->name[i] == STRING_COLOR_TAG)
1172 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1175 // if this happens to be a reset tag then we don't need one
1176 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1181 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]))
1187 if (host_client->name[i+1] == STRING_COLOR_TAG)
1194 // does not end in the default color string, so add it
1195 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1196 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1198 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1199 if (strcmp(host_client->old_name, host_client->name))
1201 if (host_client->spawned)
1202 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1203 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1204 // send notification to all clients
1205 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1206 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1207 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1208 SV_WriteNetnameIntoDemo(host_client);
1213 ======================
1215 ======================
1217 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz/Xonotic (changed by playermodel command)"};
1218 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1219 void Host_Playermodel_f (void)
1222 char newPath[sizeof(host_client->playermodel)];
1224 if (Cmd_Argc () == 1)
1226 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1230 if (Cmd_Argc () == 2)
1231 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1233 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1235 for (i = 0, j = 0;newPath[i];i++)
1236 if (newPath[i] != '\r' && newPath[i] != '\n')
1237 newPath[j++] = newPath[i];
1240 if (cmd_source == src_command)
1242 Cvar_Set ("_cl_playermodel", newPath);
1247 if (realtime < host_client->nametime)
1249 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1253 host_client->nametime = realtime + 5;
1256 // point the string back at updateclient->name to keep it safe
1257 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1258 PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(host_client->playermodel);
1259 if (strcmp(host_client->old_model, host_client->playermodel))
1261 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1262 /*// send notification to all clients
1263 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1264 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1265 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1270 ======================
1272 ======================
1274 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
1275 void Host_Playerskin_f (void)
1278 char newPath[sizeof(host_client->playerskin)];
1280 if (Cmd_Argc () == 1)
1282 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1286 if (Cmd_Argc () == 2)
1287 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1289 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1291 for (i = 0, j = 0;newPath[i];i++)
1292 if (newPath[i] != '\r' && newPath[i] != '\n')
1293 newPath[j++] = newPath[i];
1296 if (cmd_source == src_command)
1298 Cvar_Set ("_cl_playerskin", newPath);
1303 if (realtime < host_client->nametime)
1305 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1309 host_client->nametime = realtime + 5;
1312 // point the string back at updateclient->name to keep it safe
1313 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1314 PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(host_client->playerskin);
1315 if (strcmp(host_client->old_skin, host_client->playerskin))
1317 //if (host_client->spawned)
1318 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1319 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1320 /*// send notification to all clients
1321 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1322 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1323 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1327 void Host_Version_f (void)
1329 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1332 void Host_Say(qboolean teamonly)
1338 // LordHavoc: long say messages
1340 qboolean fromServer = false;
1342 if (cmd_source == src_command)
1344 if (cls.state == ca_dedicated)
1351 Cmd_ForwardToServer ();
1356 if (Cmd_Argc () < 2)
1359 if (!teamplay.integer)
1369 // note this uses the chat prefix \001
1370 if (!fromServer && !teamonly)
1371 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1372 else if (!fromServer && teamonly)
1373 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1374 else if(*(sv_adminnick.string))
1375 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1377 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1378 p2 = text + strlen(text);
1379 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1381 if (p2[-1] == '\"' && quoted)
1386 strlcat(text, "\n", sizeof(text));
1388 // note: save is not a valid edict if fromServer is true
1390 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1391 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1392 SV_ClientPrint(text);
1395 if (cls.state == ca_dedicated)
1396 Con_Print(&text[1]);
1400 void Host_Say_f(void)
1406 void Host_Say_Team_f(void)
1412 void Host_Tell_f(void)
1414 const char *playername_start = NULL;
1415 size_t playername_length = 0;
1416 int playernumber = 0;
1419 const char *p1, *p2;
1420 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1421 qboolean fromServer = false;
1423 if (cmd_source == src_command)
1425 if (cls.state == ca_dedicated)
1429 Cmd_ForwardToServer ();
1434 if (Cmd_Argc () < 2)
1437 // note this uses the chat prefix \001
1439 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1440 else if(*(sv_adminnick.string))
1441 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1443 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1446 p2 = p1 + strlen(p1);
1447 // remove the target name
1448 while (p1 < p2 && *p1 == ' ')
1453 while (p1 < p2 && *p1 == ' ')
1455 while (p1 < p2 && isdigit(*p1))
1457 playernumber = playernumber * 10 + (*p1 - '0');
1465 playername_start = p1;
1466 while (p1 < p2 && *p1 != '"')
1468 playername_length = p1 - playername_start;
1474 playername_start = p1;
1475 while (p1 < p2 && *p1 != ' ')
1477 playername_length = p1 - playername_start;
1479 while (p1 < p2 && *p1 == ' ')
1481 if(playername_start)
1483 // set playernumber to the right client
1485 if(playername_length >= sizeof(namebuf))
1488 Con_Print("Host_Tell: too long player name/ID\n");
1490 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1493 memcpy(namebuf, playername_start, playername_length);
1494 namebuf[playername_length] = 0;
1495 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1497 if (!svs.clients[playernumber].active)
1499 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1503 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1506 Con_Print("Host_Tell: invalid player name/ID\n");
1508 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1511 // remove trailing newlines
1512 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1514 // remove quotes if present
1520 else if (fromServer)
1521 Con_Print("Host_Tell: missing end quote\n");
1523 SV_ClientPrint("Host_Tell: missing end quote\n");
1525 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1528 return; // empty say
1529 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1535 host_client = svs.clients + playernumber;
1536 SV_ClientPrint(text);
1546 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1547 void Host_Color(int changetop, int changebottom)
1549 int top, bottom, playercolor;
1551 // get top and bottom either from the provided values or the current values
1552 // (allows changing only top or bottom, or both at once)
1553 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1554 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1558 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1564 playercolor = top*16 + bottom;
1566 if (cmd_source == src_command)
1568 Cvar_SetValueQuick(&cl_color, playercolor);
1572 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1575 if (host_client->edict && PRVM_clientfunction(SV_ChangeTeam))
1577 Con_DPrint("Calling SV_ChangeTeam\n");
1578 prog->globals.server->time = sv.time;
1579 prog->globals.generic[OFS_PARM0] = playercolor;
1580 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1581 PRVM_ExecuteProgram(PRVM_clientfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1585 if (host_client->edict)
1587 PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1588 host_client->edict->fields.server->team = bottom + 1;
1590 host_client->colors = playercolor;
1591 if (host_client->old_colors != host_client->colors)
1593 host_client->old_colors = host_client->colors;
1594 // send notification to all clients
1595 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1596 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1597 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1602 void Host_Color_f(void)
1606 if (Cmd_Argc() == 1)
1608 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1609 Con_Print("color <0-15> [0-15]\n");
1613 if (Cmd_Argc() == 2)
1614 top = bottom = atoi(Cmd_Argv(1));
1617 top = atoi(Cmd_Argv(1));
1618 bottom = atoi(Cmd_Argv(2));
1620 Host_Color(top, bottom);
1623 void Host_TopColor_f(void)
1625 if (Cmd_Argc() == 1)
1627 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1628 Con_Print("topcolor <0-15>\n");
1632 Host_Color(atoi(Cmd_Argv(1)), -1);
1635 void Host_BottomColor_f(void)
1637 if (Cmd_Argc() == 1)
1639 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1640 Con_Print("bottomcolor <0-15>\n");
1644 Host_Color(-1, atoi(Cmd_Argv(1)));
1647 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1648 void Host_Rate_f(void)
1652 if (Cmd_Argc() != 2)
1654 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1655 Con_Print("rate <bytespersecond>\n");
1659 rate = atoi(Cmd_Argv(1));
1661 if (cmd_source == src_command)
1663 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1667 host_client->rate = rate;
1675 void Host_Kill_f (void)
1677 if (host_client->edict->fields.server->health <= 0)
1679 SV_ClientPrint("Can't suicide -- already dead!\n");
1683 prog->globals.server->time = sv.time;
1684 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1685 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1694 void Host_Pause_f (void)
1696 if (!pausable.integer)
1697 SV_ClientPrint("Pause not allowed.\n");
1701 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1702 // send notification to all clients
1703 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1704 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1709 ======================
1711 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1712 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1713 ======================
1715 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)"};
1716 static void Host_PModel_f (void)
1720 if (Cmd_Argc () == 1)
1722 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1725 i = atoi(Cmd_Argv(1));
1727 if (cmd_source == src_command)
1729 if (cl_pmodel.integer == i)
1731 Cvar_SetValue ("_cl_pmodel", i);
1732 if (cls.state == ca_connected)
1733 Cmd_ForwardToServer ();
1737 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1740 //===========================================================================
1748 void Host_PreSpawn_f (void)
1750 if (host_client->spawned)
1752 Con_Print("prespawn not valid -- already spawned\n");
1756 if (host_client->netconnection)
1758 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1759 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1760 MSG_WriteByte (&host_client->netconnection->message, 2);
1761 host_client->sendsignon = 0; // enable unlimited sends again
1764 // reset the name change timer because the client will send name soon
1765 host_client->nametime = 0;
1773 void Host_Spawn_f (void)
1777 int stats[MAX_CL_STATS];
1779 if (host_client->spawned)
1781 Con_Print("Spawn not valid -- already spawned\n");
1785 // reset name change timer again because they might want to change name
1786 // again in the first 5 seconds after connecting
1787 host_client->nametime = 0;
1789 // LordHavoc: moved this above the QC calls at FrikaC's request
1790 // LordHavoc: commented this out
1791 //if (host_client->netconnection)
1792 // SZ_Clear (&host_client->netconnection->message);
1794 // run the entrance script
1797 // loaded games are fully initialized already
1798 if (PRVM_serverfunction(RestoreGame))
1800 Con_DPrint("Calling RestoreGame\n");
1801 prog->globals.server->time = sv.time;
1802 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1803 PRVM_ExecuteProgram(PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1808 //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);
1810 // copy spawn parms out of the client_t
1811 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1812 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1814 // call the spawn function
1815 host_client->clientconnectcalled = true;
1816 prog->globals.server->time = sv.time;
1817 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1818 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1820 if (cls.state == ca_dedicated)
1821 Con_Printf("%s connected\n", host_client->name);
1823 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1826 if (!host_client->netconnection)
1829 // send time of update
1830 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1831 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1833 // send all current names, colors, and frag counts
1834 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1836 if (!client->active)
1838 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1839 MSG_WriteByte (&host_client->netconnection->message, i);
1840 MSG_WriteString (&host_client->netconnection->message, client->name);
1841 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1842 MSG_WriteByte (&host_client->netconnection->message, i);
1843 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1844 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1845 MSG_WriteByte (&host_client->netconnection->message, i);
1846 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1849 // send all current light styles
1850 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1852 if (sv.lightstyles[i][0])
1854 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1855 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1856 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1861 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1862 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1863 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1865 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1866 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1867 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1869 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1870 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1871 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1873 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1874 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1875 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1878 // Never send a roll angle, because savegames can catch the server
1879 // in a state where it is expecting the client to correct the angle
1880 // and it won't happen if the game was just loaded, so you wind up
1881 // with a permanent head tilt
1884 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1885 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1886 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1887 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1891 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1892 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1893 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1894 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1897 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1899 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1900 MSG_WriteByte (&host_client->netconnection->message, 3);
1908 void Host_Begin_f (void)
1910 host_client->spawned = true;
1912 // LordHavoc: note: this code also exists in SV_DropClient
1916 for (i = 0;i < svs.maxclients;i++)
1917 if (svs.clients[i].active && !svs.clients[i].spawned)
1919 if (i == svs.maxclients)
1921 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1922 sv.paused = sv.loadgame = false; // we're basically done with loading now
1927 //===========================================================================
1934 Kicks a user off of the server
1937 void Host_Kick_f (void)
1940 const char *message = NULL;
1943 qboolean byNumber = false;
1951 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1953 i = (int)(atof(Cmd_Argv(2)) - 1);
1954 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1960 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1962 if (!host_client->active)
1964 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1969 if (i < svs.maxclients)
1971 if (cmd_source == src_command)
1973 if (cls.state == ca_dedicated)
1976 who = cl_name.string;
1981 // can't kick yourself!
1982 if (host_client == save)
1987 message = Cmd_Args();
1988 COM_ParseToken_Simple(&message, false, false);
1991 message++; // skip the #
1992 while (*message == ' ') // skip white space
1994 message += strlen(Cmd_Argv(2)); // skip the number
1996 while (*message && *message == ' ')
2000 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2002 SV_ClientPrintf("Kicked by %s\n", who);
2003 SV_DropClient (false); // kicked
2011 ===============================================================================
2015 ===============================================================================
2023 void Host_Give_f (void)
2030 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2035 v = atoi (Cmd_Argv(2));
2049 // MED 01/04/97 added hipnotic give stuff
2050 if (gamemode == GAME_HIPNOTIC)
2055 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
2057 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
2059 else if (t[0] == '9')
2060 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
2061 else if (t[0] == '0')
2062 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
2063 else if (t[0] >= '2')
2064 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2069 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2074 if (gamemode == GAME_ROGUE)
2075 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2077 host_client->edict->fields.server->ammo_shells = v;
2080 if (gamemode == GAME_ROGUE)
2082 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2083 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2084 host_client->edict->fields.server->ammo_nails = v;
2088 host_client->edict->fields.server->ammo_nails = v;
2092 if (gamemode == GAME_ROGUE)
2094 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2095 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2096 host_client->edict->fields.server->ammo_nails = v;
2100 if (gamemode == GAME_ROGUE)
2102 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2103 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2104 host_client->edict->fields.server->ammo_rockets = v;
2108 host_client->edict->fields.server->ammo_rockets = v;
2112 if (gamemode == GAME_ROGUE)
2114 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2115 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2116 host_client->edict->fields.server->ammo_rockets = v;
2120 host_client->edict->fields.server->health = v;
2123 if (gamemode == GAME_ROGUE)
2125 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2126 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2127 host_client->edict->fields.server->ammo_cells = v;
2131 host_client->edict->fields.server->ammo_cells = v;
2135 if (gamemode == GAME_ROGUE)
2137 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2138 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2139 host_client->edict->fields.server->ammo_cells = v;
2145 prvm_edict_t *FindViewthing (void)
2150 for (i=0 ; i<prog->num_edicts ; i++)
2152 e = PRVM_EDICT_NUM(i);
2153 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2156 Con_Print("No viewthing on map\n");
2165 void Host_Viewmodel_f (void)
2174 e = FindViewthing ();
2179 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2180 if (!m || !m->loaded || !m->Draw)
2182 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2186 e->fields.server->frame = 0;
2187 cl.model_precache[(int)e->fields.server->modelindex] = m;
2195 void Host_Viewframe_f (void)
2205 e = FindViewthing ();
2209 m = cl.model_precache[(int)e->fields.server->modelindex];
2211 f = atoi(Cmd_Argv(1));
2212 if (f >= m->numframes)
2215 e->fields.server->frame = f;
2219 void PrintFrameName (dp_model_t *m, int frame)
2222 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2224 Con_Printf("frame %i\n", frame);
2232 void Host_Viewnext_f (void)
2241 e = FindViewthing ();
2245 m = cl.model_precache[(int)e->fields.server->modelindex];
2247 e->fields.server->frame = e->fields.server->frame + 1;
2248 if (e->fields.server->frame >= m->numframes)
2249 e->fields.server->frame = m->numframes - 1;
2251 PrintFrameName (m, (int)e->fields.server->frame);
2259 void Host_Viewprev_f (void)
2268 e = FindViewthing ();
2273 m = cl.model_precache[(int)e->fields.server->modelindex];
2275 e->fields.server->frame = e->fields.server->frame - 1;
2276 if (e->fields.server->frame < 0)
2277 e->fields.server->frame = 0;
2279 PrintFrameName (m, (int)e->fields.server->frame);
2283 ===============================================================================
2287 ===============================================================================
2296 void Host_Startdemos_f (void)
2300 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2306 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2309 Con_DPrintf("%i demo(s) in loop\n", c);
2311 for (i=1 ; i<c+1 ; i++)
2312 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2314 // LordHavoc: clear the remaining slots
2315 for (;i <= MAX_DEMOS;i++)
2316 cls.demos[i-1][0] = 0;
2318 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2332 Return to looping demos
2335 void Host_Demos_f (void)
2337 if (cls.state == ca_dedicated)
2339 if (cls.demonum == -1)
2349 Return to looping demos
2352 void Host_Stopdemo_f (void)
2354 if (!cls.demoplayback)
2357 Host_ShutdownServer ();
2360 void Host_SendCvar_f (void)
2364 const char *cvarname;
2369 cvarname = Cmd_Argv(1);
2370 if (cls.state == ca_connected)
2372 c = Cvar_FindVar(cvarname);
2373 // LordHavoc: if there is no such cvar or if it is private, send a
2374 // reply indicating that it has no value
2375 if(!c || (c->flags & CVAR_PRIVATE))
2376 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2378 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2381 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2385 if (cls.state != ca_dedicated)
2389 for(;i<svs.maxclients;i++)
2390 if(svs.clients[i].active && svs.clients[i].netconnection)
2392 host_client = &svs.clients[i];
2393 Host_ClientCommands("sendcvar %s\n", cvarname);
2398 static void MaxPlayers_f(void)
2402 if (Cmd_Argc() != 2)
2404 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2410 Con_Print("maxplayers can not be changed while a server is running.\n");
2411 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2414 n = atoi(Cmd_Argv(1));
2415 n = bound(1, n, MAX_SCOREBOARD);
2416 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2418 svs.maxclients_next = n;
2420 Cvar_Set ("deathmatch", "0");
2422 Cvar_Set ("deathmatch", "1");
2426 =====================
2429 ProQuake rcon support
2430 =====================
2432 void Host_PQRcon_f (void)
2437 lhnetsocket_t *mysocket;
2438 char peer_address[64];
2440 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2442 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2446 e = strchr(rcon_password.string, ' ');
2447 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2451 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2455 if (!rcon_address.string[0])
2457 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2460 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2462 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2463 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2466 SZ_Clear(&net_message);
2467 MSG_WriteLong (&net_message, 0);
2468 MSG_WriteByte (&net_message, CCREQ_RCON);
2469 SZ_Write(&net_message, (const unsigned char*)rcon_password.string, n);
2470 MSG_WriteByte (&net_message, 0); // terminate the (possibly partial) string
2471 MSG_WriteString (&net_message, Cmd_Args());
2472 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2473 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2474 SZ_Clear (&net_message);
2478 //=============================================================================
2480 // QuakeWorld commands
2483 =====================
2486 Send the rest of the command line over as
2487 an unconnected command.
2488 =====================
2490 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2495 lhnetsocket_t *mysocket;
2497 if (!rcon_password.string || !rcon_password.string[0])
2499 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2503 e = strchr(rcon_password.string, ' ');
2504 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2507 to = cls.netcon->peeraddress;
2510 if (!rcon_address.string[0])
2512 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2515 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2517 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2518 if (mysocket && Cmd_Args()[0])
2520 // simply put together the rcon packet and send it
2521 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2523 if(cls.rcon_commands[cls.rcon_ringpos][0])
2526 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2527 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]);
2528 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2531 for (i = 0;i < MAX_RCONS;i++)
2532 if(cls.rcon_commands[i][0])
2533 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2537 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2538 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2539 cls.rcon_addresses[cls.rcon_ringpos] = to;
2540 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2541 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2543 else if(rcon_secure.integer > 0)
2547 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2548 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2549 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2552 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2553 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2558 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2564 ====================
2567 user <name or userid>
2569 Dump userdata / masterdata for a user
2570 ====================
2572 void Host_User_f (void) // credit: taken from QuakeWorld
2577 if (Cmd_Argc() != 2)
2579 Con_Printf ("Usage: user <username / userid>\n");
2583 uid = atoi(Cmd_Argv(1));
2585 for (i = 0;i < cl.maxclients;i++)
2587 if (!cl.scores[i].name[0])
2589 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2591 InfoString_Print(cl.scores[i].qw_userinfo);
2595 Con_Printf ("User not in server.\n");
2599 ====================
2602 Dump userids for all current players
2603 ====================
2605 void Host_Users_f (void) // credit: taken from QuakeWorld
2611 Con_Printf ("userid frags name\n");
2612 Con_Printf ("------ ----- ----\n");
2613 for (i = 0;i < cl.maxclients;i++)
2615 if (cl.scores[i].name[0])
2617 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2622 Con_Printf ("%i total users\n", c);
2627 Host_FullServerinfo_f
2629 Sent by server when serverinfo changes
2632 // TODO: shouldn't this be a cvar instead?
2633 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2636 if (Cmd_Argc() != 2)
2638 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2642 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2643 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2644 cl.qw_teamplay = atoi(temp);
2651 Allow clients to change userinfo
2655 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2662 if (Cmd_Argc() != 2)
2664 Con_Printf ("fullinfo <complete info string>\n");
2674 while (*s && *s != '\\')
2680 Con_Printf ("MISSING VALUE\n");
2686 while (*s && *s != '\\')
2693 CL_SetInfo(key, value, false, false, false, false);
2701 Allow clients to change userinfo
2704 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2706 if (Cmd_Argc() == 1)
2708 InfoString_Print(cls.userinfo);
2711 if (Cmd_Argc() != 3)
2713 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2716 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2720 ====================
2723 packet <destination> <contents>
2725 Contents allows \n escape character
2726 ====================
2728 void Host_Packet_f (void) // credit: taken from QuakeWorld
2734 lhnetaddress_t address;
2735 lhnetsocket_t *mysocket;
2737 if (Cmd_Argc() != 3)
2739 Con_Printf ("packet <destination> <contents>\n");
2743 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2745 Con_Printf ("Bad address\n");
2751 send[0] = send[1] = send[2] = send[3] = -1;
2753 l = (int)strlen (in);
2754 for (i=0 ; i<l ; i++)
2756 if (out >= send + sizeof(send) - 1)
2758 if (in[i] == '\\' && in[i+1] == 'n')
2763 else if (in[i] == '\\' && in[i+1] == '0')
2768 else if (in[i] == '\\' && in[i+1] == 't')
2773 else if (in[i] == '\\' && in[i+1] == 'r')
2778 else if (in[i] == '\\' && in[i+1] == '"')
2787 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2789 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2791 NetConn_Write(mysocket, send, out - send, &address);
2795 ====================
2798 Send back ping and packet loss update for all current players to this player
2799 ====================
2801 void Host_Pings_f (void)
2803 int i, j, ping, packetloss, movementloss;
2806 if (!host_client->netconnection)
2809 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2811 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2812 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2814 for (i = 0;i < svs.maxclients;i++)
2818 if (svs.clients[i].netconnection)
2820 for (j = 0;j < NETGRAPH_PACKETS;j++)
2821 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2823 for (j = 0;j < NETGRAPH_PACKETS;j++)
2824 if (svs.clients[i].movement_count[j] < 0)
2827 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2828 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2829 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2830 ping = bound(0, ping, 9999);
2831 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2833 // send qw_svc_updateping and qw_svc_updatepl messages
2834 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2835 MSG_WriteShort(&host_client->netconnection->message, ping);
2836 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2837 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2841 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2843 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2845 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2846 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2849 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2850 MSG_WriteString(&host_client->netconnection->message, "\n");
2853 void Host_PingPLReport_f(void)
2858 if (l > cl.maxclients)
2860 for (i = 0;i < l;i++)
2862 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2863 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2864 if(errbyte && *errbyte == ',')
2865 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2867 cl.scores[i].qw_movementloss = 0;
2871 //=============================================================================
2878 void Host_InitCommands (void)
2880 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2882 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2883 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2884 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2885 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2886 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2887 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2888 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2889 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2890 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2891 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2892 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2893 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)");
2894 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2895 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2896 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2897 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2898 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2899 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2900 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2901 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2902 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2903 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2905 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2906 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2907 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2909 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2910 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2911 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2912 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2914 Cvar_RegisterVariable (&cl_name);
2915 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2916 Cvar_RegisterVariable (&cl_color);
2917 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2918 Cvar_RegisterVariable (&cl_rate);
2919 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2920 Cvar_RegisterVariable (&cl_pmodel);
2921 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "(Nehahra-only) change your player model choice");
2923 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2924 Cvar_RegisterVariable (&cl_playermodel);
2925 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2926 Cvar_RegisterVariable (&cl_playerskin);
2927 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2929 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2930 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2931 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)");
2932 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2934 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2936 Cvar_RegisterVariable (&rcon_password);
2937 Cvar_RegisterVariable (&rcon_address);
2938 Cvar_RegisterVariable (&rcon_secure);
2939 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2940 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");
2941 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");
2942 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)");
2943 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2944 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2945 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2946 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2947 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2948 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2949 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2950 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2952 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)");
2953 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)");
2955 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)");
2956 Cvar_RegisterVariable (&r_fixtrans_auto);
2958 Cvar_RegisterVariable (&team);
2959 Cvar_RegisterVariable (&skin);
2960 Cvar_RegisterVariable (&noaim);
2962 Cvar_RegisterVariable(&sv_cheats);
2963 Cvar_RegisterVariable(&sv_adminnick);
2964 Cvar_RegisterVariable(&sv_status_privacy);
2965 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2968 void Host_NoOperation_f(void)