]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - host_cmd.c
Fix most compiler warnings.
[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                 PRVM_serverglobalfloat(time) = sv.time;
1607                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1608                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1609         }
1610         else
1611         {
1612                 if (host_client->edict)
1613                 {
1614                         PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1615                         PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1616                 }
1617                 host_client->colors = playercolor;
1618                 if (host_client->old_colors != host_client->colors)
1619                 {
1620                         host_client->old_colors = host_client->colors;
1621                         // send notification to all clients
1622                         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1623                         MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1624                         MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1625                 }
1626         }
1627 }
1628
1629 static void Host_Color_f(cmd_state_t *cmd)
1630 {
1631         int             top, bottom;
1632
1633         if (Cmd_Argc(cmd) == 1)
1634         {
1635                 if (cmd->source == src_command)
1636                 {
1637                         Con_Printf("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 15);
1638                         Con_Print("color <0-15> [0-15]\n");
1639                 }
1640                 return;
1641         }
1642
1643         if (Cmd_Argc(cmd) == 2)
1644                 top = bottom = atoi(Cmd_Argv(cmd, 1));
1645         else
1646         {
1647                 top = atoi(Cmd_Argv(cmd, 1));
1648                 bottom = atoi(Cmd_Argv(cmd, 2));
1649         }
1650         Host_Color(cmd, top, bottom);
1651 }
1652
1653 static void Host_TopColor_f(cmd_state_t *cmd)
1654 {
1655         if (Cmd_Argc(cmd) == 1)
1656         {
1657                 if (cmd->source == src_command)
1658                 {
1659                         Con_Printf("\"topcolor\" is \"%i\"\n", (cl_color.integer >> 4) & 15);
1660                         Con_Print("topcolor <0-15>\n");
1661                 }
1662                 return;
1663         }
1664
1665         Host_Color(cmd, atoi(Cmd_Argv(cmd, 1)), -1);
1666 }
1667
1668 static void Host_BottomColor_f(cmd_state_t *cmd)
1669 {
1670         if (Cmd_Argc(cmd) == 1)
1671         {
1672                 if (cmd->source == src_command)
1673                 {
1674                         Con_Printf("\"bottomcolor\" is \"%i\"\n", cl_color.integer & 15);
1675                         Con_Print("bottomcolor <0-15>\n");
1676                 }
1677                 return;
1678         }
1679
1680         Host_Color(cmd, -1, atoi(Cmd_Argv(cmd, 1)));
1681 }
1682
1683 cvar_t cl_rate = {CVAR_CLIENT | CVAR_SAVE | CVAR_NQUSERINFOHACK, "_cl_rate", "20000", "internal storage cvar for current rate (changed by rate command)"};
1684 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)"};
1685 static void Host_Rate_f(cmd_state_t *cmd)
1686 {
1687         int rate;
1688
1689         if (Cmd_Argc(cmd) != 2)
1690         {
1691                 if (cmd->source == src_command)
1692                 {
1693                         Con_Printf("\"rate\" is \"%i\"\n", cl_rate.integer);
1694                         Con_Print("rate <bytespersecond>\n");
1695                 }
1696                 return;
1697         }
1698
1699         rate = atoi(Cmd_Argv(cmd, 1));
1700
1701         if (cmd->source == src_command)
1702         {
1703                 Cvar_SetValue (&cvars_all, "_cl_rate", max(NET_MINRATE, rate));
1704                 return;
1705         }
1706
1707         host_client->rate = rate;
1708 }
1709 static void Host_Rate_BurstSize_f(cmd_state_t *cmd)
1710 {
1711         int rate_burstsize;
1712
1713         if (Cmd_Argc(cmd) != 2)
1714         {
1715                 Con_Printf("\"rate_burstsize\" is \"%i\"\n", cl_rate_burstsize.integer);
1716                 Con_Print("rate_burstsize <bytes>\n");
1717                 return;
1718         }
1719
1720         rate_burstsize = atoi(Cmd_Argv(cmd, 1));
1721
1722         if (cmd->source == src_command)
1723         {
1724                 Cvar_SetValue (&cvars_all, "_cl_rate_burstsize", rate_burstsize);
1725                 return;
1726         }
1727
1728         host_client->rate_burstsize = rate_burstsize;
1729 }
1730
1731 /*
1732 ==================
1733 Host_Kill_f
1734 ==================
1735 */
1736 static void Host_Kill_f(cmd_state_t *cmd)
1737 {
1738         prvm_prog_t *prog = SVVM_prog;
1739         if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
1740         {
1741                 SV_ClientPrint("Can't suicide -- already dead!\n");
1742                 return;
1743         }
1744
1745         PRVM_serverglobalfloat(time) = sv.time;
1746         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1747         prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
1748 }
1749
1750
1751 /*
1752 ==================
1753 Host_Pause_f
1754 ==================
1755 */
1756 static void Host_Pause_f(cmd_state_t *cmd)
1757 {
1758         void (*print) (const char *fmt, ...);
1759         if (cmd->source == src_command)
1760         {
1761                 // if running a client, try to send over network so the pause is handled by the server
1762                 if (cls.state == ca_connected)
1763                 {
1764                         Cmd_ForwardToServer_f(cmd);
1765                         return;
1766                 }
1767                 print = Con_Printf;
1768         }
1769         else
1770                 print = SV_ClientPrintf;
1771
1772         if (!pausable.integer)
1773         {
1774                 if (cmd->source == src_client)
1775                 {
1776                         if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
1777                         {
1778                                 print("Pause not allowed.\n");
1779                                 return;
1780                         }
1781                 }
1782         }
1783         
1784         sv.paused ^= 1;
1785         if (cmd->source != src_command)
1786                 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
1787         else if(*(sv_adminnick.string))
1788                 SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
1789         else
1790                 SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
1791         // send notification to all clients
1792         MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
1793         MSG_WriteByte(&sv.reliable_datagram, sv.paused);
1794 }
1795
1796 /*
1797 ======================
1798 Host_PModel_f
1799 LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1800 LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1801 ======================
1802 */
1803 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)"};
1804 static void Host_PModel_f(cmd_state_t *cmd)
1805 {
1806         prvm_prog_t *prog = SVVM_prog;
1807         int i;
1808
1809         if (Cmd_Argc (cmd) == 1)
1810         {
1811                 if (cmd->source == src_command)
1812                 {
1813                         Con_Printf("\"pmodel\" is \"%s\"\n", cl_pmodel.string);
1814                 }
1815                 return;
1816         }
1817         i = atoi(Cmd_Argv(cmd, 1));
1818
1819         if (cmd->source == src_command)
1820         {
1821                 if (cl_pmodel.integer == i)
1822                         return;
1823                 Cvar_SetValue (&cvars_all, "_cl_pmodel", i);
1824                 if (cls.state == ca_connected)
1825                         Cmd_ForwardToServer_f(cmd);
1826                 return;
1827         }
1828
1829         PRVM_serveredictfloat(host_client->edict, pmodel) = i;
1830 }
1831
1832 //===========================================================================
1833
1834
1835 /*
1836 ==================
1837 Host_PreSpawn_f
1838 ==================
1839 */
1840 static void Host_PreSpawn_f(cmd_state_t *cmd)
1841 {
1842         if (host_client->prespawned)
1843         {
1844                 Con_Print("prespawn not valid -- already prespawned\n");
1845                 return;
1846         }
1847         host_client->prespawned = true;
1848
1849         if (host_client->netconnection)
1850         {
1851                 SZ_Write (&host_client->netconnection->message, sv.signon.data, sv.signon.cursize);
1852                 MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
1853                 MSG_WriteByte (&host_client->netconnection->message, 2);
1854                 host_client->sendsignon = 0;            // enable unlimited sends again
1855         }
1856
1857         // reset the name change timer because the client will send name soon
1858         host_client->nametime = 0;
1859 }
1860
1861 /*
1862 ==================
1863 Host_Spawn_f
1864 ==================
1865 */
1866 static void Host_Spawn_f(cmd_state_t *cmd)
1867 {
1868         prvm_prog_t *prog = SVVM_prog;
1869         int i;
1870         client_t *client;
1871         int stats[MAX_CL_STATS];
1872
1873         if (!host_client->prespawned)
1874         {
1875                 Con_Print("Spawn not valid -- not yet prespawned\n");
1876                 return;
1877         }
1878         if (host_client->spawned)
1879         {
1880                 Con_Print("Spawn not valid -- already spawned\n");
1881                 return;
1882         }
1883         host_client->spawned = true;
1884
1885         // reset name change timer again because they might want to change name
1886         // again in the first 5 seconds after connecting
1887         host_client->nametime = 0;
1888
1889         // LadyHavoc: moved this above the QC calls at FrikaC's request
1890         // LadyHavoc: commented this out
1891         //if (host_client->netconnection)
1892         //      SZ_Clear (&host_client->netconnection->message);
1893
1894         // run the entrance script
1895         if (sv.loadgame)
1896         {
1897                 // loaded games are fully initialized already
1898                 if (PRVM_serverfunction(RestoreGame))
1899                 {
1900                         Con_DPrint("Calling RestoreGame\n");
1901                         PRVM_serverglobalfloat(time) = sv.time;
1902                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1903                         prog->ExecuteProgram(prog, PRVM_serverfunction(RestoreGame), "QC function RestoreGame is missing");
1904                 }
1905         }
1906         else
1907         {
1908                 //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);
1909
1910                 // copy spawn parms out of the client_t
1911                 for (i=0 ; i< NUM_SPAWN_PARMS ; i++)
1912                         (&PRVM_serverglobalfloat(parm1))[i] = host_client->spawn_parms[i];
1913
1914                 // call the spawn function
1915                 host_client->clientconnectcalled = true;
1916                 PRVM_serverglobalfloat(time) = sv.time;
1917                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1918                 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
1919
1920                 if (cls.state == ca_dedicated)
1921                         Con_Printf("%s connected\n", host_client->name);
1922
1923                 PRVM_serverglobalfloat(time) = sv.time;
1924                 prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
1925         }
1926
1927         if (!host_client->netconnection)
1928                 return;
1929
1930         // send time of update
1931         MSG_WriteByte (&host_client->netconnection->message, svc_time);
1932         MSG_WriteFloat (&host_client->netconnection->message, sv.time);
1933
1934         // send all current names, colors, and frag counts
1935         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
1936         {
1937                 if (!client->active)
1938                         continue;
1939                 MSG_WriteByte (&host_client->netconnection->message, svc_updatename);
1940                 MSG_WriteByte (&host_client->netconnection->message, i);
1941                 MSG_WriteString (&host_client->netconnection->message, client->name);
1942                 MSG_WriteByte (&host_client->netconnection->message, svc_updatefrags);
1943                 MSG_WriteByte (&host_client->netconnection->message, i);
1944                 MSG_WriteShort (&host_client->netconnection->message, client->frags);
1945                 MSG_WriteByte (&host_client->netconnection->message, svc_updatecolors);
1946                 MSG_WriteByte (&host_client->netconnection->message, i);
1947                 MSG_WriteByte (&host_client->netconnection->message, client->colors);
1948         }
1949
1950         // send all current light styles
1951         for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
1952         {
1953                 if (sv.lightstyles[i][0])
1954                 {
1955                         MSG_WriteByte (&host_client->netconnection->message, svc_lightstyle);
1956                         MSG_WriteByte (&host_client->netconnection->message, (char)i);
1957                         MSG_WriteString (&host_client->netconnection->message, sv.lightstyles[i]);
1958                 }
1959         }
1960
1961         // send some stats
1962         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1963         MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALSECRETS);
1964         MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_secrets));
1965
1966         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1967         MSG_WriteByte (&host_client->netconnection->message, STAT_TOTALMONSTERS);
1968         MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(total_monsters));
1969
1970         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1971         MSG_WriteByte (&host_client->netconnection->message, STAT_SECRETS);
1972         MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(found_secrets));
1973
1974         MSG_WriteByte (&host_client->netconnection->message, svc_updatestat);
1975         MSG_WriteByte (&host_client->netconnection->message, STAT_MONSTERS);
1976         MSG_WriteLong (&host_client->netconnection->message, (int)PRVM_serverglobalfloat(killed_monsters));
1977
1978         // send a fixangle
1979         // Never send a roll angle, because savegames can catch the server
1980         // in a state where it is expecting the client to correct the angle
1981         // and it won't happen if the game was just loaded, so you wind up
1982         // with a permanent head tilt
1983         if (sv.loadgame)
1984         {
1985                 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1986                 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[0], sv.protocol);
1987                 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, v_angle)[1], sv.protocol);
1988                 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1989         }
1990         else
1991         {
1992                 MSG_WriteByte (&host_client->netconnection->message, svc_setangle);
1993                 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[0], sv.protocol);
1994                 MSG_WriteAngle (&host_client->netconnection->message, PRVM_serveredictvector(host_client->edict, angles)[1], sv.protocol);
1995                 MSG_WriteAngle (&host_client->netconnection->message, 0, sv.protocol);
1996         }
1997
1998         SV_WriteClientdataToMessage (host_client, host_client->edict, &host_client->netconnection->message, stats);
1999
2000         MSG_WriteByte (&host_client->netconnection->message, svc_signonnum);
2001         MSG_WriteByte (&host_client->netconnection->message, 3);
2002 }
2003
2004 /*
2005 ==================
2006 Host_Begin_f
2007 ==================
2008 */
2009 static void Host_Begin_f(cmd_state_t *cmd)
2010 {
2011         if (!host_client->spawned)
2012         {
2013                 Con_Print("Begin not valid -- not yet spawned\n");
2014                 return;
2015         }
2016         if (host_client->begun)
2017         {
2018                 Con_Print("Begin not valid -- already begun\n");
2019                 return;
2020         }
2021         host_client->begun = true;
2022
2023         // LadyHavoc: note: this code also exists in SV_DropClient
2024         if (sv.loadgame)
2025         {
2026                 int i;
2027                 for (i = 0;i < svs.maxclients;i++)
2028                         if (svs.clients[i].active && !svs.clients[i].spawned)
2029                                 break;
2030                 if (i == svs.maxclients)
2031                 {
2032                         Con_Printf("Loaded game, everyone rejoined - unpausing\n");
2033                         sv.paused = sv.loadgame = false; // we're basically done with loading now
2034                 }
2035         }
2036 }
2037
2038 //===========================================================================
2039
2040
2041 /*
2042 ==================
2043 Host_Kick_f
2044
2045 Kicks a user off of the server
2046 ==================
2047 */
2048 static void Host_Kick_f(cmd_state_t *cmd)
2049 {
2050         const char *who;
2051         const char *message = NULL;
2052         client_t *save;
2053         int i;
2054         qboolean byNumber = false;
2055
2056         if (!sv.active)
2057                 return;
2058
2059         save = host_client;
2060
2061         if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
2062         {
2063                 i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
2064                 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
2065                         return;
2066                 byNumber = true;
2067         }
2068         else
2069         {
2070                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2071                 {
2072                         if (!host_client->active)
2073                                 continue;
2074                         if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
2075                                 break;
2076                 }
2077         }
2078
2079         if (i < svs.maxclients)
2080         {
2081                 if (cmd->source == src_command)
2082                 {
2083                         if (cls.state == ca_dedicated)
2084                                 who = "Console";
2085                         else
2086                                 who = cl_name.string;
2087                 }
2088                 else
2089                         who = save->name;
2090
2091                 // can't kick yourself!
2092                 if (host_client == save)
2093                         return;
2094
2095                 if (Cmd_Argc(cmd) > 2)
2096                 {
2097                         message = Cmd_Args(cmd);
2098                         COM_ParseToken_Simple(&message, false, false, true);
2099                         if (byNumber)
2100                         {
2101                                 message++;                                                      // skip the #
2102                                 while (*message == ' ')                         // skip white space
2103                                         message++;
2104                                 message += strlen(Cmd_Argv(cmd, 2));    // skip the number
2105                         }
2106                         while (*message && *message == ' ')
2107                                 message++;
2108                 }
2109                 if (message)
2110                         SV_ClientPrintf("Kicked by %s: %s\n", who, message);
2111                 else
2112                         SV_ClientPrintf("Kicked by %s\n", who);
2113                 SV_DropClient (false); // kicked
2114         }
2115
2116         host_client = save;
2117 }
2118
2119 /*
2120 ===============================================================================
2121
2122 DEBUGGING TOOLS
2123
2124 ===============================================================================
2125 */
2126
2127 /*
2128 ==================
2129 Host_Give_f
2130 ==================
2131 */
2132 static void Host_Give_f(cmd_state_t *cmd)
2133 {
2134         prvm_prog_t *prog = SVVM_prog;
2135         const char *t;
2136         int v;
2137
2138         if (!sv_cheats.integer)
2139         {
2140                 SV_ClientPrint("No cheats allowed, use sv_cheats 1 and restart level to enable.\n");
2141                 return;
2142         }
2143
2144         t = Cmd_Argv(cmd, 1);
2145         v = atoi (Cmd_Argv(cmd, 2));
2146
2147         switch (t[0])
2148         {
2149         case '0':
2150         case '1':
2151         case '2':
2152         case '3':
2153         case '4':
2154         case '5':
2155         case '6':
2156         case '7':
2157         case '8':
2158         case '9':
2159                 // MED 01/04/97 added hipnotic give stuff
2160                 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
2161                 {
2162                         if (t[0] == '6')
2163                         {
2164                                 if (t[1] == 'a')
2165                                         PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
2166                                 else
2167                                         PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
2168                         }
2169                         else if (t[0] == '9')
2170                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
2171                         else if (t[0] == '0')
2172                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
2173                         else if (t[0] >= '2')
2174                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2175                 }
2176                 else
2177                 {
2178                         if (t[0] >= '2')
2179                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
2180                 }
2181                 break;
2182
2183         case 's':
2184                 if (gamemode == GAME_ROGUE)
2185                         PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
2186
2187                 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
2188                 break;
2189         case 'n':
2190                 if (gamemode == GAME_ROGUE)
2191                 {
2192                         PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
2193                         if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2194                                 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2195                 }
2196                 else
2197                 {
2198                         PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2199                 }
2200                 break;
2201         case 'l':
2202                 if (gamemode == GAME_ROGUE)
2203                 {
2204                         PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
2205                         if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2206                                 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
2207                 }
2208                 break;
2209         case 'r':
2210                 if (gamemode == GAME_ROGUE)
2211                 {
2212                         PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
2213                         if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2214                                 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2215                 }
2216                 else
2217                 {
2218                         PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2219                 }
2220                 break;
2221         case 'm':
2222                 if (gamemode == GAME_ROGUE)
2223                 {
2224                         PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
2225                         if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2226                                 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
2227                 }
2228                 break;
2229         case 'h':
2230                 PRVM_serveredictfloat(host_client->edict, health) = v;
2231                 break;
2232         case 'c':
2233                 if (gamemode == GAME_ROGUE)
2234                 {
2235                         PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
2236                         if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
2237                                 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2238                 }
2239                 else
2240                 {
2241                         PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2242                 }
2243                 break;
2244         case 'p':
2245                 if (gamemode == GAME_ROGUE)
2246                 {
2247                         PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
2248                         if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
2249                                 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
2250                 }
2251                 break;
2252         }
2253 }
2254
2255 static prvm_edict_t     *FindViewthing(prvm_prog_t *prog)
2256 {
2257         int             i;
2258         prvm_edict_t    *e;
2259
2260         for (i=0 ; i<prog->num_edicts ; i++)
2261         {
2262                 e = PRVM_EDICT_NUM(i);
2263                 if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
2264                         return e;
2265         }
2266         Con_Print("No viewthing on map\n");
2267         return NULL;
2268 }
2269
2270 /*
2271 ==================
2272 Host_Viewmodel_f
2273 ==================
2274 */
2275 static void Host_Viewmodel_f(cmd_state_t *cmd)
2276 {
2277         prvm_prog_t *prog = SVVM_prog;
2278         prvm_edict_t    *e;
2279         dp_model_t      *m;
2280
2281         if (!sv.active)
2282                 return;
2283
2284         e = FindViewthing(prog);
2285         if (e)
2286         {
2287                 m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
2288                 if (m && m->loaded && m->Draw)
2289                 {
2290                         PRVM_serveredictfloat(e, frame) = 0;
2291                         cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
2292                 }
2293                 else
2294                         Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
2295         }
2296 }
2297
2298 /*
2299 ==================
2300 Host_Viewframe_f
2301 ==================
2302 */
2303 static void Host_Viewframe_f(cmd_state_t *cmd)
2304 {
2305         prvm_prog_t *prog = SVVM_prog;
2306         prvm_edict_t    *e;
2307         int             f;
2308         dp_model_t      *m;
2309
2310         if (!sv.active)
2311                 return;
2312
2313         e = FindViewthing(prog);
2314         if (e)
2315         {
2316                 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2317
2318                 f = atoi(Cmd_Argv(cmd, 1));
2319                 if (f >= m->numframes)
2320                         f = m->numframes-1;
2321
2322                 PRVM_serveredictfloat(e, frame) = f;
2323         }
2324 }
2325
2326
2327 static void PrintFrameName (dp_model_t *m, int frame)
2328 {
2329         if (m->animscenes)
2330                 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
2331         else
2332                 Con_Printf("frame %i\n", frame);
2333 }
2334
2335 /*
2336 ==================
2337 Host_Viewnext_f
2338 ==================
2339 */
2340 static void Host_Viewnext_f(cmd_state_t *cmd)
2341 {
2342         prvm_prog_t *prog = SVVM_prog;
2343         prvm_edict_t    *e;
2344         dp_model_t      *m;
2345
2346         if (!sv.active)
2347                 return;
2348
2349         e = FindViewthing(prog);
2350         if (e)
2351         {
2352                 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2353
2354                 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
2355                 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
2356                         PRVM_serveredictfloat(e, frame) = m->numframes - 1;
2357
2358                 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2359         }
2360 }
2361
2362 /*
2363 ==================
2364 Host_Viewprev_f
2365 ==================
2366 */
2367 static void Host_Viewprev_f(cmd_state_t *cmd)
2368 {
2369         prvm_prog_t *prog = SVVM_prog;
2370         prvm_edict_t    *e;
2371         dp_model_t      *m;
2372
2373         if (!sv.active)
2374                 return;
2375
2376         e = FindViewthing(prog);
2377         if (e)
2378         {
2379                 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
2380
2381                 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
2382                 if (PRVM_serveredictfloat(e, frame) < 0)
2383                         PRVM_serveredictfloat(e, frame) = 0;
2384
2385                 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
2386         }
2387 }
2388
2389 /*
2390 ===============================================================================
2391
2392 DEMO LOOP CONTROL
2393
2394 ===============================================================================
2395 */
2396
2397
2398 /*
2399 ==================
2400 Host_Startdemos_f
2401 ==================
2402 */
2403 static void Host_Startdemos_f(cmd_state_t *cmd)
2404 {
2405         int             i, c;
2406
2407         if (cls.state == ca_dedicated || COM_CheckParm("-listen") || COM_CheckParm("-benchmark") || COM_CheckParm("-demo") || COM_CheckParm("-capturedemo"))
2408                 return;
2409
2410         c = Cmd_Argc(cmd) - 1;
2411         if (c > MAX_DEMOS)
2412         {
2413                 Con_Printf("Max %i demos in demoloop\n", MAX_DEMOS);
2414                 c = MAX_DEMOS;
2415         }
2416         Con_DPrintf("%i demo(s) in loop\n", c);
2417
2418         for (i=1 ; i<c+1 ; i++)
2419                 strlcpy (cls.demos[i-1], Cmd_Argv(cmd, i), sizeof (cls.demos[i-1]));
2420
2421         // LadyHavoc: clear the remaining slots
2422         for (;i <= MAX_DEMOS;i++)
2423                 cls.demos[i-1][0] = 0;
2424
2425         if (!sv.active && cls.demonum != -1 && !cls.demoplayback)
2426         {
2427                 cls.demonum = 0;
2428                 CL_NextDemo ();
2429         }
2430         else
2431                 cls.demonum = -1;
2432 }
2433
2434
2435 /*
2436 ==================
2437 Host_Demos_f
2438
2439 Return to looping demos
2440 ==================
2441 */
2442 static void Host_Demos_f(cmd_state_t *cmd)
2443 {
2444         if (cls.state == ca_dedicated)
2445                 return;
2446         if (cls.demonum == -1)
2447                 cls.demonum = 1;
2448         CL_Disconnect_f (cmd);
2449         CL_NextDemo ();
2450 }
2451
2452 /*
2453 ==================
2454 Host_Stopdemo_f
2455
2456 Return to looping demos
2457 ==================
2458 */
2459 static void Host_Stopdemo_f(cmd_state_t *cmd)
2460 {
2461         if (!cls.demoplayback)
2462                 return;
2463         CL_Disconnect ();
2464         Host_ShutdownServer ();
2465 }
2466
2467 static void Host_SendCvar_f(cmd_state_t *cmd)
2468 {
2469         int             i;
2470         cvar_t  *c;
2471         const char *cvarname;
2472         client_t *old;
2473         char vabuf[1024];
2474
2475         if(Cmd_Argc(cmd) != 2)
2476                 return;
2477         cvarname = Cmd_Argv(cmd, 1);
2478         if (cls.state == ca_connected)
2479         {
2480                 c = Cvar_FindVar(&cvars_all, cvarname, CVAR_CLIENT | CVAR_SERVER);
2481                 // LadyHavoc: if there is no such cvar or if it is private, send a
2482                 // reply indicating that it has no value
2483                 if(!c || (c->flags & CVAR_PRIVATE))
2484                         Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s", cvarname));
2485                 else
2486                         Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "sentcvar %s \"%s\"", c->name, c->string));
2487                 return;
2488         }
2489         if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
2490                 return;
2491
2492         old = host_client;
2493         if (cls.state != ca_dedicated)
2494                 i = 1;
2495         else
2496                 i = 0;
2497         for(;i<svs.maxclients;i++)
2498                 if(svs.clients[i].active && svs.clients[i].netconnection)
2499                 {
2500                         host_client = &svs.clients[i];
2501                         Host_ClientCommands("sendcvar %s\n", cvarname);
2502                 }
2503         host_client = old;
2504 }
2505
2506 static void MaxPlayers_f(cmd_state_t *cmd)
2507 {
2508         int n;
2509
2510         if (Cmd_Argc(cmd) != 2)
2511         {
2512                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
2513                 return;
2514         }
2515
2516         if (sv.active)
2517         {
2518                 Con_Print("maxplayers can not be changed while a server is running.\n");
2519                 Con_Print("It will be changed on next server startup (\"map\" command).\n");
2520         }
2521
2522         n = atoi(Cmd_Argv(cmd, 1));
2523         n = bound(1, n, MAX_SCOREBOARD);
2524         Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
2525
2526         svs.maxclients_next = n;
2527         if (n == 1)
2528                 Cvar_Set (&cvars_all, "deathmatch", "0");
2529         else
2530                 Cvar_Set (&cvars_all, "deathmatch", "1");
2531 }
2532
2533 /*
2534 =====================
2535 Host_PQRcon_f
2536
2537 ProQuake rcon support
2538 =====================
2539 */
2540 static void Host_PQRcon_f(cmd_state_t *cmd)
2541 {
2542         int n;
2543         const char *e;
2544         lhnetsocket_t *mysocket;
2545
2546         if (Cmd_Argc(cmd) == 1)
2547         {
2548                 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2549                 return;
2550         }
2551
2552         if (!rcon_password.string || !rcon_password.string[0] || rcon_secure.integer > 0)
2553         {
2554                 Con_Printf ("You must set rcon_password before issuing an pqrcon command, and rcon_secure must be 0.\n");
2555                 return;
2556         }
2557
2558         e = strchr(rcon_password.string, ' ');
2559         n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2560
2561         if (cls.netcon)
2562                 cls.rcon_address = cls.netcon->peeraddress;
2563         else
2564         {
2565                 if (!rcon_address.string[0])
2566                 {
2567                         Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2568                         return;
2569                 }
2570                 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2571         }
2572         mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2573         if (mysocket)
2574         {
2575                 sizebuf_t buf;
2576                 unsigned char bufdata[64];
2577                 buf.data = bufdata;
2578                 SZ_Clear(&buf);
2579                 MSG_WriteLong(&buf, 0);
2580                 MSG_WriteByte(&buf, CCREQ_RCON);
2581                 SZ_Write(&buf, (const unsigned char*)rcon_password.string, n);
2582                 MSG_WriteByte(&buf, 0); // terminate the (possibly partial) string
2583                 MSG_WriteString(&buf, Cmd_Args(cmd));
2584                 StoreBigLong(buf.data, NETFLAG_CTL | (buf.cursize & NETFLAG_LENGTH_MASK));
2585                 NetConn_Write(mysocket, buf.data, buf.cursize, &cls.rcon_address);
2586                 SZ_Clear(&buf);
2587         }
2588 }
2589
2590 //=============================================================================
2591
2592 // QuakeWorld commands
2593
2594 /*
2595 =====================
2596 Host_Rcon_f
2597
2598   Send the rest of the command line over as
2599   an unconnected command.
2600 =====================
2601 */
2602 static void Host_Rcon_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2603 {
2604         int i, n;
2605         const char *e;
2606         lhnetsocket_t *mysocket;
2607
2608         if (Cmd_Argc(cmd) == 1)
2609         {
2610                 Con_Printf("%s: Usage: %s command\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, 0));
2611                 return;
2612         }
2613
2614         if (!rcon_password.string || !rcon_password.string[0])
2615         {
2616                 Con_Printf ("You must set rcon_password before issuing an rcon command.\n");
2617                 return;
2618         }
2619
2620         e = strchr(rcon_password.string, ' ');
2621         n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2622
2623         if (cls.netcon)
2624                 cls.rcon_address = cls.netcon->peeraddress;
2625         else
2626         {
2627                 if (!rcon_address.string[0])
2628                 {
2629                         Con_Printf ("You must either be connected, or set the rcon_address cvar to issue rcon commands\n");
2630                         return;
2631                 }
2632                 LHNETADDRESS_FromString(&cls.rcon_address, rcon_address.string, sv_netport.integer);
2633         }
2634         mysocket = NetConn_ChooseClientSocketForAddress(&cls.rcon_address);
2635         if (mysocket && Cmd_Args(cmd)[0])
2636         {
2637                 // simply put together the rcon packet and send it
2638                 if(Cmd_Argv(cmd, 0)[0] == 's' || rcon_secure.integer > 1)
2639                 {
2640                         if(cls.rcon_commands[cls.rcon_ringpos][0])
2641                         {
2642                                 char s[128];
2643                                 LHNETADDRESS_ToString(&cls.rcon_addresses[cls.rcon_ringpos], s, sizeof(s), true);
2644                                 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]);
2645                                 cls.rcon_commands[cls.rcon_ringpos][0] = 0;
2646                                 --cls.rcon_trying;
2647                         }
2648                         for (i = 0;i < MAX_RCONS;i++)
2649                                 if(cls.rcon_commands[i][0])
2650                                         if (!LHNETADDRESS_Compare(&cls.rcon_address, &cls.rcon_addresses[i]))
2651                                                 break;
2652                         ++cls.rcon_trying;
2653                         if(i >= MAX_RCONS)
2654                                 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", &cls.rcon_address); // otherwise we'll request the challenge later
2655                         strlcpy(cls.rcon_commands[cls.rcon_ringpos], Cmd_Args(cmd), sizeof(cls.rcon_commands[cls.rcon_ringpos]));
2656                         cls.rcon_addresses[cls.rcon_ringpos] = cls.rcon_address;
2657                         cls.rcon_timeout[cls.rcon_ringpos] = realtime + rcon_secure_challengetimeout.value;
2658                         cls.rcon_ringpos = (cls.rcon_ringpos + 1) % MAX_RCONS;
2659                 }
2660                 else if(rcon_secure.integer > 0)
2661                 {
2662                         char buf[1500];
2663                         char argbuf[1500];
2664                         dpsnprintf(argbuf, sizeof(argbuf), "%ld.%06d %s", (long) time(NULL), (int) (rand() % 1000000), Cmd_Args(cmd));
2665                         memcpy(buf, "\377\377\377\377srcon HMAC-MD4 TIME ", 24);
2666                         if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 24), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2667                         {
2668                                 buf[40] = ' ';
2669                                 strlcpy(buf + 41, argbuf, sizeof(buf) - 41);
2670                                 NetConn_Write(mysocket, buf, 41 + (int)strlen(buf + 41), &cls.rcon_address);
2671                         }
2672                 }
2673                 else
2674                 {
2675                         char buf[1500];
2676                         memcpy(buf, "\377\377\377\377", 4);
2677                         dpsnprintf(buf+4, sizeof(buf)-4, "rcon %.*s %s",  n, rcon_password.string, Cmd_Args(cmd));
2678                         NetConn_WriteString(mysocket, buf, &cls.rcon_address);
2679                 }
2680         }
2681 }
2682
2683 /*
2684 ====================
2685 Host_User_f
2686
2687 user <name or userid>
2688
2689 Dump userdata / masterdata for a user
2690 ====================
2691 */
2692 static void Host_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2693 {
2694         int             uid;
2695         int             i;
2696
2697         if (Cmd_Argc(cmd) != 2)
2698         {
2699                 Con_Printf ("Usage: user <username / userid>\n");
2700                 return;
2701         }
2702
2703         uid = atoi(Cmd_Argv(cmd, 1));
2704
2705         for (i = 0;i < cl.maxclients;i++)
2706         {
2707                 if (!cl.scores[i].name[0])
2708                         continue;
2709                 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
2710                 {
2711                         InfoString_Print(cl.scores[i].qw_userinfo);
2712                         return;
2713                 }
2714         }
2715         Con_Printf ("User not in server.\n");
2716 }
2717
2718 /*
2719 ====================
2720 Host_Users_f
2721
2722 Dump userids for all current players
2723 ====================
2724 */
2725 static void Host_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2726 {
2727         int             i;
2728         int             c;
2729
2730         c = 0;
2731         Con_Printf ("userid frags name\n");
2732         Con_Printf ("------ ----- ----\n");
2733         for (i = 0;i < cl.maxclients;i++)
2734         {
2735                 if (cl.scores[i].name[0])
2736                 {
2737                         Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
2738                         c++;
2739                 }
2740         }
2741
2742         Con_Printf ("%i total users\n", c);
2743 }
2744
2745 /*
2746 ==================
2747 Host_FullServerinfo_f
2748
2749 Sent by server when serverinfo changes
2750 ==================
2751 */
2752 // TODO: shouldn't this be a cvar instead?
2753 static void Host_FullServerinfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2754 {
2755         char temp[512];
2756         if (Cmd_Argc(cmd) != 2)
2757         {
2758                 Con_Printf ("usage: fullserverinfo <complete info string>\n");
2759                 return;
2760         }
2761
2762         strlcpy (cl.qw_serverinfo, Cmd_Argv(cmd, 1), sizeof(cl.qw_serverinfo));
2763         InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
2764         cl.qw_teamplay = atoi(temp);
2765 }
2766
2767 /*
2768 ==================
2769 Host_FullInfo_f
2770
2771 Allow clients to change userinfo
2772 ==================
2773 Casey was here :)
2774 */
2775 static void Host_FullInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2776 {
2777         char key[512];
2778         char value[512];
2779         const char *s;
2780
2781         if (Cmd_Argc(cmd) != 2)
2782         {
2783                 Con_Printf ("fullinfo <complete info string>\n");
2784                 return;
2785         }
2786
2787         s = Cmd_Argv(cmd, 1);
2788         if (*s == '\\')
2789                 s++;
2790         while (*s)
2791         {
2792                 size_t len = strcspn(s, "\\");
2793                 if (len >= sizeof(key)) {
2794                         len = sizeof(key) - 1;
2795                 }
2796                 strlcpy(key, s, len + 1);
2797                 s += len;
2798                 if (!*s)
2799                 {
2800                         Con_Printf ("MISSING VALUE\n");
2801                         return;
2802                 }
2803                 ++s; // Skip over backslash.
2804
2805                 len = strcspn(s, "\\");
2806                 if (len >= sizeof(value)) {
2807                         len = sizeof(value) - 1;
2808                 }
2809                 strlcpy(value, s, len + 1);
2810
2811                 CL_SetInfo(key, value, false, false, false, false);
2812
2813                 s += len;
2814                 if (!*s)
2815                 {
2816                         break;
2817                 }
2818                 ++s; // Skip over backslash.
2819         }
2820 }
2821
2822 /*
2823 ==================
2824 CL_SetInfo_f
2825
2826 Allow clients to change userinfo
2827 ==================
2828 */
2829 static void Host_SetInfo_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2830 {
2831         if (Cmd_Argc(cmd) == 1)
2832         {
2833                 InfoString_Print(cls.userinfo);
2834                 return;
2835         }
2836         if (Cmd_Argc(cmd) != 3)
2837         {
2838                 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
2839                 return;
2840         }
2841         CL_SetInfo(Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), true, false, false, false);
2842 }
2843
2844 /*
2845 ====================
2846 Host_Packet_f
2847
2848 packet <destination> <contents>
2849
2850 Contents allows \n escape character
2851 ====================
2852 */
2853 static void Host_Packet_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
2854 {
2855         char send[2048];
2856         int i, l;
2857         const char *in;
2858         char *out;
2859         lhnetaddress_t address;
2860         lhnetsocket_t *mysocket;
2861
2862         if (Cmd_Argc(cmd) != 3)
2863         {
2864                 Con_Printf ("packet <destination> <contents>\n");
2865                 return;
2866         }
2867
2868         if (!LHNETADDRESS_FromString (&address, Cmd_Argv(cmd, 1), sv_netport.integer))
2869         {
2870                 Con_Printf ("Bad address\n");
2871                 return;
2872         }
2873
2874         in = Cmd_Argv(cmd, 2);
2875         out = send+4;
2876         send[0] = send[1] = send[2] = send[3] = -1;
2877
2878         l = (int)strlen (in);
2879         for (i=0 ; i<l ; i++)
2880         {
2881                 if (out >= send + sizeof(send) - 1)
2882                         break;
2883                 if (in[i] == '\\' && in[i+1] == 'n')
2884                 {
2885                         *out++ = '\n';
2886                         i++;
2887                 }
2888                 else if (in[i] == '\\' && in[i+1] == '0')
2889                 {
2890                         *out++ = '\0';
2891                         i++;
2892                 }
2893                 else if (in[i] == '\\' && in[i+1] == 't')
2894                 {
2895                         *out++ = '\t';
2896                         i++;
2897                 }
2898                 else if (in[i] == '\\' && in[i+1] == 'r')
2899                 {
2900                         *out++ = '\r';
2901                         i++;
2902                 }
2903                 else if (in[i] == '\\' && in[i+1] == '"')
2904                 {
2905                         *out++ = '\"';
2906                         i++;
2907                 }
2908                 else
2909                         *out++ = in[i];
2910         }
2911
2912         mysocket = NetConn_ChooseClientSocketForAddress(&address);
2913         if (!mysocket)
2914                 mysocket = NetConn_ChooseServerSocketForAddress(&address);
2915         if (mysocket)
2916                 NetConn_Write(mysocket, send, out - send, &address);
2917 }
2918
2919 /*
2920 ====================
2921 Host_Pings_f
2922
2923 Send back ping and packet loss update for all current players to this player
2924 ====================
2925 */
2926 static void Host_Pings_f(cmd_state_t *cmd)
2927 {
2928         int             i, j, ping, packetloss, movementloss;
2929         char temp[128];
2930
2931         if (!host_client->netconnection)
2932                 return;
2933
2934         if (sv.protocol != PROTOCOL_QUAKEWORLD)
2935         {
2936                 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
2937                 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
2938         }
2939         for (i = 0;i < svs.maxclients;i++)
2940         {
2941                 packetloss = 0;
2942                 movementloss = 0;
2943                 if (svs.clients[i].netconnection)
2944                 {
2945                         for (j = 0;j < NETGRAPH_PACKETS;j++)
2946                                 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
2947                                         packetloss++;
2948                         for (j = 0;j < NETGRAPH_PACKETS;j++)
2949                                 if (svs.clients[i].movement_count[j] < 0)
2950                                         movementloss++;
2951                 }
2952                 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2953                 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
2954                 ping = (int)floor(svs.clients[i].ping*1000+0.5);
2955                 ping = bound(0, ping, 9999);
2956                 if (sv.protocol == PROTOCOL_QUAKEWORLD)
2957                 {
2958                         // send qw_svc_updateping and qw_svc_updatepl messages
2959                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
2960                         MSG_WriteShort(&host_client->netconnection->message, ping);
2961                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
2962                         MSG_WriteByte(&host_client->netconnection->message, packetloss);
2963                 }
2964                 else
2965                 {
2966                         // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
2967                         if(movementloss)
2968                                 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
2969                         else
2970                                 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
2971                         MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
2972                 }
2973         }
2974         if (sv.protocol != PROTOCOL_QUAKEWORLD)
2975                 MSG_WriteString(&host_client->netconnection->message, "\n");
2976 }
2977
2978 static void Host_PingPLReport_f(cmd_state_t *cmd)
2979 {
2980         char *errbyte;
2981         int i;
2982         int l = Cmd_Argc(cmd);
2983         if (l > cl.maxclients)
2984                 l = cl.maxclients;
2985         for (i = 0;i < l;i++)
2986         {
2987                 cl.scores[i].qw_ping = atoi(Cmd_Argv(cmd, 1+i*2));
2988                 cl.scores[i].qw_packetloss = strtol(Cmd_Argv(cmd, 1+i*2+1), &errbyte, 0);
2989                 if(errbyte && *errbyte == ',')
2990                         cl.scores[i].qw_movementloss = atoi(errbyte + 1);
2991                 else
2992                         cl.scores[i].qw_movementloss = 0;
2993         }
2994 }
2995
2996 //=============================================================================
2997
2998 /*
2999 ==================
3000 Host_InitCommands
3001 ==================
3002 */
3003 void Host_InitCommands (void)
3004 {
3005         dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
3006
3007         Cvar_RegisterVariable(&cl_name);
3008         Cvar_RegisterVariable(&cl_color);
3009         Cvar_RegisterVariable(&cl_rate);
3010         Cvar_RegisterVariable(&cl_rate_burstsize);
3011         Cvar_RegisterVariable(&cl_pmodel);
3012         Cvar_RegisterVariable(&cl_playermodel);
3013         Cvar_RegisterVariable(&cl_playerskin);
3014         Cvar_RegisterVariable(&rcon_password);
3015         Cvar_RegisterVariable(&rcon_address);
3016         Cvar_RegisterVariable(&rcon_secure);
3017         Cvar_RegisterVariable(&rcon_secure_challengetimeout);
3018         Cvar_RegisterVariable(&r_fixtrans_auto);
3019         Cvar_RegisterVariable(&team);
3020         Cvar_RegisterVariable(&skin);
3021         Cvar_RegisterVariable(&noaim);
3022         Cvar_RegisterVariable(&sv_cheats);
3023         Cvar_RegisterVariable(&sv_adminnick);
3024         Cvar_RegisterVariable(&sv_status_privacy);
3025         Cvar_RegisterVariable(&sv_status_show_qcstatus);
3026         Cvar_RegisterVariable(&sv_namechangetimer);
3027
3028         // client commands - this includes server commands because the client can host a server, so they must exist
3029         Cmd_AddCommand(&cmd_client, "quit", Host_Quit_f, "quit the game");
3030         Cmd_AddCommand(&cmd_client, "status", Host_Status_f, "print server status information");
3031         Cmd_AddCommand(&cmd_client, "map", Host_Map_f, "kick everyone off the server and start a new level");
3032         Cmd_AddCommand(&cmd_client, "restart", Host_Restart_f, "restart current level");
3033         Cmd_AddCommand(&cmd_client, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3034         Cmd_AddCommand(&cmd_client, "version", Host_Version_f, "print engine version");
3035         Cmd_AddCommand(&cmd_client, "say", Host_Say_f, "send a chat message to everyone on the server");
3036         Cmd_AddCommand(&cmd_client, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3037         Cmd_AddCommand(&cmd_client, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3038         Cmd_AddCommand(&cmd_client, "kick", Host_Kick_f, "kick a player off the server by number or name");
3039         Cmd_AddCommand(&cmd_client, "ping", Host_Ping_f, "print ping times of all players on the server");
3040         Cmd_AddCommand(&cmd_client, "load", Host_Loadgame_f, "load a saved game file");
3041         Cmd_AddCommand(&cmd_client, "save", Host_Savegame_f, "save the game to a file");
3042         Cmd_AddCommand(&cmd_client, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3043         Cmd_AddCommand(&cmd_client, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3044         Cmd_AddCommand(&cmd_client, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3045         Cmd_AddCommand(&cmd_client, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3046         Cmd_AddCommand(&cmd_client, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3047         Cmd_AddCommand(&cmd_client, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3048         Cmd_AddCommand(&cmd_client, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3049
3050         // dedicated server commands
3051         Cmd_AddCommand(&cmd_server, "quit", Host_Quit_f, "quit the game");
3052         Cmd_AddCommand(&cmd_server, "status", Host_Status_f, "print server status information");
3053         Cmd_AddCommand(&cmd_server, "map", Host_Map_f, "kick everyone off the server and start a new level");
3054         Cmd_AddCommand(&cmd_server, "restart", Host_Restart_f, "restart current level");
3055         Cmd_AddCommand(&cmd_server, "changelevel", Host_Changelevel_f, "change to another level, bringing along all connected clients");
3056         Cmd_AddCommand(&cmd_server, "version", Host_Version_f, "print engine version");
3057         Cmd_AddCommand(&cmd_server, "say", Host_Say_f, "send a chat message to everyone on the server");
3058         Cmd_AddCommand(&cmd_server, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3059         Cmd_AddCommand(&cmd_server, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3060         Cmd_AddCommand(&cmd_server, "kick", Host_Kick_f, "kick a player off the server by number or name");
3061         Cmd_AddCommand(&cmd_server, "ping", Host_Ping_f, "print ping times of all players on the server");
3062         Cmd_AddCommand(&cmd_server, "load", Host_Loadgame_f, "load a saved game file");
3063         Cmd_AddCommand(&cmd_server, "save", Host_Savegame_f, "save the game to a file");
3064         Cmd_AddCommand(&cmd_server, "viewmodel", Host_Viewmodel_f, "change model of viewthing entity in current level");
3065         Cmd_AddCommand(&cmd_server, "viewframe", Host_Viewframe_f, "change animation frame of viewthing entity in current level");
3066         Cmd_AddCommand(&cmd_server, "viewnext", Host_Viewnext_f, "change to next animation frame of viewthing entity in current level");
3067         Cmd_AddCommand(&cmd_server, "viewprev", Host_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
3068         Cmd_AddCommand(&cmd_server, "maxplayers", MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
3069         Cmd_AddCommand(&cmd_server, "user", Host_User_f, "prints additional information about a player number or name on the scoreboard");
3070         Cmd_AddCommand(&cmd_server, "users", Host_Users_f, "prints additional information about all players on the scoreboard");
3071
3072         // 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)
3073         Cmd_AddCommand(&cmd_serverfromclient, "prespawn", Host_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
3074         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)");
3075         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)");
3076         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)");
3077
3078         Cmd_AddCommand(&cmd_serverfromclient, "status", Host_Status_f, "print server status information");
3079         Cmd_AddCommand(&cmd_serverfromclient, "god", Host_God_f, "god mode (invulnerability)");
3080         Cmd_AddCommand(&cmd_serverfromclient, "notarget", Host_Notarget_f, "notarget mode (monsters do not see you)");
3081         Cmd_AddCommand(&cmd_serverfromclient, "fly", Host_Fly_f, "fly mode (flight)");
3082         Cmd_AddCommand(&cmd_serverfromclient, "noclip", Host_Noclip_f, "noclip mode (flight without collisions, move through walls)");
3083         Cmd_AddCommand(&cmd_serverfromclient, "give", Host_Give_f, "alter inventory");
3084         Cmd_AddCommand(&cmd_serverfromclient, "say", Host_Say_f, "send a chat message to everyone on the server");
3085         Cmd_AddCommand(&cmd_serverfromclient, "say_team", Host_Say_Team_f, "send a chat message to your team on the server");
3086         Cmd_AddCommand(&cmd_serverfromclient, "tell", Host_Tell_f, "send a chat message to only one person on the server");
3087         Cmd_AddCommand(&cmd_serverfromclient, "kill", Host_Kill_f, "die instantly");
3088         Cmd_AddCommand(&cmd_serverfromclient, "pause", Host_Pause_f, "pause the game (if the server allows pausing)");
3089         Cmd_AddCommand(&cmd_serverfromclient, "ping", Host_Ping_f, "print ping times of all players on the server");
3090         Cmd_AddCommand(&cmd_serverfromclient, "name", Host_Name_f, "change your player name");
3091         Cmd_AddCommand(&cmd_serverfromclient, "color", Host_Color_f, "change your player shirt and pants colors");
3092         Cmd_AddCommand(&cmd_serverfromclient, "rate", Host_Rate_f, "change your network connection speed");
3093         Cmd_AddCommand(&cmd_serverfromclient, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3094         Cmd_AddCommand(&cmd_serverfromclient, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3095         Cmd_AddCommand(&cmd_serverfromclient, "playermodel", Host_Playermodel_f, "change your player model");
3096         Cmd_AddCommand(&cmd_serverfromclient, "playerskin", Host_Playerskin_f, "change your player skin number");
3097
3098         // client commands that require a connection and are simply forwarded to server
3099         Cmd_AddCommand(&cmd_client, "god", Cmd_ForwardToServer_f, "god mode (invulnerability)");
3100         Cmd_AddCommand(&cmd_client, "notarget", Cmd_ForwardToServer_f, "notarget mode (monsters do not see you)");
3101         Cmd_AddCommand(&cmd_client, "fly", Cmd_ForwardToServer_f, "fly mode (flight)");
3102         Cmd_AddCommand(&cmd_client, "noclip", Cmd_ForwardToServer_f, "noclip mode (flight without collisions, move through walls)");
3103         Cmd_AddCommand(&cmd_client, "give", Cmd_ForwardToServer_f, "alter inventory");
3104         Cmd_AddCommand(&cmd_client, "say_team", Cmd_ForwardToServer_f, "send a chat message to your team on the server");
3105         Cmd_AddCommand(&cmd_client, "kill", Cmd_ForwardToServer_f, "die instantly");
3106
3107         Cmd_AddCommand(&cmd_client, "connect", Host_Connect_f, "connect to a server by IP address or hostname");
3108         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)");
3109         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)");
3110         Cmd_AddCommand(&cmd_client, "startdemos", Host_Startdemos_f, "start playing back the selected demos sequentially (used at end of startup script)");
3111         Cmd_AddCommand(&cmd_client, "demos", Host_Demos_f, "restart looping demos defined by the last startdemos command");
3112         Cmd_AddCommand(&cmd_client, "stopdemo", Host_Stopdemo_f, "stop playing or recording demo (like stop command) and return to looping demos");
3113         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");
3114         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");
3115         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");
3116         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)");
3117         Cmd_AddCommand(&cmd_client, "fullinfo", Host_FullInfo_f, "allows client to modify their userinfo");
3118         Cmd_AddCommand(&cmd_client, "setinfo", Host_SetInfo_f, "modifies your userinfo");
3119         Cmd_AddCommand(&cmd_client, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3120         Cmd_AddCommand(&cmd_clientfromserver, "packet", Host_Packet_f, "send a packet to the specified address:port containing a text string");
3121         Cmd_AddCommand(&cmd_client, "topcolor", Host_TopColor_f, "QW command to set top color without changing bottom color");
3122         Cmd_AddCommand(&cmd_client, "bottomcolor", Host_BottomColor_f, "QW command to set bottom color without changing top color");
3123         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)");
3124
3125         // client commands that also exist as cmd_serverfromclient and are often forwarded
3126         Cmd_AddCommand(&cmd_client, "name", Host_Name_f, "change your player name");
3127         Cmd_AddCommand(&cmd_client, "color", Host_Color_f, "change your player shirt and pants colors");
3128         Cmd_AddCommand(&cmd_client, "rate", Host_Rate_f, "change your network connection speed");
3129         Cmd_AddCommand(&cmd_client, "rate_burstsize", Host_Rate_BurstSize_f, "change your network connection speed");
3130         Cmd_AddCommand(&cmd_client, "pmodel", Host_PModel_f, "(Nehahra-only) change your player model choice");
3131         Cmd_AddCommand(&cmd_client, "playermodel", Host_Playermodel_f, "change your player model");
3132         Cmd_AddCommand(&cmd_client, "playerskin", Host_Playerskin_f, "change your player skin number");
3133
3134         // commands that are only sent by server to client for execution
3135         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)");
3136         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");
3137 }
3138
3139 void Host_NoOperation_f(cmd_state_t *cmd)
3140 {
3141 }