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