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