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