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