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