]> icculus.org git repositories - divverent/darkplaces.git/blob - host_cmd.c
SV_PushMove: ignore pushes from and to "owned" entities
[divverent/darkplaces.git] / host_cmd.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "quakedef.h"
22 #include "sv_demo.h"
23 #include "image.h"
24
25 int current_skill;
26 cvar_t sv_cheats = {0, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
27 cvar_t sv_adminnick = {CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
28 cvar_t sv_status_privacy = {CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
29 cvar_t rcon_password = {CVAR_PRIVATE, "rcon_password", "", "password to authenticate rcon commands"};
30 cvar_t rcon_address = {0, "rcon_address", "", "server address to send rcon commands to (when not connected to a server)"};
31 cvar_t team = {CVAR_USERINFO | CVAR_SAVE, "team", "none", "QW team (4 character limit, example: blue)"};
32 cvar_t skin = {CVAR_USERINFO | CVAR_SAVE, "skin", "", "QW player skin name (example: base)"};
33 cvar_t noaim = {CVAR_USERINFO | CVAR_SAVE, "noaim", "1", "QW option to disable vertical autoaim"};
34 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)"};
35 qboolean allowcheats = false;
36
37 extern qboolean host_shuttingdown;
38 extern cvar_t developer_entityparsing;
39
40 /*
41 ==================
42 Host_Quit_f
43 ==================
44 */
45
46 void Host_Quit_f (void)
47 {
48         if(host_shuttingdown)
49                 Con_Printf("shutting down already!\n");
50         else
51                 Sys_Quit (0);
52 }
53
54 /*
55 ==================
56 Host_Status_f
57 ==================
58 */
59 void Host_Status_f (void)
60 {
61         client_t *client;
62         int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
63         void (*print) (const char *fmt, ...);
64         char ip[22];
65         
66         if (cmd_source == src_command)
67         {
68                 // if running a client, try to send over network so the client's status report parser will see the report
69                 if (cls.state == ca_connected)
70                 {
71                         Cmd_ForwardToServer ();
72                         return;
73                 }
74                 print = Con_Printf;
75         }
76         else
77                 print = SV_ClientPrintf;
78
79         if (!sv.active)
80                 return;
81         
82         in = 0;
83         if (Cmd_Argc() == 2)
84         {
85                 if (strcmp(Cmd_Argv(1), "1") == 0)
86                         in = 1;
87                 else if (strcmp(Cmd_Argv(1), "2") == 0)
88                         in = 2;
89         }
90
91         for (players = 0, i = 0;i < svs.maxclients;i++)
92                 if (svs.clients[i].active)
93                         players++;
94         print ("host:     %s\n", Cvar_VariableString ("hostname"));
95         print ("version:  %s build %s\n", gamename, buildstring);
96         print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
97         print ("map:      %s\n", sv.name);
98         print ("timing:   %s\n", Host_TimingReport());
99         print ("players:  %i active (%i max)\n\n", players, svs.maxclients);
100
101         if (in == 1)
102                 print ("^2IP                   %%pl ping  time   frags  no   name\n");
103         else if (in == 2)
104                 print ("^5IP                    no   name\n");
105
106         for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
107         {
108                 if (!client->active)
109                         continue;
110
111                 ++k;
112
113                 if (in == 0 || in == 1)
114                 {
115                         seconds = (int)(realtime - client->connecttime);
116                         minutes = seconds / 60;
117                         if (minutes)
118                         {
119                                 seconds -= (minutes * 60);
120                                 hours = minutes / 60;
121                                 if (hours)
122                                         minutes -= (hours * 60);
123                         }
124                         else
125                                 hours = 0;
126                         
127                         packetloss = 0;
128                         if (client->netconnection)
129                                 for (j = 0;j < NETGRAPH_PACKETS;j++)
130                                         if (client->netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
131                                                 packetloss++;
132                         packetloss = packetloss * 100 / NETGRAPH_PACKETS;
133                         ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
134                 }
135
136                 if(sv_status_privacy.integer && cmd_source != src_command)
137                         strlcpy(ip, client->netconnection ? "hidden" : "botclient" , 22);
138                 else
139                         strlcpy(ip, (client->netconnection && client->netconnection->address) ? client->netconnection->address : "botclient", 22);
140                 
141                 if (in == 0) // default layout
142                 {
143                         print ("#%-3u ", i+1);
144                         print ("%-16.16s  ", client->name);
145                         print ("%3i  ", client->frags);
146                         print ("%2i:%02i:%02i\n   ", hours, minutes, seconds);
147                         print ("%s\n", ip);
148                 }
149                 else if (in == 1) // extended layout
150                 {
151                         k%2 ? print("^3") : print("^7");
152                         print ("%-21s ", ip);
153                         print ("%2i ", packetloss);
154                         print ("%4i ", ping);
155                         print ("%2i:%02i:%02i ", hours, minutes, seconds);
156                         print ("%4i  ", client->frags);
157                         print ("#%-3u ", i+1);
158                         print ("^7%s\n", client->name);
159                 }
160                 else if (in == 2) // reduced layout
161                 {
162                         k%2 ? print("^3") : print("^7");
163                         print ("%-21s ", ip);
164                         print ("#%-3u ", i+1);
165                         print ("^7%s\n", client->name);
166                 }
167         }
168 }
169
170
171 /*
172 ==================
173 Host_God_f
174
175 Sets client to godmode
176 ==================
177 */
178 void Host_God_f (void)
179 {
180         if (!allowcheats)
181         {
182                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
183                 return;
184         }
185
186         host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_GODMODE;
187         if (!((int)host_client->edict->fields.server->flags & FL_GODMODE) )
188                 SV_ClientPrint("godmode OFF\n");
189         else
190                 SV_ClientPrint("godmode ON\n");
191 }
192
193 void Host_Notarget_f (void)
194 {
195         if (!allowcheats)
196         {
197                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
198                 return;
199         }
200
201         host_client->edict->fields.server->flags = (int)host_client->edict->fields.server->flags ^ FL_NOTARGET;
202         if (!((int)host_client->edict->fields.server->flags & FL_NOTARGET) )
203                 SV_ClientPrint("notarget OFF\n");
204         else
205                 SV_ClientPrint("notarget ON\n");
206 }
207
208 qboolean noclip_anglehack;
209
210 void Host_Noclip_f (void)
211 {
212         if (!allowcheats)
213         {
214                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
215                 return;
216         }
217
218         if (host_client->edict->fields.server->movetype != MOVETYPE_NOCLIP)
219         {
220                 noclip_anglehack = true;
221                 host_client->edict->fields.server->movetype = MOVETYPE_NOCLIP;
222                 SV_ClientPrint("noclip ON\n");
223         }
224         else
225         {
226                 noclip_anglehack = false;
227                 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
228                 SV_ClientPrint("noclip OFF\n");
229         }
230 }
231
232 /*
233 ==================
234 Host_Fly_f
235
236 Sets client to flymode
237 ==================
238 */
239 void Host_Fly_f (void)
240 {
241         if (!allowcheats)
242         {
243                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
244                 return;
245         }
246
247         if (host_client->edict->fields.server->movetype != MOVETYPE_FLY)
248         {
249                 host_client->edict->fields.server->movetype = MOVETYPE_FLY;
250                 SV_ClientPrint("flymode ON\n");
251         }
252         else
253         {
254                 host_client->edict->fields.server->movetype = MOVETYPE_WALK;
255                 SV_ClientPrint("flymode OFF\n");
256         }
257 }
258
259
260 /*
261 ==================
262 Host_Ping_f
263
264 ==================
265 */
266 void Host_Pings_f (void); // called by Host_Ping_f
267 void Host_Ping_f (void)
268 {
269         int i;
270         client_t *client;
271         void (*print) (const char *fmt, ...);
272
273         if (cmd_source == src_command)
274         {
275                 // if running a client, try to send over network so the client's ping report parser will see the report
276                 if (cls.state == ca_connected)
277                 {
278                         Cmd_ForwardToServer ();
279                         return;
280                 }
281                 print = Con_Printf;
282         }
283         else
284                 print = SV_ClientPrintf;
285
286         if (!sv.active)
287                 return;
288
289         print("Client ping times:\n");
290         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
291         {
292                 if (!client->active)
293                         continue;
294                 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
295         }
296
297         // 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)
298         // actually, don't, it confuses old clients (resulting in "unknown command pingplreport" flooding the console)
299         //Host_Pings_f();
300 }
301
302 /*
303 ===============================================================================
304
305 SERVER TRANSITIONS
306
307 ===============================================================================
308 */
309
310 /*
311 ======================
312 Host_Map_f
313
314 handle a
315 map <servername>
316 command from the console.  Active clients are kicked off.
317 ======================
318 */
319 void Host_Map_f (void)
320 {
321         char level[MAX_QPATH];
322
323         if (Cmd_Argc() != 2)
324         {
325                 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
326                 return;
327         }
328
329         // GAME_DELUXEQUAKE - clear warpmark (used by QC)
330         if (gamemode == GAME_DELUXEQUAKE)
331                 Cvar_Set("warpmark", "");
332
333         cls.demonum = -1;               // stop demo loop in case this fails
334
335         CL_Disconnect ();
336         Host_ShutdownServer();
337
338         // remove menu
339         key_dest = key_game;
340
341         svs.serverflags = 0;                    // haven't completed an episode yet
342         allowcheats = sv_cheats.integer != 0;
343         strlcpy(level, Cmd_Argv(1), sizeof(level));
344         SV_SpawnServer(level);
345         if (sv.active && cls.state == ca_disconnected)
346                 CL_EstablishConnection("local:1");
347 }
348
349 /*
350 ==================
351 Host_Changelevel_f
352
353 Goes to a new map, taking all clients along
354 ==================
355 */
356 void Host_Changelevel_f (void)
357 {
358         char level[MAX_QPATH];
359
360         if (Cmd_Argc() != 2)
361         {
362                 Con_Print("changelevel <levelname> : continue game on a new level\n");
363                 return;
364         }
365         // HACKHACKHACK
366         if (!sv.active) {
367                 Host_Map_f();
368                 return;
369         }
370
371         // remove menu
372         key_dest = key_game;
373
374         SV_VM_Begin();
375         SV_SaveSpawnparms ();
376         SV_VM_End();
377         allowcheats = sv_cheats.integer != 0;
378         strlcpy(level, Cmd_Argv(1), sizeof(level));
379         SV_SpawnServer(level);
380         if (sv.active && cls.state == ca_disconnected)
381                 CL_EstablishConnection("local:1");
382 }
383
384 /*
385 ==================
386 Host_Restart_f
387
388 Restarts the current server for a dead player
389 ==================
390 */
391 void Host_Restart_f (void)
392 {
393         char mapname[MAX_QPATH];
394
395         if (Cmd_Argc() != 1)
396         {
397                 Con_Print("restart : restart current level\n");
398                 return;
399         }
400         if (!sv.active)
401         {
402                 Con_Print("Only the server may restart\n");
403                 return;
404         }
405
406         // remove menu
407         key_dest = key_game;
408
409         allowcheats = sv_cheats.integer != 0;
410         strlcpy(mapname, sv.name, sizeof(mapname));
411         SV_SpawnServer(mapname);
412         if (sv.active && cls.state == ca_disconnected)
413                 CL_EstablishConnection("local:1");
414 }
415
416 /*
417 ==================
418 Host_Reconnect_f
419
420 This command causes the client to wait for the signon messages again.
421 This is sent just before a server changes levels
422 ==================
423 */
424 void Host_Reconnect_f (void)
425 {
426         char temp[128];
427         // if not connected, reconnect to the most recent server
428         if (!cls.netcon)
429         {
430                 // if we have connected to a server recently, the userinfo
431                 // will still contain its IP address, so get the address...
432                 InfoString_GetValue(cls.userinfo, "*ip", temp, sizeof(temp));
433                 if (temp[0])
434                         CL_EstablishConnection(temp);
435                 else
436                         Con_Printf("Reconnect to what server?  (you have not connected to a server yet)\n");
437                 return;
438         }
439         // if connected, do something based on protocol
440         if (cls.protocol == PROTOCOL_QUAKEWORLD)
441         {
442                 // quakeworld can just re-login
443                 if (cls.qw_downloadmemory)  // don't change when downloading
444                         return;
445
446                 S_StopAllSounds();
447
448                 if (cls.state == ca_connected && cls.signon < SIGNONS)
449                 {
450                         Con_Printf("reconnecting...\n");
451                         MSG_WriteChar(&cls.netcon->message, qw_clc_stringcmd);
452                         MSG_WriteString(&cls.netcon->message, "new");
453                 }
454         }
455         else
456         {
457                 // netquake uses reconnect on level changes (silly)
458                 if (Cmd_Argc() != 1)
459                 {
460                         Con_Print("reconnect : wait for signon messages again\n");
461                         return;
462                 }
463                 if (!cls.signon)
464                 {
465                         Con_Print("reconnect: no signon, ignoring reconnect\n");
466                         return;
467                 }
468                 cls.signon = 0;         // need new connection messages
469         }
470 }
471
472 /*
473 =====================
474 Host_Connect_f
475
476 User command to connect to server
477 =====================
478 */
479 void Host_Connect_f (void)
480 {
481         if (Cmd_Argc() != 2)
482         {
483                 Con_Print("connect <serveraddress> : connect to a multiplayer game\n");
484                 return;
485         }
486         CL_EstablishConnection(Cmd_Argv(1));
487 }
488
489
490 /*
491 ===============================================================================
492
493 LOAD / SAVE GAME
494
495 ===============================================================================
496 */
497
498 #define SAVEGAME_VERSION        5
499
500 void Host_Savegame_to (const char *name)
501 {
502         qfile_t *f;
503         int             i, lightstyles = 64;
504         char    comment[SAVEGAME_COMMENT_LENGTH+1];
505         qboolean isserver;
506
507         // first we have to figure out if this can be saved in 64 lightstyles
508         // (for Quake compatibility)
509         for (i=64 ; i<MAX_LIGHTSTYLES ; i++)
510                 if (sv.lightstyles[i][0])
511                         lightstyles = i+1;
512
513         isserver = !strcmp(PRVM_NAME, "server");
514
515         Con_Printf("Saving game to %s...\n", name);
516         f = FS_OpenRealFile(name, "wb", false);
517         if (!f)
518         {
519                 Con_Print("ERROR: couldn't open.\n");
520                 return;
521         }
522
523         FS_Printf(f, "%i\n", SAVEGAME_VERSION);
524
525         memset(comment, 0, sizeof(comment));
526         if(isserver)
527                 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);
528         else
529                 dpsnprintf(comment, sizeof(comment), "(crash dump of %s progs)", PRVM_NAME);
530         // convert space to _ to make stdio happy
531         // LordHavoc: convert control characters to _ as well
532         for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
533                 if (ISWHITESPACEORCONTROL(comment[i]))
534                         comment[i] = '_';
535         comment[SAVEGAME_COMMENT_LENGTH] = '\0';
536
537         FS_Printf(f, "%s\n", comment);
538         if(isserver)
539         {
540                 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
541                         FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
542                 FS_Printf(f, "%d\n", current_skill);
543                 FS_Printf(f, "%s\n", sv.name);
544                 FS_Printf(f, "%f\n",sv.time);
545         }
546         else
547         {
548                 for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
549                         FS_Printf(f, "(dummy)\n");
550                 FS_Printf(f, "%d\n", 0);
551                 FS_Printf(f, "%s\n", "(dummy)");
552                 FS_Printf(f, "%f\n", realtime);
553         }
554
555         // write the light styles
556         for (i=0 ; i<lightstyles ; i++)
557         {
558                 if (isserver && sv.lightstyles[i][0])
559                         FS_Printf(f, "%s\n", sv.lightstyles[i]);
560                 else
561                         FS_Print(f,"m\n");
562         }
563
564         PRVM_ED_WriteGlobals (f);
565         for (i=0 ; i<prog->num_edicts ; i++)
566         {
567                 FS_Printf(f,"// edict %d\n", i);
568                 //Con_Printf("edict %d...\n", i);
569                 PRVM_ED_Write (f, PRVM_EDICT_NUM(i));
570         }
571
572 #if 1
573         FS_Printf(f,"/*\n");
574         FS_Printf(f,"// DarkPlaces extended savegame\n");
575         // darkplaces extension - extra lightstyles, support for color lightstyles
576         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
577                 if (isserver && sv.lightstyles[i][0])
578                         FS_Printf(f, "sv.lightstyles %i %s\n", i, sv.lightstyles[i]);
579
580         // darkplaces extension - model precaches
581         for (i=1 ; i<MAX_MODELS ; i++)
582                 if (sv.model_precache[i][0])
583                         FS_Printf(f,"sv.model_precache %i %s\n", i, sv.model_precache[i]);
584
585         // darkplaces extension - sound precaches
586         for (i=1 ; i<MAX_SOUNDS ; i++)
587                 if (sv.sound_precache[i][0])
588                         FS_Printf(f,"sv.sound_precache %i %s\n", i, sv.sound_precache[i]);
589         FS_Printf(f,"*/\n");
590 #endif
591
592         FS_Close (f);
593         Con_Print("done.\n");
594 }
595
596 /*
597 ===============
598 Host_Savegame_f
599 ===============
600 */
601 void Host_Savegame_f (void)
602 {
603         char    name[MAX_QPATH];
604
605         if (!sv.active)
606         {
607                 Con_Print("Can't save - no server running.\n");
608                 return;
609         }
610
611         if (cl.islocalgame)
612         {
613                 // singleplayer checks
614                 if (cl.intermission)
615                 {
616                         Con_Print("Can't save in intermission.\n");
617                         return;
618                 }
619
620                 if (svs.clients[0].active && svs.clients[0].edict->fields.server->deadflag)
621                 {
622                         Con_Print("Can't savegame with a dead player\n");
623                         return;
624                 }
625         }
626         else
627                 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");
628
629         if (Cmd_Argc() != 2)
630         {
631                 Con_Print("save <savename> : save a game\n");
632                 return;
633         }
634
635         if (strstr(Cmd_Argv(1), ".."))
636         {
637                 Con_Print("Relative pathnames are not allowed.\n");
638                 return;
639         }
640
641         strlcpy (name, Cmd_Argv(1), sizeof (name));
642         FS_DefaultExtension (name, ".sav", sizeof (name));
643
644         SV_VM_Begin();
645         Host_Savegame_to(name);
646         SV_VM_End();
647 }
648
649
650 /*
651 ===============
652 Host_Loadgame_f
653 ===============
654 */
655 void Host_Loadgame_f (void)
656 {
657         char filename[MAX_QPATH];
658         char mapname[MAX_QPATH];
659         float time;
660         const char *start;
661         const char *end;
662         const char *t;
663         char *text;
664         prvm_edict_t *ent;
665         int i;
666         int entnum;
667         int version;
668         float spawn_parms[NUM_SPAWN_PARMS];
669
670         if (Cmd_Argc() != 2)
671         {
672                 Con_Print("load <savename> : load a game\n");
673                 return;
674         }
675
676         strlcpy (filename, Cmd_Argv(1), sizeof(filename));
677         FS_DefaultExtension (filename, ".sav", sizeof (filename));
678
679         Con_Printf("Loading game from %s...\n", filename);
680
681         // stop playing demos
682         if (cls.demoplayback)
683                 CL_Disconnect ();
684
685         // remove menu
686         key_dest = key_game;
687
688         cls.demonum = -1;               // stop demo loop in case this fails
689
690         t = text = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
691         if (!text)
692         {
693                 Con_Print("ERROR: couldn't open.\n");
694                 return;
695         }
696
697         if(developer_entityparsing.integer)
698                 Con_Printf("Host_Loadgame_f: loading version\n");
699
700         // version
701         COM_ParseToken_Simple(&t, false, false);
702         version = atoi(com_token);
703         if (version != SAVEGAME_VERSION)
704         {
705                 Mem_Free(text);
706                 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
707                 return;
708         }
709
710         if(developer_entityparsing.integer)
711                 Con_Printf("Host_Loadgame_f: loading description\n");
712
713         // description
714         COM_ParseToken_Simple(&t, false, false);
715
716         for (i = 0;i < NUM_SPAWN_PARMS;i++)
717         {
718                 COM_ParseToken_Simple(&t, false, false);
719                 spawn_parms[i] = atof(com_token);
720         }
721         // skill
722         COM_ParseToken_Simple(&t, false, false);
723 // this silliness is so we can load 1.06 save files, which have float skill values
724         current_skill = (int)(atof(com_token) + 0.5);
725         Cvar_SetValue ("skill", (float)current_skill);
726
727         if(developer_entityparsing.integer)
728                 Con_Printf("Host_Loadgame_f: loading mapname\n");
729
730         // mapname
731         COM_ParseToken_Simple(&t, false, false);
732         strlcpy (mapname, com_token, sizeof(mapname));
733
734         if(developer_entityparsing.integer)
735                 Con_Printf("Host_Loadgame_f: loading time\n");
736
737         // time
738         COM_ParseToken_Simple(&t, false, false);
739         time = atof(com_token);
740
741         allowcheats = sv_cheats.integer != 0;
742
743         if(developer_entityparsing.integer)
744                 Con_Printf("Host_Loadgame_f: spawning server\n");
745
746         SV_SpawnServer (mapname);
747         if (!sv.active)
748         {
749                 Mem_Free(text);
750                 Con_Print("Couldn't load map\n");
751                 return;
752         }
753         sv.paused = true;               // pause until all clients connect
754         sv.loadgame = true;
755
756         if(developer_entityparsing.integer)
757                 Con_Printf("Host_Loadgame_f: loading light styles\n");
758
759 // load the light styles
760
761         SV_VM_Begin();
762         // -1 is the globals
763         entnum = -1;
764
765         for (i = 0;i < MAX_LIGHTSTYLES;i++)
766         {
767                 // light style
768                 start = t;
769                 COM_ParseToken_Simple(&t, false, false);
770                 // if this is a 64 lightstyle savegame produced by Quake, stop now
771                 // we have to check this because darkplaces may save more than 64
772                 if (com_token[0] == '{')
773                 {
774                         t = start;
775                         break;
776                 }
777                 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
778         }
779
780         if(developer_entityparsing.integer)
781                 Con_Printf("Host_Loadgame_f: skipping until globals\n");
782
783         // now skip everything before the first opening brace
784         // (this is for forward compatibility, so that older versions (at
785         // least ones with this fix) can load savegames with extra data before the
786         // first brace, as might be produced by a later engine version)
787         for (;;)
788         {
789                 start = t;
790                 if (!COM_ParseToken_Simple(&t, false, false))
791                         break;
792                 if (com_token[0] == '{')
793                 {
794                         t = start;
795                         break;
796                 }
797         }
798
799 // load the edicts out of the savegame file
800         end = t;
801         for (;;)
802         {
803                 start = t;
804                 while (COM_ParseToken_Simple(&t, false, false))
805                         if (!strcmp(com_token, "}"))
806                                 break;
807                 if (!COM_ParseToken_Simple(&start, false, false))
808                 {
809                         // end of file
810                         break;
811                 }
812                 if (strcmp(com_token,"{"))
813                 {
814                         Mem_Free(text);
815                         Host_Error ("First token isn't a brace");
816                 }
817
818                 if (entnum == -1)
819                 {
820                         if(developer_entityparsing.integer)
821                                 Con_Printf("Host_Loadgame_f: loading globals\n");
822
823                         // parse the global vars
824                         PRVM_ED_ParseGlobals (start);
825                 }
826                 else
827                 {
828                         // parse an edict
829                         if (entnum >= MAX_EDICTS)
830                         {
831                                 Mem_Free(text);
832                                 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)", MAX_EDICTS);
833                         }
834                         while (entnum >= prog->max_edicts)
835                                 PRVM_MEM_IncreaseEdicts();
836                         ent = PRVM_EDICT_NUM(entnum);
837                         memset (ent->fields.server, 0, prog->progs->entityfields * 4);
838                         ent->priv.server->free = false;
839
840                         if(developer_entityparsing.integer)
841                                 Con_Printf("Host_Loadgame_f: loading edict %d\n", entnum);
842
843                         PRVM_ED_ParseEdict (start, ent);
844
845                         // link it into the bsp tree
846                         if (!ent->priv.server->free)
847                                 SV_LinkEdict (ent, false);
848                 }
849
850                 end = t;
851                 entnum++;
852         }
853
854         prog->num_edicts = entnum;
855         sv.time = time;
856
857         for (i = 0;i < NUM_SPAWN_PARMS;i++)
858                 svs.clients[0].spawn_parms[i] = spawn_parms[i];
859
860         if(developer_entityparsing.integer)
861                 Con_Printf("Host_Loadgame_f: skipping until extended data\n");
862
863         // read extended data if present
864         // the extended data is stored inside a /* */ comment block, which the
865         // parser intentionally skips, so we have to check for it manually here
866         if(end)
867         {
868                 while (*end == '\r' || *end == '\n')
869                         end++;
870                 if (end[0] == '/' && end[1] == '*' && (end[2] == '\r' || end[2] == '\n'))
871                 {
872                         if(developer_entityparsing.integer)
873                                 Con_Printf("Host_Loadgame_f: loading extended data\n");
874
875                         Con_Printf("Loading extended DarkPlaces savegame\n");
876                         t = end + 2;
877                         memset(sv.lightstyles[0], 0, sizeof(sv.lightstyles));
878                         memset(sv.model_precache[0], 0, sizeof(sv.model_precache));
879                         memset(sv.sound_precache[0], 0, sizeof(sv.sound_precache));
880                         while (COM_ParseToken_Simple(&t, false, false))
881                         {
882                                 if (!strcmp(com_token, "sv.lightstyles"))
883                                 {
884                                         COM_ParseToken_Simple(&t, false, false);
885                                         i = atoi(com_token);
886                                         COM_ParseToken_Simple(&t, false, false);
887                                         if (i >= 0 && i < MAX_LIGHTSTYLES)
888                                                 strlcpy(sv.lightstyles[i], com_token, sizeof(sv.lightstyles[i]));
889                                         else
890                                                 Con_Printf("unsupported lightstyle %i \"%s\"\n", i, com_token);
891                                 }
892                                 else if (!strcmp(com_token, "sv.model_precache"))
893                                 {
894                                         COM_ParseToken_Simple(&t, false, false);
895                                         i = atoi(com_token);
896                                         COM_ParseToken_Simple(&t, false, false);
897                                         if (i >= 0 && i < MAX_MODELS)
898                                         {
899                                                 strlcpy(sv.model_precache[i], com_token, sizeof(sv.model_precache[i]));
900                                                 sv.models[i] = Mod_ForName (sv.model_precache[i], true, false, false);
901                                         }
902                                         else
903                                                 Con_Printf("unsupported model %i \"%s\"\n", i, com_token);
904                                 }
905                                 else if (!strcmp(com_token, "sv.sound_precache"))
906                                 {
907                                         COM_ParseToken_Simple(&t, false, false);
908                                         i = atoi(com_token);
909                                         COM_ParseToken_Simple(&t, false, false);
910                                         if (i >= 0 && i < MAX_SOUNDS)
911                                                 strlcpy(sv.sound_precache[i], com_token, sizeof(sv.sound_precache[i]));
912                                         else
913                                                 Con_Printf("unsupported sound %i \"%s\"\n", i, com_token);
914                                 }
915                                 // skip any trailing text or unrecognized commands
916                                 while (COM_ParseToken_Simple(&t, true, false) && strcmp(com_token, "\n"))
917                                         ;
918                         }
919                 }
920         }
921         Mem_Free(text);
922
923         if(developer_entityparsing.integer)
924                 Con_Printf("Host_Loadgame_f: finished\n");
925
926         SV_VM_End();
927
928         // make sure we're connected to loopback
929         if (sv.active && cls.state == ca_disconnected)
930                 CL_EstablishConnection("local:1");
931 }
932
933 //============================================================================
934
935 /*
936 ======================
937 Host_Name_f
938 ======================
939 */
940 cvar_t cl_name = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_name", "player", "internal storage cvar for current player name (changed by name command)"};
941 void Host_Name_f (void)
942 {
943         int i, j;
944         qboolean valid_colors;
945         char newName[sizeof(host_client->name)];
946
947         if (Cmd_Argc () == 1)
948         {
949                 Con_Printf("\"name\" is \"%s\"\n", cl_name.string);
950                 return;
951         }
952
953         if (Cmd_Argc () == 2)
954                 strlcpy (newName, Cmd_Argv(1), sizeof (newName));
955         else
956                 strlcpy (newName, Cmd_Args(), sizeof (newName));
957
958         if (cmd_source == src_command)
959         {
960                 Cvar_Set ("_cl_name", newName);
961                 return;
962         }
963
964         if (realtime < host_client->nametime)
965         {
966                 SV_ClientPrintf("You can't change name more than once every 5 seconds!\n");
967                 return;
968         }
969
970         host_client->nametime = realtime + 5;
971
972         // point the string back at updateclient->name to keep it safe
973         strlcpy (host_client->name, newName, sizeof (host_client->name));
974
975         for (i = 0, j = 0;host_client->name[i];i++)
976                 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
977                         host_client->name[j++] = host_client->name[i];
978         host_client->name[j] = 0;
979
980         if(host_client->name[0] == 1 || host_client->name[0] == 2)
981         // may interfere with chat area, and will needlessly beep; so let's add a ^7
982         {
983                 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
984                 host_client->name[sizeof(host_client->name) - 1] = 0;
985                 host_client->name[0] = STRING_COLOR_TAG;
986                 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
987         }
988
989         COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
990         if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
991         {
992                 size_t l;
993                 l = strlen(host_client->name);
994                 if(l < sizeof(host_client->name) - 1)
995                 {
996                         // duplicate the color tag to escape it
997                         host_client->name[i] = STRING_COLOR_TAG;
998                         host_client->name[i+1] = 0;
999                         //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1000                 }
1001                 else
1002                 {
1003                         // remove the last character to fix the color code
1004                         host_client->name[l-1] = 0;
1005                         //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1006                 }
1007         }
1008
1009         // find the last color tag offset and decide if we need to add a reset tag
1010         for (i = 0, j = -1;host_client->name[i];i++)
1011         {
1012                 if (host_client->name[i] == STRING_COLOR_TAG)
1013                 {
1014                         if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1015                         {
1016                                 j = i;
1017                                 // if this happens to be a reset  tag then we don't need one
1018                                 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1019                                         j = -1;
1020                                 i++;
1021                                 continue;
1022                         }
1023                         if (host_client->name[i+1] == STRING_COLOR_RGB_DEFAULT && isxdigit(host_client->name[i+2]) && isxdigit(host_client->name[i+3]) && isxdigit(host_client->name[i+4]))
1024                         {
1025                                 j = i;
1026                                 i += 4;
1027                                 continue;
1028                         }
1029                         if (host_client->name[i+1] == STRING_COLOR_TAG)
1030                         {
1031                                 i++;
1032                                 continue;
1033                         }
1034                 }
1035         }
1036         // does not end in the default color string, so add it
1037         if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1038                 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1039
1040         host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1041         if (strcmp(host_client->old_name, host_client->name))
1042         {
1043                 if (host_client->spawned)
1044                         SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1045                 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1046                 // send notification to all clients
1047                 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1048                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1049                 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1050                 SV_WriteNetnameIntoDemo(host_client);
1051         }
1052 }
1053
1054 /*
1055 ======================
1056 Host_Playermodel_f
1057 ======================
1058 */
1059 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1060 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1061 void Host_Playermodel_f (void)
1062 {
1063         int i, j;
1064         char newPath[sizeof(host_client->playermodel)];
1065
1066         if (Cmd_Argc () == 1)
1067         {
1068                 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1069                 return;
1070         }
1071
1072         if (Cmd_Argc () == 2)
1073                 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1074         else
1075                 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1076
1077         for (i = 0, j = 0;newPath[i];i++)
1078                 if (newPath[i] != '\r' && newPath[i] != '\n')
1079                         newPath[j++] = newPath[i];
1080         newPath[j] = 0;
1081
1082         if (cmd_source == src_command)
1083         {
1084                 Cvar_Set ("_cl_playermodel", newPath);
1085                 return;
1086         }
1087
1088         /*
1089         if (realtime < host_client->nametime)
1090         {
1091                 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1092                 return;
1093         }
1094
1095         host_client->nametime = realtime + 5;
1096         */
1097
1098         // point the string back at updateclient->name to keep it safe
1099         strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1100         if( prog->fieldoffsets.playermodel >= 0 )
1101                 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1102         if (strcmp(host_client->old_model, host_client->playermodel))
1103         {
1104                 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1105                 /*// send notification to all clients
1106                 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1107                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1108                 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1109         }
1110 }
1111
1112 /*
1113 ======================
1114 Host_Playerskin_f
1115 ======================
1116 */
1117 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1118 void Host_Playerskin_f (void)
1119 {
1120         int i, j;
1121         char newPath[sizeof(host_client->playerskin)];
1122
1123         if (Cmd_Argc () == 1)
1124         {
1125                 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1126                 return;
1127         }
1128
1129         if (Cmd_Argc () == 2)
1130                 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1131         else
1132                 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1133
1134         for (i = 0, j = 0;newPath[i];i++)
1135                 if (newPath[i] != '\r' && newPath[i] != '\n')
1136                         newPath[j++] = newPath[i];
1137         newPath[j] = 0;
1138
1139         if (cmd_source == src_command)
1140         {
1141                 Cvar_Set ("_cl_playerskin", newPath);
1142                 return;
1143         }
1144
1145         /*
1146         if (realtime < host_client->nametime)
1147         {
1148                 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1149                 return;
1150         }
1151
1152         host_client->nametime = realtime + 5;
1153         */
1154
1155         // point the string back at updateclient->name to keep it safe
1156         strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1157         if( prog->fieldoffsets.playerskin >= 0 )
1158                 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1159         if (strcmp(host_client->old_skin, host_client->playerskin))
1160         {
1161                 //if (host_client->spawned)
1162                 //      SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1163                 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1164                 /*// send notification to all clients
1165                 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1166                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1167                 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1168         }
1169 }
1170
1171 void Host_Version_f (void)
1172 {
1173         Con_Printf("Version: %s build %s\n", gamename, buildstring);
1174 }
1175
1176 void Host_Say(qboolean teamonly)
1177 {
1178         client_t *save;
1179         int j, quoted;
1180         const char *p1;
1181         char *p2;
1182         // LordHavoc: long say messages
1183         char text[1024];
1184         qboolean fromServer = false;
1185
1186         if (cmd_source == src_command)
1187         {
1188                 if (cls.state == ca_dedicated)
1189                 {
1190                         fromServer = true;
1191                         teamonly = false;
1192                 }
1193                 else
1194                 {
1195                         Cmd_ForwardToServer ();
1196                         return;
1197                 }
1198         }
1199
1200         if (Cmd_Argc () < 2)
1201                 return;
1202
1203         if (!teamplay.integer)
1204                 teamonly = false;
1205
1206         p1 = Cmd_Args();
1207         quoted = false;
1208         if (*p1 == '\"')
1209         {
1210                 quoted = true;
1211                 p1++;
1212         }
1213         // note this uses the chat prefix \001
1214         if (!fromServer && !teamonly)
1215                 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1216         else if (!fromServer && teamonly)
1217                 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1218         else if(*(sv_adminnick.string))
1219                 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1220         else
1221                 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1222         p2 = text + strlen(text);
1223         while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1224         {
1225                 if (p2[-1] == '\"' && quoted)
1226                         quoted = false;
1227                 p2[-1] = 0;
1228                 p2--;
1229         }
1230         strlcat(text, "\n", sizeof(text));
1231
1232         // note: save is not a valid edict if fromServer is true
1233         save = host_client;
1234         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1235                 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1236                         SV_ClientPrint(text);
1237         host_client = save;
1238
1239         if (cls.state == ca_dedicated)
1240                 Con_Print(&text[1]);
1241 }
1242
1243
1244 void Host_Say_f(void)
1245 {
1246         Host_Say(false);
1247 }
1248
1249
1250 void Host_Say_Team_f(void)
1251 {
1252         Host_Say(true);
1253 }
1254
1255
1256 void Host_Tell_f(void)
1257 {
1258         const char *playername_start = NULL;
1259         size_t playername_length = 0;
1260         int playernumber = 0;
1261         client_t *save;
1262         int j;
1263         const char *p1, *p2;
1264         char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1265         qboolean fromServer = false;
1266
1267         if (cmd_source == src_command)
1268         {
1269                 if (cls.state == ca_dedicated)
1270                         fromServer = true;
1271                 else
1272                 {
1273                         Cmd_ForwardToServer ();
1274                         return;
1275                 }
1276         }
1277
1278         if (Cmd_Argc () < 2)
1279                 return;
1280
1281         // note this uses the chat prefix \001
1282         if (!fromServer)
1283                 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1284         else if(*(sv_adminnick.string))
1285                 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1286         else
1287                 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1288
1289         p1 = Cmd_Args();
1290         p2 = p1 + strlen(p1);
1291         // remove the target name
1292         while (p1 < p2 && *p1 == ' ')
1293                 p1++;
1294         if(*p1 == '#')
1295         {
1296                 ++p1;
1297                 while (p1 < p2 && *p1 == ' ')
1298                         p1++;
1299                 while (p1 < p2 && isdigit(*p1))
1300                 {
1301                         playernumber = playernumber * 10 + (*p1 - '0');
1302                         p1++;
1303                 }
1304                 --playernumber;
1305         }
1306         else if(*p1 == '"')
1307         {
1308                 ++p1;
1309                 playername_start = p1;
1310                 while (p1 < p2 && *p1 != '"')
1311                         p1++;
1312                 playername_length = p1 - playername_start;
1313                 if(p1 < p2)
1314                         p1++;
1315         }
1316         else
1317         {
1318                 playername_start = p1;
1319                 while (p1 < p2 && *p1 != ' ')
1320                         p1++;
1321                 playername_length = p1 - playername_start;
1322         }
1323         while (p1 < p2 && *p1 == ' ')
1324                 p1++;
1325         if(playername_start)
1326         {
1327                 // set playernumber to the right client
1328                 char namebuf[128];
1329                 if(playername_length >= sizeof(namebuf))
1330                 {
1331                         if (fromServer)
1332                                 Con_Print("Host_Tell: too long player name/ID\n");
1333                         else
1334                                 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1335                         return;
1336                 }
1337                 memcpy(namebuf, playername_start, playername_length);
1338                 namebuf[playername_length] = 0;
1339                 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1340                 {
1341                         if (!svs.clients[playernumber].active)
1342                                 continue;
1343                         if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1344                                 break;
1345                 }
1346         }
1347         if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1348         {
1349                 if (fromServer)
1350                         Con_Print("Host_Tell: invalid player name/ID\n");
1351                 else
1352                         SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1353                 return;
1354         }
1355         // remove trailing newlines
1356         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1357                 p2--;
1358         // remove quotes if present
1359         if (*p1 == '"')
1360         {
1361                 p1++;
1362                 if (p2[-1] == '"')
1363                         p2--;
1364                 else if (fromServer)
1365                         Con_Print("Host_Tell: missing end quote\n");
1366                 else
1367                         SV_ClientPrint("Host_Tell: missing end quote\n");
1368         }
1369         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1370                 p2--;
1371         if(p1 == p2)
1372                 return; // empty say
1373         for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1374                 text[j++] = *p1++;
1375         text[j++] = '\n';
1376         text[j++] = 0;
1377
1378         save = host_client;
1379         host_client = svs.clients + playernumber;
1380         SV_ClientPrint(text);
1381         host_client = save;
1382 }
1383
1384
1385 /*
1386 ==================
1387 Host_Color_f
1388 ==================
1389 */
1390 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1391 void Host_Color(int changetop, int changebottom)
1392 {
1393         int top, bottom, playercolor;
1394
1395         // get top and bottom either from the provided values or the current values
1396         // (allows changing only top or bottom, or both at once)
1397         top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1398         bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1399
1400         top &= 15;
1401         bottom &= 15;
1402         // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1403         //if (top > 13)
1404         //      top = 13;
1405         //if (bottom > 13)
1406         //      bottom = 13;
1407
1408         playercolor = top*16 + bottom;
1409
1410         if (cmd_source == src_command)
1411         {
1412                 Cvar_SetValueQuick(&cl_color, playercolor);
1413                 return;
1414         }
1415
1416         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1417                 return;
1418
1419         if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1420         {
1421                 Con_DPrint("Calling SV_ChangeTeam\n");
1422                 prog->globals.server->time = sv.time;
1423                 prog->globals.generic[OFS_PARM0] = playercolor;
1424                 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1425                 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1426         }
1427         else
1428         {
1429                 prvm_eval_t *val;
1430                 if (host_client->edict)
1431                 {
1432                         if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1433                                 val->_float = playercolor;
1434                         host_client->edict->fields.server->team = bottom + 1;
1435                 }
1436                 host_client->colors = playercolor;
1437                 if (host_client->old_colors != host_client->colors)
1438                 {
1439                         host_client->old_colors = host_client->colors;
1440                         // send notification to all clients
1441                         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1442                         MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1443                         MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1444                 }
1445         }
1446 }
1447
1448 void Host_Color_f(void)
1449 {
1450         int             top, bottom;
1451
1452         if (Cmd_Argc() == 1)
1453         {
1454                 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1455                 Con_Print("color <0-15> [0-15]\n");
1456                 return;
1457         }
1458
1459         if (Cmd_Argc() == 2)
1460                 top = bottom = atoi(Cmd_Argv(1));
1461         else
1462         {
1463                 top = atoi(Cmd_Argv(1));
1464                 bottom = atoi(Cmd_Argv(2));
1465         }
1466         Host_Color(top, bottom);
1467 }
1468
1469 void Host_TopColor_f(void)
1470 {
1471         if (Cmd_Argc() == 1)
1472         {
1473                 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1474                 Con_Print("topcolor <0-15>\n");
1475                 return;
1476         }
1477
1478         Host_Color(atoi(Cmd_Argv(1)), -1);
1479 }
1480
1481 void Host_BottomColor_f(void)
1482 {
1483         if (Cmd_Argc() == 1)
1484         {
1485                 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1486                 Con_Print("bottomcolor <0-15>\n");
1487                 return;
1488         }
1489
1490         Host_Color(-1, atoi(Cmd_Argv(1)));
1491 }
1492
1493 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1494 void Host_Rate_f(void)
1495 {
1496         int rate;
1497
1498         if (Cmd_Argc() != 2)
1499         {
1500                 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1501                 Con_Print("rate <bytespersecond>\n");
1502                 return;
1503         }
1504
1505         rate = atoi(Cmd_Argv(1));
1506
1507         if (cmd_source == src_command)
1508         {
1509                 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1510                 return;
1511         }
1512
1513         host_client->rate = rate;
1514 }
1515
1516 /*
1517 ==================
1518 Host_Kill_f
1519 ==================
1520 */
1521 void Host_Kill_f (void)
1522 {
1523         if (host_client->edict->fields.server->health <= 0)
1524         {
1525                 SV_ClientPrint("Can't suicide -- already dead!\n");
1526                 return;
1527         }
1528
1529         prog->globals.server->time = sv.time;
1530         prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1531         PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1532 }
1533
1534
1535 /*
1536 ==================
1537 Host_Pause_f
1538 ==================
1539 */
1540 void Host_Pause_f (void)
1541 {
1542         if (!pausable.integer)
1543                 SV_ClientPrint("Pause not allowed.\n");
1544         else
1545         {
1546                 sv.paused ^= 1;
1547                 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1548                 // send notification to all clients
1549                 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1550                 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1551         }
1552 }
1553
1554 /*
1555 ======================
1556 Host_PModel_f
1557 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1558 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1559 ======================
1560 */
1561 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)"};
1562 static void Host_PModel_f (void)
1563 {
1564         int i;
1565         prvm_eval_t *val;
1566
1567         if (Cmd_Argc () == 1)
1568         {
1569                 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1570                 return;
1571         }
1572         i = atoi(Cmd_Argv(1));
1573
1574         if (cmd_source == src_command)
1575         {
1576                 if (cl_pmodel.integer == i)
1577                         return;
1578                 Cvar_SetValue ("_cl_pmodel", i);
1579                 if (cls.state == ca_connected)
1580                         Cmd_ForwardToServer ();
1581                 return;
1582         }
1583
1584         if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1585                 val->_float = i;
1586 }
1587
1588 //===========================================================================
1589
1590
1591 /*
1592 ==================
1593 Host_PreSpawn_f
1594 ==================
1595 */
1596 void Host_PreSpawn_f (void)
1597 {
1598         if (host_client->spawned)
1599         {
1600                 Con_Print("prespawn not valid -- already spawned\n");
1601                 return;
1602         }
1603
1604         if (host_client->netconnection)
1605         {
1606                 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1607                 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1608                 MSG_WriteByte (&host_client->netconnection->message, 2);
1609                 host_client->sendsignon = 0;            // enable unlimited sends again
1610         }
1611
1612         // reset the name change timer because the client will send name soon
1613         host_client->nametime = 0;
1614 }
1615
1616 /*
1617 ==================
1618 Host_Spawn_f
1619 ==================
1620 */
1621 void Host_Spawn_f (void)
1622 {
1623         int i;
1624         client_t *client;
1625         int stats[MAX_CL_STATS];
1626
1627         if (host_client->spawned)
1628         {
1629                 Con_Print("Spawn not valid -- already spawned\n");
1630                 return;
1631         }
1632
1633         // reset name change timer again because they might want to change name
1634         // again in the first 5 seconds after connecting
1635         host_client->nametime = 0;
1636
1637         // LordHavoc: moved this above the QC calls at FrikaC's request
1638         // LordHavoc: commented this out
1639         //if (host_client->netconnection)
1640         //      SZ_Clear (&host_client->netconnection->message);
1641
1642         // run the entrance script
1643         if (sv.loadgame)
1644         {
1645                 // loaded games are fully initialized already
1646                 if (prog->funcoffsets.RestoreGame)
1647                 {
1648                         Con_DPrint("Calling RestoreGame\n");
1649                         prog->globals.server->time = sv.time;
1650                         prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1651                         PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1652                 }
1653         }
1654         else
1655         {
1656                 //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);
1657
1658                 // copy spawn parms out of the client_t
1659                 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1660                         (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1661
1662                 // call the spawn function
1663                 host_client->clientconnectcalled = true;
1664                 prog->globals.server->time = sv.time;
1665                 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1666                 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1667
1668                 if (cls.state == ca_dedicated)
1669                         Con_Printf("%s connected\n", host_client->name);
1670
1671                 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1672         }
1673
1674         if (!host_client->netconnection)
1675                 return;
1676
1677         // send time of update
1678         MSG_WriteByte (&host_client->netconnection->message, svc_time);
1679         MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1680
1681         // send all current names, colors, and frag counts
1682         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1683         {
1684                 if (!client->active)
1685                         continue;
1686                 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1687                 MSG_WriteByte (&host_client->netconnection->message, i);
1688                 MSG_WriteString (&host_client->netconnection->message, client->name);
1689                 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1690                 MSG_WriteByte (&host_client->netconnection->message, i);
1691                 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1692                 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1693                 MSG_WriteByte (&host_client->netconnection->message, i);
1694                 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1695         }
1696
1697         // send all current light styles
1698         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1699         {
1700                 if (sv.lightstyles[i][0])
1701                 {
1702                         MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1703                         MSG_WriteByte (&host_client->netconnection->message, (char)i);
1704                         MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1705                 }
1706         }
1707
1708         // send some stats
1709         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1710         MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1711         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1712
1713         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1714         MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1715         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1716
1717         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1718         MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1719         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1720
1721         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1722         MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1723         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1724
1725         // send a fixangle
1726         // Never send a roll angle, because savegames can catch the server
1727         // in a state where it is expecting the client to correct the angle
1728         // and it won't happen if the game was just loaded, so you wind up
1729         // with a permanent head tilt
1730         if (sv.loadgame)
1731         {
1732                 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1733                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1734                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1735                 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1736         }
1737         else
1738         {
1739                 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1740                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1741                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1742                 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1743         }
1744
1745         SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1746
1747         MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1748         MSG_WriteByte (&host_client->netconnection->message, 3);
1749 }
1750
1751 /*
1752 ==================
1753 Host_Begin_f
1754 ==================
1755 */
1756 void Host_Begin_f (void)
1757 {
1758         host_client->spawned = true;
1759
1760         // LordHavoc: note: this code also exists in SV_DropClient
1761         if (sv.loadgame)
1762         {
1763                 int i;
1764                 for (i = 0;i < svs.maxclients;i++)
1765                         if (svs.clients[i].active && !svs.clients[i].spawned)
1766                                 break;
1767                 if (i == svs.maxclients)
1768                 {
1769                         Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1770                         sv.paused = sv.loadgame = false; // we're basically done with loading now
1771                 }
1772         }
1773 }
1774
1775 //===========================================================================
1776
1777
1778 /*
1779 ==================
1780 Host_Kick_f
1781
1782 Kicks a user off of the server
1783 ==================
1784 */
1785 void Host_Kick_f (void)
1786 {
1787         char *who;
1788         const char *message = NULL;
1789         client_t *save;
1790         int i;
1791         qboolean byNumber = false;
1792
1793         if (!sv.active)
1794                 return;
1795
1796         SV_VM_Begin();
1797         save = host_client;
1798
1799         if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1800         {
1801                 i = (int)(atof(Cmd_Argv(2)) - 1);
1802                 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1803                         return;
1804                 byNumber = true;
1805         }
1806         else
1807         {
1808                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1809                 {
1810                         if (!host_client->active)
1811                                 continue;
1812                         if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1813                                 break;
1814                 }
1815         }
1816
1817         if (i < svs.maxclients)
1818         {
1819                 if (cmd_source == src_command)
1820                 {
1821                         if (cls.state == ca_dedicated)
1822                                 who = "Console";
1823                         else
1824                                 who = cl_name.string;
1825                 }
1826                 else
1827                         who = save->name;
1828
1829                 // can't kick yourself!
1830                 if (host_client == save)
1831                         return;
1832
1833                 if (Cmd_Argc() > 2)
1834                 {
1835                         message = Cmd_Args();
1836                         COM_ParseToken_Simple(&message, false, false);
1837                         if (byNumber)
1838                         {
1839                                 message++;                                                      // skip the #
1840                                 while (*message == ' ')                         // skip white space
1841                                         message++;
1842                                 message += strlen(Cmd_Argv(2)); // skip the number
1843                         }
1844                         while (*message && *message == ' ')
1845                                 message++;
1846                 }
1847                 if (message)
1848                         SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1849                 else
1850                         SV_ClientPrintf("Kicked by %s\n", who);
1851                 SV_DropClient (false); // kicked
1852         }
1853
1854         host_client = save;
1855         SV_VM_End();
1856 }
1857
1858 /*
1859 ===============================================================================
1860
1861 DEBUGGING TOOLS
1862
1863 ===============================================================================
1864 */
1865
1866 /*
1867 ==================
1868 Host_Give_f
1869 ==================
1870 */
1871 void Host_Give_f (void)
1872 {
1873         const char *t;
1874         int v;
1875         prvm_eval_t *val;
1876
1877         if (!allowcheats)
1878         {
1879                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1880                 return;
1881         }
1882
1883         t = Cmd_Argv(1);
1884         v = atoi (Cmd_Argv(2));
1885
1886         switch (t[0])
1887         {
1888         case '0':
1889         case '1':
1890         case '2':
1891         case '3':
1892         case '4':
1893         case '5':
1894         case '6':
1895         case '7':
1896         case '8':
1897         case '9':
1898                 // MED 01/04/97 added hipnotic give stuff
1899                 if (gamemode == GAME_HIPNOTIC)
1900                 {
1901                         if (t[0] == '6')
1902                         {
1903                                 if (t[1] == 'a')
1904                                         host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1905                                 else
1906                                         host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1907                         }
1908                         else if (t[0] == '9')
1909                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1910                         else if (t[0] == '0')
1911                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1912                         else if (t[0] >= '2')
1913                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1914                 }
1915                 else
1916                 {
1917                         if (t[0] >= '2')
1918                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1919                 }
1920                 break;
1921
1922         case 's':
1923                 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1924                         val->_float = v;
1925
1926                 host_client->edict->fields.server->ammo_shells = v;
1927                 break;
1928         case 'n':
1929                 if (gamemode == GAME_ROGUE)
1930                 {
1931                         if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1932                         {
1933                                 val->_float = v;
1934                                 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1935                                         host_client->edict->fields.server->ammo_nails = v;
1936                         }
1937                 }
1938                 else
1939                 {
1940                         host_client->edict->fields.server->ammo_nails = v;
1941                 }
1942                 break;
1943         case 'l':
1944                 if (gamemode == GAME_ROGUE)
1945                 {
1946                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
1947                         if (val)
1948                         {
1949                                 val->_float = v;
1950                                 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1951                                         host_client->edict->fields.server->ammo_nails = v;
1952                         }
1953                 }
1954                 break;
1955         case 'r':
1956                 if (gamemode == GAME_ROGUE)
1957                 {
1958                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
1959                         if (val)
1960                         {
1961                                 val->_float = v;
1962                                 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1963                                         host_client->edict->fields.server->ammo_rockets = v;
1964                         }
1965                 }
1966                 else
1967                 {
1968                         host_client->edict->fields.server->ammo_rockets = v;
1969                 }
1970                 break;
1971         case 'm':
1972                 if (gamemode == GAME_ROGUE)
1973                 {
1974                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
1975                         if (val)
1976                         {
1977                                 val->_float = v;
1978                                 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1979                                         host_client->edict->fields.server->ammo_rockets = v;
1980                         }
1981                 }
1982                 break;
1983         case 'h':
1984                 host_client->edict->fields.server->health = v;
1985                 break;
1986         case 'c':
1987                 if (gamemode == GAME_ROGUE)
1988                 {
1989                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
1990                         if (val)
1991                         {
1992                                 val->_float = v;
1993                                 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1994                                         host_client->edict->fields.server->ammo_cells = v;
1995                         }
1996                 }
1997                 else
1998                 {
1999                         host_client->edict->fields.server->ammo_cells = v;
2000                 }
2001                 break;
2002         case 'p':
2003                 if (gamemode == GAME_ROGUE)
2004                 {
2005                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2006                         if (val)
2007                         {
2008                                 val->_float = v;
2009                                 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2010                                         host_client->edict->fields.server->ammo_cells = v;
2011                         }
2012                 }
2013                 break;
2014         }
2015 }
2016
2017 prvm_edict_t    *FindViewthing (void)
2018 {
2019         int             i;
2020         prvm_edict_t    *e;
2021
2022         for (i=0 ; i<prog->num_edicts ; i++)
2023         {
2024                 e = PRVM_EDICT_NUM(i);
2025                 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2026                         return e;
2027         }
2028         Con_Print("No viewthing on map\n");
2029         return NULL;
2030 }
2031
2032 /*
2033 ==================
2034 Host_Viewmodel_f
2035 ==================
2036 */
2037 void Host_Viewmodel_f (void)
2038 {
2039         prvm_edict_t    *e;
2040         dp_model_t      *m;
2041
2042         if (!sv.active)
2043                 return;
2044
2045         SV_VM_Begin();
2046         e = FindViewthing ();
2047         SV_VM_End();
2048         if (!e)
2049                 return;
2050
2051         m = Mod_ForName (Cmd_Argv(1), false, true, false);
2052         if (!m || !m->loaded || !m->Draw)
2053         {
2054                 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2055                 return;
2056         }
2057
2058         e->fields.server->frame = 0;
2059         cl.model_precache[(int)e->fields.server->modelindex] = m;
2060 }
2061
2062 /*
2063 ==================
2064 Host_Viewframe_f
2065 ==================
2066 */
2067 void Host_Viewframe_f (void)
2068 {
2069         prvm_edict_t    *e;
2070         int             f;
2071         dp_model_t      *m;
2072
2073         if (!sv.active)
2074                 return;
2075
2076         SV_VM_Begin();
2077         e = FindViewthing ();
2078         SV_VM_End();
2079         if (!e)
2080                 return;
2081         m = cl.model_precache[(int)e->fields.server->modelindex];
2082
2083         f = atoi(Cmd_Argv(1));
2084         if (f >= m->numframes)
2085                 f = m->numframes-1;
2086
2087         e->fields.server->frame = f;
2088 }
2089
2090
2091 void PrintFrameName (dp_model_t *m, int frame)
2092 {
2093         if (m->animscenes)
2094                 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2095         else
2096                 Con_Printf("frame %i\n", frame);
2097 }
2098
2099 /*
2100 ==================
2101 Host_Viewnext_f
2102 ==================
2103 */
2104 void Host_Viewnext_f (void)
2105 {
2106         prvm_edict_t    *e;
2107         dp_model_t      *m;
2108
2109         if (!sv.active)
2110                 return;
2111
2112         SV_VM_Begin();
2113         e = FindViewthing ();
2114         SV_VM_End();
2115         if (!e)
2116                 return;
2117         m = cl.model_precache[(int)e->fields.server->modelindex];
2118
2119         e->fields.server->frame = e->fields.server->frame + 1;
2120         if (e->fields.server->frame >= m->numframes)
2121                 e->fields.server->frame = m->numframes - 1;
2122
2123         PrintFrameName (m, (int)e->fields.server->frame);
2124 }
2125
2126 /*
2127 ==================
2128 Host_Viewprev_f
2129 ==================
2130 */
2131 void Host_Viewprev_f (void)
2132 {
2133         prvm_edict_t    *e;
2134         dp_model_t      *m;
2135
2136         if (!sv.active)
2137                 return;
2138
2139         SV_VM_Begin();
2140         e = FindViewthing ();
2141         SV_VM_End();
2142         if (!e)
2143                 return;
2144
2145         m = cl.model_precache[(int)e->fields.server->modelindex];
2146
2147         e->fields.server->frame = e->fields.server->frame - 1;
2148         if (e->fields.server->frame < 0)
2149                 e->fields.server->frame = 0;
2150
2151         PrintFrameName (m, (int)e->fields.server->frame);
2152 }
2153
2154 /*
2155 ===============================================================================
2156
2157 DEMO LOOP CONTROL
2158
2159 ===============================================================================
2160 */
2161
2162
2163 /*
2164 ==================
2165 Host_Startdemos_f
2166 ==================
2167 */
2168 void Host_Startdemos_f (void)
2169 {
2170         int             i, c;
2171
2172         if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2173                 return;
2174
2175         c = Cmd_Argc() - 1;
2176         if (c > MAX_DEMOS)
2177         {
2178                 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2179                 c = MAX_DEMOS;
2180         }
2181         Con_DPrintf("%i demo(s) in loop\n", c);
2182
2183         for (i=1 ; i<c+1 ; i++)
2184                 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2185
2186         // LordHavoc: clear the remaining slots
2187         for (;i <= MAX_DEMOS;i++)
2188                 cls.demos[i-1][0] = 0;
2189
2190         if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2191         {
2192                 cls.demonum = 0;
2193                 CL_NextDemo ();
2194         }
2195         else
2196                 cls.demonum = -1;
2197 }
2198
2199
2200 /*
2201 ==================
2202 Host_Demos_f
2203
2204 Return to looping demos
2205 ==================
2206 */
2207 void Host_Demos_f (void)
2208 {
2209         if (cls.state == ca_dedicated)
2210                 return;
2211         if (cls.demonum == -1)
2212                 cls.demonum = 1;
2213         CL_Disconnect_f ();
2214         CL_NextDemo ();
2215 }
2216
2217 /*
2218 ==================
2219 Host_Stopdemo_f
2220
2221 Return to looping demos
2222 ==================
2223 */
2224 void Host_Stopdemo_f (void)
2225 {
2226         if (!cls.demoplayback)
2227                 return;
2228         CL_Disconnect ();
2229         Host_ShutdownServer ();
2230 }
2231
2232 void Host_SendCvar_f (void)
2233 {
2234         int             i;
2235         cvar_t  *c;
2236         const char *cvarname;
2237         client_t *old;
2238
2239         if(Cmd_Argc() != 2)
2240                 return;
2241         cvarname = Cmd_Argv(1);
2242         if (cls.state == ca_connected)
2243         {
2244                 c = Cvar_FindVar(cvarname);
2245                 // LordHavoc: if there is no such cvar or if it is private, send a
2246                 // reply indicating that it has no value
2247                 if(!c || (c->flags & CVAR_PRIVATE))
2248                         Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2249                 else
2250                         Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2251                 return;
2252         }
2253         if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2254                 return;
2255
2256         old = host_client;
2257         if (cls.state != ca_dedicated)
2258                 i = 1;
2259         else
2260                 i = 0;
2261         for(;i<svs.maxclients;i++)
2262                 if(svs.clients[i].active && svs.clients[i].netconnection)
2263                 {
2264                         host_client = &svs.clients[i];
2265                         Host_ClientCommands("sendcvar %s\n", cvarname);
2266                 }
2267         host_client = old;
2268 }
2269
2270 static void MaxPlayers_f(void)
2271 {
2272         int n;
2273
2274         if (Cmd_Argc() != 2)
2275         {
2276                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
2277                 return;
2278         }
2279
2280         if (sv.active)
2281         {
2282                 Con_Print("maxplayers can not be changed while a server is running.\n");
2283                 return;
2284         }
2285
2286         n = atoi(Cmd_Argv(1));
2287         n = bound(1, n, MAX_SCOREBOARD);
2288         Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2289
2290         if (svs.clients)
2291                 Mem_Free(svs.clients);
2292         svs.maxclients = n;
2293         svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
2294         if (n == 1)
2295                 Cvar_Set ("deathmatch", "0");
2296         else
2297                 Cvar_Set ("deathmatch", "1");
2298 }
2299
2300 //=============================================================================
2301
2302 // QuakeWorld commands
2303
2304 /*
2305 =====================
2306 Host_Rcon_f
2307
2308   Send the rest of the command line over as
2309   an unconnected command.
2310 =====================
2311 */
2312 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2313 {
2314         int i;
2315         lhnetaddress_t to;
2316         lhnetsocket_t *mysocket;
2317
2318         if (!rcon_password.string || !rcon_password.string[0])
2319         {
2320                 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2321                 return;
2322         }
2323
2324         for (i = 0;rcon_password.string[i];i++)
2325         {
2326                 if (ISWHITESPACE(rcon_password.string[i]))
2327                 {
2328                         Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2329                         return;
2330                 }
2331         }
2332
2333         if (cls.netcon)
2334                 to = cls.netcon->peeraddress;
2335         else
2336         {
2337                 if (!rcon_address.string[0])
2338                 {
2339                         Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2340                         return;
2341                 }
2342                 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2343         }
2344         mysocket = NetConn_ChooseClientSocketForAddress(&to);
2345         if (mysocket)
2346         {
2347                 // simply put together the rcon packet and send it
2348                 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
2349         }
2350 }
2351
2352 /*
2353 ====================
2354 Host_User_f
2355
2356 user <name or userid>
2357
2358 Dump userdata / masterdata for a user
2359 ====================
2360 */
2361 void Host_User_f (void) // credit: taken from QuakeWorld
2362 {
2363         int             uid;
2364         int             i;
2365
2366         if (Cmd_Argc() != 2)
2367         {
2368                 Con_Printf ("Usage: user <username / userid>\n");
2369                 return;
2370         }
2371
2372         uid = atoi(Cmd_Argv(1));
2373
2374         for (i = 0;i < cl.maxclients;i++)
2375         {
2376                 if (!cl.scores[i].name[0])
2377                         continue;
2378                 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2379                 {
2380                         InfoString_Print(cl.scores[i].qw_userinfo);
2381                         return;
2382                 }
2383         }
2384         Con_Printf ("User not in server.\n");
2385 }
2386
2387 /*
2388 ====================
2389 Host_Users_f
2390
2391 Dump userids for all current players
2392 ====================
2393 */
2394 void Host_Users_f (void) // credit: taken from QuakeWorld
2395 {
2396         int             i;
2397         int             c;
2398
2399         c = 0;
2400         Con_Printf ("userid frags name\n");
2401         Con_Printf ("------ ----- ----\n");
2402         for (i = 0;i < cl.maxclients;i++)
2403         {
2404                 if (cl.scores[i].name[0])
2405                 {
2406                         Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2407                         c++;
2408                 }
2409         }
2410
2411         Con_Printf ("%i total users\n", c);
2412 }
2413
2414 /*
2415 ==================
2416 Host_FullServerinfo_f
2417
2418 Sent by server when serverinfo changes
2419 ==================
2420 */
2421 // TODO: shouldn't this be a cvar instead?
2422 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2423 {
2424         char temp[512];
2425         if (Cmd_Argc() != 2)
2426         {
2427                 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2428                 return;
2429         }
2430
2431         strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2432         InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2433         cl.qw_teamplay = atoi(temp);
2434 }
2435
2436 /*
2437 ==================
2438 Host_FullInfo_f
2439
2440 Allow clients to change userinfo
2441 ==================
2442 Casey was here :)
2443 */
2444 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2445 {
2446         char key[512];
2447         char value[512];
2448         char *o;
2449         const char *s;
2450
2451         if (Cmd_Argc() != 2)
2452         {
2453                 Con_Printf ("fullinfo <complete info string>\n");
2454                 return;
2455         }
2456
2457         s = Cmd_Argv(1);
2458         if (*s == '\\')
2459                 s++;
2460         while (*s)
2461         {
2462                 o = key;
2463                 while (*s && *s != '\\')
2464                         *o++ = *s++;
2465                 *o = 0;
2466
2467                 if (!*s)
2468                 {
2469                         Con_Printf ("MISSING VALUE\n");
2470                         return;
2471                 }
2472
2473                 o = value;
2474                 s++;
2475                 while (*s && *s != '\\')
2476                         *o++ = *s++;
2477                 *o = 0;
2478
2479                 if (*s)
2480                         s++;
2481
2482                 CL_SetInfo(key, value, false, false, false, false);
2483         }
2484 }
2485
2486 /*
2487 ==================
2488 CL_SetInfo_f
2489
2490 Allow clients to change userinfo
2491 ==================
2492 */
2493 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2494 {
2495         if (Cmd_Argc() == 1)
2496         {
2497                 InfoString_Print(cls.userinfo);
2498                 return;
2499         }
2500         if (Cmd_Argc() != 3)
2501         {
2502                 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2503                 return;
2504         }
2505         CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2506 }
2507
2508 /*
2509 ====================
2510 Host_Packet_f
2511
2512 packet <destination> <contents>
2513
2514 Contents allows \n escape character
2515 ====================
2516 */
2517 void Host_Packet_f (void) // credit: taken from QuakeWorld
2518 {
2519         char send[2048];
2520         int i, l;
2521         const char *in;
2522         char *out;
2523         lhnetaddress_t address;
2524         lhnetsocket_t *mysocket;
2525
2526         if (Cmd_Argc() != 3)
2527         {
2528                 Con_Printf ("packet <destination> <contents>\n");
2529                 return;
2530         }
2531
2532         if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2533         {
2534                 Con_Printf ("Bad address\n");
2535                 return;
2536         }
2537
2538         in = Cmd_Argv(2);
2539         out = send+4;
2540         send[0] = send[1] = send[2] = send[3] = 0xff;
2541
2542         l = (int)strlen (in);
2543         for (i=0 ; i<l ; i++)
2544         {
2545                 if (out >= send + sizeof(send) - 1)
2546                         break;
2547                 if (in[i] == '\\' && in[i+1] == 'n')
2548                 {
2549                         *out++ = '\n';
2550                         i++;
2551                 }
2552                 else if (in[i] == '\\' && in[i+1] == '0')
2553                 {
2554                         *out++ = '\0';
2555                         i++;
2556                 }
2557                 else if (in[i] == '\\' && in[i+1] == 't')
2558                 {
2559                         *out++ = '\t';
2560                         i++;
2561                 }
2562                 else if (in[i] == '\\' && in[i+1] == 'r')
2563                 {
2564                         *out++ = '\r';
2565                         i++;
2566                 }
2567                 else if (in[i] == '\\' && in[i+1] == '"')
2568                 {
2569                         *out++ = '\"';
2570                         i++;
2571                 }
2572                 else
2573                         *out++ = in[i];
2574         }
2575
2576         mysocket = NetConn_ChooseClientSocketForAddress(&address);
2577         if (!mysocket)
2578                 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2579         if (mysocket)
2580                 NetConn_Write(mysocket, send, out - send, &address);
2581 }
2582
2583 /*
2584 ====================
2585 Host_Pings_f
2586
2587 Send back ping and packet loss update for all current players to this player
2588 ====================
2589 */
2590 void Host_Pings_f (void)
2591 {
2592         int             i, j, ping, packetloss;
2593         char temp[128];
2594
2595         if (!host_client->netconnection)
2596                 return;
2597
2598         if (sv.protocol != PROTOCOL_QUAKEWORLD)
2599         {
2600                 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2601                 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2602         }
2603         for (i = 0;i < svs.maxclients;i++)
2604         {
2605                 packetloss = 0;
2606                 if (svs.clients[i].netconnection)
2607                         for (j = 0;j < NETGRAPH_PACKETS;j++)
2608                                 if (svs.clients[i].netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
2609                                         packetloss++;
2610                 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
2611                 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2612                 ping = bound(0, ping, 9999);
2613                 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2614                 {
2615                         // send qw_svc_updateping and qw_svc_updatepl messages
2616                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2617                         MSG_WriteShort(&host_client->netconnection->message, ping);
2618                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2619                         MSG_WriteByte(&host_client->netconnection->message, packetloss);
2620                 }
2621                 else
2622                 {
2623                         // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2624                         dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2625                         MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2626                 }
2627         }
2628         if (sv.protocol != PROTOCOL_QUAKEWORLD)
2629                 MSG_WriteString(&host_client->netconnection->message, "\n");
2630 }
2631
2632 void Host_PingPLReport_f(void)
2633 {
2634         int i;
2635         int l = Cmd_Argc();
2636         if (l > cl.maxclients)
2637                 l = cl.maxclients;
2638         for (i = 0;i < l;i++)
2639         {
2640                 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2641                 cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
2642         }
2643 }
2644
2645 //=============================================================================
2646
2647 /*
2648 ==================
2649 Host_InitCommands
2650 ==================
2651 */
2652 void Host_InitCommands (void)
2653 {
2654         dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2655
2656         Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2657         Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2658         if (gamemode == GAME_NEHAHRA)
2659         {
2660                 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2661                 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2662                 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2663                 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2664                 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2665         }
2666         else
2667         {
2668                 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2669                 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2670                 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2671                 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2672                 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2673         }
2674         Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2675         Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2676         Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2677         Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2678         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)");
2679         Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2680         Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2681         Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2682         Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2683         Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2684         Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2685         Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2686         Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2687         Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2688         Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2689
2690         Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2691         Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2692         Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2693
2694         Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2695         Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2696         Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2697         Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2698
2699         Cvar_RegisterVariable (&cl_name);
2700         Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2701         Cvar_RegisterVariable (&cl_color);
2702         Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2703         Cvar_RegisterVariable (&cl_rate);
2704         Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2705         if (gamemode == GAME_NEHAHRA)
2706         {
2707                 Cvar_RegisterVariable (&cl_pmodel);
2708                 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2709         }
2710
2711         // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2712         Cvar_RegisterVariable (&cl_playermodel);
2713         Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2714         Cvar_RegisterVariable (&cl_playerskin);
2715         Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2716
2717         Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2718         Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2719         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)");
2720         Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2721
2722         Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2723
2724         Cvar_RegisterVariable (&rcon_password);
2725         Cvar_RegisterVariable (&rcon_address);
2726         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)");
2727         Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2728         Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2729         Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2730         Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2731         Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2732         Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2733         Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2734         Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2735
2736         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)");
2737         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)");
2738
2739         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)");
2740         Cvar_RegisterVariable (&r_fixtrans_auto);
2741
2742         Cvar_RegisterVariable (&team);
2743         Cvar_RegisterVariable (&skin);
2744         Cvar_RegisterVariable (&noaim);
2745
2746         Cvar_RegisterVariable(&sv_cheats);
2747         Cvar_RegisterVariable(&sv_adminnick);
2748         Cvar_RegisterVariable(&sv_status_privacy);
2749 }
2750
2751 void Host_NoOperation_f(void)
2752 {
2753 }