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