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