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