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 ();
2185 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2186 if (!m || !m->loaded || !m->Draw)
2188 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2192 PRVM_serveredictfloat(e, frame) = 0;
2193 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2201 void Host_Viewframe_f (void)
2211 e = FindViewthing ();
2215 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2217 f = atoi(Cmd_Argv(1));
2218 if (f >= m->numframes)
2221 PRVM_serveredictfloat(e, frame) = f;
2225 void PrintFrameName (dp_model_t *m, int frame)
2228 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2230 Con_Printf("frame %i\n", frame);
2238 void Host_Viewnext_f (void)
2247 e = FindViewthing ();
2251 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2253 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2254 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2255 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2257 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2265 void Host_Viewprev_f (void)
2274 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));
2289 ===============================================================================
2293 ===============================================================================
2302 void Host_Startdemos_f (void)
2306 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2312 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2315 Con_DPrintf("%i demo(s) in loop\n", c);
2317 for (i=1 ; i<c+1 ; i++)
2318 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2320 // LordHavoc: clear the remaining slots
2321 for (;i <= MAX_DEMOS;i++)
2322 cls.demos[i-1][0] = 0;
2324 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2338 Return to looping demos
2341 void Host_Demos_f (void)
2343 if (cls.state == ca_dedicated)
2345 if (cls.demonum == -1)
2355 Return to looping demos
2358 void Host_Stopdemo_f (void)
2360 if (!cls.demoplayback)
2363 Host_ShutdownServer ();
2366 void Host_SendCvar_f (void)
2370 const char *cvarname;
2375 cvarname = Cmd_Argv(1);
2376 if (cls.state == ca_connected)
2378 c = Cvar_FindVar(cvarname);
2379 // LordHavoc: if there is no such cvar or if it is private, send a
2380 // reply indicating that it has no value
2381 if(!c || (c->flags & CVAR_PRIVATE))
2382 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2384 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2387 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2391 if (cls.state != ca_dedicated)
2395 for(;i<svs.maxclients;i++)
2396 if(svs.clients[i].active && svs.clients[i].netconnection)
2398 host_client = &svs.clients[i];
2399 Host_ClientCommands("sendcvar %s\n", cvarname);
2404 static void MaxPlayers_f(void)
2408 if (Cmd_Argc() != 2)
2410 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2416 Con_Print("maxplayers can not be changed while a server is running.\n");
2417 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2420 n = atoi(Cmd_Argv(1));
2421 n = bound(1, n, MAX_SCOREBOARD);
2422 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2424 svs.maxclients_next = n;
2426 Cvar_Set ("deathmatch", "0");
2428 Cvar_Set ("deathmatch", "1");
2432 =====================
2435 ProQuake rcon support
2436 =====================
2438 void Host_PQRcon_f (void)
2443 lhnetsocket_t *mysocket;
2444 char peer_address[64];
2446 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2448 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2452 e = strchr(rcon_password.string, ' ');
2453 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2457 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2461 if (!rcon_address.string[0])
2463 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2466 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2468 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2469 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2472 SZ_Clear(&net_message);
2473 MSG_WriteLong (&net_message, 0);
2474 MSG_WriteByte (&net_message, CCREQ_RCON);
2475 SZ_Write(&net_message, (const unsigned char*)rcon_password.string, n);
2476 MSG_WriteByte (&net_message, 0); // terminate the (possibly partial) string
2477 MSG_WriteString (&net_message, Cmd_Args());
2478 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2479 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2480 SZ_Clear (&net_message);
2484 //=============================================================================
2486 // QuakeWorld commands
2489 =====================
2492 Send the rest of the command line over as
2493 an unconnected command.
2494 =====================
2496 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2501 lhnetsocket_t *mysocket;
2503 if (!rcon_password.string || !rcon_password.string[0])
2505 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2509 e = strchr(rcon_password.string, ' ');
2510 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2513 to = cls.netcon->peeraddress;
2516 if (!rcon_address.string[0])
2518 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2521 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2523 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2524 if (mysocket && Cmd_Args()[0])
2526 // simply put together the rcon packet and send it
2527 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2529 if(cls.rcon_commands[cls.rcon_ringpos][0])
2532 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2533 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]);
2534 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2537 for (i = 0;i < MAX_RCONS;i++)
2538 if(cls.rcon_commands[i][0])
2539 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2543 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2544 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2545 cls.rcon_addresses[cls.rcon_ringpos] = to;
2546 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2547 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2549 else if(rcon_secure.integer > 0)
2553 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2554 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2555 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2558 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2559 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2564 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2570 ====================
2573 user <name or userid>
2575 Dump userdata / masterdata for a user
2576 ====================
2578 void Host_User_f (void) // credit: taken from QuakeWorld
2583 if (Cmd_Argc() != 2)
2585 Con_Printf ("Usage: user <username / userid>\n");
2589 uid = atoi(Cmd_Argv(1));
2591 for (i = 0;i < cl.maxclients;i++)
2593 if (!cl.scores[i].name[0])
2595 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2597 InfoString_Print(cl.scores[i].qw_userinfo);
2601 Con_Printf ("User not in server.\n");
2605 ====================
2608 Dump userids for all current players
2609 ====================
2611 void Host_Users_f (void) // credit: taken from QuakeWorld
2617 Con_Printf ("userid frags name\n");
2618 Con_Printf ("------ ----- ----\n");
2619 for (i = 0;i < cl.maxclients;i++)
2621 if (cl.scores[i].name[0])
2623 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2628 Con_Printf ("%i total users\n", c);
2633 Host_FullServerinfo_f
2635 Sent by server when serverinfo changes
2638 // TODO: shouldn't this be a cvar instead?
2639 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2642 if (Cmd_Argc() != 2)
2644 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2648 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2649 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2650 cl.qw_teamplay = atoi(temp);
2657 Allow clients to change userinfo
2661 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2668 if (Cmd_Argc() != 2)
2670 Con_Printf ("fullinfo <complete info string>\n");
2680 while (*s && *s != '\\')
2686 Con_Printf ("MISSING VALUE\n");
2692 while (*s && *s != '\\')
2699 CL_SetInfo(key, value, false, false, false, false);
2707 Allow clients to change userinfo
2710 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2712 if (Cmd_Argc() == 1)
2714 InfoString_Print(cls.userinfo);
2717 if (Cmd_Argc() != 3)
2719 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2722 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2726 ====================
2729 packet <destination> <contents>
2731 Contents allows \n escape character
2732 ====================
2734 void Host_Packet_f (void) // credit: taken from QuakeWorld
2740 lhnetaddress_t address;
2741 lhnetsocket_t *mysocket;
2743 if (Cmd_Argc() != 3)
2745 Con_Printf ("packet <destination> <contents>\n");
2749 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2751 Con_Printf ("Bad address\n");
2757 send[0] = send[1] = send[2] = send[3] = -1;
2759 l = (int)strlen (in);
2760 for (i=0 ; i<l ; i++)
2762 if (out >= send + sizeof(send) - 1)
2764 if (in[i] == '\\' && in[i+1] == 'n')
2769 else if (in[i] == '\\' && in[i+1] == '0')
2774 else if (in[i] == '\\' && in[i+1] == 't')
2779 else if (in[i] == '\\' && in[i+1] == 'r')
2784 else if (in[i] == '\\' && in[i+1] == '"')
2793 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2795 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2797 NetConn_Write(mysocket, send, out - send, &address);
2801 ====================
2804 Send back ping and packet loss update for all current players to this player
2805 ====================
2807 void Host_Pings_f (void)
2809 int i, j, ping, packetloss, movementloss;
2812 if (!host_client->netconnection)
2815 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2817 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2818 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2820 for (i = 0;i < svs.maxclients;i++)
2824 if (svs.clients[i].netconnection)
2826 for (j = 0;j < NETGRAPH_PACKETS;j++)
2827 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2829 for (j = 0;j < NETGRAPH_PACKETS;j++)
2830 if (svs.clients[i].movement_count[j] < 0)
2833 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2834 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2835 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2836 ping = bound(0, ping, 9999);
2837 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2839 // send qw_svc_updateping and qw_svc_updatepl messages
2840 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2841 MSG_WriteShort(&host_client->netconnection->message, ping);
2842 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2843 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2847 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2849 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2851 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2852 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2855 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2856 MSG_WriteString(&host_client->netconnection->message, "\n");
2859 void Host_PingPLReport_f(void)
2864 if (l > cl.maxclients)
2866 for (i = 0;i < l;i++)
2868 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2869 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2870 if(errbyte && *errbyte == ',')
2871 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2873 cl.scores[i].qw_movementloss = 0;
2877 //=============================================================================
2884 void Host_InitCommands (void)
2886 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2888 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2889 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2890 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2891 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2892 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2893 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2894 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2895 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2896 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2897 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2898 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2899 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)");
2900 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2901 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2902 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2903 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2904 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2905 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2906 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2907 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2908 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2909 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2911 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2912 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2913 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2915 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2916 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2917 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2918 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2920 Cvar_RegisterVariable (&cl_name);
2921 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2922 Cvar_RegisterVariable (&cl_color);
2923 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2924 Cvar_RegisterVariable (&cl_rate);
2925 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2926 Cvar_RegisterVariable (&cl_pmodel);
2927 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "(Nehahra-only) change your player model choice");
2929 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2930 Cvar_RegisterVariable (&cl_playermodel);
2931 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2932 Cvar_RegisterVariable (&cl_playerskin);
2933 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2935 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2936 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2937 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)");
2938 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2940 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2942 Cvar_RegisterVariable (&rcon_password);
2943 Cvar_RegisterVariable (&rcon_address);
2944 Cvar_RegisterVariable (&rcon_secure);
2945 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2946 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");
2947 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");
2948 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)");
2949 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2950 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2951 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2952 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2953 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2954 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2955 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2956 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2958 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)");
2959 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)");
2961 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)");
2962 Cvar_RegisterVariable (&r_fixtrans_auto);
2964 Cvar_RegisterVariable (&team);
2965 Cvar_RegisterVariable (&skin);
2966 Cvar_RegisterVariable (&noaim);
2968 Cvar_RegisterVariable(&sv_cheats);
2969 Cvar_RegisterVariable(&sv_adminnick);
2970 Cvar_RegisterVariable(&sv_status_privacy);
2971 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2972 Cvar_RegisterVariable(&sv_namechangetimer);
2975 void Host_NoOperation_f(void)