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