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