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