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 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
222 if (!((int)PRVM_serveredictfloat(host_client->edict, 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 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
237 if (!((int)PRVM_serveredictfloat(host_client->edict, 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 (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
255 noclip_anglehack = true;
256 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
257 SV_ClientPrint("noclip ON\n");
261 noclip_anglehack = false;
262 PRVM_serveredictfloat(host_client->edict, 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 (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
284 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
285 SV_ClientPrint("flymode ON\n");
289 PRVM_serveredictfloat(host_client->edict, 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(PRVM_serveredictstring(prog->edicts, message)), (int)PRVM_serverglobalfloat(killed_monsters), (int)PRVM_serverglobalfloat(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];
703 qboolean deadflag = false;
707 Con_Print("Can't save - no server running.\n");
712 deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
717 // singleplayer checks
720 Con_Print("Can't save in intermission.\n");
726 Con_Print("Can't savegame with a dead player\n");
731 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");
735 Con_Print("save <savename> : save a game\n");
739 if (strstr(Cmd_Argv(1), ".."))
741 Con_Print("Relative pathnames are not allowed.\n");
745 strlcpy (name, Cmd_Argv(1), sizeof (name));
746 FS_DefaultExtension (name, ".sav", sizeof (name));
749 Host_Savegame_to(name);
760 void Host_Loadgame_f (void)
762 char filename[MAX_QPATH];
763 char mapname[MAX_QPATH];
773 float spawn_parms[NUM_SPAWN_PARMS];
774 prvm_stringbuffer_t *stringbuffer;
779 Con_Print("load <savename> : load a game\n");
783 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
784 FS_DefaultExtension (filename, ".sav", sizeof (filename));
786 Con_Printf("Loading game from %s...\n", filename);
788 // stop playing demos
789 if (cls.demoplayback)
793 if (key_dest == key_menu || key_dest == key_menu_grabbed)
797 cls.demonum = -1; // stop demo loop in case this fails
799 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
802 Con_Print("ERROR: couldn't open.\n");
806 if(developer_entityparsing.integer)
807 Con_Printf("Host_Loadgame_f: loading version\n");
810 COM_ParseToken_Simple(&t, false, false);
811 version = atoi(com_token);
812 if (version != SAVEGAME_VERSION)
815 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
819 if(developer_entityparsing.integer)
820 Con_Printf("Host_Loadgame_f: loading description\n");
823 COM_ParseToken_Simple(&t, false, false);
825 for (i = 0;i < NUM_SPAWN_PARMS;i++)
827 COM_ParseToken_Simple(&t, false, false);
828 spawn_parms[i] = atof(com_token);
831 COM_ParseToken_Simple(&t, false, false);
832 // this silliness is so we can load 1.06 save files, which have float skill values
833 current_skill = (int)(atof(com_token) + 0.5);
834 Cvar_SetValue ("skill", (float)current_skill);
836 if(developer_entityparsing.integer)
837 Con_Printf("Host_Loadgame_f: loading mapname\n");
840 COM_ParseToken_Simple(&t, false, false);
841 strlcpy (mapname, com_token, sizeof(mapname));
843 if(developer_entityparsing.integer)
844 Con_Printf("Host_Loadgame_f: loading time\n");
847 COM_ParseToken_Simple(&t, false, false);
848 time = atof(com_token);
850 allowcheats = sv_cheats.integer != 0;
852 if(developer_entityparsing.integer)
853 Con_Printf("Host_Loadgame_f: spawning server\n");
855 SV_SpawnServer (mapname);
859 Con_Print("Couldn't load map\n");
862 sv.paused = true; // pause until all clients connect
865 if(developer_entityparsing.integer)
866 Con_Printf("Host_Loadgame_f: loading light styles\n");
868 // load the light styles
874 for (i = 0;i < MAX_LIGHTSTYLES;i++)
878 COM_ParseToken_Simple(&t, false, false);
879 // if this is a 64 lightstyle savegame produced by Quake, stop now
880 // we have to check this because darkplaces may save more than 64
881 if (com_token[0] == '{')
886 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
889 if(developer_entityparsing.integer)
890 Con_Printf("Host_Loadgame_f: skipping until globals\n");
892 // now skip everything before the first opening brace
893 // (this is for forward compatibility, so that older versions (at
894 // least ones with this fix) can load savegames with extra data before the
895 // first brace, as might be produced by a later engine version)
899 if (!COM_ParseToken_Simple(&t, false, false))
901 if (com_token[0] == '{')
908 // unlink all entities
909 World_UnlinkAll(&sv.world);
911 // load the edicts out of the savegame file
916 while (COM_ParseToken_Simple(&t, false, false))
917 if (!strcmp(com_token, "}"))
919 if (!COM_ParseToken_Simple(&start, false, false))
924 if (strcmp(com_token,"{"))
927 Host_Error ("First token isn't a brace");
932 if(developer_entityparsing.integer)
933 Con_Printf("Host_Loadgame_f: loading globals\n");
935 // parse the global vars
936 PRVM_ED_ParseGlobals (start);
938 // restore the autocvar globals
939 Cvar_UpdateAllAutoCvars();
944 if (entnum >= MAX_EDICTS)
947 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
949 while (entnum >= prog->max_edicts)
950 PRVM_MEM_IncreaseEdicts();
951 ent = PRVM_EDICT_NUM(entnum);
952 memset(ent->fields.vp, 0, prog->entityfields * 4);
953 ent->priv.server->free = false;
955 if(developer_entityparsing.integer)
956 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
958 PRVM_ED_ParseEdict (start, ent);
960 // link it into the bsp tree
961 if (!ent->priv.server->free)
969 prog->num_edicts = entnum;
972 for (i = 0;i < NUM_SPAWN_PARMS;i++)
973 svs.clients[0].spawn_parms[i] = spawn_parms[i];
975 if(developer_entityparsing.integer)
976 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
978 // read extended data if present
979 // the extended data is stored inside a /* */ comment block, which the
980 // parser intentionally skips, so we have to check for it manually here
983 while (*end == '\r' || *end == '\n')
985 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
987 if(developer_entityparsing.integer)
988 Con_Printf("Host_Loadgame_f: loading extended data\n");
990 Con_Printf("Loading extended DarkPlaces savegame\n");
992 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
993 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
994 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
995 while (COM_ParseToken_Simple(&t, false, false))
997 if (!strcmp(com_token, "sv.lightstyles"))
999 COM_ParseToken_Simple(&t, false, false);
1000 i = atoi(com_token);
1001 COM_ParseToken_Simple(&t, false, false);
1002 if (i >= 0 && i < MAX_LIGHTSTYLES)
1003 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
1005 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
1007 else if (!strcmp(com_token, "sv.model_precache"))
1009 COM_ParseToken_Simple(&t, false, false);
1010 i = atoi(com_token);
1011 COM_ParseToken_Simple(&t, false, false);
1012 if (i >= 0 && i < MAX_MODELS)
1014 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
1015 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
1018 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
1020 else if (!strcmp(com_token, "sv.sound_precache"))
1022 COM_ParseToken_Simple(&t, false, false);
1023 i = atoi(com_token);
1024 COM_ParseToken_Simple(&t, false, false);
1025 if (i >= 0 && i < MAX_SOUNDS)
1026 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1028 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1030 else if (!strcmp(com_token, "sv.bufstr"))
1032 COM_ParseToken_Simple(&t, false, false);
1033 i = atoi(com_token);
1034 COM_ParseToken_Simple(&t, false, false);
1035 k = atoi(com_token);
1036 COM_ParseToken_Simple(&t, false, false);
1037 stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
1038 // VorteX: nasty code, cleanup required
1039 // create buffer at this index
1041 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
1043 Con_Printf("cant write string %i into buffer %i\n", k, i);
1046 // code copied from VM_bufstr_set
1048 if (stringbuffer->max_strings <= i)
1050 char **oldstrings = stringbuffer->strings;
1051 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
1052 while (stringbuffer->max_strings <= i)
1053 stringbuffer->max_strings *= 2;
1054 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
1055 if (stringbuffer->num_strings > 0)
1056 memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
1058 Mem_Free(oldstrings);
1061 stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
1062 if(stringbuffer->strings[k])
1063 Mem_Free(stringbuffer->strings[k]);
1064 stringbuffer->strings[k] = NULL;
1065 alloclen = strlen(com_token) + 1;
1066 stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
1067 memcpy(stringbuffer->strings[k], com_token, alloclen);
1070 // skip any trailing text or unrecognized commands
1071 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
1078 if(developer_entityparsing.integer)
1079 Con_Printf("Host_Loadgame_f: finished\n");
1083 // make sure we're connected to loopback
1084 if (sv.active && cls.state == ca_disconnected)
1085 CL_EstablishConnection("local:1", -2);
1088 //============================================================================
1091 ======================
1093 ======================
1095 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1096 void Host_Name_f (void)
1099 qboolean valid_colors;
1100 const char *newNameSource;
1101 char newName[sizeof(host_client->name)];
1103 if (Cmd_Argc () == 1)
1105 Con_Printf("name: %s\n", cl_name.string);
1109 if (Cmd_Argc () == 2)
1110 newNameSource = Cmd_Argv(1);
1112 newNameSource = Cmd_Args();
1114 strlcpy(newName, newNameSource, sizeof(newName));
1116 if (cmd_source == src_command)
1118 Cvar_Set ("_cl_name", newName);
1119 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1121 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1122 Con_Printf("name: %s\n", cl_name.string);
1127 if (realtime < host_client->nametime)
1129 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1133 host_client->nametime = realtime + 5;
1135 // point the string back at updateclient->name to keep it safe
1136 strlcpy (host_client->name, newName, sizeof (host_client->name));
1138 for (i = 0, j = 0;host_client->name[i];i++)
1139 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1140 host_client->name[j++] = host_client->name[i];
1141 host_client->name[j] = 0;
1143 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1144 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1146 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1147 host_client->name[sizeof(host_client->name) - 1] = 0;
1148 host_client->name[0] = STRING_COLOR_TAG;
1149 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1152 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1153 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1156 l = strlen(host_client->name);
1157 if(l < sizeof(host_client->name) - 1)
1159 // duplicate the color tag to escape it
1160 host_client->name[i] = STRING_COLOR_TAG;
1161 host_client->name[i+1] = 0;
1162 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1166 // remove the last character to fix the color code
1167 host_client->name[l-1] = 0;
1168 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1172 // find the last color tag offset and decide if we need to add a reset tag
1173 for (i = 0, j = -1;host_client->name[i];i++)
1175 if (host_client->name[i] == STRING_COLOR_TAG)
1177 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1180 // if this happens to be a reset tag then we don't need one
1181 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1186 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]))
1192 if (host_client->name[i+1] == STRING_COLOR_TAG)
1199 // does not end in the default color string, so add it
1200 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1201 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1203 PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(host_client->name);
1204 if (strcmp(host_client->old_name, host_client->name))
1206 if (host_client->spawned)
1207 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1208 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1209 // send notification to all clients
1210 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1211 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1212 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1213 SV_WriteNetnameIntoDemo(host_client);
1218 ======================
1220 ======================
1222 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz/Xonotic (changed by playermodel command)"};
1223 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1224 void Host_Playermodel_f (void)
1227 char newPath[sizeof(host_client->playermodel)];
1229 if (Cmd_Argc () == 1)
1231 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1235 if (Cmd_Argc () == 2)
1236 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1238 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1240 for (i = 0, j = 0;newPath[i];i++)
1241 if (newPath[i] != '\r' && newPath[i] != '\n')
1242 newPath[j++] = newPath[i];
1245 if (cmd_source == src_command)
1247 Cvar_Set ("_cl_playermodel", newPath);
1252 if (realtime < host_client->nametime)
1254 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1258 host_client->nametime = realtime + 5;
1261 // point the string back at updateclient->name to keep it safe
1262 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1263 PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(host_client->playermodel);
1264 if (strcmp(host_client->old_model, host_client->playermodel))
1266 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1267 /*// send notification to all clients
1268 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1269 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1270 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1275 ======================
1277 ======================
1279 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
1280 void Host_Playerskin_f (void)
1283 char newPath[sizeof(host_client->playerskin)];
1285 if (Cmd_Argc () == 1)
1287 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1291 if (Cmd_Argc () == 2)
1292 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1294 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1296 for (i = 0, j = 0;newPath[i];i++)
1297 if (newPath[i] != '\r' && newPath[i] != '\n')
1298 newPath[j++] = newPath[i];
1301 if (cmd_source == src_command)
1303 Cvar_Set ("_cl_playerskin", newPath);
1308 if (realtime < host_client->nametime)
1310 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1314 host_client->nametime = realtime + 5;
1317 // point the string back at updateclient->name to keep it safe
1318 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1319 PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(host_client->playerskin);
1320 if (strcmp(host_client->old_skin, host_client->playerskin))
1322 //if (host_client->spawned)
1323 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1324 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1325 /*// send notification to all clients
1326 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1327 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1328 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1332 void Host_Version_f (void)
1334 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1337 void Host_Say(qboolean teamonly)
1343 // LordHavoc: long say messages
1345 qboolean fromServer = false;
1347 if (cmd_source == src_command)
1349 if (cls.state == ca_dedicated)
1356 Cmd_ForwardToServer ();
1361 if (Cmd_Argc () < 2)
1364 if (!teamplay.integer)
1374 // note this uses the chat prefix \001
1375 if (!fromServer && !teamonly)
1376 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1377 else if (!fromServer && teamonly)
1378 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1379 else if(*(sv_adminnick.string))
1380 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1382 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1383 p2 = text + strlen(text);
1384 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1386 if (p2[-1] == '\"' && quoted)
1391 strlcat(text, "\n", sizeof(text));
1393 // note: save is not a valid edict if fromServer is true
1395 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1396 if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
1397 SV_ClientPrint(text);
1400 if (cls.state == ca_dedicated)
1401 Con_Print(&text[1]);
1405 void Host_Say_f(void)
1411 void Host_Say_Team_f(void)
1417 void Host_Tell_f(void)
1419 const char *playername_start = NULL;
1420 size_t playername_length = 0;
1421 int playernumber = 0;
1424 const char *p1, *p2;
1425 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1426 qboolean fromServer = false;
1428 if (cmd_source == src_command)
1430 if (cls.state == ca_dedicated)
1434 Cmd_ForwardToServer ();
1439 if (Cmd_Argc () < 2)
1442 // note this uses the chat prefix \001
1444 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1445 else if(*(sv_adminnick.string))
1446 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1448 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1451 p2 = p1 + strlen(p1);
1452 // remove the target name
1453 while (p1 < p2 && *p1 == ' ')
1458 while (p1 < p2 && *p1 == ' ')
1460 while (p1 < p2 && isdigit(*p1))
1462 playernumber = playernumber * 10 + (*p1 - '0');
1470 playername_start = p1;
1471 while (p1 < p2 && *p1 != '"')
1473 playername_length = p1 - playername_start;
1479 playername_start = p1;
1480 while (p1 < p2 && *p1 != ' ')
1482 playername_length = p1 - playername_start;
1484 while (p1 < p2 && *p1 == ' ')
1486 if(playername_start)
1488 // set playernumber to the right client
1490 if(playername_length >= sizeof(namebuf))
1493 Con_Print("Host_Tell: too long player name/ID\n");
1495 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1498 memcpy(namebuf, playername_start, playername_length);
1499 namebuf[playername_length] = 0;
1500 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1502 if (!svs.clients[playernumber].active)
1504 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1508 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1511 Con_Print("Host_Tell: invalid player name/ID\n");
1513 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1516 // remove trailing newlines
1517 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1519 // remove quotes if present
1525 else if (fromServer)
1526 Con_Print("Host_Tell: missing end quote\n");
1528 SV_ClientPrint("Host_Tell: missing end quote\n");
1530 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1533 return; // empty say
1534 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1540 host_client = svs.clients + playernumber;
1541 SV_ClientPrint(text);
1551 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1552 void Host_Color(int changetop, int changebottom)
1554 int top, bottom, playercolor;
1556 // get top and bottom either from the provided values or the current values
1557 // (allows changing only top or bottom, or both at once)
1558 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1559 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1563 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1569 playercolor = top*16 + bottom;
1571 if (cmd_source == src_command)
1573 Cvar_SetValueQuick(&cl_color, playercolor);
1577 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1580 if (host_client->edict && PRVM_clientfunction(SV_ChangeTeam))
1582 Con_DPrint("Calling SV_ChangeTeam\n");
1583 PRVM_serverglobalfloat(time) = sv.time;
1584 prog->globals.generic[OFS_PARM0] = playercolor;
1585 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1586 PRVM_ExecuteProgram(PRVM_clientfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1590 if (host_client->edict)
1592 PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1593 PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1595 host_client->colors = playercolor;
1596 if (host_client->old_colors != host_client->colors)
1598 host_client->old_colors = host_client->colors;
1599 // send notification to all clients
1600 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1601 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1602 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1607 void Host_Color_f(void)
1611 if (Cmd_Argc() == 1)
1613 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1614 Con_Print("color <0-15> [0-15]\n");
1618 if (Cmd_Argc() == 2)
1619 top = bottom = atoi(Cmd_Argv(1));
1622 top = atoi(Cmd_Argv(1));
1623 bottom = atoi(Cmd_Argv(2));
1625 Host_Color(top, bottom);
1628 void Host_TopColor_f(void)
1630 if (Cmd_Argc() == 1)
1632 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1633 Con_Print("topcolor <0-15>\n");
1637 Host_Color(atoi(Cmd_Argv(1)), -1);
1640 void Host_BottomColor_f(void)
1642 if (Cmd_Argc() == 1)
1644 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1645 Con_Print("bottomcolor <0-15>\n");
1649 Host_Color(-1, atoi(Cmd_Argv(1)));
1652 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1653 void Host_Rate_f(void)
1657 if (Cmd_Argc() != 2)
1659 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1660 Con_Print("rate <bytespersecond>\n");
1664 rate = atoi(Cmd_Argv(1));
1666 if (cmd_source == src_command)
1668 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1672 host_client->rate = rate;
1680 void Host_Kill_f (void)
1682 if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
1684 SV_ClientPrint("Can't suicide -- already dead!\n");
1688 PRVM_serverglobalfloat(time) = sv.time;
1689 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1690 PRVM_ExecuteProgram (PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
1699 void Host_Pause_f (void)
1701 if (!pausable.integer)
1702 SV_ClientPrint("Pause not allowed.\n");
1706 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1707 // send notification to all clients
1708 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1709 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1714 ======================
1716 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1717 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1718 ======================
1720 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)"};
1721 static void Host_PModel_f (void)
1725 if (Cmd_Argc () == 1)
1727 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1730 i = atoi(Cmd_Argv(1));
1732 if (cmd_source == src_command)
1734 if (cl_pmodel.integer == i)
1736 Cvar_SetValue ("_cl_pmodel", i);
1737 if (cls.state == ca_connected)
1738 Cmd_ForwardToServer ();
1742 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1745 //===========================================================================
1753 void Host_PreSpawn_f (void)
1755 if (host_client->spawned)
1757 Con_Print("prespawn not valid -- already spawned\n");
1761 if (host_client->netconnection)
1763 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1764 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1765 MSG_WriteByte (&host_client->netconnection->message, 2);
1766 host_client->sendsignon = 0; // enable unlimited sends again
1769 // reset the name change timer because the client will send name soon
1770 host_client->nametime = 0;
1778 void Host_Spawn_f (void)
1782 int stats[MAX_CL_STATS];
1784 if (host_client->spawned)
1786 Con_Print("Spawn not valid -- already spawned\n");
1790 // reset name change timer again because they might want to change name
1791 // again in the first 5 seconds after connecting
1792 host_client->nametime = 0;
1794 // LordHavoc: moved this above the QC calls at FrikaC's request
1795 // LordHavoc: commented this out
1796 //if (host_client->netconnection)
1797 // SZ_Clear (&host_client->netconnection->message);
1799 // run the entrance script
1802 // loaded games are fully initialized already
1803 if (PRVM_serverfunction(RestoreGame))
1805 Con_DPrint("Calling RestoreGame\n");
1806 PRVM_serverglobalfloat(time) = sv.time;
1807 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1808 PRVM_ExecuteProgram(PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1813 //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), PRVM_GetString(PRVM_serveredictstring(host_client->edict, netname)), host_client->name);
1815 // copy spawn parms out of the client_t
1816 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1817 (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
1819 // call the spawn function
1820 host_client->clientconnectcalled = true;
1821 PRVM_serverglobalfloat(time) = sv.time;
1822 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1823 PRVM_ExecuteProgram (PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1825 if (cls.state == ca_dedicated)
1826 Con_Printf("%s connected\n", host_client->name);
1828 PRVM_ExecuteProgram (PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1831 if (!host_client->netconnection)
1834 // send time of update
1835 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1836 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1838 // send all current names, colors, and frag counts
1839 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1841 if (!client->active)
1843 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1844 MSG_WriteByte (&host_client->netconnection->message, i);
1845 MSG_WriteString (&host_client->netconnection->message, client->name);
1846 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1847 MSG_WriteByte (&host_client->netconnection->message, i);
1848 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1849 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1850 MSG_WriteByte (&host_client->netconnection->message, i);
1851 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1854 // send all current light styles
1855 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1857 if (sv.lightstyles[i][0])
1859 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1860 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1861 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1866 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1867 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1868 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1870 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1871 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1872 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1874 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1875 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1876 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1878 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1879 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1880 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
1883 // Never send a roll angle, because savegames can catch the server
1884 // in a state where it is expecting the client to correct the angle
1885 // and it won't happen if the game was just loaded, so you wind up
1886 // with a permanent head tilt
1889 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1890 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
1891 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
1892 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1896 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1897 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
1898 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
1899 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1902 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1904 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1905 MSG_WriteByte (&host_client->netconnection->message, 3);
1913 void Host_Begin_f (void)
1915 host_client->spawned = true;
1917 // LordHavoc: note: this code also exists in SV_DropClient
1921 for (i = 0;i < svs.maxclients;i++)
1922 if (svs.clients[i].active && !svs.clients[i].spawned)
1924 if (i == svs.maxclients)
1926 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1927 sv.paused = sv.loadgame = false; // we're basically done with loading now
1932 //===========================================================================
1939 Kicks a user off of the server
1942 void Host_Kick_f (void)
1945 const char *message = NULL;
1948 qboolean byNumber = false;
1956 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1958 i = (int)(atof(Cmd_Argv(2)) - 1);
1959 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1965 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1967 if (!host_client->active)
1969 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1974 if (i < svs.maxclients)
1976 if (cmd_source == src_command)
1978 if (cls.state == ca_dedicated)
1981 who = cl_name.string;
1986 // can't kick yourself!
1987 if (host_client == save)
1992 message = Cmd_Args();
1993 COM_ParseToken_Simple(&message, false, false);
1996 message++; // skip the #
1997 while (*message == ' ') // skip white space
1999 message += strlen(Cmd_Argv(2)); // skip the number
2001 while (*message && *message == ' ')
2005 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2007 SV_ClientPrintf("Kicked by %s\n", who);
2008 SV_DropClient (false); // kicked
2016 ===============================================================================
2020 ===============================================================================
2028 void Host_Give_f (void)
2035 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2040 v = atoi (Cmd_Argv(2));
2054 // MED 01/04/97 added hipnotic give stuff
2055 if (gamemode == GAME_HIPNOTIC)
2060 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2062 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2064 else if (t[0] == '9')
2065 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2066 else if (t[0] == '0')
2067 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2068 else if (t[0] >= '2')
2069 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2074 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2079 if (gamemode == GAME_ROGUE)
2080 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2082 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2085 if (gamemode == GAME_ROGUE)
2087 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2088 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2089 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2093 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2097 if (gamemode == GAME_ROGUE)
2099 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2100 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2101 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2105 if (gamemode == GAME_ROGUE)
2107 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2108 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2109 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2113 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2117 if (gamemode == GAME_ROGUE)
2119 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2120 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2121 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2125 PRVM_serveredictfloat(host_client->edict, health) = v;
2128 if (gamemode == GAME_ROGUE)
2130 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2131 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2132 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2136 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2140 if (gamemode == GAME_ROGUE)
2142 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2143 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2144 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2150 prvm_edict_t *FindViewthing (void)
2155 for (i=0 ; i<prog->num_edicts ; i++)
2157 e = PRVM_EDICT_NUM(i);
2158 if (!strcmp (PRVM_GetString(PRVM_serveredictstring(e, classname)), "viewthing"))
2161 Con_Print("No viewthing on map\n");
2170 void Host_Viewmodel_f (void)
2179 e = FindViewthing ();
2184 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2185 if (!m || !m->loaded || !m->Draw)
2187 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2191 PRVM_serveredictfloat(e, frame) = 0;
2192 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2200 void Host_Viewframe_f (void)
2210 e = FindViewthing ();
2214 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2216 f = atoi(Cmd_Argv(1));
2217 if (f >= m->numframes)
2220 PRVM_serveredictfloat(e, frame) = f;
2224 void PrintFrameName (dp_model_t *m, int frame)
2227 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2229 Con_Printf("frame %i\n", frame);
2237 void Host_Viewnext_f (void)
2246 e = FindViewthing ();
2250 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2252 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2253 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2254 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2256 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2264 void Host_Viewprev_f (void)
2273 e = FindViewthing ();
2278 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2280 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2281 if (PRVM_serveredictfloat(e, frame) < 0)
2282 PRVM_serveredictfloat(e, frame) = 0;
2284 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2288 ===============================================================================
2292 ===============================================================================
2301 void Host_Startdemos_f (void)
2305 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2311 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2314 Con_DPrintf("%i demo(s) in loop\n", c);
2316 for (i=1 ; i<c+1 ; i++)
2317 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2319 // LordHavoc: clear the remaining slots
2320 for (;i <= MAX_DEMOS;i++)
2321 cls.demos[i-1][0] = 0;
2323 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2337 Return to looping demos
2340 void Host_Demos_f (void)
2342 if (cls.state == ca_dedicated)
2344 if (cls.demonum == -1)
2354 Return to looping demos
2357 void Host_Stopdemo_f (void)
2359 if (!cls.demoplayback)
2362 Host_ShutdownServer ();
2365 void Host_SendCvar_f (void)
2369 const char *cvarname;
2374 cvarname = Cmd_Argv(1);
2375 if (cls.state == ca_connected)
2377 c = Cvar_FindVar(cvarname);
2378 // LordHavoc: if there is no such cvar or if it is private, send a
2379 // reply indicating that it has no value
2380 if(!c || (c->flags & CVAR_PRIVATE))
2381 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2383 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2386 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2390 if (cls.state != ca_dedicated)
2394 for(;i<svs.maxclients;i++)
2395 if(svs.clients[i].active && svs.clients[i].netconnection)
2397 host_client = &svs.clients[i];
2398 Host_ClientCommands("sendcvar %s\n", cvarname);
2403 static void MaxPlayers_f(void)
2407 if (Cmd_Argc() != 2)
2409 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2415 Con_Print("maxplayers can not be changed while a server is running.\n");
2416 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2419 n = atoi(Cmd_Argv(1));
2420 n = bound(1, n, MAX_SCOREBOARD);
2421 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2423 svs.maxclients_next = n;
2425 Cvar_Set ("deathmatch", "0");
2427 Cvar_Set ("deathmatch", "1");
2431 =====================
2434 ProQuake rcon support
2435 =====================
2437 void Host_PQRcon_f (void)
2442 lhnetsocket_t *mysocket;
2443 char peer_address[64];
2445 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2447 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2451 e = strchr(rcon_password.string, ' ');
2452 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2456 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2460 if (!rcon_address.string[0])
2462 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2465 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2467 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2468 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2471 SZ_Clear(&net_message);
2472 MSG_WriteLong (&net_message, 0);
2473 MSG_WriteByte (&net_message, CCREQ_RCON);
2474 SZ_Write(&net_message, (const unsigned char*)rcon_password.string, n);
2475 MSG_WriteByte (&net_message, 0); // terminate the (possibly partial) string
2476 MSG_WriteString (&net_message, Cmd_Args());
2477 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2478 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2479 SZ_Clear (&net_message);
2483 //=============================================================================
2485 // QuakeWorld commands
2488 =====================
2491 Send the rest of the command line over as
2492 an unconnected command.
2493 =====================
2495 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2500 lhnetsocket_t *mysocket;
2502 if (!rcon_password.string || !rcon_password.string[0])
2504 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2508 e = strchr(rcon_password.string, ' ');
2509 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2512 to = cls.netcon->peeraddress;
2515 if (!rcon_address.string[0])
2517 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2520 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2522 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2523 if (mysocket && Cmd_Args()[0])
2525 // simply put together the rcon packet and send it
2526 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2528 if(cls.rcon_commands[cls.rcon_ringpos][0])
2531 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2532 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]);
2533 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2536 for (i = 0;i < MAX_RCONS;i++)
2537 if(cls.rcon_commands[i][0])
2538 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2542 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2543 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2544 cls.rcon_addresses[cls.rcon_ringpos] = to;
2545 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2546 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2548 else if(rcon_secure.integer > 0)
2552 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2553 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2554 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2557 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2558 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2563 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2569 ====================
2572 user <name or userid>
2574 Dump userdata / masterdata for a user
2575 ====================
2577 void Host_User_f (void) // credit: taken from QuakeWorld
2582 if (Cmd_Argc() != 2)
2584 Con_Printf ("Usage: user <username / userid>\n");
2588 uid = atoi(Cmd_Argv(1));
2590 for (i = 0;i < cl.maxclients;i++)
2592 if (!cl.scores[i].name[0])
2594 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2596 InfoString_Print(cl.scores[i].qw_userinfo);
2600 Con_Printf ("User not in server.\n");
2604 ====================
2607 Dump userids for all current players
2608 ====================
2610 void Host_Users_f (void) // credit: taken from QuakeWorld
2616 Con_Printf ("userid frags name\n");
2617 Con_Printf ("------ ----- ----\n");
2618 for (i = 0;i < cl.maxclients;i++)
2620 if (cl.scores[i].name[0])
2622 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2627 Con_Printf ("%i total users\n", c);
2632 Host_FullServerinfo_f
2634 Sent by server when serverinfo changes
2637 // TODO: shouldn't this be a cvar instead?
2638 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2641 if (Cmd_Argc() != 2)
2643 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2647 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2648 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2649 cl.qw_teamplay = atoi(temp);
2656 Allow clients to change userinfo
2660 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2667 if (Cmd_Argc() != 2)
2669 Con_Printf ("fullinfo <complete info string>\n");
2679 while (*s && *s != '\\')
2685 Con_Printf ("MISSING VALUE\n");
2691 while (*s && *s != '\\')
2698 CL_SetInfo(key, value, false, false, false, false);
2706 Allow clients to change userinfo
2709 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2711 if (Cmd_Argc() == 1)
2713 InfoString_Print(cls.userinfo);
2716 if (Cmd_Argc() != 3)
2718 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2721 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2725 ====================
2728 packet <destination> <contents>
2730 Contents allows \n escape character
2731 ====================
2733 void Host_Packet_f (void) // credit: taken from QuakeWorld
2739 lhnetaddress_t address;
2740 lhnetsocket_t *mysocket;
2742 if (Cmd_Argc() != 3)
2744 Con_Printf ("packet <destination> <contents>\n");
2748 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2750 Con_Printf ("Bad address\n");
2756 send[0] = send[1] = send[2] = send[3] = -1;
2758 l = (int)strlen (in);
2759 for (i=0 ; i<l ; i++)
2761 if (out >= send + sizeof(send) - 1)
2763 if (in[i] == '\\' && in[i+1] == 'n')
2768 else if (in[i] == '\\' && in[i+1] == '0')
2773 else if (in[i] == '\\' && in[i+1] == 't')
2778 else if (in[i] == '\\' && in[i+1] == 'r')
2783 else if (in[i] == '\\' && in[i+1] == '"')
2792 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2794 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2796 NetConn_Write(mysocket, send, out - send, &address);
2800 ====================
2803 Send back ping and packet loss update for all current players to this player
2804 ====================
2806 void Host_Pings_f (void)
2808 int i, j, ping, packetloss, movementloss;
2811 if (!host_client->netconnection)
2814 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2816 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2817 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2819 for (i = 0;i < svs.maxclients;i++)
2823 if (svs.clients[i].netconnection)
2825 for (j = 0;j < NETGRAPH_PACKETS;j++)
2826 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2828 for (j = 0;j < NETGRAPH_PACKETS;j++)
2829 if (svs.clients[i].movement_count[j] < 0)
2832 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2833 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2834 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2835 ping = bound(0, ping, 9999);
2836 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2838 // send qw_svc_updateping and qw_svc_updatepl messages
2839 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2840 MSG_WriteShort(&host_client->netconnection->message, ping);
2841 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2842 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2846 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2848 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2850 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2851 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2854 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2855 MSG_WriteString(&host_client->netconnection->message, "\n");
2858 void Host_PingPLReport_f(void)
2863 if (l > cl.maxclients)
2865 for (i = 0;i < l;i++)
2867 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2868 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2869 if(errbyte && *errbyte == ',')
2870 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2872 cl.scores[i].qw_movementloss = 0;
2876 //=============================================================================
2883 void Host_InitCommands (void)
2885 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2887 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2888 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2889 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2890 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2891 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2892 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2893 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2894 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2895 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2896 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2897 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2898 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)");
2899 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2900 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2901 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2902 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2903 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2904 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2905 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2906 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2907 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2908 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2910 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2911 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2912 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2914 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2915 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2916 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2917 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2919 Cvar_RegisterVariable (&cl_name);
2920 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2921 Cvar_RegisterVariable (&cl_color);
2922 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2923 Cvar_RegisterVariable (&cl_rate);
2924 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2925 Cvar_RegisterVariable (&cl_pmodel);
2926 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "(Nehahra-only) change your player model choice");
2928 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2929 Cvar_RegisterVariable (&cl_playermodel);
2930 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2931 Cvar_RegisterVariable (&cl_playerskin);
2932 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2934 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2935 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2936 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)");
2937 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2939 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2941 Cvar_RegisterVariable (&rcon_password);
2942 Cvar_RegisterVariable (&rcon_address);
2943 Cvar_RegisterVariable (&rcon_secure);
2944 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2945 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");
2946 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");
2947 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)");
2948 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2949 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2950 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2951 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2952 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2953 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2954 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2955 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2957 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)");
2958 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)");
2960 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)");
2961 Cvar_RegisterVariable (&r_fixtrans_auto);
2963 Cvar_RegisterVariable (&team);
2964 Cvar_RegisterVariable (&skin);
2965 Cvar_RegisterVariable (&noaim);
2967 Cvar_RegisterVariable(&sv_cheats);
2968 Cvar_RegisterVariable(&sv_adminnick);
2969 Cvar_RegisterVariable(&sv_status_privacy);
2970 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2973 void Host_NoOperation_f(void)