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