]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - host_cmd.c
new tokenizer tokenize_console that matches the console tokenizing
[xonotic/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 (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_TAG)
1024                         {
1025                                 i++;
1026                                 continue;
1027                         }
1028                 }
1029         }
1030         // does not end in the default color string, so add it
1031         if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1032                 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1033
1034         host_client->edict->fields.server->netname = PRVM_SetEngineString(host_client->name);
1035         if (strcmp(host_client->old_name, host_client->name))
1036         {
1037                 if (host_client->spawned)
1038                         SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1039                 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1040                 // send notification to all clients
1041                 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1042                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1043                 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1044                 SV_WriteNetnameIntoDemo(host_client);
1045         }
1046 }
1047
1048 /*
1049 ======================
1050 Host_Playermodel_f
1051 ======================
1052 */
1053 cvar_t cl_playermodel = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playermodel", "", "internal storage cvar for current player model in Nexuiz (changed by playermodel command)"};
1054 // the old cl_playermodel in cl_main has been renamed to __cl_playermodel
1055 void Host_Playermodel_f (void)
1056 {
1057         int i, j;
1058         char newPath[sizeof(host_client->playermodel)];
1059
1060         if (Cmd_Argc () == 1)
1061         {
1062                 Con_Printf("\"playermodel\" is \"%s\"\n", cl_playermodel.string);
1063                 return;
1064         }
1065
1066         if (Cmd_Argc () == 2)
1067                 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1068         else
1069                 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1070
1071         for (i = 0, j = 0;newPath[i];i++)
1072                 if (newPath[i] != '\r' && newPath[i] != '\n')
1073                         newPath[j++] = newPath[i];
1074         newPath[j] = 0;
1075
1076         if (cmd_source == src_command)
1077         {
1078                 Cvar_Set ("_cl_playermodel", newPath);
1079                 return;
1080         }
1081
1082         /*
1083         if (realtime < host_client->nametime)
1084         {
1085                 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1086                 return;
1087         }
1088
1089         host_client->nametime = realtime + 5;
1090         */
1091
1092         // point the string back at updateclient->name to keep it safe
1093         strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1094         if( prog->fieldoffsets.playermodel >= 0 )
1095                 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playermodel)->string = PRVM_SetEngineString(host_client->playermodel);
1096         if (strcmp(host_client->old_model, host_client->playermodel))
1097         {
1098                 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1099                 /*// send notification to all clients
1100                 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1101                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1102                 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1103         }
1104 }
1105
1106 /*
1107 ======================
1108 Host_Playerskin_f
1109 ======================
1110 */
1111 cvar_t cl_playerskin = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_playerskin", "", "internal storage cvar for current player skin in Nexuiz (changed by playerskin command)"};
1112 void Host_Playerskin_f (void)
1113 {
1114         int i, j;
1115         char newPath[sizeof(host_client->playerskin)];
1116
1117         if (Cmd_Argc () == 1)
1118         {
1119                 Con_Printf("\"playerskin\" is \"%s\"\n", cl_playerskin.string);
1120                 return;
1121         }
1122
1123         if (Cmd_Argc () == 2)
1124                 strlcpy (newPath, Cmd_Argv(1), sizeof (newPath));
1125         else
1126                 strlcpy (newPath, Cmd_Args(), sizeof (newPath));
1127
1128         for (i = 0, j = 0;newPath[i];i++)
1129                 if (newPath[i] != '\r' && newPath[i] != '\n')
1130                         newPath[j++] = newPath[i];
1131         newPath[j] = 0;
1132
1133         if (cmd_source == src_command)
1134         {
1135                 Cvar_Set ("_cl_playerskin", newPath);
1136                 return;
1137         }
1138
1139         /*
1140         if (realtime < host_client->nametime)
1141         {
1142                 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1143                 return;
1144         }
1145
1146         host_client->nametime = realtime + 5;
1147         */
1148
1149         // point the string back at updateclient->name to keep it safe
1150         strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1151         if( prog->fieldoffsets.playerskin >= 0 )
1152                 PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.playerskin)->string = PRVM_SetEngineString(host_client->playerskin);
1153         if (strcmp(host_client->old_skin, host_client->playerskin))
1154         {
1155                 //if (host_client->spawned)
1156                 //      SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1157                 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1158                 /*// send notification to all clients
1159                 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1160                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1161                 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1162         }
1163 }
1164
1165 void Host_Version_f (void)
1166 {
1167         Con_Printf("Version: %s build %s\n", gamename, buildstring);
1168 }
1169
1170 void Host_Say(qboolean teamonly)
1171 {
1172         client_t *save;
1173         int j, quoted;
1174         const char *p1;
1175         char *p2;
1176         // LordHavoc: long say messages
1177         char text[1024];
1178         qboolean fromServer = false;
1179
1180         if (cmd_source == src_command)
1181         {
1182                 if (cls.state == ca_dedicated)
1183                 {
1184                         fromServer = true;
1185                         teamonly = false;
1186                 }
1187                 else
1188                 {
1189                         Cmd_ForwardToServer ();
1190                         return;
1191                 }
1192         }
1193
1194         if (Cmd_Argc () < 2)
1195                 return;
1196
1197         if (!teamplay.integer)
1198                 teamonly = false;
1199
1200         p1 = Cmd_Args();
1201         quoted = false;
1202         if (*p1 == '\"')
1203         {
1204                 quoted = true;
1205                 p1++;
1206         }
1207         // note this uses the chat prefix \001
1208         if (!fromServer && !teamonly)
1209                 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
1210         else if (!fromServer && teamonly)
1211                 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
1212         else if(*(sv_adminnick.string))
1213                 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
1214         else
1215                 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
1216         p2 = text + strlen(text);
1217         while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
1218         {
1219                 if (p2[-1] == '\"' && quoted)
1220                         quoted = false;
1221                 p2[-1] = 0;
1222                 p2--;
1223         }
1224         strlcat(text, "\n", sizeof(text));
1225
1226         // note: save is not a valid edict if fromServer is true
1227         save = host_client;
1228         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
1229                 if (host_client->active && (!teamonly || host_client->edict->fields.server->team == save->edict->fields.server->team))
1230                         SV_ClientPrint(text);
1231         host_client = save;
1232
1233         if (cls.state == ca_dedicated)
1234                 Con_Print(&text[1]);
1235 }
1236
1237
1238 void Host_Say_f(void)
1239 {
1240         Host_Say(false);
1241 }
1242
1243
1244 void Host_Say_Team_f(void)
1245 {
1246         Host_Say(true);
1247 }
1248
1249
1250 void Host_Tell_f(void)
1251 {
1252         const char *playername_start = NULL;
1253         size_t playername_length = 0;
1254         int playernumber = 0;
1255         client_t *save;
1256         int j;
1257         const char *p1, *p2;
1258         char text[MAX_INPUTLINE]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
1259         qboolean fromServer = false;
1260
1261         if (cmd_source == src_command)
1262         {
1263                 if (cls.state == ca_dedicated)
1264                         fromServer = true;
1265                 else
1266                 {
1267                         Cmd_ForwardToServer ();
1268                         return;
1269                 }
1270         }
1271
1272         if (Cmd_Argc () < 2)
1273                 return;
1274
1275         // note this uses the chat prefix \001
1276         if (!fromServer)
1277                 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
1278         else if(*(sv_adminnick.string))
1279                 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
1280         else
1281                 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
1282
1283         p1 = Cmd_Args();
1284         p2 = p1 + strlen(p1);
1285         // remove the target name
1286         while (p1 < p2 && *p1 == ' ')
1287                 p1++;
1288         if(*p1 == '#')
1289         {
1290                 ++p1;
1291                 while (p1 < p2 && *p1 == ' ')
1292                         p1++;
1293                 while (p1 < p2 && isdigit(*p1))
1294                 {
1295                         playernumber = playernumber * 10 + (*p1 - '0');
1296                         p1++;
1297                 }
1298                 --playernumber;
1299         }
1300         else if(*p1 == '"')
1301         {
1302                 ++p1;
1303                 playername_start = p1;
1304                 while (p1 < p2 && *p1 != '"')
1305                         p1++;
1306                 playername_length = p1 - playername_start;
1307                 if(p1 < p2)
1308                         p1++;
1309         }
1310         else
1311         {
1312                 playername_start = p1;
1313                 while (p1 < p2 && *p1 != ' ')
1314                         p1++;
1315                 playername_length = p1 - playername_start;
1316         }
1317         while (p1 < p2 && *p1 == ' ')
1318                 p1++;
1319         if(playername_start)
1320         {
1321                 // set playernumber to the right client
1322                 char namebuf[128];
1323                 if(playername_length >= sizeof(namebuf))
1324                 {
1325                         if (fromServer)
1326                                 Con_Print("Host_Tell: too long player name/ID\n");
1327                         else
1328                                 SV_ClientPrint("Host_Tell: too long player name/ID\n");
1329                         return;
1330                 }
1331                 memcpy(namebuf, playername_start, playername_length);
1332                 namebuf[playername_length] = 0;
1333                 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
1334                 {
1335                         if (!svs.clients[playernumber].active)
1336                                 continue;
1337                         if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
1338                                 break;
1339                 }
1340         }
1341         if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
1342         {
1343                 if (fromServer)
1344                         Con_Print("Host_Tell: invalid player name/ID\n");
1345                 else
1346                         SV_ClientPrint("Host_Tell: invalid player name/ID\n");
1347                 return;
1348         }
1349         // remove trailing newlines
1350         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1351                 p2--;
1352         // remove quotes if present
1353         if (*p1 == '"')
1354         {
1355                 p1++;
1356                 if (p2[-1] == '"')
1357                         p2--;
1358                 else if (fromServer)
1359                         Con_Print("Host_Tell: missing end quote\n");
1360                 else
1361                         SV_ClientPrint("Host_Tell: missing end quote\n");
1362         }
1363         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
1364                 p2--;
1365         if(p1 == p2)
1366                 return; // empty say
1367         for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
1368                 text[j++] = *p1++;
1369         text[j++] = '\n';
1370         text[j++] = 0;
1371
1372         save = host_client;
1373         host_client = svs.clients + playernumber;
1374         SV_ClientPrint(text);
1375         host_client = save;
1376 }
1377
1378
1379 /*
1380 ==================
1381 Host_Color_f
1382 ==================
1383 */
1384 cvar_t cl_color = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_color", "0", "internal storage cvar for current player colors (changed by color command)"};
1385 void Host_Color(int changetop, int changebottom)
1386 {
1387         int top, bottom, playercolor;
1388
1389         // get top and bottom either from the provided values or the current values
1390         // (allows changing only top or bottom, or both at once)
1391         top = changetop >= 0 ? changetop : (cl_color.integer >> 4);
1392         bottom = changebottom >= 0 ? changebottom : cl_color.integer;
1393
1394         top &= 15;
1395         bottom &= 15;
1396         // LordHavoc: allowing skin colormaps 14 and 15 by commenting this out
1397         //if (top > 13)
1398         //      top = 13;
1399         //if (bottom > 13)
1400         //      bottom = 13;
1401
1402         playercolor = top*16 + bottom;
1403
1404         if (cmd_source == src_command)
1405         {
1406                 Cvar_SetValueQuick(&cl_color, playercolor);
1407                 return;
1408         }
1409
1410         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1411                 return;
1412
1413         if (host_client->edict && prog->funcoffsets.SV_ChangeTeam)
1414         {
1415                 Con_DPrint("Calling SV_ChangeTeam\n");
1416                 prog->globals.server->time = sv.time;
1417                 prog->globals.generic[OFS_PARM0] = playercolor;
1418                 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1419                 PRVM_ExecuteProgram(prog->funcoffsets.SV_ChangeTeam, "QC function SV_ChangeTeam is missing");
1420         }
1421         else
1422         {
1423                 prvm_eval_t *val;
1424                 if (host_client->edict)
1425                 {
1426                         if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.clientcolors)))
1427                                 val->_float = playercolor;
1428                         host_client->edict->fields.server->team = bottom + 1;
1429                 }
1430                 host_client->colors = playercolor;
1431                 if (host_client->old_colors != host_client->colors)
1432                 {
1433                         host_client->old_colors = host_client->colors;
1434                         // send notification to all clients
1435                         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1436                         MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1437                         MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1438                 }
1439         }
1440 }
1441
1442 void Host_Color_f(void)
1443 {
1444         int             top, bottom;
1445
1446         if (Cmd_Argc() == 1)
1447         {
1448                 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1449                 Con_Print("color <0-15> [0-15]\n");
1450                 return;
1451         }
1452
1453         if (Cmd_Argc() == 2)
1454                 top = bottom = atoi(Cmd_Argv(1));
1455         else
1456         {
1457                 top = atoi(Cmd_Argv(1));
1458                 bottom = atoi(Cmd_Argv(2));
1459         }
1460         Host_Color(top, bottom);
1461 }
1462
1463 void Host_TopColor_f(void)
1464 {
1465         if (Cmd_Argc() == 1)
1466         {
1467                 Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1468                 Con_Print("topcolor <0-15>\n");
1469                 return;
1470         }
1471
1472         Host_Color(atoi(Cmd_Argv(1)), -1);
1473 }
1474
1475 void Host_BottomColor_f(void)
1476 {
1477         if (Cmd_Argc() == 1)
1478         {
1479                 Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1480                 Con_Print("bottomcolor <0-15>\n");
1481                 return;
1482         }
1483
1484         Host_Color(-1, atoi(Cmd_Argv(1)));
1485 }
1486
1487 cvar_t cl_rate = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1488 void Host_Rate_f(void)
1489 {
1490         int rate;
1491
1492         if (Cmd_Argc() != 2)
1493         {
1494                 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1495                 Con_Print("rate <bytespersecond>\n");
1496                 return;
1497         }
1498
1499         rate = atoi(Cmd_Argv(1));
1500
1501         if (cmd_source == src_command)
1502         {
1503                 Cvar_SetValue ("_cl_rate", max(NET_MINRATE, rate));
1504                 return;
1505         }
1506
1507         host_client->rate = rate;
1508 }
1509
1510 /*
1511 ==================
1512 Host_Kill_f
1513 ==================
1514 */
1515 void Host_Kill_f (void)
1516 {
1517         if (host_client->edict->fields.server->health <= 0)
1518         {
1519                 SV_ClientPrint("Can't suicide -- already dead!\n");
1520                 return;
1521         }
1522
1523         prog->globals.server->time = sv.time;
1524         prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1525         PRVM_ExecuteProgram (prog->globals.server->ClientKill, "QC function ClientKill is missing");
1526 }
1527
1528
1529 /*
1530 ==================
1531 Host_Pause_f
1532 ==================
1533 */
1534 void Host_Pause_f (void)
1535 {
1536         if (!pausable.integer)
1537                 SV_ClientPrint("Pause not allowed.\n");
1538         else
1539         {
1540                 sv.paused ^= 1;
1541                 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1542                 // send notification to all clients
1543                 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1544                 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1545         }
1546 }
1547
1548 /*
1549 ======================
1550 Host_PModel_f
1551 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1552 LordHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1553 ======================
1554 */
1555 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)"};
1556 static void Host_PModel_f (void)
1557 {
1558         int i;
1559         prvm_eval_t *val;
1560
1561         if (Cmd_Argc () == 1)
1562         {
1563                 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1564                 return;
1565         }
1566         i = atoi(Cmd_Argv(1));
1567
1568         if (cmd_source == src_command)
1569         {
1570                 if (cl_pmodel.integer == i)
1571                         return;
1572                 Cvar_SetValue ("_cl_pmodel", i);
1573                 if (cls.state == ca_connected)
1574                         Cmd_ForwardToServer ();
1575                 return;
1576         }
1577
1578         if (host_client->edict && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.pmodel)))
1579                 val->_float = i;
1580 }
1581
1582 //===========================================================================
1583
1584
1585 /*
1586 ==================
1587 Host_PreSpawn_f
1588 ==================
1589 */
1590 void Host_PreSpawn_f (void)
1591 {
1592         if (host_client->spawned)
1593         {
1594                 Con_Print("prespawn not valid -- already spawned\n");
1595                 return;
1596         }
1597
1598         if (host_client->netconnection)
1599         {
1600                 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1601                 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1602                 MSG_WriteByte (&host_client->netconnection->message, 2);
1603                 host_client->sendsignon = 0;            // enable unlimited sends again
1604         }
1605
1606         // reset the name change timer because the client will send name soon
1607         host_client->nametime = 0;
1608 }
1609
1610 /*
1611 ==================
1612 Host_Spawn_f
1613 ==================
1614 */
1615 void Host_Spawn_f (void)
1616 {
1617         int i;
1618         client_t *client;
1619         int stats[MAX_CL_STATS];
1620
1621         if (host_client->spawned)
1622         {
1623                 Con_Print("Spawn not valid -- already spawned\n");
1624                 return;
1625         }
1626
1627         // reset name change timer again because they might want to change name
1628         // again in the first 5 seconds after connecting
1629         host_client->nametime = 0;
1630
1631         // LordHavoc: moved this above the QC calls at FrikaC's request
1632         // LordHavoc: commented this out
1633         //if (host_client->netconnection)
1634         //      SZ_Clear (&host_client->netconnection->message);
1635
1636         // run the entrance script
1637         if (sv.loadgame)
1638         {
1639                 // loaded games are fully initialized already
1640                 if (prog->funcoffsets.RestoreGame)
1641                 {
1642                         Con_DPrint("Calling RestoreGame\n");
1643                         prog->globals.server->time = sv.time;
1644                         prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1645                         PRVM_ExecuteProgram(prog->funcoffsets.RestoreGame, "QC function RestoreGame is missing");
1646                 }
1647         }
1648         else
1649         {
1650                 //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);
1651
1652                 // copy spawn parms out of the client_t
1653                 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1654                         (&prog->globals.server->parm1)[i] = host_client->spawn_parms[i];
1655
1656                 // call the spawn function
1657                 host_client->clientconnectcalled = true;
1658                 prog->globals.server->time = sv.time;
1659                 prog->globals.server->self = PRVM_EDICT_TO_PROG(host_client->edict);
1660                 PRVM_ExecuteProgram (prog->globals.server->ClientConnect, "QC function ClientConnect is missing");
1661
1662                 if (cls.state == ca_dedicated)
1663                         Con_Printf("%s connected\n", host_client->name);
1664
1665                 PRVM_ExecuteProgram (prog->globals.server->PutClientInServer, "QC function PutClientInServer is missing");
1666         }
1667
1668         if (!host_client->netconnection)
1669                 return;
1670
1671         // send time of update
1672         MSG_WriteByte (&host_client->netconnection->message, svc_time);
1673         MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1674
1675         // send all current names, colors, and frag counts
1676         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1677         {
1678                 if (!client->active)
1679                         continue;
1680                 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1681                 MSG_WriteByte (&host_client->netconnection->message, i);
1682                 MSG_WriteString (&host_client->netconnection->message, client->name);
1683                 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1684                 MSG_WriteByte (&host_client->netconnection->message, i);
1685                 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1686                 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1687                 MSG_WriteByte (&host_client->netconnection->message, i);
1688                 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1689         }
1690
1691         // send all current light styles
1692         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1693         {
1694                 if (sv.lightstyles[i][0])
1695                 {
1696                         MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1697                         MSG_WriteByte (&host_client->netconnection->message, (char)i);
1698                         MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1699                 }
1700         }
1701
1702         // send some stats
1703         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1704         MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1705         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_secrets);
1706
1707         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1708         MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1709         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->total_monsters);
1710
1711         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1712         MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1713         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->found_secrets);
1714
1715         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1716         MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1717         MSG_WriteLong (&host_client->netconnection->message, (int)prog->globals.server->killed_monsters);
1718
1719         // send a fixangle
1720         // Never send a roll angle, because savegames can catch the server
1721         // in a state where it is expecting the client to correct the angle
1722         // and it won't happen if the game was just loaded, so you wind up
1723         // with a permanent head tilt
1724         if (sv.loadgame)
1725         {
1726                 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1727                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[0], sv.protocol);
1728                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->v_angle[1], sv.protocol);
1729                 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1730         }
1731         else
1732         {
1733                 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1734                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[0], sv.protocol);
1735                 MSG_WriteAngle (&host_client->netconnection->message, host_client->edict->fields.server->angles[1], sv.protocol);
1736                 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1737         }
1738
1739         SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1740
1741         MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1742         MSG_WriteByte (&host_client->netconnection->message, 3);
1743 }
1744
1745 /*
1746 ==================
1747 Host_Begin_f
1748 ==================
1749 */
1750 void Host_Begin_f (void)
1751 {
1752         host_client->spawned = true;
1753
1754         // LordHavoc: note: this code also exists in SV_DropClient
1755         if (sv.loadgame)
1756         {
1757                 int i;
1758                 for (i = 0;i < svs.maxclients;i++)
1759                         if (svs.clients[i].active && !svs.clients[i].spawned)
1760                                 break;
1761                 if (i == svs.maxclients)
1762                 {
1763                         Con_Printf("Loaded game, everyone rejoined - unpausing\n");
1764                         sv.paused = sv.loadgame = false; // we're basically done with loading now
1765                 }
1766         }
1767 }
1768
1769 //===========================================================================
1770
1771
1772 /*
1773 ==================
1774 Host_Kick_f
1775
1776 Kicks a user off of the server
1777 ==================
1778 */
1779 void Host_Kick_f (void)
1780 {
1781         char *who;
1782         const char *message = NULL;
1783         client_t *save;
1784         int i;
1785         qboolean byNumber = false;
1786
1787         if (!sv.active)
1788                 return;
1789
1790         SV_VM_Begin();
1791         save = host_client;
1792
1793         if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1794         {
1795                 i = (int)(atof(Cmd_Argv(2)) - 1);
1796                 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1797                         return;
1798                 byNumber = true;
1799         }
1800         else
1801         {
1802                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1803                 {
1804                         if (!host_client->active)
1805                                 continue;
1806                         if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1807                                 break;
1808                 }
1809         }
1810
1811         if (i < svs.maxclients)
1812         {
1813                 if (cmd_source == src_command)
1814                 {
1815                         if (cls.state == ca_dedicated)
1816                                 who = "Console";
1817                         else
1818                                 who = cl_name.string;
1819                 }
1820                 else
1821                         who = save->name;
1822
1823                 // can't kick yourself!
1824                 if (host_client == save)
1825                         return;
1826
1827                 if (Cmd_Argc() > 2)
1828                 {
1829                         message = Cmd_Args();
1830                         COM_ParseToken_Simple(&message, false, false);
1831                         if (byNumber)
1832                         {
1833                                 message++;                                                      // skip the #
1834                                 while (*message == ' ')                         // skip white space
1835                                         message++;
1836                                 message += strlen(Cmd_Argv(2)); // skip the number
1837                         }
1838                         while (*message && *message == ' ')
1839                                 message++;
1840                 }
1841                 if (message)
1842                         SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1843                 else
1844                         SV_ClientPrintf("Kicked by %s\n", who);
1845                 SV_DropClient (false); // kicked
1846         }
1847
1848         host_client = save;
1849         SV_VM_End();
1850 }
1851
1852 /*
1853 ===============================================================================
1854
1855 DEBUGGING TOOLS
1856
1857 ===============================================================================
1858 */
1859
1860 /*
1861 ==================
1862 Host_Give_f
1863 ==================
1864 */
1865 void Host_Give_f (void)
1866 {
1867         const char *t;
1868         int v;
1869         prvm_eval_t *val;
1870
1871         if (!allowcheats)
1872         {
1873                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1874                 return;
1875         }
1876
1877         t = Cmd_Argv(1);
1878         v = atoi (Cmd_Argv(2));
1879
1880         switch (t[0])
1881         {
1882         case '0':
1883         case '1':
1884         case '2':
1885         case '3':
1886         case '4':
1887         case '5':
1888         case '6':
1889         case '7':
1890         case '8':
1891         case '9':
1892                 // MED 01/04/97 added hipnotic give stuff
1893                 if (gamemode == GAME_HIPNOTIC)
1894                 {
1895                         if (t[0] == '6')
1896                         {
1897                                 if (t[1] == 'a')
1898                                         host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_PROXIMITY_GUN;
1899                                 else
1900                                         host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | IT_GRENADE_LAUNCHER;
1901                         }
1902                         else if (t[0] == '9')
1903                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_LASER_CANNON;
1904                         else if (t[0] == '0')
1905                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | HIT_MJOLNIR;
1906                         else if (t[0] >= '2')
1907                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1908                 }
1909                 else
1910                 {
1911                         if (t[0] >= '2')
1912                                 host_client->edict->fields.server->items = (int)host_client->edict->fields.server->items | (IT_SHOTGUN << (t[0] - '2'));
1913                 }
1914                 break;
1915
1916         case 's':
1917                 if (gamemode == GAME_ROGUE && (val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_shells1)))
1918                         val->_float = v;
1919
1920                 host_client->edict->fields.server->ammo_shells = v;
1921                 break;
1922         case 'n':
1923                 if (gamemode == GAME_ROGUE)
1924                 {
1925                         if ((val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_nails1)))
1926                         {
1927                                 val->_float = v;
1928                                 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1929                                         host_client->edict->fields.server->ammo_nails = v;
1930                         }
1931                 }
1932                 else
1933                 {
1934                         host_client->edict->fields.server->ammo_nails = v;
1935                 }
1936                 break;
1937         case 'l':
1938                 if (gamemode == GAME_ROGUE)
1939                 {
1940                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_lava_nails);
1941                         if (val)
1942                         {
1943                                 val->_float = v;
1944                                 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1945                                         host_client->edict->fields.server->ammo_nails = v;
1946                         }
1947                 }
1948                 break;
1949         case 'r':
1950                 if (gamemode == GAME_ROGUE)
1951                 {
1952                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_rockets1);
1953                         if (val)
1954                         {
1955                                 val->_float = v;
1956                                 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1957                                         host_client->edict->fields.server->ammo_rockets = v;
1958                         }
1959                 }
1960                 else
1961                 {
1962                         host_client->edict->fields.server->ammo_rockets = v;
1963                 }
1964                 break;
1965         case 'm':
1966                 if (gamemode == GAME_ROGUE)
1967                 {
1968                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_multi_rockets);
1969                         if (val)
1970                         {
1971                                 val->_float = v;
1972                                 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
1973                                         host_client->edict->fields.server->ammo_rockets = v;
1974                         }
1975                 }
1976                 break;
1977         case 'h':
1978                 host_client->edict->fields.server->health = v;
1979                 break;
1980         case 'c':
1981                 if (gamemode == GAME_ROGUE)
1982                 {
1983                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_cells1);
1984                         if (val)
1985                         {
1986                                 val->_float = v;
1987                                 if (host_client->edict->fields.server->weapon <= IT_LIGHTNING)
1988                                         host_client->edict->fields.server->ammo_cells = v;
1989                         }
1990                 }
1991                 else
1992                 {
1993                         host_client->edict->fields.server->ammo_cells = v;
1994                 }
1995                 break;
1996         case 'p':
1997                 if (gamemode == GAME_ROGUE)
1998                 {
1999                         val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.ammo_plasma);
2000                         if (val)
2001                         {
2002                                 val->_float = v;
2003                                 if (host_client->edict->fields.server->weapon > IT_LIGHTNING)
2004                                         host_client->edict->fields.server->ammo_cells = v;
2005                         }
2006                 }
2007                 break;
2008         }
2009 }
2010
2011 prvm_edict_t    *FindViewthing (void)
2012 {
2013         int             i;
2014         prvm_edict_t    *e;
2015
2016         for (i=0 ; i<prog->num_edicts ; i++)
2017         {
2018                 e = PRVM_EDICT_NUM(i);
2019                 if (!strcmp (PRVM_GetString(e->fields.server->classname), "viewthing"))
2020                         return e;
2021         }
2022         Con_Print("No viewthing on map\n");
2023         return NULL;
2024 }
2025
2026 /*
2027 ==================
2028 Host_Viewmodel_f
2029 ==================
2030 */
2031 void Host_Viewmodel_f (void)
2032 {
2033         prvm_edict_t    *e;
2034         dp_model_t      *m;
2035
2036         if (!sv.active)
2037                 return;
2038
2039         SV_VM_Begin();
2040         e = FindViewthing ();
2041         SV_VM_End();
2042         if (!e)
2043                 return;
2044
2045         m = Mod_ForName (Cmd_Argv(1), false, true, false);
2046         if (!m || !m->loaded || !m->Draw)
2047         {
2048                 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(1));
2049                 return;
2050         }
2051
2052         e->fields.server->frame = 0;
2053         cl.model_precache[(int)e->fields.server->modelindex] = m;
2054 }
2055
2056 /*
2057 ==================
2058 Host_Viewframe_f
2059 ==================
2060 */
2061 void Host_Viewframe_f (void)
2062 {
2063         prvm_edict_t    *e;
2064         int             f;
2065         dp_model_t      *m;
2066
2067         if (!sv.active)
2068                 return;
2069
2070         SV_VM_Begin();
2071         e = FindViewthing ();
2072         SV_VM_End();
2073         if (!e)
2074                 return;
2075         m = cl.model_precache[(int)e->fields.server->modelindex];
2076
2077         f = atoi(Cmd_Argv(1));
2078         if (f >= m->numframes)
2079                 f = m->numframes-1;
2080
2081         e->fields.server->frame = f;
2082 }
2083
2084
2085 void PrintFrameName (dp_model_t *m, int frame)
2086 {
2087         if (m->animscenes)
2088                 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2089         else
2090                 Con_Printf("frame %i\n", frame);
2091 }
2092
2093 /*
2094 ==================
2095 Host_Viewnext_f
2096 ==================
2097 */
2098 void Host_Viewnext_f (void)
2099 {
2100         prvm_edict_t    *e;
2101         dp_model_t      *m;
2102
2103         if (!sv.active)
2104                 return;
2105
2106         SV_VM_Begin();
2107         e = FindViewthing ();
2108         SV_VM_End();
2109         if (!e)
2110                 return;
2111         m = cl.model_precache[(int)e->fields.server->modelindex];
2112
2113         e->fields.server->frame = e->fields.server->frame + 1;
2114         if (e->fields.server->frame >= m->numframes)
2115                 e->fields.server->frame = m->numframes - 1;
2116
2117         PrintFrameName (m, (int)e->fields.server->frame);
2118 }
2119
2120 /*
2121 ==================
2122 Host_Viewprev_f
2123 ==================
2124 */
2125 void Host_Viewprev_f (void)
2126 {
2127         prvm_edict_t    *e;
2128         dp_model_t      *m;
2129
2130         if (!sv.active)
2131                 return;
2132
2133         SV_VM_Begin();
2134         e = FindViewthing ();
2135         SV_VM_End();
2136         if (!e)
2137                 return;
2138
2139         m = cl.model_precache[(int)e->fields.server->modelindex];
2140
2141         e->fields.server->frame = e->fields.server->frame - 1;
2142         if (e->fields.server->frame < 0)
2143                 e->fields.server->frame = 0;
2144
2145         PrintFrameName (m, (int)e->fields.server->frame);
2146 }
2147
2148 /*
2149 ===============================================================================
2150
2151 DEMO LOOP CONTROL
2152
2153 ===============================================================================
2154 */
2155
2156
2157 /*
2158 ==================
2159 Host_Startdemos_f
2160 ==================
2161 */
2162 void Host_Startdemos_f (void)
2163 {
2164         int             i, c;
2165
2166         if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2167                 return;
2168
2169         c = Cmd_Argc() - 1;
2170         if (c > MAX_DEMOS)
2171         {
2172                 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2173                 c = MAX_DEMOS;
2174         }
2175         Con_DPrintf("%i demo(s) in loop\n", c);
2176
2177         for (i=1 ; i<c+1 ; i++)
2178                 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
2179
2180         // LordHavoc: clear the remaining slots
2181         for (;i <= MAX_DEMOS;i++)
2182                 cls.demos[i-1][0] = 0;
2183
2184         if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2185         {
2186                 cls.demonum = 0;
2187                 CL_NextDemo ();
2188         }
2189         else
2190                 cls.demonum = -1;
2191 }
2192
2193
2194 /*
2195 ==================
2196 Host_Demos_f
2197
2198 Return to looping demos
2199 ==================
2200 */
2201 void Host_Demos_f (void)
2202 {
2203         if (cls.state == ca_dedicated)
2204                 return;
2205         if (cls.demonum == -1)
2206                 cls.demonum = 1;
2207         CL_Disconnect_f ();
2208         CL_NextDemo ();
2209 }
2210
2211 /*
2212 ==================
2213 Host_Stopdemo_f
2214
2215 Return to looping demos
2216 ==================
2217 */
2218 void Host_Stopdemo_f (void)
2219 {
2220         if (!cls.demoplayback)
2221                 return;
2222         CL_Disconnect ();
2223         Host_ShutdownServer ();
2224 }
2225
2226 void Host_SendCvar_f (void)
2227 {
2228         int             i;
2229         cvar_t  *c;
2230         const char *cvarname;
2231         client_t *old;
2232
2233         if(Cmd_Argc() != 2)
2234                 return;
2235         cvarname = Cmd_Argv(1);
2236         if (cls.state == ca_connected)
2237         {
2238                 c = Cvar_FindVar(cvarname);
2239                 // LordHavoc: if there is no such cvar or if it is private, send a
2240                 // reply indicating that it has no value
2241                 if(!c || (c->flags & CVAR_PRIVATE))
2242                         Cmd_ForwardStringToServer(va("sentcvar %s", cvarname));
2243                 else
2244                         Cmd_ForwardStringToServer(va("sentcvar %s \"%s\"", c->name, c->string));
2245                 return;
2246         }
2247         if(!sv.active)// || !prog->funcoffsets.SV_ParseClientCommand)
2248                 return;
2249
2250         old = host_client;
2251         if (cls.state != ca_dedicated)
2252                 i = 1;
2253         else
2254                 i = 0;
2255         for(;i<svs.maxclients;i++)
2256                 if(svs.clients[i].active && svs.clients[i].netconnection)
2257                 {
2258                         host_client = &svs.clients[i];
2259                         Host_ClientCommands("sendcvar %s\n", cvarname);
2260                 }
2261         host_client = old;
2262 }
2263
2264 static void MaxPlayers_f(void)
2265 {
2266         int n;
2267
2268         if (Cmd_Argc() != 2)
2269         {
2270                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
2271                 return;
2272         }
2273
2274         if (sv.active)
2275         {
2276                 Con_Print("maxplayers can not be changed while a server is running.\n");
2277                 return;
2278         }
2279
2280         n = atoi(Cmd_Argv(1));
2281         n = bound(1, n, MAX_SCOREBOARD);
2282         Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2283
2284         if (svs.clients)
2285                 Mem_Free(svs.clients);
2286         svs.maxclients = n;
2287         svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
2288         if (n == 1)
2289                 Cvar_Set ("deathmatch", "0");
2290         else
2291                 Cvar_Set ("deathmatch", "1");
2292 }
2293
2294 //=============================================================================
2295
2296 // QuakeWorld commands
2297
2298 /*
2299 =====================
2300 Host_Rcon_f
2301
2302   Send the rest of the command line over as
2303   an unconnected command.
2304 =====================
2305 */
2306 void Host_Rcon_f (void) // credit: taken from QuakeWorld
2307 {
2308         int i;
2309         lhnetaddress_t to;
2310         lhnetsocket_t *mysocket;
2311
2312         if (!rcon_password.string || !rcon_password.string[0])
2313         {
2314                 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2315                 return;
2316         }
2317
2318         for (i = 0;rcon_password.string[i];i++)
2319         {
2320                 if (rcon_password.string[i] <= ' ')
2321                 {
2322                         Con_Printf("rcon_password is not allowed to have any whitespace.\n");
2323                         return;
2324                 }
2325         }
2326
2327         if (cls.netcon)
2328                 to = cls.netcon->peeraddress;
2329         else
2330         {
2331                 if (!rcon_address.string[0])
2332                 {
2333                         Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2334                         return;
2335                 }
2336                 LHNETADDRESS_FromString(&to, rcon_address.string, sv_netport.integer);
2337         }
2338         mysocket = NetConn_ChooseClientSocketForAddress(&to);
2339         if (mysocket)
2340         {
2341                 // simply put together the rcon packet and send it
2342                 NetConn_WriteString(mysocket, va("\377\377\377\377rcon %s %s", rcon_password.string, Cmd_Args()), &to);
2343         }
2344 }
2345
2346 /*
2347 ====================
2348 Host_User_f
2349
2350 user <name or userid>
2351
2352 Dump userdata / masterdata for a user
2353 ====================
2354 */
2355 void Host_User_f (void) // credit: taken from QuakeWorld
2356 {
2357         int             uid;
2358         int             i;
2359
2360         if (Cmd_Argc() != 2)
2361         {
2362                 Con_Printf ("Usage: user <username / userid>\n");
2363                 return;
2364         }
2365
2366         uid = atoi(Cmd_Argv(1));
2367
2368         for (i = 0;i < cl.maxclients;i++)
2369         {
2370                 if (!cl.scores[i].name[0])
2371                         continue;
2372                 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(1)))
2373                 {
2374                         InfoString_Print(cl.scores[i].qw_userinfo);
2375                         return;
2376                 }
2377         }
2378         Con_Printf ("User not in server.\n");
2379 }
2380
2381 /*
2382 ====================
2383 Host_Users_f
2384
2385 Dump userids for all current players
2386 ====================
2387 */
2388 void Host_Users_f (void) // credit: taken from QuakeWorld
2389 {
2390         int             i;
2391         int             c;
2392
2393         c = 0;
2394         Con_Printf ("userid frags name\n");
2395         Con_Printf ("------ ----- ----\n");
2396         for (i = 0;i < cl.maxclients;i++)
2397         {
2398                 if (cl.scores[i].name[0])
2399                 {
2400                         Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2401                         c++;
2402                 }
2403         }
2404
2405         Con_Printf ("%i total users\n", c);
2406 }
2407
2408 /*
2409 ==================
2410 Host_FullServerinfo_f
2411
2412 Sent by server when serverinfo changes
2413 ==================
2414 */
2415 // TODO: shouldn't this be a cvar instead?
2416 void Host_FullServerinfo_f (void) // credit: taken from QuakeWorld
2417 {
2418         char temp[512];
2419         if (Cmd_Argc() != 2)
2420         {
2421                 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2422                 return;
2423         }
2424
2425         strlcpy (cl.qw_serverinfo, Cmd_Argv(1), sizeof(cl.qw_serverinfo));
2426         InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2427         cl.qw_teamplay = atoi(temp);
2428 }
2429
2430 /*
2431 ==================
2432 Host_FullInfo_f
2433
2434 Allow clients to change userinfo
2435 ==================
2436 Casey was here :)
2437 */
2438 void Host_FullInfo_f (void) // credit: taken from QuakeWorld
2439 {
2440         char key[512];
2441         char value[512];
2442         char *o;
2443         const char *s;
2444
2445         if (Cmd_Argc() != 2)
2446         {
2447                 Con_Printf ("fullinfo <complete info string>\n");
2448                 return;
2449         }
2450
2451         s = Cmd_Argv(1);
2452         if (*s == '\\')
2453                 s++;
2454         while (*s)
2455         {
2456                 o = key;
2457                 while (*s && *s != '\\')
2458                         *o++ = *s++;
2459                 *o = 0;
2460
2461                 if (!*s)
2462                 {
2463                         Con_Printf ("MISSING VALUE\n");
2464                         return;
2465                 }
2466
2467                 o = value;
2468                 s++;
2469                 while (*s && *s != '\\')
2470                         *o++ = *s++;
2471                 *o = 0;
2472
2473                 if (*s)
2474                         s++;
2475
2476                 CL_SetInfo(key, value, false, false, false, false);
2477         }
2478 }
2479
2480 /*
2481 ==================
2482 CL_SetInfo_f
2483
2484 Allow clients to change userinfo
2485 ==================
2486 */
2487 void Host_SetInfo_f (void) // credit: taken from QuakeWorld
2488 {
2489         if (Cmd_Argc() == 1)
2490         {
2491                 InfoString_Print(cls.userinfo);
2492                 return;
2493         }
2494         if (Cmd_Argc() != 3)
2495         {
2496                 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2497                 return;
2498         }
2499         CL_SetInfo(Cmd_Argv(1), Cmd_Argv(2), true, false, false, false);
2500 }
2501
2502 /*
2503 ====================
2504 Host_Packet_f
2505
2506 packet <destination> <contents>
2507
2508 Contents allows \n escape character
2509 ====================
2510 */
2511 void Host_Packet_f (void) // credit: taken from QuakeWorld
2512 {
2513         char send[2048];
2514         int i, l;
2515         const char *in;
2516         char *out;
2517         lhnetaddress_t address;
2518         lhnetsocket_t *mysocket;
2519
2520         if (Cmd_Argc() != 3)
2521         {
2522                 Con_Printf ("packet <destination> <contents>\n");
2523                 return;
2524         }
2525
2526         if (!LHNETADDRESS_FromString (&address, Cmd_Argv(1), sv_netport.integer))
2527         {
2528                 Con_Printf ("Bad address\n");
2529                 return;
2530         }
2531
2532         in = Cmd_Argv(2);
2533         out = send+4;
2534         send[0] = send[1] = send[2] = send[3] = 0xff;
2535
2536         l = (int)strlen (in);
2537         for (i=0 ; i<l ; i++)
2538         {
2539                 if (out >= send + sizeof(send) - 1)
2540                         break;
2541                 if (in[i] == '\\' && in[i+1] == 'n')
2542                 {
2543                         *out++ = '\n';
2544                         i++;
2545                 }
2546                 else if (in[i] == '\\' && in[i+1] == '0')
2547                 {
2548                         *out++ = '\0';
2549                         i++;
2550                 }
2551                 else if (in[i] == '\\' && in[i+1] == 't')
2552                 {
2553                         *out++ = '\t';
2554                         i++;
2555                 }
2556                 else if (in[i] == '\\' && in[i+1] == 'r')
2557                 {
2558                         *out++ = '\r';
2559                         i++;
2560                 }
2561                 else if (in[i] == '\\' && in[i+1] == '"')
2562                 {
2563                         *out++ = '\"';
2564                         i++;
2565                 }
2566                 else
2567                         *out++ = in[i];
2568         }
2569
2570         mysocket = NetConn_ChooseClientSocketForAddress(&address);
2571         if (!mysocket)
2572                 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2573         if (mysocket)
2574                 NetConn_Write(mysocket, send, out - send, &address);
2575 }
2576
2577 /*
2578 ====================
2579 Host_Pings_f
2580
2581 Send back ping and packet loss update for all current players to this player
2582 ====================
2583 */
2584 void Host_Pings_f (void)
2585 {
2586         int             i, j, ping, packetloss;
2587         char temp[128];
2588
2589         if (!host_client->netconnection)
2590                 return;
2591
2592         if (sv.protocol != PROTOCOL_QUAKEWORLD)
2593         {
2594                 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2595                 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2596         }
2597         for (i = 0;i < svs.maxclients;i++)
2598         {
2599                 packetloss = 0;
2600                 if (svs.clients[i].netconnection)
2601                         for (j = 0;j < NETGRAPH_PACKETS;j++)
2602                                 if (svs.clients[i].netconnection->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
2603                                         packetloss++;
2604                 packetloss = packetloss * 100 / NETGRAPH_PACKETS;
2605                 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2606                 ping = bound(0, ping, 9999);
2607                 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2608                 {
2609                         // send qw_svc_updateping and qw_svc_updatepl messages
2610                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2611                         MSG_WriteShort(&host_client->netconnection->message, ping);
2612                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2613                         MSG_WriteByte(&host_client->netconnection->message, packetloss);
2614                 }
2615                 else
2616                 {
2617                         // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2618                         dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2619                         MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2620                 }
2621         }
2622         if (sv.protocol != PROTOCOL_QUAKEWORLD)
2623                 MSG_WriteString(&host_client->netconnection->message, "\n");
2624 }
2625
2626 void Host_PingPLReport_f(void)
2627 {
2628         int i;
2629         int l = Cmd_Argc();
2630         if (l > cl.maxclients)
2631                 l = cl.maxclients;
2632         for (i = 0;i < l;i++)
2633         {
2634                 cl.scores[i].qw_ping = atoi(Cmd_Argv(1+i*2));
2635                 cl.scores[i].qw_packetloss = atoi(Cmd_Argv(1+i*2+1));
2636         }
2637 }
2638
2639 //=============================================================================
2640
2641 /*
2642 ==================
2643 Host_InitCommands
2644 ==================
2645 */
2646 void Host_InitCommands (void)
2647 {
2648         dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
2649
2650         Cmd_AddCommand_WithClientCommand ("status", Host_Status_f, Host_Status_f, "print server status information");
2651         Cmd_AddCommand ("quit", Host_Quit_f, "quit the game");
2652         if (gamemode == GAME_NEHAHRA)
2653         {
2654                 Cmd_AddCommand_WithClientCommand ("max", NULL, Host_God_f, "god mode (invulnerability)");
2655                 Cmd_AddCommand_WithClientCommand ("monster", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2656                 Cmd_AddCommand_WithClientCommand ("scrag", NULL, Host_Fly_f, "fly mode (flight)");
2657                 Cmd_AddCommand_WithClientCommand ("wraith", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2658                 Cmd_AddCommand_WithClientCommand ("gimme", NULL, Host_Give_f, "alter inventory");
2659         }
2660         else
2661         {
2662                 Cmd_AddCommand_WithClientCommand ("god", NULL, Host_God_f, "god mode (invulnerability)");
2663                 Cmd_AddCommand_WithClientCommand ("notarget", NULL, Host_Notarget_f, "notarget mode (monsters do not see you)");
2664                 Cmd_AddCommand_WithClientCommand ("fly", NULL, Host_Fly_f, "fly mode (flight)");
2665                 Cmd_AddCommand_WithClientCommand ("noclip", NULL, Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
2666                 Cmd_AddCommand_WithClientCommand ("give", NULL, Host_Give_f, "alter inventory");
2667         }
2668         Cmd_AddCommand ("map", Host_Map_f, "kick everyone off the server and start a new level");
2669         Cmd_AddCommand ("restart", Host_Restart_f, "restart current level");
2670         Cmd_AddCommand ("changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
2671         Cmd_AddCommand ("connect", Host_Connect_f, "connect to a server by IP address or hostname");
2672         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)");
2673         Cmd_AddCommand ("version", Host_Version_f, "print engine version");
2674         Cmd_AddCommand_WithClientCommand ("say", Host_Say_f, Host_Say_f, "send a chat message to everyone on the server");
2675         Cmd_AddCommand_WithClientCommand ("say_team", Host_Say_Team_f, Host_Say_Team_f, "send a chat message to your team on the server");
2676         Cmd_AddCommand_WithClientCommand ("tell", Host_Tell_f, Host_Tell_f, "send a chat message to only one person on the server");
2677         Cmd_AddCommand_WithClientCommand ("kill", NULL, Host_Kill_f, "die instantly");
2678         Cmd_AddCommand_WithClientCommand ("pause", NULL, Host_Pause_f, "pause the game (if the server allows pausing)");
2679         Cmd_AddCommand ("kick", Host_Kick_f, "kick a player off the server by number or name");
2680         Cmd_AddCommand_WithClientCommand ("ping", Host_Ping_f, Host_Ping_f, "print ping times of all players on the server");
2681         Cmd_AddCommand ("load", Host_Loadgame_f, "load a saved game file");
2682         Cmd_AddCommand ("save", Host_Savegame_f, "save the game to a file");
2683
2684         Cmd_AddCommand ("startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
2685         Cmd_AddCommand ("demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
2686         Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
2687
2688         Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
2689         Cmd_AddCommand ("viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
2690         Cmd_AddCommand ("viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
2691         Cmd_AddCommand ("viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
2692
2693         Cvar_RegisterVariable (&cl_name);
2694         Cmd_AddCommand_WithClientCommand ("name", Host_Name_f, Host_Name_f, "change your player name");
2695         Cvar_RegisterVariable (&cl_color);
2696         Cmd_AddCommand_WithClientCommand ("color", Host_Color_f, Host_Color_f, "change your player shirt and pants colors");
2697         Cvar_RegisterVariable (&cl_rate);
2698         Cmd_AddCommand_WithClientCommand ("rate", Host_Rate_f, Host_Rate_f, "change your network connection speed");
2699         if (gamemode == GAME_NEHAHRA)
2700         {
2701                 Cvar_RegisterVariable (&cl_pmodel);
2702                 Cmd_AddCommand_WithClientCommand ("pmodel", Host_PModel_f, Host_PModel_f, "change your player model choice (Nehahra specific)");
2703         }
2704
2705         // BLACK: This isnt game specific anymore (it was GAME_NEXUIZ at first)
2706         Cvar_RegisterVariable (&cl_playermodel);
2707         Cmd_AddCommand_WithClientCommand ("playermodel", Host_Playermodel_f, Host_Playermodel_f, "change your player model");
2708         Cvar_RegisterVariable (&cl_playerskin);
2709         Cmd_AddCommand_WithClientCommand ("playerskin", Host_Playerskin_f, Host_Playerskin_f, "change your player skin number");
2710
2711         Cmd_AddCommand_WithClientCommand ("prespawn", NULL, Host_PreSpawn_f, "signon 1 (client acknowledges that server information has been received)");
2712         Cmd_AddCommand_WithClientCommand ("spawn", NULL, Host_Spawn_f, "signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
2713         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)");
2714         Cmd_AddCommand ("maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
2715
2716         Cmd_AddCommand ("sendcvar", Host_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
2717
2718         Cvar_RegisterVariable (&rcon_password);
2719         Cvar_RegisterVariable (&rcon_address);
2720         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)");
2721         Cmd_AddCommand ("user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
2722         Cmd_AddCommand ("users", Host_Users_f, "prints additional information about all players on the scoreboard");
2723         Cmd_AddCommand ("fullserverinfo", Host_FullServerinfo_f, "internal use only, sent by server to client to update client's local copy of serverinfo string");
2724         Cmd_AddCommand ("fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
2725         Cmd_AddCommand ("setinfo", Host_SetInfo_f, "modifies your userinfo");
2726         Cmd_AddCommand ("packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
2727         Cmd_AddCommand ("topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
2728         Cmd_AddCommand ("bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
2729
2730         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)");
2731         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)");
2732
2733         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)");
2734         Cvar_RegisterVariable (&r_fixtrans_auto);
2735
2736         Cvar_RegisterVariable (&team);
2737         Cvar_RegisterVariable (&skin);
2738         Cvar_RegisterVariable (&noaim);
2739
2740         Cvar_RegisterVariable(&sv_cheats);
2741         Cvar_RegisterVariable(&sv_adminnick);
2742         Cvar_RegisterVariable(&sv_status_privacy);
2743 }
2744
2745 void Host_NoOperation_f(void)
2746 {
2747 }