2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // for secure rcon authentication
33 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
34 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
35 cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
36 cvar_t sv_status_show_qcstatus = {CVAR_SAVE, "sv_status_show_qcstatus", "0", "show the 'qcstatus' field in status replies, not the 'frags' field. Turn this on if your mod uses this field, and the 'frags' field on the other hand has no meaningful value."};
37 cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands; NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
38 cvar_t rcon_secure = {CVAR_NQUSERINFOHACK, "rcon_secure", "0", "force secure rcon authentication (1 = time based, 2 = challenge based); NOTE: changing rcon_secure clears rcon_password, so set rcon_secure always before rcon_password"};
39 cvar_t rcon_secure_challengetimeout = {0, "rcon_secure_challengetimeout", "5", "challenge-based secure rcon: time out requests if no challenge came within this time interval"};
40 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
41 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
42 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
43 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
44 cvar_t r_fixtrans_auto = {0, "r_fixtrans_auto", "0", "automatically fixtrans textures (when set to 2, it also saves the fixed versions to a fixtrans directory)"};
45 qboolean allowcheats = false;
47 extern qboolean host_shuttingdown;
48 extern cvar_t developer_entityparsing;
56 void Host_Quit_f (void)
59 Con_Printf("shutting down already!\n");
69 void Host_Status_f (void)
73 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
74 void (*print) (const char *fmt, ...);
78 if (cmd_source == src_command)
80 // if running a client, try to send over network so the client's status report parser will see the report
81 if (cls.state == ca_connected)
83 Cmd_ForwardToServer ();
89 print = SV_ClientPrintf;
94 if(cmd_source == src_command)
100 if (strcmp(Cmd_Argv(1), "1") == 0)
102 else if (strcmp(Cmd_Argv(1), "2") == 0)
106 for (players = 0, i = 0;i < svs.maxclients;i++)
107 if (svs.clients[i].active)
109 print ("host: %s\n", Cvar_VariableString ("hostname"));
110 print ("version: %s build %s\n", gamename, buildstring);
111 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
112 print ("map: %s\n", sv.name);
113 print ("timing: %s\n", Host_TimingReport());
114 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
117 print ("^2IP %%pl ping time frags no name\n");
119 print ("^5IP no name\n");
121 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
128 if (in == 0 || in == 1)
130 seconds = (int)(realtime - client->connecttime);
131 minutes = seconds / 60;
134 seconds -= (minutes * 60);
135 hours = minutes / 60;
137 minutes -= (hours * 60);
143 if (client->netconnection)
144 for (j = 0;j < NETGRAPH_PACKETS;j++)
145 if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
147 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
148 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
151 if(sv_status_privacy.integer && cmd_source != src_command)
152 strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
154 strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
156 frags = client->frags;
158 if(sv_status_show_qcstatus.integer && prog->fieldoffsets.clientstatus >= 0)
160 const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
166 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
167 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
171 frags = atoi(qcstatus);
175 if (in == 0) // default layout
177 print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
180 else if (in == 1) // extended layout
182 print ("%s%-21s %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);
184 else if (in == 2) // reduced layout
186 print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
190 if(cmd_source == src_command)
199 Sets client to godmode
202 void Host_God_f (void)
206 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
210 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
211 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
212 SV_ClientPrint("godmode OFF\n");
214 SV_ClientPrint("godmode ON\n");
217 void Host_Notarget_f (void)
221 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
225 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
226 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
227 SV_ClientPrint("notarget OFF\n");
229 SV_ClientPrint("notarget ON\n");
232 qboolean noclip_anglehack;
234 void Host_Noclip_f (void)
238 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
242 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
244 noclip_anglehack = true;
245 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
246 SV_ClientPrint("noclip ON\n");
250 noclip_anglehack = false;
251 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
252 SV_ClientPrint("noclip OFF\n");
260 Sets client to flymode
263 void Host_Fly_f (void)
267 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
271 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
273 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
274 SV_ClientPrint("flymode ON\n");
278 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
279 SV_ClientPrint("flymode OFF\n");
290 void Host_Pings_f (void); // called by Host_Ping_f
291 void Host_Ping_f (void)
295 void (*print) (const char *fmt, ...);
297 if (cmd_source == src_command)
299 // if running a client, try to send over network so the client's ping report parser will see the report
300 if (cls.state == ca_connected)
302 Cmd_ForwardToServer ();
308 print = SV_ClientPrintf;
313 print("Client ping times:\n");
314 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
318 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
321 // 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)
322 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
327 ===============================================================================
331 ===============================================================================
335 ======================
340 command from the console. Active clients are kicked off.
341 ======================
343 void Host_Map_f (void)
345 char level[MAX_QPATH];
349 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
353 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
354 if (gamemode == GAME_DELUXEQUAKE)
355 Cvar_Set("warpmark", "");
357 cls.demonum = -1; // stop demo loop in case this fails
360 Host_ShutdownServer();
362 if(svs.maxclients != svs.maxclients_next)
364 svs.maxclients = svs.maxclients_next;
366 Mem_Free(svs.clients);
367 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
373 svs.serverflags = 0; // haven't completed an episode yet
374 allowcheats = sv_cheats.integer != 0;
375 strlcpy(level, Cmd_Argv(1), sizeof(level));
376 SV_SpawnServer(level);
377 if (sv.active && cls.state == ca_disconnected)
378 CL_EstablishConnection("local:1");
385 Goes to a new map, taking all clients along
388 void Host_Changelevel_f (void)
390 char level[MAX_QPATH];
394 Con_Print("changelevel <levelname> : continue game on a new level\n");
407 SV_SaveSpawnparms ();
409 allowcheats = sv_cheats.integer != 0;
410 strlcpy(level, Cmd_Argv(1), sizeof(level));
411 SV_SpawnServer(level);
412 if (sv.active && cls.state == ca_disconnected)
413 CL_EstablishConnection("local:1");
420 Restarts the current server for a dead player
423 void Host_Restart_f (void)
425 char mapname[MAX_QPATH];
429 Con_Print("restart : restart current level\n");
434 Con_Print("Only the server may restart\n");
441 allowcheats = sv_cheats.integer != 0;
442 strlcpy(mapname, sv.name, sizeof(mapname));
443 SV_SpawnServer(mapname);
444 if (sv.active && cls.state == ca_disconnected)
445 CL_EstablishConnection("local:1");
452 This command causes the client to wait for the signon messages again.
453 This is sent just before a server changes levels
456 void Host_Reconnect_f (void)
459 // if not connected, reconnect to the most recent server
462 // if we have connected to a server recently, the userinfo
463 // will still contain its IP address, so get the address...
464 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
466 CL_EstablishConnection(temp);
468 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
471 // if connected, do something based on protocol
472 if (cls.protocol == PROTOCOL_QUAKEWORLD)
474 // quakeworld can just re-login
475 if (cls.qw_downloadmemory) // don't change when downloading
480 if (cls.state == ca_connected && cls.signon < SIGNONS)
482 Con_Printf("reconnecting...\n");
483 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
484 MSG_WriteString(&cls.netcon->message, "new");
489 // netquake uses reconnect on level changes (silly)
492 Con_Print("reconnect : wait for signon messages again\n");
497 Con_Print("reconnect: no signon, ignoring reconnect\n");
500 cls.signon = 0; // need new connection messages
505 =====================
508 User command to connect to server
509 =====================
511 void Host_Connect_f (void)
515 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
518 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
519 if(rcon_secure.integer <= 0)
520 Cvar_SetQuick(&rcon_password, "");
521 CL_EstablishConnection(Cmd_Argv(1));
526 ===============================================================================
530 ===============================================================================
533 #define SAVEGAME_VERSION 5
535 void Host_Savegame_to (const char *name)
538 int i, k, l, lightstyles = 64;
539 char comment[SAVEGAME_COMMENT_LENGTH+1];
540 char line[MAX_INPUTLINE];
544 // first we have to figure out if this can be saved in 64 lightstyles
545 // (for Quake compatibility)
546 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
547 if (sv.lightstyles[i][0])
550 isserver = !strcmp(PRVM_NAME, "server");
552 Con_Printf("Saving game to %s...\n", name);
553 f = FS_OpenRealFile(name, "wb", false);
556 Con_Print("ERROR: couldn't open.\n");
560 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
562 memset(comment, 0, sizeof(comment));
564 dpsnprintf(comment, sizeof(comment), "%-21.21s kills:%3i/%3i", PRVM_GetString(prog->edicts->fields.server->message), (int)prog->globals.server->killed_monsters, (int)prog->globals.server->total_monsters);
566 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
567 // convert space to _ to make stdio happy
568 // LordHavoc: convert control characters to _ as well
569 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
570 if (ISWHITESPACEORCONTROL(comment[i]))
572 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
574 FS_Printf(f, "%s\n", comment);
577 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
578 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
579 FS_Printf(f, "%d\n", current_skill);
580 FS_Printf(f, "%s\n", sv.name);
581 FS_Printf(f, "%f\n",sv.time);
585 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
586 FS_Printf(f, "(dummy)\n");
587 FS_Printf(f, "%d\n", 0);
588 FS_Printf(f, "%s\n", "(dummy)");
589 FS_Printf(f, "%f\n", realtime);
592 // write the light styles
593 for (i=0 ; i<lightstyles ; i++)
595 if (isserver && sv.lightstyles[i][0])
596 FS_Printf(f, "%s\n", sv.lightstyles[i]);
601 PRVM_ED_WriteGlobals (f);
602 for (i=0 ; i<prog->num_edicts ; i++)
604 FS_Printf(f,"// edict %d\n", i);
605 //Con_Printf("edict %d...\n", i);
606 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
611 FS_Printf(f,"// DarkPlaces extended savegame\n");
612 // darkplaces extension - extra lightstyles, support for color lightstyles
613 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
614 if (isserver && sv.lightstyles[i][0])
615 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
617 // darkplaces extension - model precaches
618 for (i=1 ; i<MAX_MODELS ; i++)
619 if (sv.model_precache[i][0])
620 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
622 // darkplaces extension - sound precaches
623 for (i=1 ; i<MAX_SOUNDS ; i++)
624 if (sv.sound_precache[i][0])
625 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
627 // darkplaces extension - save buffers
628 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); i++)
630 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
631 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
633 for(k = 0; k < stringbuffer->num_strings; k++)
635 if (!stringbuffer->strings[k])
637 // Parse the string a bit to turn special characters
638 // (like newline, specifically) into escape codes
639 s = stringbuffer->strings[k];
640 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
667 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
675 Con_Print("done.\n");
683 void Host_Savegame_f (void)
685 char name[MAX_QPATH];
689 Con_Print("Can't save - no server running.\n");
695 // singleplayer checks
698 Con_Print("Can't save in intermission.\n");
702 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
704 Con_Print("Can't savegame with a dead player\n");
709 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");
713 Con_Print("save <savename> : save a game\n");
717 if (strstr(Cmd_Argv(1), ".."))
719 Con_Print("Relative pathnames are not allowed.\n");
723 strlcpy (name, Cmd_Argv(1), sizeof (name));
724 FS_DefaultExtension (name, ".sav", sizeof (name));
727 Host_Savegame_to(name);
738 void Host_Loadgame_f (void)
740 char filename[MAX_QPATH];
741 char mapname[MAX_QPATH];
751 float spawn_parms[NUM_SPAWN_PARMS];
752 prvm_stringbuffer_t *stringbuffer;
757 Con_Print("load <savename> : load a game\n");
761 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
762 FS_DefaultExtension (filename, ".sav", sizeof (filename));
764 Con_Printf("Loading game from %s...\n", filename);
766 // stop playing demos
767 if (cls.demoplayback)
773 cls.demonum = -1; // stop demo loop in case this fails
775 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
778 Con_Print("ERROR: couldn't open.\n");
782 if(developer_entityparsing.integer)
783 Con_Printf("Host_Loadgame_f: loading version\n");
786 COM_ParseToken_Simple(&t, false, false);
787 version = atoi(com_token);
788 if (version != SAVEGAME_VERSION)
791 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
795 if(developer_entityparsing.integer)
796 Con_Printf("Host_Loadgame_f: loading description\n");
799 COM_ParseToken_Simple(&t, false, false);
801 for (i = 0;i < NUM_SPAWN_PARMS;i++)
803 COM_ParseToken_Simple(&t, false, false);
804 spawn_parms[i] = atof(com_token);
807 COM_ParseToken_Simple(&t, false, false);
808 // this silliness is so we can load 1.06 save files, which have float skill values
809 current_skill = (int)(atof(com_token) + 0.5);
810 Cvar_SetValue ("skill", (float)current_skill);
812 if(developer_entityparsing.integer)
813 Con_Printf("Host_Loadgame_f: loading mapname\n");
816 COM_ParseToken_Simple(&t, false, false);
817 strlcpy (mapname, com_token, sizeof(mapname));
819 if(developer_entityparsing.integer)
820 Con_Printf("Host_Loadgame_f: loading time\n");
823 COM_ParseToken_Simple(&t, false, false);
824 time = atof(com_token);
826 allowcheats = sv_cheats.integer != 0;
828 if(developer_entityparsing.integer)
829 Con_Printf("Host_Loadgame_f: spawning server\n");
831 SV_SpawnServer (mapname);
835 Con_Print("Couldn't load map\n");
838 sv.paused = true; // pause until all clients connect
841 if(developer_entityparsing.integer)
842 Con_Printf("Host_Loadgame_f: loading light styles\n");
844 // load the light styles
850 for (i = 0;i < MAX_LIGHTSTYLES;i++)
854 COM_ParseToken_Simple(&t, false, false);
855 // if this is a 64 lightstyle savegame produced by Quake, stop now
856 // we have to check this because darkplaces may save more than 64
857 if (com_token[0] == '{')
862 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
865 if(developer_entityparsing.integer)
866 Con_Printf("Host_Loadgame_f: skipping until globals\n");
868 // now skip everything before the first opening brace
869 // (this is for forward compatibility, so that older versions (at
870 // least ones with this fix) can load savegames with extra data before the
871 // first brace, as might be produced by a later engine version)
875 if (!COM_ParseToken_Simple(&t, false, false))
877 if (com_token[0] == '{')
884 // unlink all entities
885 World_UnlinkAll(&sv.world);
887 // load the edicts out of the savegame file
892 while (COM_ParseToken_Simple(&t, false, false))
893 if (!strcmp(com_token, "}"))
895 if (!COM_ParseToken_Simple(&start, false, false))
900 if (strcmp(com_token,"{"))
903 Host_Error ("First token isn't a brace");
908 if(developer_entityparsing.integer)
909 Con_Printf("Host_Loadgame_f: loading globals\n");
911 // parse the global vars
912 PRVM_ED_ParseGlobals (start);
917 if (entnum >= MAX_EDICTS)
920 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
922 while (entnum >= prog->max_edicts)
923 PRVM_MEM_IncreaseEdicts();
924 ent = PRVM_EDICT_NUM(entnum);
925 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
926 ent->priv.server->free = false;
928 if(developer_entityparsing.integer)
929 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
931 PRVM_ED_ParseEdict (start, ent);
933 // link it into the bsp tree
934 if (!ent->priv.server->free)
942 prog->num_edicts = entnum;
945 for (i = 0;i < NUM_SPAWN_PARMS;i++)
946 svs.clients[0].spawn_parms[i] = spawn_parms[i];
948 if(developer_entityparsing.integer)
949 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
951 // read extended data if present
952 // the extended data is stored inside a /* */ comment block, which the
953 // parser intentionally skips, so we have to check for it manually here
956 while (*end == '\r' || *end == '\n')
958 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
960 if(developer_entityparsing.integer)
961 Con_Printf("Host_Loadgame_f: loading extended data\n");
963 Con_Printf("Loading extended DarkPlaces savegame\n");
965 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
966 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
967 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
968 while (COM_ParseToken_Simple(&t, false, false))
970 if (!strcmp(com_token, "sv.lightstyles"))
972 COM_ParseToken_Simple(&t, false, false);
974 COM_ParseToken_Simple(&t, false, false);
975 if (i >= 0 && i < MAX_LIGHTSTYLES)
976 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
978 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
980 else if (!strcmp(com_token, "sv.model_precache"))
982 COM_ParseToken_Simple(&t, false, false);
984 COM_ParseToken_Simple(&t, false, false);
985 if (i >= 0 && i < MAX_MODELS)
987 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
988 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.modelname : NULL);
991 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
993 else if (!strcmp(com_token, "sv.sound_precache"))
995 COM_ParseToken_Simple(&t, false, false);
997 COM_ParseToken_Simple(&t, false, false);
998 if (i >= 0 && i < MAX_SOUNDS)
999 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1001 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1003 else if (!strcmp(com_token, "sv.bufstr"))
1005 COM_ParseToken_Simple(&t, false, false);
1006 i = atoi(com_token);
1007 COM_ParseToken_Simple(&t, false, false);
1008 k = atoi(com_token);
1009 COM_ParseToken_Simple(&t, false, false);
1010 stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
1011 // VorteX: nasty code, cleanup required
1012 // create buffer at this index
1014 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
1016 Con_Printf("cant write string %i into buffer %i\n", k, i);
1019 // code copied from VM_bufstr_set
1021 if (stringbuffer->max_strings <= i)
1023 char **oldstrings = stringbuffer->strings;
1024 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
1025 while (stringbuffer->max_strings <= i)
1026 stringbuffer->max_strings *= 2;
1027 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
1028 if (stringbuffer->num_strings > 0)
1029 memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
1031 Mem_Free(oldstrings);
1034 stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
1035 if(stringbuffer->strings[k])
1036 Mem_Free(stringbuffer->strings[k]);
1037 stringbuffer->strings[k] = NULL;
1038 alloclen = strlen(com_token) + 1;
1039 stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
1040 memcpy(stringbuffer->strings[k], com_token, alloclen);
1043 // skip any trailing text or unrecognized commands
1044 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
1051 if(developer_entityparsing.integer)
1052 Con_Printf("Host_Loadgame_f: finished\n");
1056 // make sure we're connected to loopback
1057 if (sv.active && cls.state == ca_disconnected)
1058 CL_EstablishConnection("local:1");
1061 //============================================================================
1064 ======================
1066 ======================
1068 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1069 void Host_Name_f (void)
1072 qboolean valid_colors;
1073 const char *newNameSource;
1074 char newName[sizeof(host_client->name)];
1076 if (Cmd_Argc () == 1)
1078 Con_Printf("name: %s\n", cl_name.string);
1082 if (Cmd_Argc () == 2)
1083 newNameSource = Cmd_Argv(1);
1085 newNameSource = Cmd_Args();
1087 strlcpy(newName, newNameSource, sizeof(newName));
1089 if (cmd_source == src_command)
1091 Cvar_Set ("_cl_name", newName);
1092 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1094 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1095 Con_Printf("name: %s\n", cl_name.string);
1100 if (realtime < host_client->nametime)
1102 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1106 host_client->nametime = realtime + 5;
1108 // point the string back at updateclient->name to keep it safe
1109 strlcpy (host_client->name, newName, sizeof (host_client->name));
1111 for (i = 0, j = 0;host_client->name[i];i++)
1112 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1113 host_client->name[j++] = host_client->name[i];
1114 host_client->name[j] = 0;
1116 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1117 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1119 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1120 host_client->name[sizeof(host_client->name) - 1] = 0;
1121 host_client->name[0] = STRING_COLOR_TAG;
1122 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1125 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1126 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1129 l = strlen(host_client->name);
1130 if(l < sizeof(host_client->name) - 1)
1132 // duplicate the color tag to escape it
1133 host_client->name[i] = STRING_COLOR_TAG;
1134 host_client->name[i+1] = 0;
1135 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1139 // remove the last character to fix the color code
1140 host_client->name[l-1] = 0;
1141 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1145 // find the last color tag offset and decide if we need to add a reset tag
1146 for (i = 0, j = -1;host_client->name[i];i++)
1148 if (host_client->name[i] == STRING_COLOR_TAG)
1150 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1153 // if this happens to be a reset tag then we don't need one
1154 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1159 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]))
1165 if (host_client->name[i+1] == STRING_COLOR_TAG)
1172 // does not end in the default color string, so add it
1173 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1174 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1176 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1177 if (strcmp(host_client->old_name, host_client->name))
1179 if (host_client->spawned)
1180 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1181 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1182 // send notification to all clients
1183 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1184 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1185 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1186 SV_WriteNetnameIntoDemo(host_client);
1191 ======================
1193 ======================
1195 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1196 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1197 void Host_Playermodel_f (void)
1200 char newPath[sizeof(host_client->playermodel)];
1202 if (Cmd_Argc () == 1)
1204 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1208 if (Cmd_Argc () == 2)
1209 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1211 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1213 for (i = 0, j = 0;newPath[i];i++)
1214 if (newPath[i] != '\r' && newPath[i] != '\n')
1215 newPath[j++] = newPath[i];
1218 if (cmd_source == src_command)
1220 Cvar_Set ("_cl_playermodel", newPath);
1225 if (realtime < host_client->nametime)
1227 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1231 host_client->nametime = realtime + 5;
1234 // point the string back at updateclient->name to keep it safe
1235 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1236 if( prog->fieldoffsets.playermodel >= 0 )
1237 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1238 if (strcmp(host_client->old_model, host_client->playermodel))
1240 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1241 /*// send notification to all clients
1242 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1243 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1244 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1249 ======================
1251 ======================
1253 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1254 void Host_Playerskin_f (void)
1257 char newPath[sizeof(host_client->playerskin)];
1259 if (Cmd_Argc () == 1)
1261 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1265 if (Cmd_Argc () == 2)
1266 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1268 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1270 for (i = 0, j = 0;newPath[i];i++)
1271 if (newPath[i] != '\r' && newPath[i] != '\n')
1272 newPath[j++] = newPath[i];
1275 if (cmd_source == src_command)
1277 Cvar_Set ("_cl_playerskin", newPath);
1282 if (realtime < host_client->nametime)
1284 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1288 host_client->nametime = realtime + 5;
1291 // point the string back at updateclient->name to keep it safe
1292 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1293 if( prog->fieldoffsets.playerskin >= 0 )
1294 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1295 if (strcmp(host_client->old_skin, host_client->playerskin))
1297 //if (host_client->spawned)
1298 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1299 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1300 /*// send notification to all clients
1301 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1302 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1303 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1307 void Host_Version_f (void)
1309 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1312 void Host_Say(qboolean teamonly)
1318 // LordHavoc: long say messages
1320 qboolean fromServer = false;
1322 if (cmd_source == src_command)
1324 if (cls.state == ca_dedicated)
1331 Cmd_ForwardToServer ();
1336 if (Cmd_Argc () < 2)
1339 if (!teamplay.integer)
1349 // note this uses the chat prefix \001
1350 if (!fromServer && !teamonly)
1351 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1352 else if (!fromServer && teamonly)
1353 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1354 else if(*(sv_adminnick.string))
1355 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1357 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1358 p2 = text + strlen(text);
1359 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1361 if (p2[-1] == '\"' && quoted)
1366 strlcat(text, "\n", sizeof(text));
1368 // note: save is not a valid edict if fromServer is true
1370 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1371 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1372 SV_ClientPrint(text);
1375 if (cls.state == ca_dedicated)
1376 Con_Print(&text[1]);
1380 void Host_Say_f(void)
1386 void Host_Say_Team_f(void)
1392 void Host_Tell_f(void)
1394 const char *playername_start = NULL;
1395 size_t playername_length = 0;
1396 int playernumber = 0;
1399 const char *p1, *p2;
1400 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1401 qboolean fromServer = false;
1403 if (cmd_source == src_command)
1405 if (cls.state == ca_dedicated)
1409 Cmd_ForwardToServer ();
1414 if (Cmd_Argc () < 2)
1417 // note this uses the chat prefix \001
1419 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1420 else if(*(sv_adminnick.string))
1421 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1423 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1426 p2 = p1 + strlen(p1);
1427 // remove the target name
1428 while (p1 < p2 && *p1 == ' ')
1433 while (p1 < p2 && *p1 == ' ')
1435 while (p1 < p2 && isdigit(*p1))
1437 playernumber = playernumber * 10 + (*p1 - '0');
1445 playername_start = p1;
1446 while (p1 < p2 && *p1 != '"')
1448 playername_length = p1 - playername_start;
1454 playername_start = p1;
1455 while (p1 < p2 && *p1 != ' ')
1457 playername_length = p1 - playername_start;
1459 while (p1 < p2 && *p1 == ' ')
1461 if(playername_start)
1463 // set playernumber to the right client
1465 if(playername_length >= sizeof(namebuf))
1468 Con_Print("Host_Tell: too long player name/ID\n");
1470 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1473 memcpy(namebuf, playername_start, playername_length);
1474 namebuf[playername_length] = 0;
1475 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1477 if (!svs.clients[playernumber].active)
1479 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1483 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1486 Con_Print("Host_Tell: invalid player name/ID\n");
1488 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1491 // remove trailing newlines
1492 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1494 // remove quotes if present
1500 else if (fromServer)
1501 Con_Print("Host_Tell: missing end quote\n");
1503 SV_ClientPrint("Host_Tell: missing end quote\n");
1505 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1508 return; // empty say
1509 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1515 host_client = svs.clients + playernumber;
1516 SV_ClientPrint(text);
1526 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1527 void Host_Color(int changetop, int changebottom)
1529 int top, bottom, playercolor;
1531 // get top and bottom either from the provided values or the current values
1532 // (allows changing only top or bottom, or both at once)
1533 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1534 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1538 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1544 playercolor = top*16 + bottom;
1546 if (cmd_source == src_command)
1548 Cvar_SetValueQuick(&cl_color, playercolor);
1552 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1555 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1557 Con_DPrint("Calling SV_ChangeTeam\n");
1558 prog->globals.server->time = sv.time;
1559 prog->globals.generic[OFS_PARM0] = playercolor;
1560 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1561 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1566 if (host_client->edict)
1568 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1569 val->_float = playercolor;
1570 host_client->edict->fields.server->team = bottom + 1;
1572 host_client->colors = playercolor;
1573 if (host_client->old_colors != host_client->colors)
1575 host_client->old_colors = host_client->colors;
1576 // send notification to all clients
1577 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1578 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1579 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1584 void Host_Color_f(void)
1588 if (Cmd_Argc() == 1)
1590 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1591 Con_Print("color <0-15> [0-15]\n");
1595 if (Cmd_Argc() == 2)
1596 top = bottom = atoi(Cmd_Argv(1));
1599 top = atoi(Cmd_Argv(1));
1600 bottom = atoi(Cmd_Argv(2));
1602 Host_Color(top, bottom);
1605 void Host_TopColor_f(void)
1607 if (Cmd_Argc() == 1)
1609 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1610 Con_Print("topcolor <0-15>\n");
1614 Host_Color(atoi(Cmd_Argv(1)), -1);
1617 void Host_BottomColor_f(void)
1619 if (Cmd_Argc() == 1)
1621 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1622 Con_Print("bottomcolor <0-15>\n");
1626 Host_Color(-1, atoi(Cmd_Argv(1)));
1629 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1630 void Host_Rate_f(void)
1634 if (Cmd_Argc() != 2)
1636 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1637 Con_Print("rate <bytespersecond>\n");
1641 rate = atoi(Cmd_Argv(1));
1643 if (cmd_source == src_command)
1645 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1649 host_client->rate = rate;
1657 void Host_Kill_f (void)
1659 if (host_client->edict->fields.server->health <= 0)
1661 SV_ClientPrint("Can't suicide -- already dead!\n");
1665 prog->globals.server->time = sv.time;
1666 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1667 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1676 void Host_Pause_f (void)
1678 if (!pausable.integer)
1679 SV_ClientPrint("Pause not allowed.\n");
1683 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1684 // send notification to all clients
1685 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1686 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1691 ======================
1693 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1694 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1695 ======================
1697 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)"};
1698 static void Host_PModel_f (void)
1703 if (Cmd_Argc () == 1)
1705 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1708 i = atoi(Cmd_Argv(1));
1710 if (cmd_source == src_command)
1712 if (cl_pmodel.integer == i)
1714 Cvar_SetValue ("_cl_pmodel", i);
1715 if (cls.state == ca_connected)
1716 Cmd_ForwardToServer ();
1720 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1724 //===========================================================================
1732 void Host_PreSpawn_f (void)
1734 if (host_client->spawned)
1736 Con_Print("prespawn not valid -- already spawned\n");
1740 if (host_client->netconnection)
1742 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1743 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1744 MSG_WriteByte (&host_client->netconnection->message, 2);
1745 host_client->sendsignon = 0; // enable unlimited sends again
1748 // reset the name change timer because the client will send name soon
1749 host_client->nametime = 0;
1757 void Host_Spawn_f (void)
1761 int stats[MAX_CL_STATS];
1763 if (host_client->spawned)
1765 Con_Print("Spawn not valid -- already spawned\n");
1769 // reset name change timer again because they might want to change name
1770 // again in the first 5 seconds after connecting
1771 host_client->nametime = 0;
1773 // LordHavoc: moved this above the QC calls at FrikaC's request
1774 // LordHavoc: commented this out
1775 //if (host_client->netconnection)
1776 // SZ_Clear (&host_client->netconnection->message);
1778 // run the entrance script
1781 // loaded games are fully initialized already
1782 if (prog->funcoffsets.RestoreGame)
1784 Con_DPrint("Calling RestoreGame\n");
1785 prog->globals.server->time = sv.time;
1786 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1787 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1792 //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, host_client->edict->netname = %s, host_client->name = %s\n", PRVM_GetString(host_client->edict->fields.server->netname), PRVM_GetString(host_client->edict->fields.server->netname), host_client->name);
1794 // copy spawn parms out of the client_t
1795 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1796 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1798 // call the spawn function
1799 host_client->clientconnectcalled = true;
1800 prog->globals.server->time = sv.time;
1801 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1802 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1804 if (cls.state == ca_dedicated)
1805 Con_Printf("%s connected\n", host_client->name);
1807 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1810 if (!host_client->netconnection)
1813 // send time of update
1814 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1815 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1817 // send all current names, colors, and frag counts
1818 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1820 if (!client->active)
1822 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1823 MSG_WriteByte (&host_client->netconnection->message, i);
1824 MSG_WriteString (&host_client->netconnection->message, client->name);
1825 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1826 MSG_WriteByte (&host_client->netconnection->message, i);
1827 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1828 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1829 MSG_WriteByte (&host_client->netconnection->message, i);
1830 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1833 // send all current light styles
1834 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1836 if (sv.lightstyles[i][0])
1838 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1839 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1840 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1845 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1846 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1847 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1849 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1850 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1851 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1853 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1854 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1855 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1857 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1858 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1859 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1862 // Never send a roll angle, because savegames can catch the server
1863 // in a state where it is expecting the client to correct the angle
1864 // and it won't happen if the game was just loaded, so you wind up
1865 // with a permanent head tilt
1868 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1869 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1870 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1871 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1875 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1876 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1877 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1878 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1881 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1883 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1884 MSG_WriteByte (&host_client->netconnection->message, 3);
1892 void Host_Begin_f (void)
1894 host_client->spawned = true;
1896 // LordHavoc: note: this code also exists in SV_DropClient
1900 for (i = 0;i < svs.maxclients;i++)
1901 if (svs.clients[i].active && !svs.clients[i].spawned)
1903 if (i == svs.maxclients)
1905 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1906 sv.paused = sv.loadgame = false; // we're basically done with loading now
1911 //===========================================================================
1918 Kicks a user off of the server
1921 void Host_Kick_f (void)
1924 const char *message = NULL;
1927 qboolean byNumber = false;
1935 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1937 i = (int)(atof(Cmd_Argv(2)) - 1);
1938 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1944 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1946 if (!host_client->active)
1948 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1953 if (i < svs.maxclients)
1955 if (cmd_source == src_command)
1957 if (cls.state == ca_dedicated)
1960 who = cl_name.string;
1965 // can't kick yourself!
1966 if (host_client == save)
1971 message = Cmd_Args();
1972 COM_ParseToken_Simple(&message, false, false);
1975 message++; // skip the #
1976 while (*message == ' ') // skip white space
1978 message += strlen(Cmd_Argv(2)); // skip the number
1980 while (*message && *message == ' ')
1984 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1986 SV_ClientPrintf("Kicked by %s\n", who);
1987 SV_DropClient (false); // kicked
1995 ===============================================================================
1999 ===============================================================================
2007 void Host_Give_f (void)
2015 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2020 v = atoi (Cmd_Argv(2));
2034 // MED 01/04/97 added hipnotic give stuff
2035 if (gamemode == GAME_HIPNOTIC)
2040 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
2042 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
2044 else if (t[0] == '9')
2045 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
2046 else if (t[0] == '0')
2047 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
2048 else if (t[0] >= '2')
2049 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2054 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2059 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
2062 host_client->edict->fields.server->ammo_shells = v;
2065 if (gamemode == GAME_ROGUE)
2067 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
2070 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2071 host_client->edict->fields.server->ammo_nails = v;
2076 host_client->edict->fields.server->ammo_nails = v;
2080 if (gamemode == GAME_ROGUE)
2082 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
2086 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2087 host_client->edict->fields.server->ammo_nails = v;
2092 if (gamemode == GAME_ROGUE)
2094 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2098 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2099 host_client->edict->fields.server->ammo_rockets = v;
2104 host_client->edict->fields.server->ammo_rockets = v;
2108 if (gamemode == GAME_ROGUE)
2110 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2114 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2115 host_client->edict->fields.server->ammo_rockets = v;
2120 host_client->edict->fields.server->health = v;
2123 if (gamemode == GAME_ROGUE)
2125 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2129 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2130 host_client->edict->fields.server->ammo_cells = v;
2135 host_client->edict->fields.server->ammo_cells = v;
2139 if (gamemode == GAME_ROGUE)
2141 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2145 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2146 host_client->edict->fields.server->ammo_cells = v;
2153 prvm_edict_t *FindViewthing (void)
2158 for (i=0 ; i<prog->num_edicts ; i++)
2160 e = PRVM_EDICT_NUM(i);
2161 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2164 Con_Print("No viewthing on map\n");
2173 void Host_Viewmodel_f (void)
2182 e = FindViewthing ();
2187 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2188 if (!m || !m->loaded || !m->Draw)
2190 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2194 e->fields.server->frame = 0;
2195 cl.model_precache[(int)e->fields.server->modelindex] = m;
2203 void Host_Viewframe_f (void)
2213 e = FindViewthing ();
2217 m = cl.model_precache[(int)e->fields.server->modelindex];
2219 f = atoi(Cmd_Argv(1));
2220 if (f >= m->numframes)
2223 e->fields.server->frame = f;
2227 void PrintFrameName (dp_model_t *m, int frame)
2230 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2232 Con_Printf("frame %i\n", frame);
2240 void Host_Viewnext_f (void)
2249 e = FindViewthing ();
2253 m = cl.model_precache[(int)e->fields.server->modelindex];
2255 e->fields.server->frame = e->fields.server->frame + 1;
2256 if (e->fields.server->frame >= m->numframes)
2257 e->fields.server->frame = m->numframes - 1;
2259 PrintFrameName (m, (int)e->fields.server->frame);
2267 void Host_Viewprev_f (void)
2276 e = FindViewthing ();
2281 m = cl.model_precache[(int)e->fields.server->modelindex];
2283 e->fields.server->frame = e->fields.server->frame - 1;
2284 if (e->fields.server->frame < 0)
2285 e->fields.server->frame = 0;
2287 PrintFrameName (m, (int)e->fields.server->frame);
2291 ===============================================================================
2295 ===============================================================================
2304 void Host_Startdemos_f (void)
2308 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2314 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2317 Con_DPrintf("%i demo(s) in loop\n", c);
2319 for (i=1 ; i<c+1 ; i++)
2320 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2322 // LordHavoc: clear the remaining slots
2323 for (;i <= MAX_DEMOS;i++)
2324 cls.demos[i-1][0] = 0;
2326 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2340 Return to looping demos
2343 void Host_Demos_f (void)
2345 if (cls.state == ca_dedicated)
2347 if (cls.demonum == -1)
2357 Return to looping demos
2360 void Host_Stopdemo_f (void)
2362 if (!cls.demoplayback)
2365 Host_ShutdownServer ();
2368 void Host_SendCvar_f (void)
2372 const char *cvarname;
2377 cvarname = Cmd_Argv(1);
2378 if (cls.state == ca_connected)
2380 c = Cvar_FindVar(cvarname);
2381 // LordHavoc: if there is no such cvar or if it is private, send a
2382 // reply indicating that it has no value
2383 if(!c || (c->flags & CVAR_PRIVATE))
2384 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2386 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2389 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2393 if (cls.state != ca_dedicated)
2397 for(;i<svs.maxclients;i++)
2398 if(svs.clients[i].active && svs.clients[i].netconnection)
2400 host_client = &svs.clients[i];
2401 Host_ClientCommands("sendcvar %s\n", cvarname);
2406 static void MaxPlayers_f(void)
2410 if (Cmd_Argc() != 2)
2412 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2418 Con_Print("maxplayers can not be changed while a server is running.\n");
2419 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2422 n = atoi(Cmd_Argv(1));
2423 n = bound(1, n, MAX_SCOREBOARD);
2424 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2426 svs.maxclients_next = n;
2428 Cvar_Set ("deathmatch", "0");
2430 Cvar_Set ("deathmatch", "1");
2434 =====================
2437 ProQuake rcon support
2438 =====================
2440 void Host_PQRcon_f (void)
2445 lhnetsocket_t *mysocket;
2446 char peer_address[64];
2448 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2450 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2454 e = strchr(rcon_password.string, ' ');
2455 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2459 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2463 if (!rcon_address.string[0])
2465 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2468 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2470 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2471 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2474 SZ_Clear(&net_message);
2475 MSG_WriteLong (&net_message, 0);
2476 MSG_WriteByte (&net_message, CCREQ_RCON);
2477 SZ_Write(&net_message, (void*)rcon_password.string, n);
2478 MSG_WriteString (&net_message, Cmd_Args());
2479 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2480 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2481 SZ_Clear (&net_message);
2485 //=============================================================================
2487 // QuakeWorld commands
2490 =====================
2493 Send the rest of the command line over as
2494 an unconnected command.
2495 =====================
2497 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2502 lhnetsocket_t *mysocket;
2504 if (!rcon_password.string || !rcon_password.string[0])
2506 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2510 e = strchr(rcon_password.string, ' ');
2511 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2514 to = cls.netcon->peeraddress;
2517 if (!rcon_address.string[0])
2519 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2522 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2524 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2525 if (mysocket && Cmd_Args()[0])
2527 // simply put together the rcon packet and send it
2528 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2530 if(cls.rcon_commands[cls.rcon_ringpos][0])
2533 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2534 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]);
2535 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2538 for (i = 0;i < MAX_RCONS;i++)
2539 if(cls.rcon_commands[i][0])
2540 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2544 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2545 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2546 cls.rcon_addresses[cls.rcon_ringpos] = to;
2547 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2548 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2550 else if(rcon_secure.integer > 0)
2554 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2555 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2556 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2559 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2560 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2565 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2571 ====================
2574 user <name or userid>
2576 Dump userdata / masterdata for a user
2577 ====================
2579 void Host_User_f (void) // credit: taken from QuakeWorld
2584 if (Cmd_Argc() != 2)
2586 Con_Printf ("Usage: user <username / userid>\n");
2590 uid = atoi(Cmd_Argv(1));
2592 for (i = 0;i < cl.maxclients;i++)
2594 if (!cl.scores[i].name[0])
2596 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2598 InfoString_Print(cl.scores[i].qw_userinfo);
2602 Con_Printf ("User not in server.\n");
2606 ====================
2609 Dump userids for all current players
2610 ====================
2612 void Host_Users_f (void) // credit: taken from QuakeWorld
2618 Con_Printf ("userid frags name\n");
2619 Con_Printf ("------ ----- ----\n");
2620 for (i = 0;i < cl.maxclients;i++)
2622 if (cl.scores[i].name[0])
2624 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2629 Con_Printf ("%i total users\n", c);
2634 Host_FullServerinfo_f
2636 Sent by server when serverinfo changes
2639 // TODO: shouldn't this be a cvar instead?
2640 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2643 if (Cmd_Argc() != 2)
2645 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2649 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2650 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2651 cl.qw_teamplay = atoi(temp);
2658 Allow clients to change userinfo
2662 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2669 if (Cmd_Argc() != 2)
2671 Con_Printf ("fullinfo <complete info string>\n");
2681 while (*s && *s != '\\')
2687 Con_Printf ("MISSING VALUE\n");
2693 while (*s && *s != '\\')
2700 CL_SetInfo(key, value, false, false, false, false);
2708 Allow clients to change userinfo
2711 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2713 if (Cmd_Argc() == 1)
2715 InfoString_Print(cls.userinfo);
2718 if (Cmd_Argc() != 3)
2720 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2723 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2727 ====================
2730 packet <destination> <contents>
2732 Contents allows \n escape character
2733 ====================
2735 void Host_Packet_f (void) // credit: taken from QuakeWorld
2741 lhnetaddress_t address;
2742 lhnetsocket_t *mysocket;
2744 if (Cmd_Argc() != 3)
2746 Con_Printf ("packet <destination> <contents>\n");
2750 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2752 Con_Printf ("Bad address\n");
2758 send[0] = send[1] = send[2] = send[3] = -1;
2760 l = (int)strlen (in);
2761 for (i=0 ; i<l ; i++)
2763 if (out >= send + sizeof(send) - 1)
2765 if (in[i] == '\\' && in[i+1] == 'n')
2770 else if (in[i] == '\\' && in[i+1] == '0')
2775 else if (in[i] == '\\' && in[i+1] == 't')
2780 else if (in[i] == '\\' && in[i+1] == 'r')
2785 else if (in[i] == '\\' && in[i+1] == '"')
2794 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2796 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2798 NetConn_Write(mysocket, send, out - send, &address);
2802 ====================
2805 Send back ping and packet loss update for all current players to this player
2806 ====================
2808 void Host_Pings_f (void)
2810 int i, j, ping, packetloss, movementloss;
2813 if (!host_client->netconnection)
2816 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2818 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2819 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2821 for (i = 0;i < svs.maxclients;i++)
2825 if (svs.clients[i].netconnection)
2827 for (j = 0;j < NETGRAPH_PACKETS;j++)
2828 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2830 for (j = 0;j < NETGRAPH_PACKETS;j++)
2831 if (svs.clients[i].movement_count[j] < 0)
2834 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2835 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2836 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2837 ping = bound(0, ping, 9999);
2838 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2840 // send qw_svc_updateping and qw_svc_updatepl messages
2841 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2842 MSG_WriteShort(&host_client->netconnection->message, ping);
2843 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2844 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2848 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2850 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2852 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2853 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2856 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2857 MSG_WriteString(&host_client->netconnection->message, "\n");
2860 void Host_PingPLReport_f(void)
2865 if (l > cl.maxclients)
2867 for (i = 0;i < l;i++)
2869 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2870 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2871 if(errbyte && *errbyte == ',')
2872 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2874 cl.scores[i].qw_movementloss = 0;
2878 //=============================================================================
2885 void Host_InitCommands (void)
2887 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2889 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2890 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2891 if (gamemode == GAME_NEHAHRA)
2893 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2894 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2895 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2896 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2897 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2901 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2902 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2903 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2904 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2905 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2907 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2908 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2909 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2910 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2911 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)");
2912 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2913 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2914 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2915 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2916 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2917 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2918 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2919 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2920 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2921 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2923 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2924 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2925 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2927 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2928 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2929 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2930 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2932 Cvar_RegisterVariable (&cl_name);
2933 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2934 Cvar_RegisterVariable (&cl_color);
2935 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2936 Cvar_RegisterVariable (&cl_rate);
2937 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2938 if (gamemode == GAME_NEHAHRA)
2940 Cvar_RegisterVariable (&cl_pmodel);
2941 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2944 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2945 Cvar_RegisterVariable (&cl_playermodel);
2946 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2947 Cvar_RegisterVariable (&cl_playerskin);
2948 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2950 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2951 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2952 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)");
2953 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2955 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2957 Cvar_RegisterVariable (&rcon_password);
2958 Cvar_RegisterVariable (&rcon_address);
2959 Cvar_RegisterVariable (&rcon_secure);
2960 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2961 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");
2962 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");
2963 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)");
2964 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2965 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2966 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2967 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2968 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2969 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2970 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2971 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2973 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)");
2974 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)");
2976 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)");
2977 Cvar_RegisterVariable (&r_fixtrans_auto);
2979 Cvar_RegisterVariable (&team);
2980 Cvar_RegisterVariable (&skin);
2981 Cvar_RegisterVariable (&noaim);
2983 Cvar_RegisterVariable(&sv_cheats);
2984 Cvar_RegisterVariable(&sv_adminnick);
2985 Cvar_RegisterVariable(&sv_status_privacy);
2986 Cvar_RegisterVariable(&sv_status_show_qcstatus);
2989 void Host_NoOperation_f(void)