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