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