fixed very annoying '5 second timeout' bug in Host_ShutdownServer (the local client...
[xonotic/darkplaces.git] / host_cmd.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 int current_skill;
24 cvar_t sv_cheats = {0, "sv_cheats", "0"};
25 qboolean allowcheats = false;
26
27 mfunction_t *ED_FindFunction (char *name);
28
29 /*
30 ==================
31 Host_Quit_f
32 ==================
33 */
34
35 extern qboolean host_shuttingdown;
36 void Host_Quit_f (void)
37 {
38         host_shuttingdown = true;
39         CL_Disconnect ();
40         Host_ShutdownServer(false);
41
42         Sys_Quit ();
43 }
44
45
46 /*
47 ==================
48 Host_Status_f
49 ==================
50 */
51 void Host_Status_f (void)
52 {
53         client_t *client;
54         int seconds, minutes, hours = 0, j, players;
55         void (*print) (const char *fmt, ...);
56
57         if (cmd_source == src_command)
58         {
59                 if (!sv.active)
60                 {
61                         Cmd_ForwardToServer ();
62                         return;
63                 }
64                 print = Con_Printf;
65         }
66         else
67                 print = SV_ClientPrintf;
68
69         for (players = 0, j = 0;j < svs.maxclients;j++)
70                 if (svs.clients[j].active)
71                         players++;
72         print ("host:    %s\n", Cvar_VariableString ("hostname"));
73         print ("version: %s build %s\n", gamename, buildstring);
74         print ("map:     %s\n", sv.name);
75         print ("players: %i active (%i max)\n\n", players, svs.maxclients);
76         for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++)
77         {
78                 if (!client->active)
79                         continue;
80                 seconds = (int)(realtime - client->netconnection->connecttime);
81                 minutes = seconds / 60;
82                 if (minutes)
83                 {
84                         seconds -= (minutes * 60);
85                         hours = minutes / 60;
86                         if (hours)
87                                 minutes -= (hours * 60);
88                 }
89                 else
90                         hours = 0;
91                 print ("#%-2u %-16.16s  %3i  %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v->frags, hours, minutes, seconds);
92                 print ("   %s\n", client->netconnection->address);
93         }
94 }
95
96
97 /*
98 ==================
99 Host_God_f
100
101 Sets client to godmode
102 ==================
103 */
104 void Host_God_f (void)
105 {
106         if (cmd_source == src_command)
107         {
108                 Cmd_ForwardToServer ();
109                 return;
110         }
111
112         if (!sv_player)
113                 return;
114
115         if (!allowcheats)
116         {
117                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
118                 return;
119         }
120
121         sv_player->v->flags = (int)sv_player->v->flags ^ FL_GODMODE;
122         if (!((int)sv_player->v->flags & FL_GODMODE) )
123                 SV_ClientPrint("godmode OFF\n");
124         else
125                 SV_ClientPrint("godmode ON\n");
126 }
127
128 void Host_Notarget_f (void)
129 {
130         if (cmd_source == src_command)
131         {
132                 Cmd_ForwardToServer ();
133                 return;
134         }
135
136         if (!sv_player)
137                 return;
138
139         if (!allowcheats)
140         {
141                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
142                 return;
143         }
144
145         sv_player->v->flags = (int)sv_player->v->flags ^ FL_NOTARGET;
146         if (!((int)sv_player->v->flags & FL_NOTARGET) )
147                 SV_ClientPrint("notarget OFF\n");
148         else
149                 SV_ClientPrint("notarget ON\n");
150 }
151
152 qboolean noclip_anglehack;
153
154 void Host_Noclip_f (void)
155 {
156         if (cmd_source == src_command)
157         {
158                 Cmd_ForwardToServer ();
159                 return;
160         }
161
162         if (!sv_player)
163                 return;
164
165         if (!allowcheats)
166         {
167                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
168                 return;
169         }
170
171         if (sv_player->v->movetype != MOVETYPE_NOCLIP)
172         {
173                 noclip_anglehack = true;
174                 sv_player->v->movetype = MOVETYPE_NOCLIP;
175                 SV_ClientPrint("noclip ON\n");
176         }
177         else
178         {
179                 noclip_anglehack = false;
180                 sv_player->v->movetype = MOVETYPE_WALK;
181                 SV_ClientPrint("noclip OFF\n");
182         }
183 }
184
185 /*
186 ==================
187 Host_Fly_f
188
189 Sets client to flymode
190 ==================
191 */
192 void Host_Fly_f (void)
193 {
194         if (cmd_source == src_command)
195         {
196                 Cmd_ForwardToServer ();
197                 return;
198         }
199
200         if (!sv_player)
201                 return;
202
203         if (!allowcheats)
204         {
205                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
206                 return;
207         }
208
209         if (sv_player->v->movetype != MOVETYPE_FLY)
210         {
211                 sv_player->v->movetype = MOVETYPE_FLY;
212                 SV_ClientPrint("flymode ON\n");
213         }
214         else
215         {
216                 sv_player->v->movetype = MOVETYPE_WALK;
217                 SV_ClientPrint("flymode OFF\n");
218         }
219 }
220
221
222 /*
223 ==================
224 Host_Ping_f
225
226 ==================
227 */
228 void Host_Ping_f (void)
229 {
230         int             i, j;
231         float   total;
232         client_t        *client;
233
234         if (cmd_source == src_command)
235         {
236                 Cmd_ForwardToServer ();
237                 return;
238         }
239
240         SV_ClientPrint("Client ping times:\n");
241         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
242         {
243                 if (!client->active)
244                         continue;
245                 total = 0;
246                 for (j=0 ; j<NUM_PING_TIMES ; j++)
247                         total+=client->ping_times[j];
248                 total /= NUM_PING_TIMES;
249                 SV_ClientPrintf("%4i %s\n", (int)(total*1000), client->name);
250         }
251 }
252
253 /*
254 ===============================================================================
255
256 SERVER TRANSITIONS
257
258 ===============================================================================
259 */
260
261 /*
262 ======================
263 Host_Map_f
264
265 handle a
266 map <servername>
267 command from the console.  Active clients are kicked off.
268 ======================
269 */
270 void Host_Map_f (void)
271 {
272         char level[MAX_QPATH];
273
274         if (cmd_source != src_command)
275                 return;
276
277         cls.demonum = -1;               // stop demo loop in case this fails
278
279         CL_Disconnect ();
280         Host_ShutdownServer(false);
281
282         key_dest = key_game;                    // remove console or menu
283         SCR_BeginLoadingPlaque ();
284
285         svs.serverflags = 0;                    // haven't completed an episode yet
286         allowcheats = sv_cheats.integer != 0;
287         strcpy(level, Cmd_Argv(1));
288         SV_SpawnServer(level);
289         if (sv.active && cls.state == ca_disconnected)
290                 CL_EstablishConnection("local:1");
291 }
292
293 /*
294 ==================
295 Host_Changelevel_f
296
297 Goes to a new map, taking all clients along
298 ==================
299 */
300 void Host_Changelevel_f (void)
301 {
302         char level[MAX_QPATH];
303         
304         if (Cmd_Argc() != 2)
305         {
306                 Con_Print("changelevel <levelname> : continue game on a new level\n");
307                 return;
308         }
309         if (!sv.active || cls.demoplayback)
310         {
311                 Con_Print("Only the server may changelevel\n");
312                 return;
313         }
314         SV_SaveSpawnparms ();
315         allowcheats = sv_cheats.integer != 0;
316         strcpy(level, Cmd_Argv(1));
317         SV_SpawnServer(level);
318         if (sv.active && cls.state == ca_disconnected)
319                 CL_EstablishConnection("local:1");
320 }
321
322 /*
323 ==================
324 Host_Restart_f
325
326 Restarts the current server for a dead player
327 ==================
328 */
329 void Host_Restart_f (void)
330 {
331         char mapname[MAX_QPATH];
332         
333         if (cls.demoplayback || !sv.active)
334                 return;
335
336         if (cmd_source != src_command)
337                 return;
338         allowcheats = sv_cheats.integer != 0;
339         strcpy(mapname, sv.name);
340         SV_SpawnServer(mapname);
341         if (sv.active && cls.state == ca_disconnected)
342                 CL_EstablishConnection("local:1");
343 }
344
345 /*
346 ==================
347 Host_Reconnect_f
348
349 This command causes the client to wait for the signon messages again.
350 This is sent just before a server changes levels
351 ==================
352 */
353 void Host_Reconnect_f (void)
354 {
355         SCR_BeginLoadingPlaque();
356         cls.signon = 0;         // need new connection messages
357 }
358
359 /*
360 =====================
361 Host_Connect_f
362
363 User command to connect to server
364 =====================
365 */
366 void Host_Connect_f (void)
367 {
368         CL_EstablishConnection(Cmd_Argv(1));
369 }
370
371
372 /*
373 ===============================================================================
374
375 LOAD / SAVE GAME
376
377 ===============================================================================
378 */
379
380 #define SAVEGAME_VERSION        5
381
382 /*
383 ===============
384 Host_SavegameComment
385
386 Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current
387 ===============
388 */
389 void Host_SavegameComment (char *text)
390 {
391         int             i;
392         char    kills[20];
393
394         for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
395                 text[i] = ' ';
396         memcpy (text, cl.levelname, strlen(cl.levelname));
397         sprintf (kills,"kills:%3i/%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]);
398         memcpy (text+22, kills, strlen(kills));
399 // convert space to _ to make stdio happy
400         for (i=0 ; i<SAVEGAME_COMMENT_LENGTH ; i++)
401                 if (text[i] == ' ')
402                         text[i] = '_';
403         text[SAVEGAME_COMMENT_LENGTH] = '\0';
404 }
405
406
407 /*
408 ===============
409 Host_Savegame_f
410 ===============
411 */
412 void Host_Savegame_f (void)
413 {
414         char    name[256];
415         qfile_t *f;
416         int             i;
417         char    comment[SAVEGAME_COMMENT_LENGTH+1];
418
419         if (cmd_source != src_command)
420                 return;
421
422         if (cls.state != ca_connected || !sv.active)
423         {
424                 Con_Print("Not playing a local game.\n");
425                 return;
426         }
427
428         if (cl.intermission)
429         {
430                 Con_Print("Can't save in intermission.\n");
431                 return;
432         }
433
434         for (i = 0;i < svs.maxclients;i++)
435         {
436                 if (svs.clients[i].active)
437                 {
438                         if (i > 0)
439                         {
440                                 Con_Print("Can't save multiplayer games.\n");
441                                 return;
442                         }
443                         if (svs.clients[i].edict->v->deadflag)
444                         {
445                                 Con_Print("Can't savegame with a dead player\n");
446                                 return;
447                         }
448                 }
449         }
450
451         if (Cmd_Argc() != 2)
452         {
453                 Con_Print("save <savename> : save a game\n");
454                 return;
455         }
456
457         if (strstr(Cmd_Argv(1), ".."))
458         {
459                 Con_Print("Relative pathnames are not allowed.\n");
460                 return;
461         }
462
463         strlcpy (name, Cmd_Argv(1), sizeof (name));
464         FS_DefaultExtension (name, ".sav", sizeof (name));
465
466         Con_Printf("Saving game to %s...\n", name);
467         f = FS_Open (name, "w", false);
468         if (!f)
469         {
470                 Con_Print("ERROR: couldn't open.\n");
471                 return;
472         }
473
474         FS_Printf(f, "%i\n", SAVEGAME_VERSION);
475         Host_SavegameComment (comment);
476         FS_Printf(f, "%s\n", comment);
477         for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
478                 FS_Printf(f, "%f\n", svs.clients[0].spawn_parms[i]);
479         FS_Printf(f, "%d\n", current_skill);
480         FS_Printf(f, "%s\n", sv.name);
481         FS_Printf(f, "%f\n",sv.time);
482
483 // write the light styles
484
485         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
486         {
487                 if (sv.lightstyles[i])
488                         FS_Printf(f, "%s\n", sv.lightstyles[i]);
489                 else
490                         FS_Print(f,"m\n");
491         }
492
493
494         ED_WriteGlobals (f);
495         for (i=0 ; i<sv.num_edicts ; i++)
496         {
497                 ED_Write (f, EDICT_NUM(i));
498                 FS_Flush (f);
499         }
500         FS_Close (f);
501         Con_Print("done.\n");
502 }
503
504
505 extern mempool_t *edictstring_mempool;
506
507 /*
508 ===============
509 Host_Loadgame_f
510 ===============
511 */
512 void Host_Loadgame_f (void)
513 {
514         qfile_t *f;
515         char filename[MAX_QPATH];
516         char mapname[MAX_QPATH];
517         float time, tfloat;
518         char buf[32768];
519         const char *start;
520         char *str;
521         int i, r;
522         edict_t *ent;
523         int entnum;
524         int version;
525         float spawn_parms[NUM_SPAWN_PARMS];
526
527         if (cmd_source != src_command)
528                 return;
529
530         if (Cmd_Argc() != 2)
531         {
532                 Con_Print("load <savename> : load a game\n");
533                 return;
534         }
535
536         strcpy (filename, Cmd_Argv(1));
537         FS_DefaultExtension (filename, ".sav", sizeof (filename));
538
539         Con_Printf("Loading game from %s...\n", filename);
540
541         cls.demonum = -1;               // stop demo loop in case this fails
542
543         f = FS_Open (filename, "r", false);
544         if (!f)
545         {
546                 Con_Print("ERROR: couldn't open.\n");
547                 return;
548         }
549
550         str = FS_Getline (f);
551         sscanf (str, "%i\n", &version);
552         if (version != SAVEGAME_VERSION)
553         {
554                 FS_Close (f);
555                 Con_Printf("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION);
556                 return;
557         }
558
559         SCR_BeginLoadingPlaque ();
560
561         str = FS_Getline (f);
562         for (i = 0;i < NUM_SPAWN_PARMS;i++)
563         {
564                 str = FS_Getline (f);
565                 sscanf (str, "%f\n", &spawn_parms[i]);
566         }
567 // this silliness is so we can load 1.06 save files, which have float skill values
568         str = FS_Getline (f);
569         sscanf (str, "%f\n", &tfloat);
570         current_skill = (int)(tfloat + 0.1);
571         Cvar_SetValue ("skill", (float)current_skill);
572
573         strcpy (mapname, FS_Getline (f));
574
575         str = FS_Getline (f);
576         sscanf (str, "%f\n",&time);
577
578         allowcheats = sv_cheats.integer != 0;
579         SV_SpawnServer (mapname);
580         if (!sv.active)
581         {
582                 Con_Print("Couldn't load map\n");
583                 return;
584         }
585         sv.paused = true;               // pause until all clients connect
586         sv.loadgame = true;
587
588 // load the light styles
589
590         for (i = 0;i < MAX_LIGHTSTYLES;i++)
591         {
592                 str = FS_Getline (f);
593                 sv.lightstyles[i] = Mem_Alloc(edictstring_mempool, strlen(str)+1);
594                 strcpy (sv.lightstyles[i], str);
595         }
596
597 // load the edicts out of the savegame file
598         // -1 is the globals
599         entnum = -1;
600         while (!FS_Eof (f))
601         {
602                 for (i = 0;i < (int)sizeof(buf) - 1;i++)
603                 {
604                         r = FS_Getc (f);
605                         if (r == EOF || !r)
606                                 break;
607                         buf[i] = r;
608                         if (r == '}')
609                         {
610                                 i++;
611                                 break;
612                         }
613                 }
614                 if (i == sizeof(buf)-1)
615                         Host_Error ("Loadgame buffer overflow");
616                 buf[i] = 0;
617                 start = buf;
618                 if (!COM_ParseToken(&start, false))
619                 {
620                         // end of file
621                         break;
622                 }
623                 if (strcmp(com_token,"{"))
624                         Host_Error ("First token isn't a brace");
625
626                 if (entnum == -1)
627                 {
628                         // parse the global vars
629                         ED_ParseGlobals (start);
630                 }
631                 else
632                 {
633                         // parse an edict
634                         if (entnum >= MAX_EDICTS)
635                                 Host_Error("Host_PerformLoadGame: too many edicts in save file (reached MAX_EDICTS %i)\n", MAX_EDICTS);
636                         while (entnum >= sv.max_edicts)
637                                 SV_IncreaseEdicts();
638                         ent = EDICT_NUM(entnum);
639                         memset (ent->v, 0, progs->entityfields * 4);
640                         ent->e->free = false;
641                         ED_ParseEdict (start, ent);
642
643                         // link it into the bsp tree
644                         if (!ent->e->free)
645                                 SV_LinkEdict (ent, false);
646                 }
647
648                 entnum++;
649         }
650
651         sv.num_edicts = entnum;
652         sv.time = time;
653
654         FS_Close (f);
655
656         for (i = 0;i < NUM_SPAWN_PARMS;i++)
657                 svs.clients[0].spawn_parms[i] = spawn_parms[i];
658
659         // make sure we're connected to loopback
660         if (cls.state == ca_disconnected || !(cls.state == ca_connected && cls.netcon != NULL && LHNETADDRESS_GetAddressType(&cls.netcon->peeraddress) == LHNETADDRESSTYPE_LOOP))
661                 CL_EstablishConnection("local:1");
662 }
663
664 //============================================================================
665
666 /*
667 ======================
668 Host_Name_f
669 ======================
670 */
671 cvar_t cl_name = {CVAR_SAVE, "_cl_name", "player"};
672 void Host_Name_f (void)
673 {
674         char newName[sizeof(host_client->name)];
675
676         if (Cmd_Argc () == 1)
677         {
678                 Con_Printf("\"name\" is \"%s\"\n", cl_name.string);
679                 return;
680         }
681
682         if (Cmd_Argc () == 2)
683                 strlcpy (newName, Cmd_Argv(1), sizeof (newName));
684         else
685                 strlcpy (newName, Cmd_Args(), sizeof (newName));
686
687         if (cmd_source == src_command)
688         {
689                 if (strcmp(cl_name.string, newName) == 0)
690                         return;
691                 Cvar_Set ("_cl_name", newName);
692                 if (cls.state == ca_connected)
693                         Cmd_ForwardToServer ();
694                 return;
695         }
696
697         if (strcmp(host_client->name, newName) && host_client->name[0] && strcmp(host_client->name, "unconnected"))
698                 SV_BroadcastPrintf("%s changed name to %s\n", host_client->name, newName);
699         strcpy(host_client->name, newName);
700         strcpy(host_client->old_name, newName);
701         if (sv_player)
702                 sv_player->v->netname = PR_SetString(host_client->name);
703         //Con_Printf("Host_Name_f: host_client->edict->netname = %s, sv_player->netname = %s, host_client->name = %s\n", PR_GetString(host_client->edict->v->netname), PR_GetString(sv_player->v->netname), host_client->name);
704
705 // send notification to all clients
706
707         MSG_WriteByte(&sv.reliable_datagram, svc_updatename);
708         MSG_WriteByte(&sv.reliable_datagram, host_client->number);
709         MSG_WriteString(&sv.reliable_datagram, host_client->name);
710 }
711
712
713 void Host_Version_f (void)
714 {
715         Con_Printf("Version: %s build %s\n", gamename, buildstring);
716 }
717
718 void Host_Say(qboolean teamonly)
719 {
720         client_t *save;
721         int j;
722         const char *p1, *p2;
723         // LordHavoc: 256 char say messages
724         unsigned char text[256];
725         qboolean fromServer = false;
726
727         if (cmd_source == src_command)
728         {
729                 if (cls.state == ca_dedicated)
730                 {
731                         fromServer = true;
732                         teamonly = false;
733                 }
734                 else
735                 {
736                         Cmd_ForwardToServer ();
737                         return;
738                 }
739         }
740
741         if (Cmd_Argc () < 2)
742                 return;
743
744         save = host_client;
745
746 // turn on color set 1
747         if (!fromServer)
748                 sprintf (text, "%c%s: ", 1, host_client->name);
749         else
750                 sprintf (text, "%c<%s> ", 1, hostname.string);
751
752         p1 = Cmd_Args();
753         p2 = p1 + strlen(p1);
754         // remove trailing newlines
755         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
756                 p2--;
757         // remove quotes if present
758         if (*p1 == '"')
759         {
760                 p1++;
761                 if (p2[-1] == '"')
762                         p2--;
763                 else if (fromServer)
764                         Con_Print("Host_Say: missing end quote\n");
765                 else
766                         SV_ClientPrint("Host_Say: missing end quote\n");
767         }
768         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
769                 p2--;
770         for (j = strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
771                 text[j++] = *p1++;
772         text[j++] = '\n';
773         text[j++] = 0;
774
775         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
776                 if (host_client->spawned && (!teamplay.integer || host_client->edict->v->team == save->edict->v->team))
777                         SV_ClientPrint(text);
778         host_client = save;
779
780         Sys_Print(&text[1]);
781 }
782
783
784 void Host_Say_f(void)
785 {
786         Host_Say(false);
787 }
788
789
790 void Host_Say_Team_f(void)
791 {
792         Host_Say(true);
793 }
794
795
796 void Host_Tell_f(void)
797 {
798         client_t *save;
799         int j;
800         const char *p1, *p2;
801         char text[1024]; // LordHavoc: FIXME: temporary buffer overflow fix (was 64)
802         qboolean fromServer = false;
803
804         if (cmd_source == src_command)
805         {
806                 if (cls.state == ca_dedicated)
807                         fromServer = true;
808                 else
809                 {
810                         Cmd_ForwardToServer ();
811                         return;
812                 }
813         }
814
815         if (Cmd_Argc () < 3)
816                 return;
817
818         if (!fromServer)
819                 sprintf (text, "%s: ", host_client->name);
820         else
821                 sprintf (text, "<%s> ", hostname.string);
822
823         p1 = Cmd_Args();
824         p2 = p1 + strlen(p1);
825         // remove the target name
826         while (p1 < p2 && *p1 != ' ')
827                 p1++;
828         while (p1 < p2 && *p1 == ' ')
829                 p1++;
830         // remove trailing newlines
831         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
832                 p2--;
833         // remove quotes if present
834         if (*p1 == '"')
835         {
836                 p1++;
837                 if (p2[-1] == '"')
838                         p2--;
839                 else if (fromServer)
840                         Con_Print("Host_Tell: missing end quote\n");
841                 else
842                         SV_ClientPrint("Host_Tell: missing end quote\n");
843         }
844         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
845                 p2--;
846         for (j = strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
847                 text[j++] = *p1++;
848         text[j++] = '\n';
849         text[j++] = 0;
850
851         save = host_client;
852         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
853                 if (host_client->spawned && !strcasecmp(host_client->name, Cmd_Argv(1)))
854                         SV_ClientPrint(text);
855         host_client = save;
856 }
857
858
859 /*
860 ==================
861 Host_Color_f
862 ==================
863 */
864 cvar_t cl_color = {CVAR_SAVE, "_cl_color", "0"};
865 void Host_Color_f(void)
866 {
867         int             top, bottom;
868         int             playercolor;
869         mfunction_t *f;
870         func_t  SV_ChangeTeam;
871
872         if (Cmd_Argc() == 1)
873         {
874                 Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
875                 Con_Print("color <0-15> [0-15]\n");
876                 return;
877         }
878
879         if (Cmd_Argc() == 2)
880                 top = bottom = atoi(Cmd_Argv(1));
881         else
882         {
883                 top = atoi(Cmd_Argv(1));
884                 bottom = atoi(Cmd_Argv(2));
885         }
886
887         top &= 15;
888         // LordHavoc: allow skin colormaps 14 and 15 (was 13)
889         if (top > 15)
890                 top = 15;
891         bottom &= 15;
892         // LordHavoc: allow skin colormaps 14 and 15 (was 13)
893         if (bottom > 15)
894                 bottom = 15;
895
896         playercolor = top*16 + bottom;
897
898         if (cmd_source == src_command)
899         {
900                 Cvar_SetValue ("_cl_color", playercolor);
901                 if (cls.state == ca_connected)
902                         Cmd_ForwardToServer ();
903                 return;
904         }
905
906         if (sv_player && (f = ED_FindFunction ("SV_ChangeTeam")) && (SV_ChangeTeam = (func_t)(f - pr_functions)))
907         {
908                 Con_DPrint("Calling SV_ChangeTeam\n");
909                 pr_global_struct->time = sv.time;
910                 pr_globals[OFS_PARM0] = playercolor;
911                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
912                 PR_ExecuteProgram (SV_ChangeTeam, "");
913         }
914         else
915         {
916                 eval_t *val;
917                 if (sv_player)
918                 {
919                         if ((val = GETEDICTFIELDVALUE(sv_player, eval_clientcolors)))
920                                 val->_float = playercolor;
921                         sv_player->v->team = bottom + 1;
922                 }
923                 host_client->colors = playercolor;
924                 host_client->old_colors = playercolor;
925
926                 // send notification to all clients
927                 MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
928                 MSG_WriteByte (&sv.reliable_datagram, host_client->number);
929                 MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
930         }
931 }
932
933 cvar_t cl_rate = {CVAR_SAVE, "_cl_rate", "10000"};
934 cvar_t sv_maxrate = {CVAR_SAVE | CVAR_NOTIFY, "sv_maxrate", "10000"};
935 void Host_Rate_f(void)
936 {
937         int rate, maxrate;
938
939         if (Cmd_Argc() != 2)
940         {
941                 Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
942                 Con_Print("rate <500-25000>\n");
943                 return;
944         }
945
946         rate = atoi(Cmd_Argv(1));
947
948         if (cmd_source == src_command)
949         {
950                 Cvar_SetValue ("_cl_rate", bound(NET_MINRATE, rate, NET_MAXRATE));
951                 if (cls.state == ca_connected)
952                         Cmd_ForwardToServer ();
953                 return;
954         }
955
956         maxrate = bound(NET_MINRATE, sv_maxrate.integer, NET_MAXRATE);
957         if (sv_maxrate.integer != maxrate)
958                 Cvar_SetValueQuick(&sv_maxrate, maxrate);
959
960         if (LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
961                 host_client->netconnection->rate = bound(NET_MINRATE, rate, maxrate);
962 }
963
964 /*
965 ==================
966 Host_Kill_f
967 ==================
968 */
969 void Host_Kill_f (void)
970 {
971         if (cmd_source == src_command)
972         {
973                 Cmd_ForwardToServer ();
974                 return;
975         }
976
977         if (!sv_player || sv_player->v->health <= 0)
978         {
979                 SV_ClientPrint("Can't suicide -- already dead!\n");
980                 return;
981         }
982
983         pr_global_struct->time = sv.time;
984         pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
985         PR_ExecuteProgram (pr_global_struct->ClientKill, "QC function ClientKill is missing");
986 }
987
988
989 /*
990 ==================
991 Host_Pause_f
992 ==================
993 */
994 void Host_Pause_f (void)
995 {
996
997         if (cmd_source == src_command)
998         {
999                 Cmd_ForwardToServer ();
1000                 return;
1001         }
1002         if (!pausable.integer)
1003                 SV_ClientPrint("Pause not allowed.\n");
1004         else
1005         {
1006                 sv.paused ^= 1;
1007                 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1008                 // send notification to all clients
1009                 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1010                 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1011         }
1012 }
1013
1014 /*
1015 ======================
1016 Host_PModel_f
1017 LordHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1018 ======================
1019 */
1020 cvar_t cl_pmodel = {CVAR_SAVE, "_cl_pmodel", "0"};
1021 static void Host_PModel_f (void)
1022 {
1023         int i;
1024         eval_t *val;
1025
1026         if (Cmd_Argc () == 1)
1027         {
1028                 Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1029                 return;
1030         }
1031         i = atoi(Cmd_Argv(1));
1032
1033         if (cmd_source == src_command)
1034         {
1035                 if (cl_pmodel.integer == i)
1036                         return;
1037                 Cvar_SetValue ("_cl_pmodel", i);
1038                 if (cls.state == ca_connected)
1039                         Cmd_ForwardToServer ();
1040                 return;
1041         }
1042
1043         host_client->pmodel = i;
1044         if (sv_player && (val = GETEDICTFIELDVALUE(sv_player, eval_pmodel)))
1045                 val->_float = i;
1046 }
1047
1048 //===========================================================================
1049
1050
1051 /*
1052 ==================
1053 Host_PreSpawn_f
1054 ==================
1055 */
1056 void Host_PreSpawn_f (void)
1057 {
1058         if (cmd_source == src_command)
1059         {
1060                 Con_Print("prespawn is not valid from the console\n");
1061                 return;
1062         }
1063
1064         if (host_client->spawned)
1065         {
1066                 Con_Print("prespawn not valid -- already spawned\n");
1067                 return;
1068         }
1069
1070         SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
1071         MSG_WriteByte (&host_client->message, svc_signonnum);
1072         MSG_WriteByte (&host_client->message, 2);
1073         host_client->sendsignon = true;
1074 }
1075
1076 /*
1077 ==================
1078 Host_Spawn_f
1079 ==================
1080 */
1081 void Host_Spawn_f (void)
1082 {
1083         int i;
1084         client_t *client;
1085         func_t RestoreGame;
1086         mfunction_t *f;
1087
1088         if (cmd_source == src_command)
1089         {
1090                 Con_Print("spawn is not valid from the console\n");
1091                 return;
1092         }
1093
1094         if (host_client->spawned)
1095         {
1096                 Con_Print("Spawn not valid -- already spawned\n");
1097                 return;
1098         }
1099
1100         if (!sv_player)
1101         {
1102                 Con_Print("Host_Spawn: no edict??\n");
1103                 return;
1104         }
1105
1106         // LordHavoc: moved this above the QC calls at FrikaC's request
1107         // send all current names, colors, and frag counts
1108         SZ_Clear (&host_client->message);
1109
1110         // run the entrance script
1111         if (sv.loadgame)
1112         {
1113                 // loaded games are fully initialized already
1114                 // if this is the last client to be connected, unpause
1115                 sv.paused = false;
1116
1117                 if ((f = ED_FindFunction ("RestoreGame")))
1118                 if ((RestoreGame = (func_t)(f - pr_functions)))
1119                 {
1120                         Con_DPrint("Calling RestoreGame\n");
1121                         pr_global_struct->time = sv.time;
1122                         pr_global_struct->self = EDICT_TO_PROG(sv_player);
1123                         PR_ExecuteProgram (RestoreGame, "");
1124                 }
1125         }
1126         else
1127         {
1128                 // set up the edict
1129                 ED_ClearEdict(sv_player);
1130
1131                 //Con_Printf("Host_Spawn_f: host_client->edict->netname = %s, sv_player->netname = %s, host_client->name = %s\n", PR_GetString(host_client->edict->v->netname), PR_GetString(sv_player->v->netname), host_client->name);
1132
1133                 // copy spawn parms out of the client_t
1134                 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1135                         (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i];
1136
1137                 // call the spawn function
1138                 pr_global_struct->time = sv.time;
1139                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
1140                 PR_ExecuteProgram (pr_global_struct->ClientConnect, "QC function ClientConnect is missing");
1141
1142                 if ((Sys_DoubleTime() - host_client->netconnection->connecttime) <= sv.time)
1143                         Sys_Printf("%s entered the game\n", host_client->name);
1144
1145                 PR_ExecuteProgram (pr_global_struct->PutClientInServer, "QC function PutClientInServer is missing");
1146         }
1147
1148
1149         // send time of update
1150         MSG_WriteByte (&host_client->message, svc_time);
1151         MSG_WriteFloat (&host_client->message, sv.time);
1152
1153         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1154         {
1155                 if (!client->active)
1156                         continue;
1157                 MSG_WriteByte (&host_client->message, svc_updatename);
1158                 MSG_WriteByte (&host_client->message, i);
1159                 MSG_WriteString (&host_client->message, client->old_name);
1160                 MSG_WriteByte (&host_client->message, svc_updatefrags);
1161                 MSG_WriteByte (&host_client->message, i);
1162                 MSG_WriteShort (&host_client->message, client->old_frags);
1163                 MSG_WriteByte (&host_client->message, svc_updatecolors);
1164                 MSG_WriteByte (&host_client->message, i);
1165                 MSG_WriteByte (&host_client->message, client->old_colors);
1166         }
1167
1168         // send all current light styles
1169         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1170         {
1171                 MSG_WriteByte (&host_client->message, svc_lightstyle);
1172                 MSG_WriteByte (&host_client->message, (char)i);
1173                 MSG_WriteString (&host_client->message, sv.lightstyles[i]);
1174         }
1175
1176         // send some stats
1177         MSG_WriteByte (&host_client->message, svc_updatestat);
1178         MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
1179         MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets);
1180
1181         MSG_WriteByte (&host_client->message, svc_updatestat);
1182         MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
1183         MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters);
1184
1185         MSG_WriteByte (&host_client->message, svc_updatestat);
1186         MSG_WriteByte (&host_client->message, STAT_SECRETS);
1187         MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets);
1188
1189         MSG_WriteByte (&host_client->message, svc_updatestat);
1190         MSG_WriteByte (&host_client->message, STAT_MONSTERS);
1191         MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters);
1192
1193         // send a fixangle
1194         // Never send a roll angle, because savegames can catch the server
1195         // in a state where it is expecting the client to correct the angle
1196         // and it won't happen if the game was just loaded, so you wind up
1197         // with a permanent head tilt
1198         MSG_WriteByte (&host_client->message, svc_setangle);
1199         for (i=0 ; i < 2 ; i++)
1200                 MSG_WriteAngle (&host_client->message, sv_player->v->angles[i] );
1201         MSG_WriteAngle (&host_client->message, 0 );
1202
1203         SV_WriteClientdataToMessage (sv_player, &host_client->message);
1204
1205         MSG_WriteByte (&host_client->message, svc_signonnum);
1206         MSG_WriteByte (&host_client->message, 3);
1207         host_client->sendsignon = true;
1208 }
1209
1210 /*
1211 ==================
1212 Host_Begin_f
1213 ==================
1214 */
1215 void Host_Begin_f (void)
1216 {
1217         if (cmd_source == src_command)
1218         {
1219                 Con_Print("begin is not valid from the console\n");
1220                 return;
1221         }
1222
1223         host_client->spawned = true;
1224 }
1225
1226 //===========================================================================
1227
1228
1229 /*
1230 ==================
1231 Host_Kick_f
1232
1233 Kicks a user off of the server
1234 ==================
1235 */
1236 void Host_Kick_f (void)
1237 {
1238         char *who;
1239         const char *message = NULL;
1240         client_t *save;
1241         int i;
1242         qboolean byNumber = false;
1243
1244         if (cmd_source != src_command || !sv.active)
1245                 return;
1246
1247         save = host_client;
1248
1249         if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0)
1250         {
1251                 i = atof(Cmd_Argv(2)) - 1;
1252                 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1253                         return;
1254                 byNumber = true;
1255         }
1256         else
1257         {
1258                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1259                 {
1260                         if (!host_client->active)
1261                                 continue;
1262                         if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0)
1263                                 break;
1264                 }
1265         }
1266
1267         if (i < svs.maxclients)
1268         {
1269                 if (cmd_source == src_command)
1270                 {
1271                         if (cls.state == ca_dedicated)
1272                                 who = "Console";
1273                         else
1274                                 who = cl_name.string;
1275                 }
1276                 else
1277                         who = save->name;
1278
1279                 // can't kick yourself!
1280                 if (host_client == save)
1281                         return;
1282
1283                 if (Cmd_Argc() > 2)
1284                 {
1285                         message = Cmd_Args();
1286                         COM_ParseToken(&message, false);
1287                         if (byNumber)
1288                         {
1289                                 message++;                                                      // skip the #
1290                                 while (*message == ' ')                         // skip white space
1291                                         message++;
1292                                 message += strlen(Cmd_Argv(2)); // skip the number
1293                         }
1294                         while (*message && *message == ' ')
1295                                 message++;
1296                 }
1297                 if (message)
1298                         SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1299                 else
1300                         SV_ClientPrintf("Kicked by %s\n", who);
1301                 SV_DropClient (false); // kicked
1302         }
1303
1304         host_client = save;
1305 }
1306
1307 /*
1308 ===============================================================================
1309
1310 DEBUGGING TOOLS
1311
1312 ===============================================================================
1313 */
1314
1315 /*
1316 ==================
1317 Host_Give_f
1318 ==================
1319 */
1320 void Host_Give_f (void)
1321 {
1322         const char *t;
1323         int v;
1324         eval_t *val;
1325
1326         if (cmd_source == src_command)
1327         {
1328                 Cmd_ForwardToServer ();
1329                 return;
1330         }
1331
1332         if (!sv_player)
1333                 return;
1334
1335         if (!allowcheats)
1336         {
1337                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
1338                 return;
1339         }
1340
1341         t = Cmd_Argv(1);
1342         v = atoi (Cmd_Argv(2));
1343
1344         switch (t[0])
1345         {
1346         case '0':
1347         case '1':
1348         case '2':
1349         case '3':
1350         case '4':
1351         case '5':
1352         case '6':
1353         case '7':
1354         case '8':
1355         case '9':
1356                 // MED 01/04/97 added hipnotic give stuff
1357                 if (gamemode == GAME_HIPNOTIC)
1358                 {
1359                         if (t[0] == '6')
1360                         {
1361                                 if (t[1] == 'a')
1362                                         sv_player->v->items = (int)sv_player->v->items | HIT_PROXIMITY_GUN;
1363                                 else
1364                                         sv_player->v->items = (int)sv_player->v->items | IT_GRENADE_LAUNCHER;
1365                         }
1366                         else if (t[0] == '9')
1367                                 sv_player->v->items = (int)sv_player->v->items | HIT_LASER_CANNON;
1368                         else if (t[0] == '0')
1369                                 sv_player->v->items = (int)sv_player->v->items | HIT_MJOLNIR;
1370                         else if (t[0] >= '2')
1371                                 sv_player->v->items = (int)sv_player->v->items | (IT_SHOTGUN << (t[0] - '2'));
1372                 }
1373                 else
1374                 {
1375                         if (t[0] >= '2')
1376                                 sv_player->v->items = (int)sv_player->v->items | (IT_SHOTGUN << (t[0] - '2'));
1377                 }
1378                 break;
1379
1380         case 's':
1381                 if (gamemode == GAME_ROGUE && (val = GETEDICTFIELDVALUE(sv_player, eval_ammo_shells1)))
1382                         val->_float = v;
1383
1384                 sv_player->v->ammo_shells = v;
1385                 break;
1386         case 'n':
1387                 if (gamemode == GAME_ROGUE)
1388                 {
1389                         if ((val = GETEDICTFIELDVALUE(sv_player, eval_ammo_nails1)))
1390                         {
1391                                 val->_float = v;
1392                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1393                                         sv_player->v->ammo_nails = v;
1394                         }
1395                 }
1396                 else
1397                 {
1398                         sv_player->v->ammo_nails = v;
1399                 }
1400                 break;
1401         case 'l':
1402                 if (gamemode == GAME_ROGUE)
1403                 {
1404                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_lava_nails);
1405                         if (val)
1406                         {
1407                                 val->_float = v;
1408                                 if (sv_player->v->weapon > IT_LIGHTNING)
1409                                         sv_player->v->ammo_nails = v;
1410                         }
1411                 }
1412                 break;
1413         case 'r':
1414                 if (gamemode == GAME_ROGUE)
1415                 {
1416                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_rockets1);
1417                         if (val)
1418                         {
1419                                 val->_float = v;
1420                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1421                                         sv_player->v->ammo_rockets = v;
1422                         }
1423                 }
1424                 else
1425                 {
1426                         sv_player->v->ammo_rockets = v;
1427                 }
1428                 break;
1429         case 'm':
1430                 if (gamemode == GAME_ROGUE)
1431                 {
1432                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_multi_rockets);
1433                         if (val)
1434                         {
1435                                 val->_float = v;
1436                                 if (sv_player->v->weapon > IT_LIGHTNING)
1437                                         sv_player->v->ammo_rockets = v;
1438                         }
1439                 }
1440                 break;
1441         case 'h':
1442                 sv_player->v->health = v;
1443                 break;
1444         case 'c':
1445                 if (gamemode == GAME_ROGUE)
1446                 {
1447                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_cells1);
1448                         if (val)
1449                         {
1450                                 val->_float = v;
1451                                 if (sv_player->v->weapon <= IT_LIGHTNING)
1452                                         sv_player->v->ammo_cells = v;
1453                         }
1454                 }
1455                 else
1456                 {
1457                         sv_player->v->ammo_cells = v;
1458                 }
1459                 break;
1460         case 'p':
1461                 if (gamemode == GAME_ROGUE)
1462                 {
1463                         val = GETEDICTFIELDVALUE(sv_player, eval_ammo_plasma);
1464                         if (val)
1465                         {
1466                                 val->_float = v;
1467                                 if (sv_player->v->weapon > IT_LIGHTNING)
1468                                         sv_player->v->ammo_cells = v;
1469                         }
1470                 }
1471                 break;
1472         }
1473 }
1474
1475 edict_t *FindViewthing (void)
1476 {
1477         int             i;
1478         edict_t *e;
1479
1480         for (i=0 ; i<sv.num_edicts ; i++)
1481         {
1482                 e = EDICT_NUM(i);
1483                 if (!strcmp (PR_GetString(e->v->classname), "viewthing"))
1484                         return e;
1485         }
1486         Con_Print("No viewthing on map\n");
1487         return NULL;
1488 }
1489
1490 /*
1491 ==================
1492 Host_Viewmodel_f
1493 ==================
1494 */
1495 void Host_Viewmodel_f (void)
1496 {
1497         edict_t *e;
1498         model_t *m;
1499
1500         e = FindViewthing ();
1501         if (!e)
1502                 return;
1503
1504         m = Mod_ForName (Cmd_Argv(1), false, true, false);
1505         if (!m)
1506         {
1507                 Con_Printf("Can't load %s\n", Cmd_Argv(1));
1508                 return;
1509         }
1510
1511         e->v->frame = 0;
1512         cl.model_precache[(int)e->v->modelindex] = m;
1513 }
1514
1515 /*
1516 ==================
1517 Host_Viewframe_f
1518 ==================
1519 */
1520 void Host_Viewframe_f (void)
1521 {
1522         edict_t *e;
1523         int             f;
1524         model_t *m;
1525
1526         e = FindViewthing ();
1527         if (!e)
1528                 return;
1529         m = cl.model_precache[(int)e->v->modelindex];
1530
1531         f = atoi(Cmd_Argv(1));
1532         if (f >= m->numframes)
1533                 f = m->numframes-1;
1534
1535         e->v->frame = f;
1536 }
1537
1538
1539 void PrintFrameName (model_t *m, int frame)
1540 {
1541         if (m->animscenes)
1542                 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
1543         else
1544                 Con_Printf("frame %i\n", frame);
1545 }
1546
1547 /*
1548 ==================
1549 Host_Viewnext_f
1550 ==================
1551 */
1552 void Host_Viewnext_f (void)
1553 {
1554         edict_t *e;
1555         model_t *m;
1556
1557         e = FindViewthing ();
1558         if (!e)
1559                 return;
1560         m = cl.model_precache[(int)e->v->modelindex];
1561
1562         e->v->frame = e->v->frame + 1;
1563         if (e->v->frame >= m->numframes)
1564                 e->v->frame = m->numframes - 1;
1565
1566         PrintFrameName (m, e->v->frame);
1567 }
1568
1569 /*
1570 ==================
1571 Host_Viewprev_f
1572 ==================
1573 */
1574 void Host_Viewprev_f (void)
1575 {
1576         edict_t *e;
1577         model_t *m;
1578
1579         e = FindViewthing ();
1580         if (!e)
1581                 return;
1582
1583         m = cl.model_precache[(int)e->v->modelindex];
1584
1585         e->v->frame = e->v->frame - 1;
1586         if (e->v->frame < 0)
1587                 e->v->frame = 0;
1588
1589         PrintFrameName (m, e->v->frame);
1590 }
1591
1592 /*
1593 ===============================================================================
1594
1595 DEMO LOOP CONTROL
1596
1597 ===============================================================================
1598 */
1599
1600
1601 /*
1602 ==================
1603 Host_Startdemos_f
1604 ==================
1605 */
1606 void Host_Startdemos_f (void)
1607 {
1608         int             i, c;
1609
1610         if (cls.state == ca_dedicated || COM_CheckParm("-listen"))
1611         {
1612                 if (!sv.active)
1613                 {
1614                         if (gamemode == GAME_TRANSFUSION)
1615                                 Cbuf_AddText ("map bb1\n");
1616                         else
1617                                 Cbuf_AddText ("map start\n");
1618                 }
1619                 return;
1620         }
1621
1622         c = Cmd_Argc() - 1;
1623         if (c > MAX_DEMOS)
1624         {
1625                 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
1626                 c = MAX_DEMOS;
1627         }
1628         Con_DPrintf("%i demo(s) in loop\n", c);
1629
1630         for (i=1 ; i<c+1 ; i++)
1631                 strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof (cls.demos[i-1]));
1632
1633         // LordHavoc: clear the remaining slots
1634         for (;i <= MAX_DEMOS;i++)
1635                 cls.demos[i-1][0] = 0;
1636
1637         if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
1638         {
1639                 cls.demonum = 0;
1640                 CL_NextDemo ();
1641         }
1642         else
1643                 cls.demonum = -1;
1644 }
1645
1646
1647 /*
1648 ==================
1649 Host_Demos_f
1650
1651 Return to looping demos
1652 ==================
1653 */
1654 void Host_Demos_f (void)
1655 {
1656         if (cls.state == ca_dedicated)
1657                 return;
1658         if (cls.demonum == -1)
1659                 cls.demonum = 1;
1660         CL_Disconnect_f ();
1661         CL_NextDemo ();
1662 }
1663
1664 /*
1665 ==================
1666 Host_Stopdemo_f
1667
1668 Return to looping demos
1669 ==================
1670 */
1671 void Host_Stopdemo_f (void)
1672 {
1673         if (!cls.demoplayback)
1674                 return;
1675         CL_Disconnect ();
1676         Host_ShutdownServer (false);
1677 }
1678
1679 static void MaxPlayers_f(void)
1680 {
1681         int n;
1682
1683         if (Cmd_Argc() != 2)
1684         {
1685                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients);
1686                 return;
1687         }
1688
1689         if (sv.active)
1690         {
1691                 Con_Print("maxplayers can not be changed while a server is running.\n");
1692                 return;
1693         }
1694
1695         n = atoi(Cmd_Argv(1));
1696         n = bound(1, n, MAX_SCOREBOARD);
1697         Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
1698
1699         if (svs.clients)
1700                 Mem_Free(svs.clients);
1701         svs.maxclients = n;
1702         svs.clients = Mem_Alloc(sv_clients_mempool, sizeof(client_t) * svs.maxclients);
1703         if (n == 1)
1704                 Cvar_Set ("deathmatch", "0");
1705         else
1706                 Cvar_Set ("deathmatch", "1");
1707 }
1708
1709 //=============================================================================
1710
1711 /*
1712 ==================
1713 Host_InitCommands
1714 ==================
1715 */
1716 void Host_InitCommands (void)
1717 {
1718         Cmd_AddCommand ("status", Host_Status_f);
1719         Cmd_AddCommand ("quit", Host_Quit_f);
1720         if (gamemode == GAME_NEHAHRA)
1721         {
1722                 Cmd_AddCommand ("max", Host_God_f);
1723                 Cmd_AddCommand ("monster", Host_Notarget_f);
1724                 Cmd_AddCommand ("scrag", Host_Fly_f);
1725                 Cmd_AddCommand ("wraith", Host_Noclip_f);
1726                 Cmd_AddCommand ("gimme", Host_Give_f);
1727         }
1728         else
1729         {
1730                 Cmd_AddCommand ("god", Host_God_f);
1731                 Cmd_AddCommand ("notarget", Host_Notarget_f);
1732                 Cmd_AddCommand ("fly", Host_Fly_f);
1733                 Cmd_AddCommand ("noclip", Host_Noclip_f);
1734                 Cmd_AddCommand ("give", Host_Give_f);
1735         }
1736         Cmd_AddCommand ("map", Host_Map_f);
1737         Cmd_AddCommand ("restart", Host_Restart_f);
1738         Cmd_AddCommand ("changelevel", Host_Changelevel_f);
1739         Cmd_AddCommand ("connect", Host_Connect_f);
1740         Cmd_AddCommand ("reconnect", Host_Reconnect_f);
1741         Cmd_AddCommand ("version", Host_Version_f);
1742         Cmd_AddCommand ("say", Host_Say_f);
1743         Cmd_AddCommand ("say_team", Host_Say_Team_f);
1744         Cmd_AddCommand ("tell", Host_Tell_f);
1745         Cmd_AddCommand ("kill", Host_Kill_f);
1746         Cmd_AddCommand ("pause", Host_Pause_f);
1747         Cmd_AddCommand ("kick", Host_Kick_f);
1748         Cmd_AddCommand ("ping", Host_Ping_f);
1749         Cmd_AddCommand ("load", Host_Loadgame_f);
1750         Cmd_AddCommand ("save", Host_Savegame_f);
1751
1752         Cmd_AddCommand ("startdemos", Host_Startdemos_f);
1753         Cmd_AddCommand ("demos", Host_Demos_f);
1754         Cmd_AddCommand ("stopdemo", Host_Stopdemo_f);
1755
1756         Cmd_AddCommand ("viewmodel", Host_Viewmodel_f);
1757         Cmd_AddCommand ("viewframe", Host_Viewframe_f);
1758         Cmd_AddCommand ("viewnext", Host_Viewnext_f);
1759         Cmd_AddCommand ("viewprev", Host_Viewprev_f);
1760
1761         Cvar_RegisterVariable (&cl_name);
1762         Cmd_AddCommand ("name", Host_Name_f);
1763         Cvar_RegisterVariable (&cl_color);
1764         Cmd_AddCommand ("color", Host_Color_f);
1765         Cvar_RegisterVariable (&cl_rate);
1766         Cmd_AddCommand ("rate", Host_Rate_f);
1767         Cvar_RegisterVariable (&sv_maxrate);
1768         if (gamemode == GAME_NEHAHRA)
1769         {
1770                 Cvar_RegisterVariable (&cl_pmodel);
1771                 Cmd_AddCommand ("pmodel", Host_PModel_f);
1772         }
1773         Cmd_AddCommand ("prespawn", Host_PreSpawn_f);
1774         Cmd_AddCommand ("spawn", Host_Spawn_f);
1775         Cmd_AddCommand ("begin", Host_Begin_f);
1776         Cmd_AddCommand ("maxplayers", MaxPlayers_f);
1777
1778         Cvar_RegisterVariable(&sv_cheats);
1779 }
1780