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