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