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 // LordHavoc: we must use multiple prints for ProQuake compatibility
178 print ("#%-3u ", i+1);
179 print ("%-16.16s ", client->name);
180 print ("%4i ", frags);
181 print ("%2i:%02i:%02i\n ", hours, minutes, seconds);
183 // print ("#%-3u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
184 // print (" %s\n", ip);
186 else if (in == 1) // extended layout
188 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);
190 else if (in == 2) // reduced layout
192 print ("%s%-21s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
196 if(cmd_source == src_command)
205 Sets client to godmode
208 void Host_God_f (void)
212 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
216 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
217 if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
218 SV_ClientPrint("godmode OFF\n");
220 SV_ClientPrint("godmode ON\n");
223 void Host_Notarget_f (void)
227 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
231 host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
232 if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
233 SV_ClientPrint("notarget OFF\n");
235 SV_ClientPrint("notarget ON\n");
238 qboolean noclip_anglehack;
240 void Host_Noclip_f (void)
244 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
248 if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
250 noclip_anglehack = true;
251 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
252 SV_ClientPrint("noclip ON\n");
256 noclip_anglehack = false;
257 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
258 SV_ClientPrint("noclip OFF\n");
266 Sets client to flymode
269 void Host_Fly_f (void)
273 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
277 if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
279 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
280 SV_ClientPrint("flymode ON\n");
284 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
285 SV_ClientPrint("flymode OFF\n");
296 void Host_Pings_f (void); // called by Host_Ping_f
297 void Host_Ping_f (void)
301 void (*print) (const char *fmt, ...);
303 if (cmd_source == src_command)
305 // if running a client, try to send over network so the client's ping report parser will see the report
306 if (cls.state == ca_connected)
308 Cmd_ForwardToServer ();
314 print = SV_ClientPrintf;
319 print("Client ping times:\n");
320 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
324 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
327 // 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)
328 // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
333 ===============================================================================
337 ===============================================================================
341 ======================
346 command from the console. Active clients are kicked off.
347 ======================
349 void Host_Map_f (void)
351 char level[MAX_QPATH];
355 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
359 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
360 if (gamemode == GAME_DELUXEQUAKE)
361 Cvar_Set("warpmark", "");
363 cls.demonum = -1; // stop demo loop in case this fails
366 Host_ShutdownServer();
368 if(svs.maxclients != svs.maxclients_next)
370 svs.maxclients = svs.maxclients_next;
372 Mem_Free(svs.clients);
373 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
377 if (key_dest == key_menu || key_dest == key_menu_grabbed)
381 svs.serverflags = 0; // haven't completed an episode yet
382 allowcheats = sv_cheats.integer != 0;
383 strlcpy(level, Cmd_Argv(1), sizeof(level));
384 SV_SpawnServer(level);
385 if (sv.active && cls.state == ca_disconnected)
386 CL_EstablishConnection("local:1");
393 Goes to a new map, taking all clients along
396 void Host_Changelevel_f (void)
398 char level[MAX_QPATH];
402 Con_Print("changelevel <levelname> : continue game on a new level\n");
412 if (key_dest == key_menu || key_dest == key_menu_grabbed)
417 SV_SaveSpawnparms ();
419 allowcheats = sv_cheats.integer != 0;
420 strlcpy(level, Cmd_Argv(1), sizeof(level));
421 SV_SpawnServer(level);
422 if (sv.active && cls.state == ca_disconnected)
423 CL_EstablishConnection("local:1");
430 Restarts the current server for a dead player
433 void Host_Restart_f (void)
435 char mapname[MAX_QPATH];
439 Con_Print("restart : restart current level\n");
444 Con_Print("Only the server may restart\n");
449 if (key_dest == key_menu || key_dest == key_menu_grabbed)
453 allowcheats = sv_cheats.integer != 0;
454 strlcpy(mapname, sv.name, sizeof(mapname));
455 SV_SpawnServer(mapname);
456 if (sv.active && cls.state == ca_disconnected)
457 CL_EstablishConnection("local:1");
464 This command causes the client to wait for the signon messages again.
465 This is sent just before a server changes levels
468 void Host_Reconnect_f (void)
471 // if not connected, reconnect to the most recent server
474 // if we have connected to a server recently, the userinfo
475 // will still contain its IP address, so get the address...
476 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
478 CL_EstablishConnection(temp);
480 Con_Printf("Reconnect to what server? (you have not connected to a server yet)\n");
483 // if connected, do something based on protocol
484 if (cls.protocol == PROTOCOL_QUAKEWORLD)
486 // quakeworld can just re-login
487 if (cls.qw_downloadmemory) // don't change when downloading
492 if (cls.state == ca_connected && cls.signon < SIGNONS)
494 Con_Printf("reconnecting...\n");
495 MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
496 MSG_WriteString(&cls.netcon->message, "new");
501 // netquake uses reconnect on level changes (silly)
504 Con_Print("reconnect : wait for signon messages again\n");
509 Con_Print("reconnect: no signon, ignoring reconnect\n");
512 cls.signon = 0; // need new connection messages
517 =====================
520 User command to connect to server
521 =====================
523 void Host_Connect_f (void)
527 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
530 // clear the rcon password, to prevent vulnerability by stuffcmd-ing a connect command
531 if(rcon_secure.integer <= 0)
532 Cvar_SetQuick(&rcon_password, "");
533 CL_EstablishConnection(Cmd_Argv(1));
538 ===============================================================================
542 ===============================================================================
545 #define SAVEGAME_VERSION 5
547 void Host_Savegame_to (const char *name)
550 int i, k, l, lightstyles = 64;
551 char comment[SAVEGAME_COMMENT_LENGTH+1];
552 char line[MAX_INPUTLINE];
556 // first we have to figure out if this can be saved in 64 lightstyles
557 // (for Quake compatibility)
558 for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
559 if (sv.lightstyles[i][0])
562 isserver = !strcmp(PRVM_NAME, "server");
564 Con_Printf("Saving game to %s...\n", name);
565 f = FS_OpenRealFile(name, "wb", false);
568 Con_Print("ERROR: couldn't open.\n");
572 FS_Printf(f, "%i\n", SAVEGAME_VERSION);
574 memset(comment, 0, sizeof(comment));
576 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);
578 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
579 // convert space to _ to make stdio happy
580 // LordHavoc: convert control characters to _ as well
581 for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
582 if (ISWHITESPACEORCONTROL(comment[i]))
584 comment[SAVEGAME_COMMENT_LENGTH] = '\0';
586 FS_Printf(f, "%s\n", comment);
589 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
590 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
591 FS_Printf(f, "%d\n", current_skill);
592 FS_Printf(f, "%s\n", sv.name);
593 FS_Printf(f, "%f\n",sv.time);
597 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
598 FS_Printf(f, "(dummy)\n");
599 FS_Printf(f, "%d\n", 0);
600 FS_Printf(f, "%s\n", "(dummy)");
601 FS_Printf(f, "%f\n", realtime);
604 // write the light styles
605 for (i=0 ; i<lightstyles ; i++)
607 if (isserver && sv.lightstyles[i][0])
608 FS_Printf(f, "%s\n", sv.lightstyles[i]);
613 PRVM_ED_WriteGlobals (f);
614 for (i=0 ; i<prog->num_edicts ; i++)
616 FS_Printf(f,"// edict %d\n", i);
617 //Con_Printf("edict %d...\n", i);
618 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
623 FS_Printf(f,"// DarkPlaces extended savegame\n");
624 // darkplaces extension - extra lightstyles, support for color lightstyles
625 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
626 if (isserver && sv.lightstyles[i][0])
627 FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
629 // darkplaces extension - model precaches
630 for (i=1 ; i<MAX_MODELS ; i++)
631 if (sv.model_precache[i][0])
632 FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
634 // darkplaces extension - sound precaches
635 for (i=1 ; i<MAX_SOUNDS ; i++)
636 if (sv.sound_precache[i][0])
637 FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
639 // darkplaces extension - save buffers
640 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); i++)
642 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
643 if(stringbuffer && (stringbuffer->flags & STRINGBUFFER_SAVED))
645 for(k = 0; k < stringbuffer->num_strings; k++)
647 if (!stringbuffer->strings[k])
649 // Parse the string a bit to turn special characters
650 // (like newline, specifically) into escape codes
651 s = stringbuffer->strings[k];
652 for (l = 0;l < (int)sizeof(line) - 2 && *s;)
679 FS_Printf(f,"sv.bufstr %i %i \"%s\"\n", i, k, line);
687 Con_Print("done.\n");
695 void Host_Savegame_f (void)
697 char name[MAX_QPATH];
701 Con_Print("Can't save - no server running.\n");
707 // singleplayer checks
710 Con_Print("Can't save in intermission.\n");
714 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
716 Con_Print("Can't savegame with a dead player\n");
721 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");
725 Con_Print("save <savename> : save a game\n");
729 if (strstr(Cmd_Argv(1), ".."))
731 Con_Print("Relative pathnames are not allowed.\n");
735 strlcpy (name, Cmd_Argv(1), sizeof (name));
736 FS_DefaultExtension (name, ".sav", sizeof (name));
739 Host_Savegame_to(name);
750 void Host_Loadgame_f (void)
752 char filename[MAX_QPATH];
753 char mapname[MAX_QPATH];
763 float spawn_parms[NUM_SPAWN_PARMS];
764 prvm_stringbuffer_t *stringbuffer;
769 Con_Print("load <savename> : load a game\n");
773 strlcpy (filename, Cmd_Argv(1), sizeof(filename));
774 FS_DefaultExtension (filename, ".sav", sizeof (filename));
776 Con_Printf("Loading game from %s...\n", filename);
778 // stop playing demos
779 if (cls.demoplayback)
783 if (key_dest == key_menu || key_dest == key_menu_grabbed)
787 cls.demonum = -1; // stop demo loop in case this fails
789 t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
792 Con_Print("ERROR: couldn't open.\n");
796 if(developer_entityparsing.integer)
797 Con_Printf("Host_Loadgame_f: loading version\n");
800 COM_ParseToken_Simple(&t, false, false);
801 version = atoi(com_token);
802 if (version != SAVEGAME_VERSION)
805 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
809 if(developer_entityparsing.integer)
810 Con_Printf("Host_Loadgame_f: loading description\n");
813 COM_ParseToken_Simple(&t, false, false);
815 for (i = 0;i < NUM_SPAWN_PARMS;i++)
817 COM_ParseToken_Simple(&t, false, false);
818 spawn_parms[i] = atof(com_token);
821 COM_ParseToken_Simple(&t, false, false);
822 // this silliness is so we can load 1.06 save files, which have float skill values
823 current_skill = (int)(atof(com_token) + 0.5);
824 Cvar_SetValue ("skill", (float)current_skill);
826 if(developer_entityparsing.integer)
827 Con_Printf("Host_Loadgame_f: loading mapname\n");
830 COM_ParseToken_Simple(&t, false, false);
831 strlcpy (mapname, com_token, sizeof(mapname));
833 if(developer_entityparsing.integer)
834 Con_Printf("Host_Loadgame_f: loading time\n");
837 COM_ParseToken_Simple(&t, false, false);
838 time = atof(com_token);
840 allowcheats = sv_cheats.integer != 0;
842 if(developer_entityparsing.integer)
843 Con_Printf("Host_Loadgame_f: spawning server\n");
845 SV_SpawnServer (mapname);
849 Con_Print("Couldn't load map\n");
852 sv.paused = true; // pause until all clients connect
855 if(developer_entityparsing.integer)
856 Con_Printf("Host_Loadgame_f: loading light styles\n");
858 // load the light styles
864 for (i = 0;i < MAX_LIGHTSTYLES;i++)
868 COM_ParseToken_Simple(&t, false, false);
869 // if this is a 64 lightstyle savegame produced by Quake, stop now
870 // we have to check this because darkplaces may save more than 64
871 if (com_token[0] == '{')
876 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
879 if(developer_entityparsing.integer)
880 Con_Printf("Host_Loadgame_f: skipping until globals\n");
882 // now skip everything before the first opening brace
883 // (this is for forward compatibility, so that older versions (at
884 // least ones with this fix) can load savegames with extra data before the
885 // first brace, as might be produced by a later engine version)
889 if (!COM_ParseToken_Simple(&t, false, false))
891 if (com_token[0] == '{')
898 // unlink all entities
899 World_UnlinkAll(&sv.world);
901 // load the edicts out of the savegame file
906 while (COM_ParseToken_Simple(&t, false, false))
907 if (!strcmp(com_token, "}"))
909 if (!COM_ParseToken_Simple(&start, false, false))
914 if (strcmp(com_token,"{"))
917 Host_Error ("First token isn't a brace");
922 if(developer_entityparsing.integer)
923 Con_Printf("Host_Loadgame_f: loading globals\n");
925 // parse the global vars
926 PRVM_ED_ParseGlobals (start);
931 if (entnum >= MAX_EDICTS)
934 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
936 while (entnum >= prog->max_edicts)
937 PRVM_MEM_IncreaseEdicts();
938 ent = PRVM_EDICT_NUM(entnum);
939 memset (ent->fields.server, 0, prog->progs->entityfields * 4);
940 ent->priv.server->free = false;
942 if(developer_entityparsing.integer)
943 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
945 PRVM_ED_ParseEdict (start, ent);
947 // link it into the bsp tree
948 if (!ent->priv.server->free)
956 prog->num_edicts = entnum;
959 for (i = 0;i < NUM_SPAWN_PARMS;i++)
960 svs.clients[0].spawn_parms[i] = spawn_parms[i];
962 if(developer_entityparsing.integer)
963 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
965 // read extended data if present
966 // the extended data is stored inside a /* */ comment block, which the
967 // parser intentionally skips, so we have to check for it manually here
970 while (*end == '\r' || *end == '\n')
972 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
974 if(developer_entityparsing.integer)
975 Con_Printf("Host_Loadgame_f: loading extended data\n");
977 Con_Printf("Loading extended DarkPlaces savegame\n");
979 memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
980 memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
981 memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
982 while (COM_ParseToken_Simple(&t, false, false))
984 if (!strcmp(com_token, "sv.lightstyles"))
986 COM_ParseToken_Simple(&t, false, false);
988 COM_ParseToken_Simple(&t, false, false);
989 if (i >= 0 && i < MAX_LIGHTSTYLES)
990 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
992 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
994 else if (!strcmp(com_token, "sv.model_precache"))
996 COM_ParseToken_Simple(&t, false, false);
998 COM_ParseToken_Simple(&t, false, false);
999 if (i >= 0 && i < MAX_MODELS)
1001 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
1002 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, sv.model_precache[i][0] == '*' ? sv.worldname : NULL);
1005 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
1007 else if (!strcmp(com_token, "sv.sound_precache"))
1009 COM_ParseToken_Simple(&t, false, false);
1010 i = atoi(com_token);
1011 COM_ParseToken_Simple(&t, false, false);
1012 if (i >= 0 && i < MAX_SOUNDS)
1013 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
1015 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
1017 else if (!strcmp(com_token, "sv.bufstr"))
1019 COM_ParseToken_Simple(&t, false, false);
1020 i = atoi(com_token);
1021 COM_ParseToken_Simple(&t, false, false);
1022 k = atoi(com_token);
1023 COM_ParseToken_Simple(&t, false, false);
1024 stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
1025 // VorteX: nasty code, cleanup required
1026 // create buffer at this index
1028 stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecordAtIndex(&prog->stringbuffersarray, i);
1030 Con_Printf("cant write string %i into buffer %i\n", k, i);
1033 // code copied from VM_bufstr_set
1035 if (stringbuffer->max_strings <= i)
1037 char **oldstrings = stringbuffer->strings;
1038 stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
1039 while (stringbuffer->max_strings <= i)
1040 stringbuffer->max_strings *= 2;
1041 stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
1042 if (stringbuffer->num_strings > 0)
1043 memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
1045 Mem_Free(oldstrings);
1048 stringbuffer->num_strings = max(stringbuffer->num_strings, k + 1);
1049 if(stringbuffer->strings[k])
1050 Mem_Free(stringbuffer->strings[k]);
1051 stringbuffer->strings[k] = NULL;
1052 alloclen = strlen(com_token) + 1;
1053 stringbuffer->strings[k] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
1054 memcpy(stringbuffer->strings[k], com_token, alloclen);
1057 // skip any trailing text or unrecognized commands
1058 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
1065 if(developer_entityparsing.integer)
1066 Con_Printf("Host_Loadgame_f: finished\n");
1070 // make sure we're connected to loopback
1071 if (sv.active && cls.state == ca_disconnected)
1072 CL_EstablishConnection("local:1");
1075 //============================================================================
1078 ======================
1080 ======================
1082 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
1083 void Host_Name_f (void)
1086 qboolean valid_colors;
1087 const char *newNameSource;
1088 char newName[sizeof(host_client->name)];
1090 if (Cmd_Argc () == 1)
1092 Con_Printf("name: %s\n", cl_name.string);
1096 if (Cmd_Argc () == 2)
1097 newNameSource = Cmd_Argv(1);
1099 newNameSource = Cmd_Args();
1101 strlcpy(newName, newNameSource, sizeof(newName));
1103 if (cmd_source == src_command)
1105 Cvar_Set ("_cl_name", newName);
1106 if (strlen(newNameSource) >= sizeof(newName)) // overflowed
1108 Con_Printf("Your name is longer than %i chars! It has been truncated.\n", (int) (sizeof(newName) - 1));
1109 Con_Printf("name: %s\n", cl_name.string);
1114 if (realtime < host_client->nametime)
1116 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
1120 host_client->nametime = realtime + 5;
1122 // point the string back at updateclient->name to keep it safe
1123 strlcpy (host_client->name, newName, sizeof (host_client->name));
1125 for (i = 0, j = 0;host_client->name[i];i++)
1126 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
1127 host_client->name[j++] = host_client->name[i];
1128 host_client->name[j] = 0;
1130 if(host_client->name[0] == 1 || host_client->name[0] == 2)
1131 // may interfere with chat area, and will needlessly beep; so let's add a ^7
1133 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
1134 host_client->name[sizeof(host_client->name) - 1] = 0;
1135 host_client->name[0] = STRING_COLOR_TAG;
1136 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
1139 u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
1140 if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
1143 l = strlen(host_client->name);
1144 if(l < sizeof(host_client->name) - 1)
1146 // duplicate the color tag to escape it
1147 host_client->name[i] = STRING_COLOR_TAG;
1148 host_client->name[i+1] = 0;
1149 //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1153 // remove the last character to fix the color code
1154 host_client->name[l-1] = 0;
1155 //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1159 // find the last color tag offset and decide if we need to add a reset tag
1160 for (i = 0, j = -1;host_client->name[i];i++)
1162 if (host_client->name[i] == STRING_COLOR_TAG)
1164 if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1167 // if this happens to be a reset tag then we don't need one
1168 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1173 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]))
1179 if (host_client->name[i+1] == STRING_COLOR_TAG)
1186 // does not end in the default color string, so add it
1187 if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1188 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1190 host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1191 if (strcmp(host_client->old_name, host_client->name))
1193 if (host_client->spawned)
1194 SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1195 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1196 // send notification to all clients
1197 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1198 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1199 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1200 SV_WriteNetnameIntoDemo(host_client);
1205 ======================
1207 ======================
1209 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1210 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1211 void Host_Playermodel_f (void)
1214 char newPath[sizeof(host_client->playermodel)];
1216 if (Cmd_Argc () == 1)
1218 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1222 if (Cmd_Argc () == 2)
1223 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1225 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1227 for (i = 0, j = 0;newPath[i];i++)
1228 if (newPath[i] != '\r' && newPath[i] != '\n')
1229 newPath[j++] = newPath[i];
1232 if (cmd_source == src_command)
1234 Cvar_Set ("_cl_playermodel", newPath);
1239 if (realtime < host_client->nametime)
1241 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1245 host_client->nametime = realtime + 5;
1248 // point the string back at updateclient->name to keep it safe
1249 strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1250 if( prog->fieldoffsets.playermodel >= 0 )
1251 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1252 if (strcmp(host_client->old_model, host_client->playermodel))
1254 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1255 /*// send notification to all clients
1256 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1257 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1258 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1263 ======================
1265 ======================
1267 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1268 void Host_Playerskin_f (void)
1271 char newPath[sizeof(host_client->playerskin)];
1273 if (Cmd_Argc () == 1)
1275 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1279 if (Cmd_Argc () == 2)
1280 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1282 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1284 for (i = 0, j = 0;newPath[i];i++)
1285 if (newPath[i] != '\r' && newPath[i] != '\n')
1286 newPath[j++] = newPath[i];
1289 if (cmd_source == src_command)
1291 Cvar_Set ("_cl_playerskin", newPath);
1296 if (realtime < host_client->nametime)
1298 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1302 host_client->nametime = realtime + 5;
1305 // point the string back at updateclient->name to keep it safe
1306 strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1307 if( prog->fieldoffsets.playerskin >= 0 )
1308 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1309 if (strcmp(host_client->old_skin, host_client->playerskin))
1311 //if (host_client->spawned)
1312 // SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1313 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1314 /*// send notification to all clients
1315 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1316 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1317 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1321 void Host_Version_f (void)
1323 Con_Printf("Version: %s build %s\n", gamename, buildstring);
1326 void Host_Say(qboolean teamonly)
1332 // LordHavoc: long say messages
1334 qboolean fromServer = false;
1336 if (cmd_source == src_command)
1338 if (cls.state == ca_dedicated)
1345 Cmd_ForwardToServer ();
1350 if (Cmd_Argc () < 2)
1353 if (!teamplay.integer)
1363 // note this uses the chat prefix \001
1364 if (!fromServer && !teamonly)
1365 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1366 else if (!fromServer && teamonly)
1367 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1368 else if(*(sv_adminnick.string))
1369 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1371 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1372 p2 = text + strlen(text);
1373 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1375 if (p2[-1] == '\"' && quoted)
1380 strlcat(text, "\n", sizeof(text));
1382 // note: save is not a valid edict if fromServer is true
1384 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1385 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1386 SV_ClientPrint(text);
1389 if (cls.state == ca_dedicated)
1390 Con_Print(&text[1]);
1394 void Host_Say_f(void)
1400 void Host_Say_Team_f(void)
1406 void Host_Tell_f(void)
1408 const char *playername_start = NULL;
1409 size_t playername_length = 0;
1410 int playernumber = 0;
1413 const char *p1, *p2;
1414 char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1415 qboolean fromServer = false;
1417 if (cmd_source == src_command)
1419 if (cls.state == ca_dedicated)
1423 Cmd_ForwardToServer ();
1428 if (Cmd_Argc () < 2)
1431 // note this uses the chat prefix \001
1433 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1434 else if(*(sv_adminnick.string))
1435 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1437 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1440 p2 = p1 + strlen(p1);
1441 // remove the target name
1442 while (p1 < p2 && *p1 == ' ')
1447 while (p1 < p2 && *p1 == ' ')
1449 while (p1 < p2 && isdigit(*p1))
1451 playernumber = playernumber * 10 + (*p1 - '0');
1459 playername_start = p1;
1460 while (p1 < p2 && *p1 != '"')
1462 playername_length = p1 - playername_start;
1468 playername_start = p1;
1469 while (p1 < p2 && *p1 != ' ')
1471 playername_length = p1 - playername_start;
1473 while (p1 < p2 && *p1 == ' ')
1475 if(playername_start)
1477 // set playernumber to the right client
1479 if(playername_length >= sizeof(namebuf))
1482 Con_Print("Host_Tell: too long player name/ID\n");
1484 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1487 memcpy(namebuf, playername_start, playername_length);
1488 namebuf[playername_length] = 0;
1489 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1491 if (!svs.clients[playernumber].active)
1493 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1497 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1500 Con_Print("Host_Tell: invalid player name/ID\n");
1502 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1505 // remove trailing newlines
1506 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1508 // remove quotes if present
1514 else if (fromServer)
1515 Con_Print("Host_Tell: missing end quote\n");
1517 SV_ClientPrint("Host_Tell: missing end quote\n");
1519 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1522 return; // empty say
1523 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1529 host_client = svs.clients + playernumber;
1530 SV_ClientPrint(text);
1540 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1541 void Host_Color(int changetop, int changebottom)
1543 int top, bottom, playercolor;
1545 // get top and bottom either from the provided values or the current values
1546 // (allows changing only top or bottom, or both at once)
1547 top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1548 bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1552 // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1558 playercolor = top*16 + bottom;
1560 if (cmd_source == src_command)
1562 Cvar_SetValueQuick(&cl_color, playercolor);
1566 if (cls.protocol == PROTOCOL_QUAKEWORLD)
1569 if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1571 Con_DPrint("Calling SV_ChangeTeam\n");
1572 prog->globals.server->time = sv.time;
1573 prog->globals.generic[OFS_PARM0] = playercolor;
1574 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1575 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1580 if (host_client->edict)
1582 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1583 val->_float = playercolor;
1584 host_client->edict->fields.server->team = bottom + 1;
1586 host_client->colors = playercolor;
1587 if (host_client->old_colors != host_client->colors)
1589 host_client->old_colors = host_client->colors;
1590 // send notification to all clients
1591 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1592 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1593 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1598 void Host_Color_f(void)
1602 if (Cmd_Argc() == 1)
1604 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1605 Con_Print("color <0-15> [0-15]\n");
1609 if (Cmd_Argc() == 2)
1610 top = bottom = atoi(Cmd_Argv(1));
1613 top = atoi(Cmd_Argv(1));
1614 bottom = atoi(Cmd_Argv(2));
1616 Host_Color(top, bottom);
1619 void Host_TopColor_f(void)
1621 if (Cmd_Argc() == 1)
1623 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1624 Con_Print("topcolor <0-15>\n");
1628 Host_Color(atoi(Cmd_Argv(1)), -1);
1631 void Host_BottomColor_f(void)
1633 if (Cmd_Argc() == 1)
1635 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1636 Con_Print("bottomcolor <0-15>\n");
1640 Host_Color(-1, atoi(Cmd_Argv(1)));
1643 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1644 void Host_Rate_f(void)
1648 if (Cmd_Argc() != 2)
1650 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1651 Con_Print("rate <bytespersecond>\n");
1655 rate = atoi(Cmd_Argv(1));
1657 if (cmd_source == src_command)
1659 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1663 host_client->rate = rate;
1671 void Host_Kill_f (void)
1673 if (host_client->edict->fields.server->health <= 0)
1675 SV_ClientPrint("Can't suicide -- already dead!\n");
1679 prog->globals.server->time = sv.time;
1680 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1681 PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1690 void Host_Pause_f (void)
1692 if (!pausable.integer)
1693 SV_ClientPrint("Pause not allowed.\n");
1697 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1698 // send notification to all clients
1699 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1700 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1705 ======================
1707 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1708 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1709 ======================
1711 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)"};
1712 static void Host_PModel_f (void)
1717 if (Cmd_Argc () == 1)
1719 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1722 i = atoi(Cmd_Argv(1));
1724 if (cmd_source == src_command)
1726 if (cl_pmodel.integer == i)
1728 Cvar_SetValue ("_cl_pmodel", i);
1729 if (cls.state == ca_connected)
1730 Cmd_ForwardToServer ();
1734 if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1738 //===========================================================================
1746 void Host_PreSpawn_f (void)
1748 if (host_client->spawned)
1750 Con_Print("prespawn not valid -- already spawned\n");
1754 if (host_client->netconnection)
1756 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1757 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1758 MSG_WriteByte (&host_client->netconnection->message, 2);
1759 host_client->sendsignon = 0; // enable unlimited sends again
1762 // reset the name change timer because the client will send name soon
1763 host_client->nametime = 0;
1771 void Host_Spawn_f (void)
1775 int stats[MAX_CL_STATS];
1777 if (host_client->spawned)
1779 Con_Print("Spawn not valid -- already spawned\n");
1783 // reset name change timer again because they might want to change name
1784 // again in the first 5 seconds after connecting
1785 host_client->nametime = 0;
1787 // LordHavoc: moved this above the QC calls at FrikaC's request
1788 // LordHavoc: commented this out
1789 //if (host_client->netconnection)
1790 // SZ_Clear (&host_client->netconnection->message);
1792 // run the entrance script
1795 // loaded games are fully initialized already
1796 if (prog->funcoffsets.RestoreGame)
1798 Con_DPrint("Calling RestoreGame\n");
1799 prog->globals.server->time = sv.time;
1800 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1801 PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1806 //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);
1808 // copy spawn parms out of the client_t
1809 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1810 (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1812 // call the spawn function
1813 host_client->clientconnectcalled = true;
1814 prog->globals.server->time = sv.time;
1815 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1816 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1818 if (cls.state == ca_dedicated)
1819 Con_Printf("%s connected\n", host_client->name);
1821 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1824 if (!host_client->netconnection)
1827 // send time of update
1828 MSG_WriteByte (&host_client->netconnection->message, svc_time);
1829 MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1831 // send all current names, colors, and frag counts
1832 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1834 if (!client->active)
1836 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1837 MSG_WriteByte (&host_client->netconnection->message, i);
1838 MSG_WriteString (&host_client->netconnection->message, client->name);
1839 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1840 MSG_WriteByte (&host_client->netconnection->message, i);
1841 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1842 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1843 MSG_WriteByte (&host_client->netconnection->message, i);
1844 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1847 // send all current light styles
1848 for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1850 if (sv.lightstyles[i][0])
1852 MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1853 MSG_WriteByte (&host_client->netconnection->message, (char)i);
1854 MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1859 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1860 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1861 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1863 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1864 MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1865 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1867 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1868 MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1869 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1871 MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1872 MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1873 MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1876 // Never send a roll angle, because savegames can catch the server
1877 // in a state where it is expecting the client to correct the angle
1878 // and it won't happen if the game was just loaded, so you wind up
1879 // with a permanent head tilt
1882 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1883 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1884 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1885 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1889 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1890 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1891 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1892 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1895 SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1897 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1898 MSG_WriteByte (&host_client->netconnection->message, 3);
1906 void Host_Begin_f (void)
1908 host_client->spawned = true;
1910 // LordHavoc: note: this code also exists in SV_DropClient
1914 for (i = 0;i < svs.maxclients;i++)
1915 if (svs.clients[i].active && !svs.clients[i].spawned)
1917 if (i == svs.maxclients)
1919 Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1920 sv.paused = sv.loadgame = false; // we're basically done with loading now
1925 //===========================================================================
1932 Kicks a user off of the server
1935 void Host_Kick_f (void)
1938 const char *message = NULL;
1941 qboolean byNumber = false;
1949 if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1951 i = (int)(atof(Cmd_Argv(2)) - 1);
1952 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1958 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1960 if (!host_client->active)
1962 if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1967 if (i < svs.maxclients)
1969 if (cmd_source == src_command)
1971 if (cls.state == ca_dedicated)
1974 who = cl_name.string;
1979 // can't kick yourself!
1980 if (host_client == save)
1985 message = Cmd_Args();
1986 COM_ParseToken_Simple(&message, false, false);
1989 message++; // skip the #
1990 while (*message == ' ') // skip white space
1992 message += strlen(Cmd_Argv(2)); // skip the number
1994 while (*message && *message == ' ')
1998 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2000 SV_ClientPrintf("Kicked by %s\n", who);
2001 SV_DropClient (false); // kicked
2009 ===============================================================================
2013 ===============================================================================
2021 void Host_Give_f (void)
2029 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2034 v = atoi (Cmd_Argv(2));
2048 // MED 01/04/97 added hipnotic give stuff
2049 if (gamemode == GAME_HIPNOTIC)
2054 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
2056 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
2058 else if (t[0] == '9')
2059 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
2060 else if (t[0] == '0')
2061 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
2062 else if (t[0] >= '2')
2063 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2068 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
2073 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
2076 host_client->edict->fields.server->ammo_shells = v;
2079 if (gamemode == GAME_ROGUE)
2081 if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
2084 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2085 host_client->edict->fields.server->ammo_nails = v;
2090 host_client->edict->fields.server->ammo_nails = v;
2094 if (gamemode == GAME_ROGUE)
2096 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
2100 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2101 host_client->edict->fields.server->ammo_nails = v;
2106 if (gamemode == GAME_ROGUE)
2108 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
2112 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2113 host_client->edict->fields.server->ammo_rockets = v;
2118 host_client->edict->fields.server->ammo_rockets = v;
2122 if (gamemode == GAME_ROGUE)
2124 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
2128 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2129 host_client->edict->fields.server->ammo_rockets = v;
2134 host_client->edict->fields.server->health = v;
2137 if (gamemode == GAME_ROGUE)
2139 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
2143 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
2144 host_client->edict->fields.server->ammo_cells = v;
2149 host_client->edict->fields.server->ammo_cells = v;
2153 if (gamemode == GAME_ROGUE)
2155 val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2159 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2160 host_client->edict->fields.server->ammo_cells = v;
2167 prvm_edict_t *FindViewthing (void)
2172 for (i=0 ; i<prog->num_edicts ; i++)
2174 e = PRVM_EDICT_NUM(i);
2175 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2178 Con_Print("No viewthing on map\n");
2187 void Host_Viewmodel_f (void)
2196 e = FindViewthing ();
2201 m = Mod_ForName (Cmd_Argv(1), false, true, NULL);
2202 if (!m || !m->loaded || !m->Draw)
2204 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2208 e->fields.server->frame = 0;
2209 cl.model_precache[(int)e->fields.server->modelindex] = m;
2217 void Host_Viewframe_f (void)
2227 e = FindViewthing ();
2231 m = cl.model_precache[(int)e->fields.server->modelindex];
2233 f = atoi(Cmd_Argv(1));
2234 if (f >= m->numframes)
2237 e->fields.server->frame = f;
2241 void PrintFrameName (dp_model_t *m, int frame)
2244 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2246 Con_Printf("frame %i\n", frame);
2254 void Host_Viewnext_f (void)
2263 e = FindViewthing ();
2267 m = cl.model_precache[(int)e->fields.server->modelindex];
2269 e->fields.server->frame = e->fields.server->frame + 1;
2270 if (e->fields.server->frame >= m->numframes)
2271 e->fields.server->frame = m->numframes - 1;
2273 PrintFrameName (m, (int)e->fields.server->frame);
2281 void Host_Viewprev_f (void)
2290 e = FindViewthing ();
2295 m = cl.model_precache[(int)e->fields.server->modelindex];
2297 e->fields.server->frame = e->fields.server->frame - 1;
2298 if (e->fields.server->frame < 0)
2299 e->fields.server->frame = 0;
2301 PrintFrameName (m, (int)e->fields.server->frame);
2305 ===============================================================================
2309 ===============================================================================
2318 void Host_Startdemos_f (void)
2322 if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2328 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2331 Con_DPrintf("%i demo(s) in loop\n", c);
2333 for (i=1 ; i<c+1 ; i++)
2334 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2336 // LordHavoc: clear the remaining slots
2337 for (;i <= MAX_DEMOS;i++)
2338 cls.demos[i-1][0] = 0;
2340 if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2354 Return to looping demos
2357 void Host_Demos_f (void)
2359 if (cls.state == ca_dedicated)
2361 if (cls.demonum == -1)
2371 Return to looping demos
2374 void Host_Stopdemo_f (void)
2376 if (!cls.demoplayback)
2379 Host_ShutdownServer ();
2382 void Host_SendCvar_f (void)
2386 const char *cvarname;
2391 cvarname = Cmd_Argv(1);
2392 if (cls.state == ca_connected)
2394 c = Cvar_FindVar(cvarname);
2395 // LordHavoc: if there is no such cvar or if it is private, send a
2396 // reply indicating that it has no value
2397 if(!c || (c->flags & CVAR_PRIVATE))
2398 Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2400 Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2403 if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2407 if (cls.state != ca_dedicated)
2411 for(;i<svs.maxclients;i++)
2412 if(svs.clients[i].active && svs.clients[i].netconnection)
2414 host_client = &svs.clients[i];
2415 Host_ClientCommands("sendcvar %s\n", cvarname);
2420 static void MaxPlayers_f(void)
2424 if (Cmd_Argc() != 2)
2426 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2432 Con_Print("maxplayers can not be changed while a server is running.\n");
2433 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2436 n = atoi(Cmd_Argv(1));
2437 n = bound(1, n, MAX_SCOREBOARD);
2438 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2440 svs.maxclients_next = n;
2442 Cvar_Set ("deathmatch", "0");
2444 Cvar_Set ("deathmatch", "1");
2448 =====================
2451 ProQuake rcon support
2452 =====================
2454 void Host_PQRcon_f (void)
2459 lhnetsocket_t *mysocket;
2460 char peer_address[64];
2462 if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2464 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2468 e = strchr(rcon_password.string, ' ');
2469 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2473 InfoString_GetValue(cls.userinfo, "*ip", peer_address, sizeof(peer_address));
2477 if (!rcon_address.string[0])
2479 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2482 strlcpy(peer_address, rcon_address.string, strlen(rcon_address.string)+1);
2484 LHNETADDRESS_FromString(&to, peer_address, sv_netport.integer);
2485 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2488 SZ_Clear(&net_message);
2489 MSG_WriteLong (&net_message, 0);
2490 MSG_WriteByte (&net_message, CCREQ_RCON);
2491 SZ_Write(&net_message, (void*)rcon_password.string, n);
2492 MSG_WriteString (&net_message, Cmd_Args());
2493 StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
2494 NetConn_Write(mysocket, net_message.data, net_message.cursize, &to);
2495 SZ_Clear (&net_message);
2499 //=============================================================================
2501 // QuakeWorld commands
2504 =====================
2507 Send the rest of the command line over as
2508 an unconnected command.
2509 =====================
2511 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2516 lhnetsocket_t *mysocket;
2518 if (!rcon_password.string || !rcon_password.string[0])
2520 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2524 e = strchr(rcon_password.string, ' ');
2525 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2528 to = cls.netcon->peeraddress;
2531 if (!rcon_address.string[0])
2533 Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2536 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2538 mysocket = NetConn_ChooseClientSocketForAddress(&to);
2539 if (mysocket && Cmd_Args()[0])
2541 // simply put together the rcon packet and send it
2542 if(Cmd_Argv(0)[0] == 's' || rcon_secure.integer > 1)
2544 if(cls.rcon_commands[cls.rcon_ringpos][0])
2547 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2548 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]);
2549 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2552 for (i = 0;i < MAX_RCONS;i++)
2553 if(cls.rcon_commands[i][0])
2554 if (!LHNETADDRESS_Compare(&to, &cls.rcon_addresses[i]))
2558 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &to); // otherwise we'll request the challenge later
2559 strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2560 cls.rcon_addresses[cls.rcon_ringpos] = to;
2561 cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2562 cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2564 else if(rcon_secure.integer > 0)
2568 dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args());
2569 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2570 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
2573 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2574 NetConn_Write(mysocket, buf, 41 + strlen(buf + 41), &to);
2579 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %.*s %s", n, rcon_password.string, Cmd_Args()), &to);
2585 ====================
2588 user <name or userid>
2590 Dump userdata / masterdata for a user
2591 ====================
2593 void Host_User_f (void) // credit: taken from QuakeWorld
2598 if (Cmd_Argc() != 2)
2600 Con_Printf ("Usage: user <username / userid>\n");
2604 uid = atoi(Cmd_Argv(1));
2606 for (i = 0;i < cl.maxclients;i++)
2608 if (!cl.scores[i].name[0])
2610 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2612 InfoString_Print(cl.scores[i].qw_userinfo);
2616 Con_Printf ("User not in server.\n");
2620 ====================
2623 Dump userids for all current players
2624 ====================
2626 void Host_Users_f (void) // credit: taken from QuakeWorld
2632 Con_Printf ("userid frags name\n");
2633 Con_Printf ("------ ----- ----\n");
2634 for (i = 0;i < cl.maxclients;i++)
2636 if (cl.scores[i].name[0])
2638 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2643 Con_Printf ("%i total users\n", c);
2648 Host_FullServerinfo_f
2650 Sent by server when serverinfo changes
2653 // TODO: shouldn't this be a cvar instead?
2654 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2657 if (Cmd_Argc() != 2)
2659 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2663 strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2664 InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2665 cl.qw_teamplay = atoi(temp);
2672 Allow clients to change userinfo
2676 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2683 if (Cmd_Argc() != 2)
2685 Con_Printf ("fullinfo <complete info string>\n");
2695 while (*s && *s != '\\')
2701 Con_Printf ("MISSING VALUE\n");
2707 while (*s && *s != '\\')
2714 CL_SetInfo(key, value, false, false, false, false);
2722 Allow clients to change userinfo
2725 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2727 if (Cmd_Argc() == 1)
2729 InfoString_Print(cls.userinfo);
2732 if (Cmd_Argc() != 3)
2734 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2737 CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2741 ====================
2744 packet <destination> <contents>
2746 Contents allows \n escape character
2747 ====================
2749 void Host_Packet_f (void) // credit: taken from QuakeWorld
2755 lhnetaddress_t address;
2756 lhnetsocket_t *mysocket;
2758 if (Cmd_Argc() != 3)
2760 Con_Printf ("packet <destination> <contents>\n");
2764 if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2766 Con_Printf ("Bad address\n");
2772 send[0] = send[1] = send[2] = send[3] = -1;
2774 l = (int)strlen (in);
2775 for (i=0 ; i<l ; i++)
2777 if (out >= send + sizeof(send) - 1)
2779 if (in[i] == '\\' && in[i+1] == 'n')
2784 else if (in[i] == '\\' && in[i+1] == '0')
2789 else if (in[i] == '\\' && in[i+1] == 't')
2794 else if (in[i] == '\\' && in[i+1] == 'r')
2799 else if (in[i] == '\\' && in[i+1] == '"')
2808 mysocket = NetConn_ChooseClientSocketForAddress(&address);
2810 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2812 NetConn_Write(mysocket, send, out - send, &address);
2816 ====================
2819 Send back ping and packet loss update for all current players to this player
2820 ====================
2822 void Host_Pings_f (void)
2824 int i, j, ping, packetloss, movementloss;
2827 if (!host_client->netconnection)
2830 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2832 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2833 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2835 for (i = 0;i < svs.maxclients;i++)
2839 if (svs.clients[i].netconnection)
2841 for (j = 0;j < NETGRAPH_PACKETS;j++)
2842 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2844 for (j = 0;j < NETGRAPH_PACKETS;j++)
2845 if (svs.clients[i].movement_count[j] < 0)
2848 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2849 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2850 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2851 ping = bound(0, ping, 9999);
2852 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2854 // send qw_svc_updateping and qw_svc_updatepl messages
2855 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2856 MSG_WriteShort(&host_client->netconnection->message, ping);
2857 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2858 MSG_WriteByte(&host_client->netconnection->message, packetloss);
2862 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2864 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2866 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2867 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2870 if (sv.protocol != PROTOCOL_QUAKEWORLD)
2871 MSG_WriteString(&host_client->netconnection->message, "\n");
2874 void Host_PingPLReport_f(void)
2879 if (l > cl.maxclients)
2881 for (i = 0;i < l;i++)
2883 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2884 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(1+i*2+1), &errbyte, 0);
2885 if(errbyte && *errbyte == ',')
2886 cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2888 cl.scores[i].qw_movementloss = 0;
2892 //=============================================================================
2899 void Host_InitCommands (void)
2901 dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2903 Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2904 Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2905 if (gamemode == GAME_NEHAHRA)
2907 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2908 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2909 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2910 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2911 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2915 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2916 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2917 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2918 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2919 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2921 Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2922 Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2923 Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2924 Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2925 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)");
2926 Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2927 Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2928 Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2929 Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2930 Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2931 Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2932 Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2933 Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2934 Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2935 Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2937 Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2938 Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2939 Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2941 Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2942 Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2943 Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2944 Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2946 Cvar_RegisterVariable (&cl_name);
2947 Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2948 Cvar_RegisterVariable (&cl_color);
2949 Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2950 Cvar_RegisterVariable (&cl_rate);
2951 Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2952 if (gamemode == GAME_NEHAHRA)
2954 Cvar_RegisterVariable (&cl_pmodel);
2955 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2958 // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2959 Cvar_RegisterVariable (&cl_playermodel);
2960 Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2961 Cvar_RegisterVariable (&cl_playerskin);
2962 Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2964 Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2965 Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2966 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)");
2967 Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2969 Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2971 Cvar_RegisterVariable (&rcon_password);
2972 Cvar_RegisterVariable (&rcon_address);
2973 Cvar_RegisterVariable (&rcon_secure);
2974 Cvar_RegisterVariable (&rcon_secure_challengetimeout);
2975 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");
2976 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");
2977 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)");
2978 Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2979 Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2980 Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2981 Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2982 Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2983 Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2984 Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2985 Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2987 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)");
2988 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)");
2990 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)");
2991 Cvar_RegisterVariable (&r_fixtrans_auto);
2993 Cvar_RegisterVariable (&team);
2994 Cvar_RegisterVariable (&skin);
2995 Cvar_RegisterVariable (&noaim);
2997 Cvar_RegisterVariable(&sv_cheats);
2998 Cvar_RegisterVariable(&sv_adminnick);
2999 Cvar_RegisterVariable(&sv_status_privacy);
3000 Cvar_RegisterVariable(&sv_status_show_qcstatus);
3003 void Host_NoOperation_f(void)