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