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