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, true);
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, true);
826 for (i = 0;i < NUM_SPAWN_PARMS;i++)
828 COM_ParseToken_Simple(&t, false, false, true);
829 spawn_parms[i] = atof(com_token);
832 COM_ParseToken_Simple(&t, false, false, true);
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, true);
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, true);
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, true);
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, true))
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, true))
918 if (!strcmp(com_token, "}"))
920 if (!COM_ParseToken_Simple(&start, false, false, true))
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, true))
998 if (!strcmp(com_token, "sv.lightstyles"))
1000 COM_ParseToken_Simple(&t, false, false, true);
1001 i = atoi(com_token);
1002 COM_ParseToken_Simple(&t, false, false, true);
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, true);
1011 i = atoi(com_token);
1012 COM_ParseToken_Simple(&t, false, false, true);
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, true);
1024 i = atoi(com_token);
1025 COM_ParseToken_Simple(&t, false, false, true);
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, true);
1034 i = atoi(com_token);
1035 COM_ParseToken_Simple(&t, false, false, true);
1036 k = atoi(com_token);
1037 COM_ParseToken_Simple(&t, false, false, true);
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, true) && 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 void (*print) (const char *fmt, ...);
1703 if (cmd_source == src_command)
1705 // if running a client, try to send over network so the pause is handled by the server
1706 if (cls.state == ca_connected)
1708 Cmd_ForwardToServer ();
1714 print = SV_ClientPrintf;
1716 if (!pausable.integer)
1718 if (cmd_source == src_client)
1720 if(cls.state == ca_dedicated || host_client == &svs.clients[0]) // non-admin
1722 print("Pause not allowed.\n");
1729 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1730 // send notification to all clients
1731 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1732 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1736 ======================
1738 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1739 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1740 ======================
1742 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)"};
1743 static void Host_PModel_f (void)
1747 if (Cmd_Argc () == 1)
1749 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1752 i = atoi(Cmd_Argv(1));
1754 if (cmd_source == src_command)
1756 if (cl_pmodel.integer == i)
1758 Cvar_SetValue ("_cl_pmodel", i);
1759 if (cls.state == ca_connected)
1760 Cmd_ForwardToServer ();
1764 PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1767 //===========================================================================
1775 void Host_PreSpawn_f (void)
1777 if (host_client->spawned)
1779 Con_Print("prespawn not valid -- already spawned\n");
1783 if (host_client->netconnection)
1785 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1786 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1787 MSG_WriteByte (&host_client->netconnection->message, 2);
1788 host_client->sendsignon = 0; // enable unlimited sends again
1791 // reset the name change timer because the client will send name soon
1792 host_client->nametime = 0;
1800 void Host_Spawn_f (void)
1804 int stats[MAX_CL_STATS];
1806 if (host_client->spawned)
1808 Con_Print("Spawn not valid -- already spawned\n");
1812 // reset name change timer again because they might want to change name
1813 // again in the first 5 seconds after connecting
1814 host_client->nametime = 0;
1816 // LordHavoc: moved this above the QC calls at FrikaC's request
1817 // LordHavoc: commented this out
1818 //if (host_client->netconnection)
1819 // SZ_Clear (&host_client->netconnection->message);
1821 // run the entrance script
1824 // loaded games are fully initialized already
1825 if (PRVM_serverfunction(RestoreGame))
1827 Con_DPrint("Calling RestoreGame\n");
1828 PRVM_serverglobalfloat(time) = sv.time;
1829 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1830 PRVM_ExecuteProgram(PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1835 //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);
1837 // copy spawn parms out of the client_t
1838 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1839 (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
1841 // call the spawn function
1842 host_client->clientconnectcalled = true;
1843 PRVM_serverglobalfloat(time) = sv.time;
1844 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1845 PRVM_ExecuteProgram (PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1847 if (cls.state == ca_dedicated)
1848 Con_Printf("%s connected\n", host_client->name);
1850 PRVM_ExecuteProgram (PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1853 if (!host_client->netconnection)
1856 // send time of update
1857 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1858 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1860 // send all current names, colors, and frag counts
1861 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1863 if (!client->active)
1865 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1866 MSG_WriteByte (&host_client->netconnection->message, i);
1867 MSG_WriteString (&host_client->netconnection->message, client->name);
1868 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1869 MSG_WriteByte (&host_client->netconnection->message, i);
1870 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1871 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1872 MSG_WriteByte (&host_client->netconnection->message, i);
1873 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1876 // send all current light styles
1877 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1879 if (sv.lightstyles[i][0])
1881 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1882 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1883 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1888 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1889 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1890 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1892 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1893 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1894 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1896 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1897 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1898 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1900 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1901 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1902 MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
1905 // Never send a roll angle, because savegames can catch the server
1906 // in a state where it is expecting the client to correct the angle
1907 // and it won't happen if the game was just loaded, so you wind up
1908 // with a permanent head tilt
1911 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1912 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
1913 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
1914 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1918 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1919 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
1920 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
1921 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1924 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1926 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1927 MSG_WriteByte (&host_client->netconnection->message, 3);
1935 void Host_Begin_f (void)
1937 host_client->spawned = true;
1939 // LordHavoc: note: this code also exists in SV_DropClient
1943 for (i = 0;i < svs.maxclients;i++)
1944 if (svs.clients[i].active && !svs.clients[i].spawned)
1946 if (i == svs.maxclients)
1948 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1949 sv.paused = sv.loadgame = false; // we're basically done with loading now
1954 //===========================================================================
1961 Kicks a user off of the server
1964 void Host_Kick_f (void)
1967 const char *message = NULL;
1970 qboolean byNumber = false;
1978 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1980 i = (int)(atof(Cmd_Argv(2)) - 1);
1981 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1987 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1989 if (!host_client->active)
1991 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1996 if (i < svs.maxclients)
1998 if (cmd_source == src_command)
2000 if (cls.state == ca_dedicated)
2003 who = cl_name.string;
2008 // can't kick yourself!
2009 if (host_client == save)
2014 message = Cmd_Args();
2015 COM_ParseToken_Simple(&message, false, false, true);
2018 message++; // skip the #
2019 while (*message == ' ') // skip white space
2021 message += strlen(Cmd_Argv(2)); // skip the number
2023 while (*message && *message == ' ')
2027 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2029 SV_ClientPrintf("Kicked by %s\n", who);
2030 SV_DropClient (false); // kicked
2038 ===============================================================================
2042 ===============================================================================
2050 void Host_Give_f (void)
2057 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2062 v = atoi (Cmd_Argv(2));
2076 // MED 01/04/97 added hipnotic give stuff
2077 if (gamemode == GAME_HIPNOTIC)
2082 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2084 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2086 else if (t[0] == '9')
2087 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2088 else if (t[0] == '0')
2089 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2090 else if (t[0] >= '2')
2091 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2096 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2101 if (gamemode == GAME_ROGUE)
2102 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2104 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2107 if (gamemode == GAME_ROGUE)
2109 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2110 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2111 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2115 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2119 if (gamemode == GAME_ROGUE)
2121 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2122 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2123 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2127 if (gamemode == GAME_ROGUE)
2129 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2130 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2131 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2135 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2139 if (gamemode == GAME_ROGUE)
2141 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2142 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2143 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2147 PRVM_serveredictfloat(host_client->edict, health) = v;
2150 if (gamemode == GAME_ROGUE)
2152 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2153 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2154 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2158 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2162 if (gamemode == GAME_ROGUE)
2164 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2165 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2166 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2172 prvm_edict_t *FindViewthing (void)
2177 for (i=0 ; i<prog->num_edicts ; i++)
2179 e = PRVM_EDICT_NUM(i);
2180 if (!strcmp (PRVM_GetString(PRVM_serveredictstring(e, classname)), "viewthing"))
2183 Con_Print("No viewthing on map\n");
2192 void Host_Viewmodel_f (void)
2201 e = FindViewthing ();
2204 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2205 if (m && m->loaded && m->Draw)
2207 PRVM_serveredictfloat(e, frame) = 0;
2208 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2211 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2222 void Host_Viewframe_f (void)
2232 e = FindViewthing ();
2235 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2237 f = atoi(Cmd_Argv(1));
2238 if (f >= m->numframes)
2241 PRVM_serveredictfloat(e, frame) = f;
2247 void PrintFrameName (dp_model_t *m, int frame)
2250 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2252 Con_Printf("frame %i\n", frame);
2260 void Host_Viewnext_f (void)
2269 e = FindViewthing ();
2273 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2275 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2276 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2277 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2279 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2288 void Host_Viewprev_f (void)
2297 e = FindViewthing ();
2300 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2302 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2303 if (PRVM_serveredictfloat(e, frame) < 0)
2304 PRVM_serveredictfloat(e, frame) = 0;
2306 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2312 ===============================================================================
2316 ===============================================================================
2325 void Host_Startdemos_f (void)
2329 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2335 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2338 Con_DPrintf("%i demo(s) in loop\n", c);
2340 for (i=1 ; i<c+1 ; i++)
2341 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2343 // LordHavoc: clear the remaining slots
2344 for (;i <= MAX_DEMOS;i++)
2345 cls.demos[i-1][0] = 0;
2347 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2361 Return to looping demos
2364 void Host_Demos_f (void)
2366 if (cls.state == ca_dedicated)
2368 if (cls.demonum == -1)
2378 Return to looping demos
2381 void Host_Stopdemo_f (void)
2383 if (!cls.demoplayback)
2386 Host_ShutdownServer ();
2389 void Host_SendCvar_f (void)
2393 const char *cvarname;
2398 cvarname = Cmd_Argv(1);
2399 if (cls.state == ca_connected)
2401 c = Cvar_FindVar(cvarname);
2402 // LordHavoc: if there is no such cvar or if it is private, send a
2403 // reply indicating that it has no value
2404 if(!c || (c->flags & CVAR_PRIVATE))
2405 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2407 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2410 if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2414 if (cls.state != ca_dedicated)
2418 for(;i<svs.maxclients;i++)
2419 if(svs.clients[i].active && svs.clients[i].netconnection)
2421 host_client = &svs.clients[i];
2422 Host_ClientCommands("sendcvar %s\n", cvarname);
2427 static void MaxPlayers_f(void)
2431 if (Cmd_Argc() != 2)
2433 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2439 Con_Print("maxplayers can not be changed while a server is running.\n");
2440 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2443 n = atoi(Cmd_Argv(1));
2444 n = bound(1, n, MAX_SCOREBOARD);
2445 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2447 svs.maxclients_next = n;
2449 Cvar_Set ("deathmatch", "0");
2451 Cvar_Set ("deathmatch", "1");
2455 =====================
2458 ProQuake rcon support
2459 =====================
2461 void Host_PQRcon_f (void)
2466 lhnetsocket_t *mysocket;
2467 char peer_address[64];
2469 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2471 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2475 e = strchr(rcon_password.string, ' ');
2476 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2480 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2484 if (!rcon_address.string[0])
2486 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2489 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2491 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2492 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2495 SZ_Clear(&net_message);
2496 MSG_WriteLong (&net_message, 0);
2497 MSG_WriteByte (&net_message, CCREQ_RCON);
2498 SZ_Write(&net_message, (const unsigned char*)rcon_password.string, n);
2499 MSG_WriteByte (&net_message, 0); // terminate the (possibly partial) string
2500 MSG_WriteString (&net_message, Cmd_Args());
2501 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2502 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2503 SZ_Clear (&net_message);
2507 //=============================================================================
2509 // QuakeWorld commands
2512 =====================
2515 Send the rest of the command line over as
2516 an unconnected command.
2517 =====================
2519 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2524 lhnetsocket_t *mysocket;
2526 if (!rcon_password.string || !rcon_password.string[0])
2528 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2532 e = strchr(rcon_password.string, ' ');
2533 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2536 to = cls.netcon->peeraddress;
2539 if (!rcon_address.string[0])
2541 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2544 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2546 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2547 if (mysocket && Cmd_Args()[0])
2549 // simply put together the rcon packet and send it
2550 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2552 if(cls.rcon_commands[cls.rcon_ringpos][0])
2555 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2556 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]);
2557 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2560 for (i = 0;i < MAX_RCONS;i++)
2561 if(cls.rcon_commands[i][0])
2562 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2566 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2567 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2568 cls.rcon_addresses[cls.rcon_ringpos] = to;
2569 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2570 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2572 else if(rcon_secure.integer > 0)
2576 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2577 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2578 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2581 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2582 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2587 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2593 ====================
2596 user <name or userid>
2598 Dump userdata / masterdata for a user
2599 ====================
2601 void Host_User_f (void) // credit: taken from QuakeWorld
2606 if (Cmd_Argc() != 2)
2608 Con_Printf ("Usage: user <username / userid>\n");
2612 uid = atoi(Cmd_Argv(1));
2614 for (i = 0;i < cl.maxclients;i++)
2616 if (!cl.scores[i].name[0])
2618 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2620 InfoString_Print(cl.scores[i].qw_userinfo);
2624 Con_Printf ("User not in server.\n");
2628 ====================
2631 Dump userids for all current players
2632 ====================
2634 void Host_Users_f (void) // credit: taken from QuakeWorld
2640 Con_Printf ("userid frags name\n");
2641 Con_Printf ("------ ----- ----\n");
2642 for (i = 0;i < cl.maxclients;i++)
2644 if (cl.scores[i].name[0])
2646 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2651 Con_Printf ("%i total users\n", c);
2656 Host_FullServerinfo_f
2658 Sent by server when serverinfo changes
2661 // TODO: shouldn't this be a cvar instead?
2662 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2665 if (Cmd_Argc() != 2)
2667 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2671 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2672 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2673 cl.qw_teamplay = atoi(temp);
2680 Allow clients to change userinfo
2684 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2691 if (Cmd_Argc() != 2)
2693 Con_Printf ("fullinfo <complete info string>\n");
2703 while (*s && *s != '\\')
2709 Con_Printf ("MISSING VALUE\n");
2715 while (*s && *s != '\\')
2722 CL_SetInfo(key, value, false, false, false, false);
2730 Allow clients to change userinfo
2733 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2735 if (Cmd_Argc() == 1)
2737 InfoString_Print(cls.userinfo);
2740 if (Cmd_Argc() != 3)
2742 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2745 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2749 ====================
2752 packet <destination> <contents>
2754 Contents allows \n escape character
2755 ====================
2757 void Host_Packet_f (void) // credit: taken from QuakeWorld
2763 lhnetaddress_t address;
2764 lhnetsocket_t *mysocket;
2766 if (Cmd_Argc() != 3)
2768 Con_Printf ("packet <destination> <contents>\n");
2772 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2774 Con_Printf ("Bad address\n");
2780 send[0] = send[1] = send[2] = send[3] = -1;
2782 l = (int)strlen (in);
2783 for (i=0 ; i<l ; i++)
2785 if (out >= send + sizeof(send) - 1)
2787 if (in[i] == '\\' && in[i+1] == 'n')
2792 else if (in[i] == '\\' && in[i+1] == '0')
2797 else if (in[i] == '\\' && in[i+1] == 't')
2802 else if (in[i] == '\\' && in[i+1] == 'r')
2807 else if (in[i] == '\\' && in[i+1] == '"')
2816 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2818 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2820 NetConn_Write(mysocket, send, out - send, &address);
2824 ====================
2827 Send back ping and packet loss update for all current players to this player
2828 ====================
2830 void Host_Pings_f (void)
2832 int i, j, ping, packetloss, movementloss;
2835 if (!host_client->netconnection)
2838 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2840 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2841 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2843 for (i = 0;i < svs.maxclients;i++)
2847 if (svs.clients[i].netconnection)
2849 for (j = 0;j < NETGRAPH_PACKETS;j++)
2850 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2852 for (j = 0;j < NETGRAPH_PACKETS;j++)
2853 if (svs.clients[i].movement_count[j] < 0)
2856 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2857 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2858 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2859 ping = bound(0, ping, 9999);
2860 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2862 // send qw_svc_updateping and qw_svc_updatepl messages
2863 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2864 MSG_WriteShort(&host_client->netconnection->message, ping);
2865 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2866 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2870 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2872 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2874 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2875 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2878 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2879 MSG_WriteString(&host_client->netconnection->message, "\n");
2882 void Host_PingPLReport_f(void)
2887 if (l > cl.maxclients)
2889 for (i = 0;i < l;i++)
2891 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2892 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2893 if(errbyte && *errbyte == ',')
2894 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2896 cl.scores[i].qw_movementloss = 0;
2900 //=============================================================================
2907 void Host_InitCommands (void)
2909 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2911 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2912 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2913 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2914 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2915 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2916 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2917 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2918 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2919 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2920 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2921 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2922 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)");
2923 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2924 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2925 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2926 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2927 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2928 Cmd_AddCommand_WithClientCommand ("pause", Host_Pause_f, Host_Pause_f, "pause the game (if the server allows pausing)");
2929 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2930 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2931 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2932 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2934 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2935 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2936 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2938 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2939 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2940 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2941 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2943 Cvar_RegisterVariable (&cl_name);
2944 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2945 Cvar_RegisterVariable (&cl_color);
2946 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2947 Cvar_RegisterVariable (&cl_rate);
2948 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2949 Cvar_RegisterVariable (&cl_pmodel);
2950 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "(Nehahra-only) change your player model choice");
2952 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2953 Cvar_RegisterVariable (&cl_playermodel);
2954 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2955 Cvar_RegisterVariable (&cl_playerskin);
2956 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2958 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2959 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2960 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)");
2961 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2963 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2965 Cvar_RegisterVariable (&rcon_password);
2966 Cvar_RegisterVariable (&rcon_address);
2967 Cvar_RegisterVariable (&rcon_secure);
2968 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2969 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");
2970 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");
2971 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)");
2972 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2973 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2974 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2975 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2976 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2977 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2978 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2979 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2981 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)");
2982 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)");
2984 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)");
2985 Cvar_RegisterVariable (&r_fixtrans_auto);
2987 Cvar_RegisterVariable (&team);
2988 Cvar_RegisterVariable (&skin);
2989 Cvar_RegisterVariable (&noaim);
2991 Cvar_RegisterVariable(&sv_cheats);
2992 Cvar_RegisterVariable(&sv_adminnick);
2993 Cvar_RegisterVariable(&sv_status_privacy);
2994 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2995 Cvar_RegisterVariable(&sv_namechangetimer);
2998 void Host_NoOperation_f(void)