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 sv_namechangetimer = {CVAR_SAVE, "sv_namechangetimer", "5", "how often to allow name changes, in seconds (prevents people from using animated names and other tricks"};
38 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"};
39 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"};
40 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"};
41 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
42 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
43 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
44 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
45 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)"};
46 qboolean allowcheats = false;
48 extern qboolean host_shuttingdown;
49 extern cvar_t developer_entityparsing;
57 void Host_Quit_f (void)
60 Con_Printf("shutting down already!\n");
70 void Host_Status_f (void)
74 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
75 void (*print) (const char *fmt, ...);
76 char ip[48]; // can contain a full length v6 address with [] and a port
79 if (cmd_source == src_command)
81 // if running a client, try to send over network so the client's status report parser will see the report
82 if (cls.state == ca_connected)
84 Cmd_ForwardToServer ();
90 print = SV_ClientPrintf;
95 if(cmd_source == src_command)
101 if (strcmp(Cmd_Argv(1), "1") == 0)
103 else if (strcmp(Cmd_Argv(1), "2") == 0)
107 for (players = 0, i = 0;i < svs.maxclients;i++)
108 if (svs.clients[i].active)
110 print ("host: %s\n", Cvar_VariableString ("hostname"));
111 print ("version: %s build %s\n", gamename, buildstring);
112 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
113 print ("map: %s\n", sv.name);
114 print ("timing: %s\n", Host_TimingReport());
115 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
118 print ("^2IP %%pl ping time frags no name\n");
120 print ("^5IP no name\n");
122 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
129 if (in == 0 || in == 1)
131 seconds = (int)(realtime - client->connecttime);
132 minutes = seconds / 60;
135 seconds -= (minutes * 60);
136 hours = minutes / 60;
138 minutes -= (hours * 60);
144 if (client->netconnection)
145 for (j = 0;j < NETGRAPH_PACKETS;j++)
146 if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
148 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
149 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
152 if(sv_status_privacy.integer && cmd_source != src_command)
153 strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
155 strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 48);
157 frags = client->frags;
159 if(sv_status_show_qcstatus.integer)
161 prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
162 const char *str = PRVM_GetString(PRVM_serveredictstring(ed, clientstatus));
168 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
169 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
173 frags = atoi(qcstatus);
177 if (in == 0) // default layout
179 if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
181 // LordHavoc: this is very touchy because we must maintain ProQuake compatible status output
182 print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
187 // LordHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
188 print ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
192 else if (in == 1) // extended layout
194 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);
196 else if (in == 2) // reduced layout
198 print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
202 if(cmd_source == src_command)
211 Sets client to godmode
214 void Host_God_f (void)
218 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
222 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
223 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
224 SV_ClientPrint("godmode OFF\n");
226 SV_ClientPrint("godmode ON\n");
229 void Host_Notarget_f (void)
233 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
237 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
238 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
239 SV_ClientPrint("notarget OFF\n");
241 SV_ClientPrint("notarget ON\n");
244 qboolean noclip_anglehack;
246 void Host_Noclip_f (void)
250 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
254 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
256 noclip_anglehack = true;
257 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
258 SV_ClientPrint("noclip ON\n");
262 noclip_anglehack = false;
263 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
264 SV_ClientPrint("noclip OFF\n");
272 Sets client to flymode
275 void Host_Fly_f (void)
279 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
283 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
285 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
286 SV_ClientPrint("flymode ON\n");
290 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
291 SV_ClientPrint("flymode OFF\n");
302 void Host_Pings_f (void); // called by Host_Ping_f
303 void Host_Ping_f (void)
307 void (*print) (const char *fmt, ...);
309 if (cmd_source == src_command)
311 // if running a client, try to send over network so the client's ping report parser will see the report
312 if (cls.state == ca_connected)
314 Cmd_ForwardToServer ();
320 print = SV_ClientPrintf;
325 print("Client ping times:\n");
326 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
330 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
333 // 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)
334 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
339 ===============================================================================
343 ===============================================================================
347 ======================
352 command from the console. Active clients are kicked off.
353 ======================
355 void Host_Map_f (void)
357 char level[MAX_QPATH];
361 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
365 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
366 if (gamemode == GAME_DELUXEQUAKE)
367 Cvar_Set("warpmark", "");
369 cls.demonum = -1; // stop demo loop in case this fails
372 Host_ShutdownServer();
374 if(svs.maxclients != svs.maxclients_next)
376 svs.maxclients = svs.maxclients_next;
378 Mem_Free(svs.clients);
379 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
383 if (key_dest == key_menu || key_dest == key_menu_grabbed)
387 svs.serverflags = 0; // haven't completed an episode yet
388 allowcheats = sv_cheats.integer != 0;
389 strlcpy(level, Cmd_Argv(1), sizeof(level));
390 SV_SpawnServer(level);
391 if (sv.active && cls.state == ca_disconnected)
392 CL_EstablishConnection("local:1", -2);
399 Goes to a new map, taking all clients along
402 void Host_Changelevel_f (void)
404 char level[MAX_QPATH];
408 Con_Print("changelevel <levelname> : continue game on a new level\n");
418 if (key_dest == key_menu || key_dest == key_menu_grabbed)
423 SV_SaveSpawnparms ();
425 allowcheats = sv_cheats.integer != 0;
426 strlcpy(level, Cmd_Argv(1), sizeof(level));
427 SV_SpawnServer(level);
428 if (sv.active && cls.state == ca_disconnected)
429 CL_EstablishConnection("local:1", -2);
436 Restarts the current server for a dead player
439 void Host_Restart_f (void)
441 char mapname[MAX_QPATH];
445 Con_Print("restart : restart current level\n");
450 Con_Print("Only the server may restart\n");
455 if (key_dest == key_menu || key_dest == key_menu_grabbed)
459 allowcheats = sv_cheats.integer != 0;
460 strlcpy(mapname, sv.name, sizeof(mapname));
461 SV_SpawnServer(mapname);
462 if (sv.active && cls.state == ca_disconnected)
463 CL_EstablishConnection("local:1", -2);
470 This command causes the client to wait for the signon messages again.
471 This is sent just before a server changes levels
474 void Host_Reconnect_f (void)
477 // if not connected, reconnect to the most recent server
480 // if we have connected to a server recently, the userinfo
481 // will still contain its IP address, so get the address...
482 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
484 CL_EstablishConnection(temp, -1);
486 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
489 // if connected, do something based on protocol
490 if (cls.protocol == PROTOCOL_QUAKEWORLD)
492 // quakeworld can just re-login
493 if (cls.qw_downloadmemory) // don't change when downloading
498 if (cls.state == ca_connected && cls.signon < SIGNONS)
500 Con_Printf("reconnecting...\n");
501 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
502 MSG_WriteString(&cls.netcon->message, "new");
507 // netquake uses reconnect on level changes (silly)
510 Con_Print("reconnect : wait for signon messages again\n");
515 Con_Print("reconnect: no signon, ignoring reconnect\n");
518 cls.signon = 0; // need new connection messages
523 =====================
526 User command to connect to server
527 =====================
529 void Host_Connect_f (void)
533 Con_Print("connect <serveraddress> [<key> <value> ...]: connect to a multiplayer game\n");
536 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
537 if(rcon_secure.integer <= 0)
538 Cvar_SetQuick(&rcon_password, "");
539 CL_EstablishConnection(Cmd_Argv(1), 2);
544 ===============================================================================
548 ===============================================================================
551 #define SAVEGAME_VERSION 5
553 void Host_Savegame_to (const char *name)
556 int i, k, l, lightstyles = 64;
557 char comment[SAVEGAME_COMMENT_LENGTH+1];
558 char line[MAX_INPUTLINE];
562 // first we have to figure out if this can be saved in 64 lightstyles
563 // (for Quake compatibility)
564 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
565 if (sv.lightstyles[i][0])
568 isserver = !strcmp(PRVM_NAME, "server");
570 Con_Printf("Saving game to %s...\n", name);
571 f = FS_OpenRealFile(name, "wb", false);
574 Con_Print("ERROR: couldn't open.\n");
578 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
580 memset(comment, 0, sizeof(comment));
582 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));
584 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
585 // convert space to _ to make stdio happy
586 // LordHavoc: convert control characters to _ as well
587 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
588 if (ISWHITESPACEORCONTROL(comment[i]))
590 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
592 FS_Printf(f, "%s\n", comment);
595 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
596 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
597 FS_Printf(f, "%d\n", current_skill);
598 FS_Printf(f, "%s\n", sv.name);
599 FS_Printf(f, "%f\n",sv.time);
603 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
604 FS_Printf(f, "(dummy)\n");
605 FS_Printf(f, "%d\n", 0);
606 FS_Printf(f, "%s\n", "(dummy)");
607 FS_Printf(f, "%f\n", realtime);
610 // write the light styles
611 for (i=0 ; i<lightstyles ; i++)
613 if (isserver && sv.lightstyles[i][0])
614 FS_Printf(f, "%s\n", sv.lightstyles[i]);
619 PRVM_ED_WriteGlobals (f);
620 for (i=0 ; i<prog->num_edicts ; i++)
622 FS_Printf(f,"// edict %d\n", i);
623 //Con_Printf("edict %d...\n", i);
624 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
629 FS_Printf(f,"// DarkPlaces extended savegame\n");
630 // darkplaces extension - extra lightstyles, support for color lightstyles
631 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
632 if (isserver && sv.lightstyles[i][0])
633 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
635 // darkplaces extension - model precaches
636 for (i=1 ; i<MAX_MODELS ; i++)
637 if (sv.model_precache[i][0])
638 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
640 // darkplaces extension - sound precaches
641 for (i=1 ; i<MAX_SOUNDS ; i++)
642 if (sv.sound_precache[i][0])
643 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
645 // darkplaces extension - save buffers
646 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); i++)
648 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
649 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
651 for(k = 0; k < stringbuffer->num_strings; k++)
653 if (!stringbuffer->strings[k])
655 // Parse the string a bit to turn special characters
656 // (like newline, specifically) into escape codes
657 s = stringbuffer->strings[k];
658 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
685 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
693 Con_Print("done.\n");
701 void Host_Savegame_f (void)
703 char name[MAX_QPATH];
704 qboolean deadflag = false;
708 Con_Print("Can't save - no server running.\n");
713 deadflag = cl.islocalgame && svs.clients[0].active && PRVM_serveredictfloat(svs.clients[0].edict, deadflag);
718 // singleplayer checks
721 Con_Print("Can't save in intermission.\n");
727 Con_Print("Can't savegame with a dead player\n");
732 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");
736 Con_Print("save <savename> : save a game\n");
740 if (strstr(Cmd_Argv(1), ".."))
742 Con_Print("Relative pathnames are not allowed.\n");
746 strlcpy (name, Cmd_Argv(1), sizeof (name));
747 FS_DefaultExtension (name, ".sav", sizeof (name));
750 Host_Savegame_to(name);
761 void Host_Loadgame_f (void)
763 char filename[MAX_QPATH];
764 char mapname[MAX_QPATH];
774 float spawn_parms[NUM_SPAWN_PARMS];
775 prvm_stringbuffer_t *stringbuffer;
780 Con_Print("load <savename> : load a game\n");
784 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
785 FS_DefaultExtension (filename, ".sav", sizeof (filename));
787 Con_Printf("Loading game from %s...\n", filename);
789 // stop playing demos
790 if (cls.demoplayback)
794 if (key_dest == key_menu || key_dest == key_menu_grabbed)
798 cls.demonum = -1; // stop demo loop in case this fails
800 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
803 Con_Print("ERROR: couldn't open.\n");
807 if(developer_entityparsing.integer)
808 Con_Printf("Host_Loadgame_f: loading version\n");
811 COM_ParseToken_Simple(&t, false, false);
812 version = atoi(com_token);
813 if (version != SAVEGAME_VERSION)
816 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
820 if(developer_entityparsing.integer)
821 Con_Printf("Host_Loadgame_f: loading description\n");
824 COM_ParseToken_Simple(&t, false, false);
826 for (i = 0;i < NUM_SPAWN_PARMS;i++)
828 COM_ParseToken_Simple(&t, false, false);
829 spawn_parms[i] = atof(com_token);
832 COM_ParseToken_Simple(&t, false, false);
833 // this silliness is so we can load 1.06 save files, which have float skill values
834 current_skill = (int)(atof(com_token) + 0.5);
835 Cvar_SetValue ("skill", (float)current_skill);
837 if(developer_entityparsing.integer)
838 Con_Printf("Host_Loadgame_f: loading mapname\n");
841 COM_ParseToken_Simple(&t, false, false);
842 strlcpy (mapname, com_token, sizeof(mapname));
844 if(developer_entityparsing.integer)
845 Con_Printf("Host_Loadgame_f: loading time\n");
848 COM_ParseToken_Simple(&t, false, false);
849 time = atof(com_token);
851 allowcheats = sv_cheats.integer != 0;
853 if(developer_entityparsing.integer)
854 Con_Printf("Host_Loadgame_f: spawning server\n");
856 SV_SpawnServer (mapname);
860 Con_Print("Couldn't load map\n");
863 sv.paused = true; // pause until all clients connect
866 if(developer_entityparsing.integer)
867 Con_Printf("Host_Loadgame_f: loading light styles\n");
869 // load the light styles
875 for (i = 0;i < MAX_LIGHTSTYLES;i++)
879 COM_ParseToken_Simple(&t, false, false);
880 // if this is a 64 lightstyle savegame produced by Quake, stop now
881 // we have to check this because darkplaces may save more than 64
882 if (com_token[0] == '{')
887 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
890 if(developer_entityparsing.integer)
891 Con_Printf("Host_Loadgame_f: skipping until globals\n");
893 // now skip everything before the first opening brace
894 // (this is for forward compatibility, so that older versions (at
895 // least ones with this fix) can load savegames with extra data before the
896 // first brace, as might be produced by a later engine version)
900 if (!COM_ParseToken_Simple(&t, false, false))
902 if (com_token[0] == '{')
909 // unlink all entities
910 World_UnlinkAll(&sv.world);
912 // load the edicts out of the savegame file
917 while (COM_ParseToken_Simple(&t, false, false))
918 if (!strcmp(com_token, "}"))
920 if (!COM_ParseToken_Simple(&start, false, false))
925 if (strcmp(com_token,"{"))
928 Host_Error ("First token isn't a brace");
933 if(developer_entityparsing.integer)
934 Con_Printf("Host_Loadgame_f: loading globals\n");
936 // parse the global vars
937 PRVM_ED_ParseGlobals (start);
939 // restore the autocvar globals
940 Cvar_UpdateAllAutoCvars();
945 if (entnum >= MAX_EDICTS)
948 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
950 while (entnum >= prog->max_edicts)
951 PRVM_MEM_IncreaseEdicts();
952 ent = PRVM_EDICT_NUM(entnum);
953 memset(ent->fields.vp, 0, prog->entityfields * 4);
954 ent->priv.server->free = false;
956 if(developer_entityparsing.integer)
957 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
959 PRVM_ED_ParseEdict (start, ent);
961 // link it into the bsp tree
962 if (!ent->priv.server->free)
970 prog->num_edicts = entnum;
973 for (i = 0;i < NUM_SPAWN_PARMS;i++)
974 svs.clients[0].spawn_parms[i] = spawn_parms[i];
976 if(developer_entityparsing.integer)
977 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
979 // read extended data if present
980 // the extended data is stored inside a /* */ comment block, which the
981 // parser intentionally skips, so we have to check for it manually here
984 while (*end == '\r' || *end == '\n')
986 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
988 if(developer_entityparsing.integer)
989 Con_Printf("Host_Loadgame_f: loading extended data\n");
991 Con_Printf("Loading extended DarkPlaces savegame\n");
993 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
994 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
995 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
996 while (COM_ParseToken_Simple(&t, false, false))
998 if (!strcmp(com_token, "sv.lightstyles"))
1000 COM_ParseToken_Simple(&t, false, false);
1001 i = atoi(com_token);
1002 COM_ParseToken_Simple(&t, false, false);
1003 if (i >= 0 && i < MAX_LIGHTSTYLES)
1004 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
1006 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
1008 else if (!strcmp(com_token, "sv.model_precache"))
1010 COM_ParseToken_Simple(&t, false, false);
1011 i = atoi(com_token);
1012 COM_ParseToken_Simple(&t, false, false);
1013 if (i >= 0 && i < MAX_MODELS)
1015 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
1016 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
1019 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
1021 else if (!strcmp(com_token, "sv.sound_precache"))
1023 COM_ParseToken_Simple(&t, false, false);
1024 i = atoi(com_token);
1025 COM_ParseToken_Simple(&t, false, false);
1026 if (i >= 0 && i < MAX_SOUNDS)
1027 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1029 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1031 else if (!strcmp(com_token, "sv.bufstr"))
1033 COM_ParseToken_Simple(&t, false, false);
1034 i = atoi(com_token);
1035 COM_ParseToken_Simple(&t, false, false);
1036 k = atoi(com_token);
1037 COM_ParseToken_Simple(&t, false, false);
1038 stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
1039 // VorteX: nasty code, cleanup required
1040 // create buffer at this index
1042 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
1044 Con_Printf("cant write string %i into buffer %i\n", k, i);
1047 // code copied from VM_bufstr_set
1049 if (stringbuffer->max_strings <= i)
1051 char **oldstrings = stringbuffer->strings;
1052 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
1053 while (stringbuffer->max_strings <= i)
1054 stringbuffer->max_strings *= 2;
1055 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
1056 if (stringbuffer->num_strings > 0)
1057 memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
1059 Mem_Free(oldstrings);
1062 stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
1063 if(stringbuffer->strings[k])
1064 Mem_Free(stringbuffer->strings[k]);
1065 stringbuffer->strings[k] = NULL;
1066 alloclen = strlen(com_token) + 1;
1067 stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
1068 memcpy(stringbuffer->strings[k], com_token, alloclen);
1071 // skip any trailing text or unrecognized commands
1072 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
1079 if(developer_entityparsing.integer)
1080 Con_Printf("Host_Loadgame_f: finished\n");
1084 // make sure we're connected to loopback
1085 if (sv.active && cls.state == ca_disconnected)
1086 CL_EstablishConnection("local:1", -2);
1089 //============================================================================
1092 ======================
1094 ======================
1096 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1097 void Host_Name_f (void)
1100 qboolean valid_colors;
1101 const char *newNameSource;
1102 char newName[sizeof(host_client->name)];
1104 if (Cmd_Argc () == 1)
1106 Con_Printf("name: %s\n", cl_name.string);
1110 if (Cmd_Argc () == 2)
1111 newNameSource = Cmd_Argv(1);
1113 newNameSource = Cmd_Args();
1115 strlcpy(newName, newNameSource, sizeof(newName));
1117 if (cmd_source == src_command)
1119 Cvar_Set ("_cl_name", newName);
1120 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1122 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1123 Con_Printf("name: %s\n", cl_name.string);
1128 if (realtime < host_client->nametime)
1130 SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
1134 host_client->nametime = realtime + max(0.0f, sv_namechangetimer.value);
1136 // point the string back at updateclient->name to keep it safe
1137 strlcpy (host_client->name, newName, sizeof (host_client->name));
1139 for (i = 0, j = 0;host_client->name[i];i++)
1140 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1141 host_client->name[j++] = host_client->name[i];
1142 host_client->name[j] = 0;
1144 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1145 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1147 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1148 host_client->name[sizeof(host_client->name) - 1] = 0;
1149 host_client->name[0] = STRING_COLOR_TAG;
1150 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1153 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1154 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1157 l = strlen(host_client->name);
1158 if(l < sizeof(host_client->name) - 1)
1160 // duplicate the color tag to escape it
1161 host_client->name[i] = STRING_COLOR_TAG;
1162 host_client->name[i+1] = 0;
1163 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1167 // remove the last character to fix the color code
1168 host_client->name[l-1] = 0;
1169 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1173 // find the last color tag offset and decide if we need to add a reset tag
1174 for (i = 0, j = -1;host_client->name[i];i++)
1176 if (host_client->name[i] == STRING_COLOR_TAG)
1178 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1181 // if this happens to be a reset tag then we don't need one
1182 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1187 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]))
1193 if (host_client->name[i+1] == STRING_COLOR_TAG)
1200 // does not end in the default color string, so add it
1201 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1202 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1204 PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(host_client->name);
1205 if (strcmp(host_client->old_name, host_client->name))
1207 if (host_client->spawned)
1208 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1209 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1210 // send notification to all clients
1211 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1212 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1213 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1214 SV_WriteNetnameIntoDemo(host_client);
1219 ======================
1221 ======================
1223 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz/Xonotic (changed by playermodel command)"};
1224 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1225 void Host_Playermodel_f (void)
1228 char newPath[sizeof(host_client->playermodel)];
1230 if (Cmd_Argc () == 1)
1232 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1236 if (Cmd_Argc () == 2)
1237 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1239 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1241 for (i = 0, j = 0;newPath[i];i++)
1242 if (newPath[i] != '\r' && newPath[i] != '\n')
1243 newPath[j++] = newPath[i];
1246 if (cmd_source == src_command)
1248 Cvar_Set ("_cl_playermodel", newPath);
1253 if (realtime < host_client->nametime)
1255 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1259 host_client->nametime = realtime + 5;
1262 // point the string back at updateclient->name to keep it safe
1263 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1264 PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(host_client->playermodel);
1265 if (strcmp(host_client->old_model, host_client->playermodel))
1267 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1268 /*// send notification to all clients
1269 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1270 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1271 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1276 ======================
1278 ======================
1280 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz/Xonotic (changed by playerskin command)"};
1281 void Host_Playerskin_f (void)
1284 char newPath[sizeof(host_client->playerskin)];
1286 if (Cmd_Argc () == 1)
1288 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1292 if (Cmd_Argc () == 2)
1293 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1295 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1297 for (i = 0, j = 0;newPath[i];i++)
1298 if (newPath[i] != '\r' && newPath[i] != '\n')
1299 newPath[j++] = newPath[i];
1302 if (cmd_source == src_command)
1304 Cvar_Set ("_cl_playerskin", newPath);
1309 if (realtime < host_client->nametime)
1311 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1315 host_client->nametime = realtime + 5;
1318 // point the string back at updateclient->name to keep it safe
1319 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1320 PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(host_client->playerskin);
1321 if (strcmp(host_client->old_skin, host_client->playerskin))
1323 //if (host_client->spawned)
1324 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1325 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1326 /*// send notification to all clients
1327 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1328 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1329 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1333 void Host_Version_f (void)
1335 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1338 void Host_Say(qboolean teamonly)
1344 // LordHavoc: long say messages
1346 qboolean fromServer = false;
1348 if (cmd_source == src_command)
1350 if (cls.state == ca_dedicated)
1357 Cmd_ForwardToServer ();
1362 if (Cmd_Argc () < 2)
1365 if (!teamplay.integer)
1375 // note this uses the chat prefix \001
1376 if (!fromServer && !teamonly)
1377 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1378 else if (!fromServer && teamonly)
1379 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1380 else if(*(sv_adminnick.string))
1381 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1383 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1384 p2 = text + strlen(text);
1385 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1387 if (p2[-1] == '\"' && quoted)
1392 strlcat(text, "\n", sizeof(text));
1394 // note: save is not a valid edict if fromServer is true
1396 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1397 if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
1398 SV_ClientPrint(text);
1401 if (cls.state == ca_dedicated)
1402 Con_Print(&text[1]);
1406 void Host_Say_f(void)
1412 void Host_Say_Team_f(void)
1418 void Host_Tell_f(void)
1420 const char *playername_start = NULL;
1421 size_t playername_length = 0;
1422 int playernumber = 0;
1425 const char *p1, *p2;
1426 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1427 qboolean fromServer = false;
1429 if (cmd_source == src_command)
1431 if (cls.state == ca_dedicated)
1435 Cmd_ForwardToServer ();
1440 if (Cmd_Argc () < 2)
1443 // note this uses the chat prefix \001
1445 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1446 else if(*(sv_adminnick.string))
1447 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1449 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1452 p2 = p1 + strlen(p1);
1453 // remove the target name
1454 while (p1 < p2 && *p1 == ' ')
1459 while (p1 < p2 && *p1 == ' ')
1461 while (p1 < p2 && isdigit(*p1))
1463 playernumber = playernumber * 10 + (*p1 - '0');
1471 playername_start = p1;
1472 while (p1 < p2 && *p1 != '"')
1474 playername_length = p1 - playername_start;
1480 playername_start = p1;
1481 while (p1 < p2 && *p1 != ' ')
1483 playername_length = p1 - playername_start;
1485 while (p1 < p2 && *p1 == ' ')
1487 if(playername_start)
1489 // set playernumber to the right client
1491 if(playername_length >= sizeof(namebuf))
1494 Con_Print("Host_Tell: too long player name/ID\n");
1496 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1499 memcpy(namebuf, playername_start, playername_length);
1500 namebuf[playername_length] = 0;
1501 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1503 if (!svs.clients[playernumber].active)
1505 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1509 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1512 Con_Print("Host_Tell: invalid player name/ID\n");
1514 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1517 // remove trailing newlines
1518 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1520 // remove quotes if present
1526 else if (fromServer)
1527 Con_Print("Host_Tell: missing end quote\n");
1529 SV_ClientPrint("Host_Tell: missing end quote\n");
1531 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1534 return; // empty say
1535 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1541 host_client = svs.clients + playernumber;
1542 SV_ClientPrint(text);
1552 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1553 void Host_Color(int changetop, int changebottom)
1555 int top, bottom, playercolor;
1557 // get top and bottom either from the provided values or the current values
1558 // (allows changing only top or bottom, or both at once)
1559 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1560 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1564 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1570 playercolor = top*16 + bottom;
1572 if (cmd_source == src_command)
1574 Cvar_SetValueQuick(&cl_color, playercolor);
1578 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1581 if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
1583 Con_DPrint("Calling SV_ChangeTeam\n");
1584 PRVM_serverglobalfloat(time) = sv.time;
1585 prog->globals.generic[OFS_PARM0] = playercolor;
1586 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1587 PRVM_ExecuteProgram(PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1591 if (host_client->edict)
1593 PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1594 PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1596 host_client->colors = playercolor;
1597 if (host_client->old_colors != host_client->colors)
1599 host_client->old_colors = host_client->colors;
1600 // send notification to all clients
1601 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1602 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1603 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1608 void Host_Color_f(void)
1612 if (Cmd_Argc() == 1)
1614 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1615 Con_Print("color <0-15> [0-15]\n");
1619 if (Cmd_Argc() == 2)
1620 top = bottom = atoi(Cmd_Argv(1));
1623 top = atoi(Cmd_Argv(1));
1624 bottom = atoi(Cmd_Argv(2));
1626 Host_Color(top, bottom);
1629 void Host_TopColor_f(void)
1631 if (Cmd_Argc() == 1)
1633 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1634 Con_Print("topcolor <0-15>\n");
1638 Host_Color(atoi(Cmd_Argv(1)), -1);
1641 void Host_BottomColor_f(void)
1643 if (Cmd_Argc() == 1)
1645 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1646 Con_Print("bottomcolor <0-15>\n");
1650 Host_Color(-1, atoi(Cmd_Argv(1)));
1653 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1654 void Host_Rate_f(void)
1658 if (Cmd_Argc() != 2)
1660 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1661 Con_Print("rate <bytespersecond>\n");
1665 rate = atoi(Cmd_Argv(1));
1667 if (cmd_source == src_command)
1669 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1673 host_client->rate = rate;
1681 void Host_Kill_f (void)
1683 if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
1685 SV_ClientPrint("Can't suicide -- already dead!\n");
1689 PRVM_serverglobalfloat(time) = sv.time;
1690 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1691 PRVM_ExecuteProgram (PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
1700 void Host_Pause_f (void)
1702 if (!pausable.integer)
1703 SV_ClientPrint("Pause not allowed.\n");
1707 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1708 // send notification to all clients
1709 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1710 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1715 ======================
1717 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1718 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1719 ======================
1721 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)"};
1722 static void Host_PModel_f (void)
1726 if (Cmd_Argc () == 1)
1728 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1731 i = atoi(Cmd_Argv(1));
1733 if (cmd_source == src_command)
1735 if (cl_pmodel.integer == i)
1737 Cvar_SetValue ("_cl_pmodel", i);
1738 if (cls.state == ca_connected)
1739 Cmd_ForwardToServer ();
1743 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1746 //===========================================================================
1754 void Host_PreSpawn_f (void)
1756 if (host_client->spawned)
1758 Con_Print("prespawn not valid -- already spawned\n");
1762 if (host_client->netconnection)
1764 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1765 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1766 MSG_WriteByte (&host_client->netconnection->message, 2);
1767 host_client->sendsignon = 0; // enable unlimited sends again
1770 // reset the name change timer because the client will send name soon
1771 host_client->nametime = 0;
1779 void Host_Spawn_f (void)
1783 int stats[MAX_CL_STATS];
1785 if (host_client->spawned)
1787 Con_Print("Spawn not valid -- already spawned\n");
1791 // reset name change timer again because they might want to change name
1792 // again in the first 5 seconds after connecting
1793 host_client->nametime = 0;
1795 // LordHavoc: moved this above the QC calls at FrikaC's request
1796 // LordHavoc: commented this out
1797 //if (host_client->netconnection)
1798 // SZ_Clear (&host_client->netconnection->message);
1800 // run the entrance script
1803 // loaded games are fully initialized already
1804 if (PRVM_serverfunction(RestoreGame))
1806 Con_DPrint("Calling RestoreGame\n");
1807 PRVM_serverglobalfloat(time) = sv.time;
1808 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1809 PRVM_ExecuteProgram(PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1814 //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);
1816 // copy spawn parms out of the client_t
1817 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1818 (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
1820 // call the spawn function
1821 host_client->clientconnectcalled = true;
1822 PRVM_serverglobalfloat(time) = sv.time;
1823 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1824 PRVM_ExecuteProgram (PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1826 if (cls.state == ca_dedicated)
1827 Con_Printf("%s connected\n", host_client->name);
1829 PRVM_ExecuteProgram (PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1832 if (!host_client->netconnection)
1835 // send time of update
1836 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1837 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1839 // send all current names, colors, and frag counts
1840 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1842 if (!client->active)
1844 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1845 MSG_WriteByte (&host_client->netconnection->message, i);
1846 MSG_WriteString (&host_client->netconnection->message, client->name);
1847 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1848 MSG_WriteByte (&host_client->netconnection->message, i);
1849 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1850 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1851 MSG_WriteByte (&host_client->netconnection->message, i);
1852 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1855 // send all current light styles
1856 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1858 if (sv.lightstyles[i][0])
1860 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1861 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1862 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1867 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1868 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1869 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1871 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1872 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1873 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1875 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1876 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1877 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1879 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1880 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1881 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
1884 // Never send a roll angle, because savegames can catch the server
1885 // in a state where it is expecting the client to correct the angle
1886 // and it won't happen if the game was just loaded, so you wind up
1887 // with a permanent head tilt
1890 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1891 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
1892 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
1893 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1897 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1898 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
1899 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
1900 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1903 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1905 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1906 MSG_WriteByte (&host_client->netconnection->message, 3);
1914 void Host_Begin_f (void)
1916 host_client->spawned = true;
1918 // LordHavoc: note: this code also exists in SV_DropClient
1922 for (i = 0;i < svs.maxclients;i++)
1923 if (svs.clients[i].active && !svs.clients[i].spawned)
1925 if (i == svs.maxclients)
1927 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1928 sv.paused = sv.loadgame = false; // we're basically done with loading now
1933 //===========================================================================
1940 Kicks a user off of the server
1943 void Host_Kick_f (void)
1946 const char *message = NULL;
1949 qboolean byNumber = false;
1957 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1959 i = (int)(atof(Cmd_Argv(2)) - 1);
1960 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1966 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1968 if (!host_client->active)
1970 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1975 if (i < svs.maxclients)
1977 if (cmd_source == src_command)
1979 if (cls.state == ca_dedicated)
1982 who = cl_name.string;
1987 // can't kick yourself!
1988 if (host_client == save)
1993 message = Cmd_Args();
1994 COM_ParseToken_Simple(&message, false, false);
1997 message++; // skip the #
1998 while (*message == ' ') // skip white space
2000 message += strlen(Cmd_Argv(2)); // skip the number
2002 while (*message && *message == ' ')
2006 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2008 SV_ClientPrintf("Kicked by %s\n", who);
2009 SV_DropClient (false); // kicked
2017 ===============================================================================
2021 ===============================================================================
2029 void Host_Give_f (void)
2036 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2041 v = atoi (Cmd_Argv(2));
2055 // MED 01/04/97 added hipnotic give stuff
2056 if (gamemode == GAME_HIPNOTIC)
2061 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2063 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2065 else if (t[0] == '9')
2066 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2067 else if (t[0] == '0')
2068 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2069 else if (t[0] >= '2')
2070 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2075 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2080 if (gamemode == GAME_ROGUE)
2081 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2083 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2086 if (gamemode == GAME_ROGUE)
2088 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2089 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2090 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2094 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2098 if (gamemode == GAME_ROGUE)
2100 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2101 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2102 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2106 if (gamemode == GAME_ROGUE)
2108 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2109 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2110 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2114 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2118 if (gamemode == GAME_ROGUE)
2120 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2121 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2122 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2126 PRVM_serveredictfloat(host_client->edict, health) = v;
2129 if (gamemode == GAME_ROGUE)
2131 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2132 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2133 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2137 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2141 if (gamemode == GAME_ROGUE)
2143 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2144 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2145 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2151 prvm_edict_t *FindViewthing (void)
2156 for (i=0 ; i<prog->num_edicts ; i++)
2158 e = PRVM_EDICT_NUM(i);
2159 if (!strcmp (PRVM_GetString(PRVM_serveredictstring(e, classname)), "viewthing"))
2162 Con_Print("No viewthing on map\n");
2171 void Host_Viewmodel_f (void)
2180 e = FindViewthing ();
2183 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2184 if (m && m->loaded && m->Draw)
2186 PRVM_serveredictfloat(e, frame) = 0;
2187 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2190 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2201 void Host_Viewframe_f (void)
2211 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;
2226 void PrintFrameName (dp_model_t *m, int frame)
2229 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2231 Con_Printf("frame %i\n", frame);
2239 void Host_Viewnext_f (void)
2248 e = FindViewthing ();
2252 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2254 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2255 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2256 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2258 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2267 void Host_Viewprev_f (void)
2276 e = FindViewthing ();
2279 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2281 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2282 if (PRVM_serveredictfloat(e, frame) < 0)
2283 PRVM_serveredictfloat(e, frame) = 0;
2285 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2291 ===============================================================================
2295 ===============================================================================
2304 void Host_Startdemos_f (void)
2308 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2314 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2317 Con_DPrintf("%i demo(s) in loop\n", c);
2319 for (i=1 ; i<c+1 ; i++)
2320 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2322 // LordHavoc: clear the remaining slots
2323 for (;i <= MAX_DEMOS;i++)
2324 cls.demos[i-1][0] = 0;
2326 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2340 Return to looping demos
2343 void Host_Demos_f (void)
2345 if (cls.state == ca_dedicated)
2347 if (cls.demonum == -1)
2357 Return to looping demos
2360 void Host_Stopdemo_f (void)
2362 if (!cls.demoplayback)
2365 Host_ShutdownServer ();
2368 void Host_SendCvar_f (void)
2372 const char *cvarname;
2377 cvarname = Cmd_Argv(1);
2378 if (cls.state == ca_connected)
2380 c = Cvar_FindVar(cvarname);
2381 // LordHavoc: if there is no such cvar or if it is private, send a
2382 // reply indicating that it has no value
2383 if(!c || (c->flags & CVAR_PRIVATE))
2384 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2386 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2389 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2393 if (cls.state != ca_dedicated)
2397 for(;i<svs.maxclients;i++)
2398 if(svs.clients[i].active && svs.clients[i].netconnection)
2400 host_client = &svs.clients[i];
2401 Host_ClientCommands("sendcvar %s\n", cvarname);
2406 static void MaxPlayers_f(void)
2410 if (Cmd_Argc() != 2)
2412 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2418 Con_Print("maxplayers can not be changed while a server is running.\n");
2419 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2422 n = atoi(Cmd_Argv(1));
2423 n = bound(1, n, MAX_SCOREBOARD);
2424 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2426 svs.maxclients_next = n;
2428 Cvar_Set ("deathmatch", "0");
2430 Cvar_Set ("deathmatch", "1");
2434 =====================
2437 ProQuake rcon support
2438 =====================
2440 void Host_PQRcon_f (void)
2445 lhnetsocket_t *mysocket;
2446 char peer_address[64];
2448 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2450 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2454 e = strchr(rcon_password.string, ' ');
2455 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2459 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2463 if (!rcon_address.string[0])
2465 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2468 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2470 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2471 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2474 SZ_Clear(&net_message);
2475 MSG_WriteLong (&net_message, 0);
2476 MSG_WriteByte (&net_message, CCREQ_RCON);
2477 SZ_Write(&net_message, (const unsigned char*)rcon_password.string, n);
2478 MSG_WriteByte (&net_message, 0); // terminate the (possibly partial) string
2479 MSG_WriteString (&net_message, Cmd_Args());
2480 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2481 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2482 SZ_Clear (&net_message);
2486 //=============================================================================
2488 // QuakeWorld commands
2491 =====================
2494 Send the rest of the command line over as
2495 an unconnected command.
2496 =====================
2498 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2503 lhnetsocket_t *mysocket;
2505 if (!rcon_password.string || !rcon_password.string[0])
2507 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2511 e = strchr(rcon_password.string, ' ');
2512 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2515 to = cls.netcon->peeraddress;
2518 if (!rcon_address.string[0])
2520 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2523 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2525 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2526 if (mysocket && Cmd_Args()[0])
2528 // simply put together the rcon packet and send it
2529 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2531 if(cls.rcon_commands[cls.rcon_ringpos][0])
2534 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2535 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]);
2536 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2539 for (i = 0;i < MAX_RCONS;i++)
2540 if(cls.rcon_commands[i][0])
2541 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2545 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2546 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2547 cls.rcon_addresses[cls.rcon_ringpos] = to;
2548 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2549 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2551 else if(rcon_secure.integer > 0)
2555 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2556 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2557 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2560 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2561 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2566 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2572 ====================
2575 user <name or userid>
2577 Dump userdata / masterdata for a user
2578 ====================
2580 void Host_User_f (void) // credit: taken from QuakeWorld
2585 if (Cmd_Argc() != 2)
2587 Con_Printf ("Usage: user <username / userid>\n");
2591 uid = atoi(Cmd_Argv(1));
2593 for (i = 0;i < cl.maxclients;i++)
2595 if (!cl.scores[i].name[0])
2597 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2599 InfoString_Print(cl.scores[i].qw_userinfo);
2603 Con_Printf ("User not in server.\n");
2607 ====================
2610 Dump userids for all current players
2611 ====================
2613 void Host_Users_f (void) // credit: taken from QuakeWorld
2619 Con_Printf ("userid frags name\n");
2620 Con_Printf ("------ ----- ----\n");
2621 for (i = 0;i < cl.maxclients;i++)
2623 if (cl.scores[i].name[0])
2625 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2630 Con_Printf ("%i total users\n", c);
2635 Host_FullServerinfo_f
2637 Sent by server when serverinfo changes
2640 // TODO: shouldn't this be a cvar instead?
2641 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2644 if (Cmd_Argc() != 2)
2646 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2650 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2651 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2652 cl.qw_teamplay = atoi(temp);
2659 Allow clients to change userinfo
2663 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2670 if (Cmd_Argc() != 2)
2672 Con_Printf ("fullinfo <complete info string>\n");
2682 while (*s && *s != '\\')
2688 Con_Printf ("MISSING VALUE\n");
2694 while (*s && *s != '\\')
2701 CL_SetInfo(key, value, false, false, false, false);
2709 Allow clients to change userinfo
2712 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2714 if (Cmd_Argc() == 1)
2716 InfoString_Print(cls.userinfo);
2719 if (Cmd_Argc() != 3)
2721 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2724 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2728 ====================
2731 packet <destination> <contents>
2733 Contents allows \n escape character
2734 ====================
2736 void Host_Packet_f (void) // credit: taken from QuakeWorld
2742 lhnetaddress_t address;
2743 lhnetsocket_t *mysocket;
2745 if (Cmd_Argc() != 3)
2747 Con_Printf ("packet <destination> <contents>\n");
2751 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2753 Con_Printf ("Bad address\n");
2759 send[0] = send[1] = send[2] = send[3] = -1;
2761 l = (int)strlen (in);
2762 for (i=0 ; i<l ; i++)
2764 if (out >= send + sizeof(send) - 1)
2766 if (in[i] == '\\' && in[i+1] == 'n')
2771 else if (in[i] == '\\' && in[i+1] == '0')
2776 else if (in[i] == '\\' && in[i+1] == 't')
2781 else if (in[i] == '\\' && in[i+1] == 'r')
2786 else if (in[i] == '\\' && in[i+1] == '"')
2795 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2797 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2799 NetConn_Write(mysocket, send, out - send, &address);
2803 ====================
2806 Send back ping and packet loss update for all current players to this player
2807 ====================
2809 void Host_Pings_f (void)
2811 int i, j, ping, packetloss, movementloss;
2814 if (!host_client->netconnection)
2817 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2819 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2820 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2822 for (i = 0;i < svs.maxclients;i++)
2826 if (svs.clients[i].netconnection)
2828 for (j = 0;j < NETGRAPH_PACKETS;j++)
2829 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2831 for (j = 0;j < NETGRAPH_PACKETS;j++)
2832 if (svs.clients[i].movement_count[j] < 0)
2835 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2836 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2837 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2838 ping = bound(0, ping, 9999);
2839 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2841 // send qw_svc_updateping and qw_svc_updatepl messages
2842 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2843 MSG_WriteShort(&host_client->netconnection->message, ping);
2844 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2845 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2849 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2851 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2853 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2854 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2857 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2858 MSG_WriteString(&host_client->netconnection->message, "\n");
2861 void Host_PingPLReport_f(void)
2866 if (l > cl.maxclients)
2868 for (i = 0;i < l;i++)
2870 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2871 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2872 if(errbyte && *errbyte == ',')
2873 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2875 cl.scores[i].qw_movementloss = 0;
2879 //=============================================================================
2886 void Host_InitCommands (void)
2888 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2890 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2891 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2892 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2893 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2894 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2895 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2896 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2897 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2898 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2899 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2900 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2901 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)");
2902 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2903 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2904 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2905 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2906 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2907 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2908 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2909 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2910 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2911 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2913 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2914 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2915 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2917 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2918 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2919 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2920 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2922 Cvar_RegisterVariable (&cl_name);
2923 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2924 Cvar_RegisterVariable (&cl_color);
2925 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2926 Cvar_RegisterVariable (&cl_rate);
2927 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2928 Cvar_RegisterVariable (&cl_pmodel);
2929 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "(Nehahra-only) change your player model choice");
2931 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2932 Cvar_RegisterVariable (&cl_playermodel);
2933 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2934 Cvar_RegisterVariable (&cl_playerskin);
2935 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2937 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2938 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2939 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)");
2940 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2942 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2944 Cvar_RegisterVariable (&rcon_password);
2945 Cvar_RegisterVariable (&rcon_address);
2946 Cvar_RegisterVariable (&rcon_secure);
2947 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2948 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");
2949 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");
2950 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)");
2951 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2952 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2953 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2954 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2955 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2956 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2957 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2958 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2960 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)");
2961 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)");
2963 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)");
2964 Cvar_RegisterVariable (&r_fixtrans_auto);
2966 Cvar_RegisterVariable (&team);
2967 Cvar_RegisterVariable (&skin);
2968 Cvar_RegisterVariable (&noaim);
2970 Cvar_RegisterVariable(&sv_cheats);
2971 Cvar_RegisterVariable(&sv_adminnick);
2972 Cvar_RegisterVariable(&sv_status_privacy);
2973 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2974 Cvar_RegisterVariable(&sv_namechangetimer);
2977 void Host_NoOperation_f(void)