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