]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_ccmds.c
Implement "ent_create" command. Creates an entity where you're aiming.
[xonotic/darkplaces.git] / sv_ccmds.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 "utf8lib.h"
23 #include "server.h"
24 #include "sv_demo.h"
25
26 int current_skill;
27 cvar_t sv_cheats = {CVAR_SERVER | CVAR_NOTIFY, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
28 cvar_t sv_adminnick = {CVAR_SERVER | CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
29 cvar_t sv_status_privacy = {CVAR_SERVER | CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
30 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."};
31 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"};
32
33 /*
34 ===============================================================================
35
36 SERVER TRANSITIONS
37
38 ===============================================================================
39 */
40
41 /*
42 ======================
43 SV_Map_f
44
45 handle a
46 map <servername>
47 command from the console.  Active clients are kicked off.
48 ======================
49 */
50 static void SV_Map_f(cmd_state_t *cmd)
51 {
52         char level[MAX_QPATH];
53
54         if (Cmd_Argc(cmd) != 2)
55         {
56                 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
57                 return;
58         }
59
60         // GAME_DELUXEQUAKE - clear warpmark (used by QC)
61         if (gamemode == GAME_DELUXEQUAKE)
62                 Cvar_Set(&cvars_all, "warpmark", "");
63
64         cls.demonum = -1;               // stop demo loop in case this fails
65
66         CL_Disconnect ();
67         SV_Shutdown();
68
69         if(svs.maxclients != svs.maxclients_next)
70         {
71                 svs.maxclients = svs.maxclients_next;
72                 if (svs.clients)
73                         Mem_Free(svs.clients);
74                 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
75         }
76
77 #ifdef CONFIG_MENU
78         // remove menu
79         if (key_dest == key_menu || key_dest == key_menu_grabbed)
80                 MR_ToggleMenu(0);
81 #endif
82         key_dest = key_game;
83
84         svs.serverflags = 0;                    // haven't completed an episode yet
85         strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
86         SV_SpawnServer(level);
87         if (sv.active && cls.state == ca_disconnected)
88                 CL_EstablishConnection("local:1", -2);
89 }
90
91 /*
92 ==================
93 SV_Changelevel_f
94
95 Goes to a new map, taking all clients along
96 ==================
97 */
98 static void SV_Changelevel_f(cmd_state_t *cmd)
99 {
100         char level[MAX_QPATH];
101
102         if (Cmd_Argc(cmd) != 2)
103         {
104                 Con_Print("changelevel <levelname> : continue game on a new level\n");
105                 return;
106         }
107
108         if (!sv.active)
109         {
110                 Con_Printf("You must be running a server to changelevel. Use 'map %s' instead\n", Cmd_Argv(cmd, 1));
111                 return;
112         }
113
114 #ifdef CONFIG_MENU
115         // remove menu
116         if (key_dest == key_menu || key_dest == key_menu_grabbed)
117                 MR_ToggleMenu(0);
118 #endif
119         key_dest = key_game;
120
121         SV_SaveSpawnparms ();
122         strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
123         SV_SpawnServer(level);
124         if (sv.active && cls.state == ca_disconnected)
125                 CL_EstablishConnection("local:1", -2);
126 }
127
128 /*
129 ==================
130 SV_Restart_f
131
132 Restarts the current server for a dead player
133 ==================
134 */
135 static void SV_Restart_f(cmd_state_t *cmd)
136 {
137         char mapname[MAX_QPATH];
138
139         if (Cmd_Argc(cmd) != 1)
140         {
141                 Con_Print("restart : restart current level\n");
142                 return;
143         }
144         if (!sv.active)
145         {
146                 Con_Print("Only the server may restart\n");
147                 return;
148         }
149
150 #ifdef CONFIG_MENU
151         // remove menu
152         if (key_dest == key_menu || key_dest == key_menu_grabbed)
153                 MR_ToggleMenu(0);
154 #endif
155         key_dest = key_game;
156
157         strlcpy(mapname, sv.name, sizeof(mapname));
158         SV_SpawnServer(mapname);
159         if (sv.active && cls.state == ca_disconnected)
160                 CL_EstablishConnection("local:1", -2);
161 }
162
163 //===========================================================================
164
165 // Disable cheats if sv_cheats is turned off
166 static void SV_DisableCheats_c(cvar_t *var)
167 {
168         prvm_prog_t *prog = SVVM_prog;
169         int i = 0;
170
171         if (var->value == 0)
172         {
173                 while (svs.clients[i].edict)
174                 {
175                         if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_GODMODE))
176                                 PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_GODMODE;
177                         if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_NOTARGET))
178                                 PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_NOTARGET;
179                         if (PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_NOCLIP ||
180                                 PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_FLY)
181                         {
182                                 noclip_anglehack = false;
183                                 PRVM_serveredictfloat(svs.clients[i].edict, movetype) = MOVETYPE_WALK;
184                         }
185                         i++;
186                 }
187         }
188 }
189
190 /*
191 ==================
192 SV_God_f
193
194 Sets client to godmode
195 ==================
196 */
197 static void SV_God_f(cmd_state_t *cmd)
198 {
199         prvm_prog_t *prog = SVVM_prog;
200
201         PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
202         if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
203                 SV_ClientPrint("godmode OFF\n");
204         else
205                 SV_ClientPrint("godmode ON\n");
206 }
207
208 qboolean noclip_anglehack;
209
210 static void SV_Noclip_f(cmd_state_t *cmd)
211 {
212         prvm_prog_t *prog = SVVM_prog;
213
214         if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
215         {
216                 noclip_anglehack = true;
217                 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
218                 SV_ClientPrint("noclip ON\n");
219         }
220         else
221         {
222                 noclip_anglehack = false;
223                 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
224                 SV_ClientPrint("noclip OFF\n");
225         }
226 }
227
228 /*
229 ==================
230 SV_Give_f
231 ==================
232 */
233 static void SV_Give_f(cmd_state_t *cmd)
234 {
235         prvm_prog_t *prog = SVVM_prog;
236         const char *t;
237         int v;
238
239         t = Cmd_Argv(cmd, 1);
240         v = atoi (Cmd_Argv(cmd, 2));
241
242         switch (t[0])
243         {
244         case '0':
245         case '1':
246         case '2':
247         case '3':
248         case '4':
249         case '5':
250         case '6':
251         case '7':
252         case '8':
253         case '9':
254                 // MED 01/04/97 added hipnotic give stuff
255                 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
256                 {
257                         if (t[0] == '6')
258                         {
259                                 if (t[1] == 'a')
260                                         PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
261                                 else
262                                         PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
263                         }
264                         else if (t[0] == '9')
265                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
266                         else if (t[0] == '0')
267                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
268                         else if (t[0] >= '2')
269                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
270                 }
271                 else
272                 {
273                         if (t[0] >= '2')
274                                 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
275                 }
276                 break;
277
278         case 's':
279                 if (gamemode == GAME_ROGUE)
280                         PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
281
282                 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
283                 break;
284         case 'n':
285                 if (gamemode == GAME_ROGUE)
286                 {
287                         PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
288                         if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
289                                 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
290                 }
291                 else
292                 {
293                         PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
294                 }
295                 break;
296         case 'l':
297                 if (gamemode == GAME_ROGUE)
298                 {
299                         PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
300                         if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
301                                 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
302                 }
303                 break;
304         case 'r':
305                 if (gamemode == GAME_ROGUE)
306                 {
307                         PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
308                         if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
309                                 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
310                 }
311                 else
312                 {
313                         PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
314                 }
315                 break;
316         case 'm':
317                 if (gamemode == GAME_ROGUE)
318                 {
319                         PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
320                         if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
321                                 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
322                 }
323                 break;
324         case 'h':
325                 PRVM_serveredictfloat(host_client->edict, health) = v;
326                 break;
327         case 'c':
328                 if (gamemode == GAME_ROGUE)
329                 {
330                         PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
331                         if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
332                                 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
333                 }
334                 else
335                 {
336                         PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
337                 }
338                 break;
339         case 'p':
340                 if (gamemode == GAME_ROGUE)
341                 {
342                         PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
343                         if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
344                                 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
345                 }
346                 break;
347         }
348 }
349
350 /*
351 ==================
352 SV_Fly_f
353
354 Sets client to flymode
355 ==================
356 */
357 static void SV_Fly_f(cmd_state_t *cmd)
358 {
359         prvm_prog_t *prog = SVVM_prog;
360
361         if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
362         {
363                 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
364                 SV_ClientPrint("flymode ON\n");
365         }
366         else
367         {
368                 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
369                 SV_ClientPrint("flymode OFF\n");
370         }
371 }
372
373 static void SV_Notarget_f(cmd_state_t *cmd)
374 {
375         prvm_prog_t *prog = SVVM_prog;
376
377         PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
378         if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
379                 SV_ClientPrint("notarget OFF\n");
380         else
381                 SV_ClientPrint("notarget ON\n");
382 }
383
384 /*
385 ==================
386 SV_Kill_f
387 ==================
388 */
389 static void SV_Kill_f(cmd_state_t *cmd)
390 {
391         prvm_prog_t *prog = SVVM_prog;
392         if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
393         {
394                 SV_ClientPrint("Can't suicide -- already dead!\n");
395                 return;
396         }
397
398         PRVM_serverglobalfloat(time) = sv.time;
399         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
400         prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
401 }
402
403 /*
404 ==================
405 SV_Pause_f
406 ==================
407 */
408 static void SV_Pause_f(cmd_state_t *cmd)
409 {
410         void (*print) (const char *fmt, ...);
411         if (cmd->source == src_command)
412         {
413                 // if running a client, try to send over network so the pause is handled by the server
414                 if (cls.state == ca_connected)
415                 {
416                         CL_ForwardToServer_f(cmd);
417                         return;
418                 }
419                 print = Con_Printf;
420         }
421         else
422                 print = SV_ClientPrintf;
423
424         if (!pausable.integer)
425         {
426                 if (cmd->source == src_client)
427                 {
428                         if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
429                         {
430                                 print("Pause not allowed.\n");
431                                 return;
432                         }
433                 }
434         }
435         
436         sv.paused ^= 1;
437         if (cmd->source != src_command)
438                 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
439         else if(*(sv_adminnick.string))
440                 SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
441         else
442                 SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
443         // send notification to all clients
444         MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
445         MSG_WriteByte(&sv.reliable_datagram, sv.paused);
446 }
447
448 static void SV_Say(cmd_state_t *cmd, qboolean teamonly)
449 {
450         prvm_prog_t *prog = SVVM_prog;
451         client_t *save;
452         int j, quoted;
453         const char *p1;
454         char *p2;
455         // LadyHavoc: long say messages
456         char text[1024];
457         qboolean fromServer = false;
458
459         if (cmd->source == src_command)
460         {
461                 if (cls.state == ca_dedicated)
462                 {
463                         fromServer = true;
464                         teamonly = false;
465                 }
466                 else
467                 {
468                         CL_ForwardToServer_f(cmd);
469                         return;
470                 }
471         }
472
473         if (Cmd_Argc (cmd) < 2)
474                 return;
475
476         if (!teamplay.integer)
477                 teamonly = false;
478
479         p1 = Cmd_Args(cmd);
480         quoted = false;
481         if (*p1 == '\"')
482         {
483                 quoted = true;
484                 p1++;
485         }
486         // note this uses the chat prefix \001
487         if (!fromServer && !teamonly)
488                 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
489         else if (!fromServer && teamonly)
490                 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
491         else if(*(sv_adminnick.string))
492                 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
493         else
494                 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
495         p2 = text + strlen(text);
496         while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
497         {
498                 if (p2[-1] == '\"' && quoted)
499                         quoted = false;
500                 p2[-1] = 0;
501                 p2--;
502         }
503         strlcat(text, "\n", sizeof(text));
504
505         // note: save is not a valid edict if fromServer is true
506         save = host_client;
507         for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
508                 if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
509                         SV_ClientPrint(text);
510         host_client = save;
511
512         if (cls.state == ca_dedicated)
513                 Con_Print(&text[1]);
514 }
515
516 static void SV_Say_f(cmd_state_t *cmd)
517 {
518         SV_Say(cmd, false);
519 }
520
521 static void SV_Say_Team_f(cmd_state_t *cmd)
522 {
523         SV_Say(cmd, true);
524 }
525
526 static void SV_Tell_f(cmd_state_t *cmd)
527 {
528         const char *playername_start = NULL;
529         size_t playername_length = 0;
530         int playernumber = 0;
531         client_t *save;
532         int j;
533         const char *p1, *p2;
534         char text[MAX_INPUTLINE]; // LadyHavoc: FIXME: temporary buffer overflow fix (was 64)
535         qboolean fromServer = false;
536
537         if (cmd->source == src_command)
538         {
539                 if (cls.state == ca_dedicated)
540                         fromServer = true;
541                 else
542                 {
543                         CL_ForwardToServer_f(cmd);
544                         return;
545                 }
546         }
547
548         if (Cmd_Argc (cmd) < 2)
549                 return;
550
551         // note this uses the chat prefix \001
552         if (!fromServer)
553                 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
554         else if(*(sv_adminnick.string))
555                 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
556         else
557                 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
558
559         p1 = Cmd_Args(cmd);
560         p2 = p1 + strlen(p1);
561         // remove the target name
562         while (p1 < p2 && *p1 == ' ')
563                 p1++;
564         if(*p1 == '#')
565         {
566                 ++p1;
567                 while (p1 < p2 && *p1 == ' ')
568                         p1++;
569                 while (p1 < p2 && isdigit(*p1))
570                 {
571                         playernumber = playernumber * 10 + (*p1 - '0');
572                         p1++;
573                 }
574                 --playernumber;
575         }
576         else if(*p1 == '"')
577         {
578                 ++p1;
579                 playername_start = p1;
580                 while (p1 < p2 && *p1 != '"')
581                         p1++;
582                 playername_length = p1 - playername_start;
583                 if(p1 < p2)
584                         p1++;
585         }
586         else
587         {
588                 playername_start = p1;
589                 while (p1 < p2 && *p1 != ' ')
590                         p1++;
591                 playername_length = p1 - playername_start;
592         }
593         while (p1 < p2 && *p1 == ' ')
594                 p1++;
595         if(playername_start)
596         {
597                 // set playernumber to the right client
598                 char namebuf[128];
599                 if(playername_length >= sizeof(namebuf))
600                 {
601                         if (fromServer)
602                                 Con_Print("Host_Tell: too long player name/ID\n");
603                         else
604                                 SV_ClientPrint("Host_Tell: too long player name/ID\n");
605                         return;
606                 }
607                 memcpy(namebuf, playername_start, playername_length);
608                 namebuf[playername_length] = 0;
609                 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
610                 {
611                         if (!svs.clients[playernumber].active)
612                                 continue;
613                         if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
614                                 break;
615                 }
616         }
617         if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
618         {
619                 if (fromServer)
620                         Con_Print("Host_Tell: invalid player name/ID\n");
621                 else
622                         SV_ClientPrint("Host_Tell: invalid player name/ID\n");
623                 return;
624         }
625         // remove trailing newlines
626         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
627                 p2--;
628         // remove quotes if present
629         if (*p1 == '"')
630         {
631                 p1++;
632                 if (p2[-1] == '"')
633                         p2--;
634                 else if (fromServer)
635                         Con_Print("Host_Tell: missing end quote\n");
636                 else
637                         SV_ClientPrint("Host_Tell: missing end quote\n");
638         }
639         while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
640                 p2--;
641         if(p1 == p2)
642                 return; // empty say
643         for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
644                 text[j++] = *p1++;
645         text[j++] = '\n';
646         text[j++] = 0;
647
648         save = host_client;
649         host_client = svs.clients + playernumber;
650         SV_ClientPrint(text);
651         host_client = save;
652 }
653
654 /*
655 ==================
656 SV_Ping_f
657
658 ==================
659 */
660 static void SV_Ping_f(cmd_state_t *cmd)
661 {
662         int i;
663         client_t *client;
664         void (*print) (const char *fmt, ...);
665
666         if (cmd->source == src_command)
667         {
668                 // if running a client, try to send over network so the client's ping report parser will see the report
669                 if (cls.state == ca_connected)
670                 {
671                         CL_ForwardToServer_f(cmd);
672                         return;
673                 }
674                 print = Con_Printf;
675         }
676         else
677                 print = SV_ClientPrintf;
678
679         if (!sv.active)
680                 return;
681
682         print("Client ping times:\n");
683         for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
684         {
685                 if (!client->active)
686                         continue;
687                 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
688         }
689 }
690
691 /*
692 ====================
693 SV_Pings_f
694
695 Send back ping and packet loss update for all current players to this player
696 ====================
697 */
698 static void SV_Pings_f(cmd_state_t *cmd)
699 {
700         int             i, j, ping, packetloss, movementloss;
701         char temp[128];
702
703         if (!host_client->netconnection)
704                 return;
705
706         if (sv.protocol != PROTOCOL_QUAKEWORLD)
707         {
708                 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
709                 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
710         }
711         for (i = 0;i < svs.maxclients;i++)
712         {
713                 packetloss = 0;
714                 movementloss = 0;
715                 if (svs.clients[i].netconnection)
716                 {
717                         for (j = 0;j < NETGRAPH_PACKETS;j++)
718                                 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
719                                         packetloss++;
720                         for (j = 0;j < NETGRAPH_PACKETS;j++)
721                                 if (svs.clients[i].movement_count[j] < 0)
722                                         movementloss++;
723                 }
724                 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
725                 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
726                 ping = (int)floor(svs.clients[i].ping*1000+0.5);
727                 ping = bound(0, ping, 9999);
728                 if (sv.protocol == PROTOCOL_QUAKEWORLD)
729                 {
730                         // send qw_svc_updateping and qw_svc_updatepl messages
731                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
732                         MSG_WriteShort(&host_client->netconnection->message, ping);
733                         MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
734                         MSG_WriteByte(&host_client->netconnection->message, packetloss);
735                 }
736                 else
737                 {
738                         // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
739                         if(movementloss)
740                                 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
741                         else
742                                 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
743                         MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
744                 }
745         }
746         if (sv.protocol != PROTOCOL_QUAKEWORLD)
747                 MSG_WriteString(&host_client->netconnection->message, "\n");
748 }
749
750 /*
751 ====================
752 SV_User_f
753
754 user <name or userid>
755
756 Dump userdata / masterdata for a user
757 ====================
758 */
759 static void SV_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
760 {
761         int             uid;
762         int             i;
763
764         if (Cmd_Argc(cmd) != 2)
765         {
766                 Con_Printf ("Usage: user <username / userid>\n");
767                 return;
768         }
769
770         uid = atoi(Cmd_Argv(cmd, 1));
771
772         for (i = 0;i < cl.maxclients;i++)
773         {
774                 if (!cl.scores[i].name[0])
775                         continue;
776                 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
777                 {
778                         InfoString_Print(cl.scores[i].qw_userinfo);
779                         return;
780                 }
781         }
782         Con_Printf ("User not in server.\n");
783 }
784
785 /*
786 ====================
787 SV_Users_f
788
789 Dump userids for all current players
790 ====================
791 */
792 static void SV_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
793 {
794         int             i;
795         int             c;
796
797         c = 0;
798         Con_Printf ("userid frags name\n");
799         Con_Printf ("------ ----- ----\n");
800         for (i = 0;i < cl.maxclients;i++)
801         {
802                 if (cl.scores[i].name[0])
803                 {
804                         Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
805                         c++;
806                 }
807         }
808
809         Con_Printf ("%i total users\n", c);
810 }
811
812 /*
813 ==================
814 SV_Status_f
815 ==================
816 */
817 static void SV_Status_f(cmd_state_t *cmd)
818 {
819         prvm_prog_t *prog = SVVM_prog;
820         char qcstatus[256];
821         client_t *client;
822         int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
823         void (*print) (const char *fmt, ...);
824         char ip[48]; // can contain a full length v6 address with [] and a port
825         int frags;
826         char vabuf[1024];
827
828         if (cmd->source == src_command)
829                 print = Con_Printf;
830         else
831                 print = SV_ClientPrintf;
832
833         if (!sv.active)
834                 return;
835
836         in = 0;
837         if (Cmd_Argc(cmd) == 2)
838         {
839                 if (strcmp(Cmd_Argv(cmd, 1), "1") == 0)
840                         in = 1;
841                 else if (strcmp(Cmd_Argv(cmd, 1), "2") == 0)
842                         in = 2;
843         }
844
845         for (players = 0, i = 0;i < svs.maxclients;i++)
846                 if (svs.clients[i].active)
847                         players++;
848         print ("host:     %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
849         print ("version:  %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
850         print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
851         print ("map:      %s\n", sv.name);
852         print ("timing:   %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
853         print ("players:  %i active (%i max)\n\n", players, svs.maxclients);
854
855         if (in == 1)
856                 print ("^2IP                                             %%pl ping  time   frags  no   name\n");
857         else if (in == 2)
858                 print ("^5IP                                              no   name\n");
859
860         for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
861         {
862                 if (!client->active)
863                         continue;
864
865                 ++k;
866
867                 if (in == 0 || in == 1)
868                 {
869                         seconds = (int)(host.realtime - client->connecttime);
870                         minutes = seconds / 60;
871                         if (minutes)
872                         {
873                                 seconds -= (minutes * 60);
874                                 hours = minutes / 60;
875                                 if (hours)
876                                         minutes -= (hours * 60);
877                         }
878                         else
879                                 hours = 0;
880                         
881                         packetloss = 0;
882                         if (client->netconnection)
883                                 for (j = 0;j < NETGRAPH_PACKETS;j++)
884                                         if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
885                                                 packetloss++;
886                         packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
887                         ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
888                 }
889
890                 if(sv_status_privacy.integer && cmd->source != src_command && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
891                         strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
892                 else
893                         strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
894
895                 frags = client->frags;
896
897                 if(sv_status_show_qcstatus.integer)
898                 {
899                         prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
900                         const char *str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
901                         if(str && *str)
902                         {
903                                 char *p;
904                                 const char *q;
905                                 p = qcstatus;
906                                 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
907                                         if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
908                                                 *p++ = *q;
909                                 *p = 0;
910                                 if(*qcstatus)
911                                         frags = atoi(qcstatus);
912                         }
913                 }
914                 
915                 if (in == 0) // default layout
916                 {
917                         if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
918                         {
919                                 // LadyHavoc: this is very touchy because we must maintain ProQuake compatible status output
920                                 print ("#%-2u %-16.16s  %3i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
921                                 print ("   %s\n", ip);
922                         }
923                         else
924                         {
925                                 // LadyHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
926                                 print ("#%-3u %-16.16s %4i  %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
927                                 print ("   %s\n", ip);
928                         }
929                 }
930                 else if (in == 1) // extended layout
931                 {
932                         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);
933                 }
934                 else if (in == 2) // reduced layout
935                 {
936                         print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
937                 }
938         }
939 }
940
941 /*
942 ======================
943 SV_Name_f
944 ======================
945 */
946 static void SV_Name_f(cmd_state_t *cmd)
947 {
948         prvm_prog_t *prog = SVVM_prog;
949         int i, j;
950         qboolean valid_colors;
951         const char *newNameSource;
952         char newName[sizeof(host_client->name)];
953
954         if (Cmd_Argc (cmd) == 1)
955                 return;
956
957         if (Cmd_Argc (cmd) == 2)
958                 newNameSource = Cmd_Argv(cmd, 1);
959         else
960                 newNameSource = Cmd_Args(cmd);
961
962         strlcpy(newName, newNameSource, sizeof(newName));
963
964         if (cmd->source == src_command)
965                 return;
966
967         if (host.realtime < host_client->nametime && strcmp(newName, host_client->name))
968         {
969                 SV_ClientPrintf("You can't change name more than once every %.1f seconds!\n", max(0.0f, sv_namechangetimer.value));
970                 return;
971         }
972
973         host_client->nametime = host.realtime + max(0.0f, sv_namechangetimer.value);
974
975         // point the string back at updateclient->name to keep it safe
976         strlcpy (host_client->name, newName, sizeof (host_client->name));
977
978         for (i = 0, j = 0;host_client->name[i];i++)
979                 if (host_client->name[i] != '\r' && host_client->name[i] != '\n')
980                         host_client->name[j++] = host_client->name[i];
981         host_client->name[j] = 0;
982
983         if(host_client->name[0] == 1 || host_client->name[0] == 2)
984         // may interfere with chat area, and will needlessly beep; so let's add a ^7
985         {
986                 memmove(host_client->name + 2, host_client->name, sizeof(host_client->name) - 2);
987                 host_client->name[sizeof(host_client->name) - 1] = 0;
988                 host_client->name[0] = STRING_COLOR_TAG;
989                 host_client->name[1] = '0' + STRING_COLOR_DEFAULT;
990         }
991
992         u8_COM_StringLengthNoColors(host_client->name, 0, &valid_colors);
993         if(!valid_colors) // NOTE: this also proves the string is not empty, as "" is a valid colored string
994         {
995                 size_t l;
996                 l = strlen(host_client->name);
997                 if(l < sizeof(host_client->name) - 1)
998                 {
999                         // duplicate the color tag to escape it
1000                         host_client->name[i] = STRING_COLOR_TAG;
1001                         host_client->name[i+1] = 0;
1002                         //Con_DPrintf("abuse detected, adding another trailing color tag\n");
1003                 }
1004                 else
1005                 {
1006                         // remove the last character to fix the color code
1007                         host_client->name[l-1] = 0;
1008                         //Con_DPrintf("abuse detected, removing a trailing color tag\n");
1009                 }
1010         }
1011
1012         // find the last color tag offset and decide if we need to add a reset tag
1013         for (i = 0, j = -1;host_client->name[i];i++)
1014         {
1015                 if (host_client->name[i] == STRING_COLOR_TAG)
1016                 {
1017                         if (host_client->name[i+1] >= '0' && host_client->name[i+1] <= '9')
1018                         {
1019                                 j = i;
1020                                 // if this happens to be a reset  tag then we don't need one
1021                                 if (host_client->name[i+1] == '0' + STRING_COLOR_DEFAULT)
1022                                         j = -1;
1023                                 i++;
1024                                 continue;
1025                         }
1026                         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]))
1027                         {
1028                                 j = i;
1029                                 i += 4;
1030                                 continue;
1031                         }
1032                         if (host_client->name[i+1] == STRING_COLOR_TAG)
1033                         {
1034                                 i++;
1035                                 continue;
1036                         }
1037                 }
1038         }
1039         // does not end in the default color string, so add it
1040         if (j >= 0 && strlen(host_client->name) < sizeof(host_client->name) - 2)
1041                 memcpy(host_client->name + strlen(host_client->name), STRING_COLOR_DEFAULT_STR, strlen(STRING_COLOR_DEFAULT_STR) + 1);
1042
1043         PRVM_serveredictstring(host_client->edict, netname) = PRVM_SetEngineString(prog, host_client->name);
1044         if (strcmp(host_client->old_name, host_client->name))
1045         {
1046                 if (host_client->begun)
1047                         SV_BroadcastPrintf("%s ^7changed name to %s\n", host_client->old_name, host_client->name);
1048                 strlcpy(host_client->old_name, host_client->name, sizeof(host_client->old_name));
1049                 // send notification to all clients
1050                 MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
1051                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1052                 MSG_WriteString (&sv.reliable_datagram, host_client->name);
1053                 SV_WriteNetnameIntoDemo(host_client);
1054         }
1055 }
1056
1057 static void SV_Rate_f(cmd_state_t *cmd)
1058 {
1059         int rate;
1060
1061         rate = atoi(Cmd_Argv(cmd, 1));
1062
1063         if (cmd->source == src_command)
1064                 return;
1065
1066         host_client->rate = rate;
1067 }
1068
1069 static void SV_Rate_BurstSize_f(cmd_state_t *cmd)
1070 {
1071         int rate_burstsize;
1072
1073         if (Cmd_Argc(cmd) != 2)
1074                 return;
1075
1076         rate_burstsize = atoi(Cmd_Argv(cmd, 1));
1077
1078         host_client->rate_burstsize = rate_burstsize;
1079 }
1080
1081 static void SV_Color_f(cmd_state_t *cmd)
1082 {
1083         prvm_prog_t *prog = SVVM_prog;
1084
1085         int top, bottom, playercolor;
1086
1087         top = atoi(Cmd_Argv(cmd, 1));
1088         bottom = atoi(Cmd_Argv(cmd, 2));
1089
1090         top &= 15;
1091         bottom &= 15;
1092
1093         playercolor = top*16 + bottom;
1094
1095         if (host_client->edict && PRVM_serverfunction(SV_ChangeTeam))
1096         {
1097                 Con_DPrint("Calling SV_ChangeTeam\n");
1098                 prog->globals.fp[OFS_PARM0] = playercolor;
1099                 PRVM_serverglobalfloat(time) = sv.time;
1100                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
1101                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_ChangeTeam), "QC function SV_ChangeTeam is missing");
1102         }
1103         else
1104         {
1105                 if (host_client->edict)
1106                 {
1107                         PRVM_serveredictfloat(host_client->edict, clientcolors) = playercolor;
1108                         PRVM_serveredictfloat(host_client->edict, team) = bottom + 1;
1109                 }
1110                 host_client->colors = playercolor;
1111                 if (host_client->old_colors != host_client->colors)
1112                 {
1113                         host_client->old_colors = host_client->colors;
1114                         // send notification to all clients
1115                         MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors);
1116                         MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1117                         MSG_WriteByte (&sv.reliable_datagram, host_client->colors);
1118                 }
1119         }
1120 }
1121
1122 /*
1123 ==================
1124 SV_Kick_f
1125
1126 Kicks a user off of the server
1127 ==================
1128 */
1129 static void SV_Kick_f(cmd_state_t *cmd)
1130 {
1131         const char *who;
1132         const char *message = NULL;
1133         client_t *save;
1134         int i;
1135         qboolean byNumber = false;
1136
1137         if (!sv.active)
1138                 return;
1139
1140         save = host_client;
1141
1142         if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
1143         {
1144                 i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
1145                 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
1146                         return;
1147                 byNumber = true;
1148         }
1149         else
1150         {
1151                 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
1152                 {
1153                         if (!host_client->active)
1154                                 continue;
1155                         if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
1156                                 break;
1157                 }
1158         }
1159
1160         if (i < svs.maxclients)
1161         {
1162                 if (cmd->source == src_command)
1163                 {
1164                         if (cls.state == ca_dedicated)
1165                                 who = "Console";
1166                         else
1167                                 who = cl_name.string;
1168                 }
1169                 else
1170                         who = save->name;
1171
1172                 // can't kick yourself!
1173                 if (host_client == save)
1174                         return;
1175
1176                 if (Cmd_Argc(cmd) > 2)
1177                 {
1178                         message = Cmd_Args(cmd);
1179                         COM_ParseToken_Simple(&message, false, false, true);
1180                         if (byNumber)
1181                         {
1182                                 message++;                                                      // skip the #
1183                                 while (*message == ' ')                         // skip white space
1184                                         message++;
1185                                 message += strlen(Cmd_Argv(cmd, 2));    // skip the number
1186                         }
1187                         while (*message && *message == ' ')
1188                                 message++;
1189                 }
1190                 if (message)
1191                         SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1192                 else
1193                         SV_ClientPrintf("Kicked by %s\n", who);
1194                 SV_DropClient (false); // kicked
1195         }
1196
1197         host_client = save;
1198 }
1199
1200 static void SV_MaxPlayers_f(cmd_state_t *cmd)
1201 {
1202         int n;
1203
1204         if (Cmd_Argc(cmd) != 2)
1205         {
1206                 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
1207                 return;
1208         }
1209
1210         if (sv.active)
1211         {
1212                 Con_Print("maxplayers can not be changed while a server is running.\n");
1213                 Con_Print("It will be changed on next server startup (\"map\" command).\n");
1214         }
1215
1216         n = atoi(Cmd_Argv(cmd, 1));
1217         n = bound(1, n, MAX_SCOREBOARD);
1218         Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
1219
1220         svs.maxclients_next = n;
1221         if (n == 1)
1222                 Cvar_Set (&cvars_all, "deathmatch", "0");
1223         else
1224                 Cvar_Set (&cvars_all, "deathmatch", "1");
1225 }
1226
1227 /*
1228 ======================
1229 SV_Playermodel_f
1230 ======================
1231 */
1232 // the old playermodel in cl_main has been renamed to __cl_playermodel
1233 static void SV_Playermodel_f(cmd_state_t *cmd)
1234 {
1235         prvm_prog_t *prog = SVVM_prog;
1236         int i, j;
1237         char newPath[sizeof(host_client->playermodel)];
1238
1239         if (Cmd_Argc (cmd) == 1)
1240                 return;
1241
1242         if (Cmd_Argc (cmd) == 2)
1243                 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1244         else
1245                 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1246
1247         for (i = 0, j = 0;newPath[i];i++)
1248                 if (newPath[i] != '\r' && newPath[i] != '\n')
1249                         newPath[j++] = newPath[i];
1250         newPath[j] = 0;
1251
1252         /*
1253         if (host.realtime < host_client->nametime)
1254         {
1255                 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1256                 return;
1257         }
1258
1259         host_client->nametime = host.realtime + 5;
1260         */
1261
1262         // point the string back at updateclient->name to keep it safe
1263         strlcpy (host_client->playermodel, newPath, sizeof (host_client->playermodel));
1264         PRVM_serveredictstring(host_client->edict, playermodel) = PRVM_SetEngineString(prog, host_client->playermodel);
1265         if (strcmp(host_client->old_model, host_client->playermodel))
1266         {
1267                 strlcpy(host_client->old_model, host_client->playermodel, sizeof(host_client->old_model));
1268                 /*// send notification to all clients
1269                 MSG_WriteByte (&sv.reliable_datagram, svc_updatepmodel);
1270                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1271                 MSG_WriteString (&sv.reliable_datagram, host_client->playermodel);*/
1272         }
1273 }
1274
1275 /*
1276 ======================
1277 SV_Playerskin_f
1278 ======================
1279 */
1280 static void SV_Playerskin_f(cmd_state_t *cmd)
1281 {
1282         prvm_prog_t *prog = SVVM_prog;
1283         int i, j;
1284         char newPath[sizeof(host_client->playerskin)];
1285
1286         if (Cmd_Argc (cmd) == 1)
1287                 return;
1288
1289         if (Cmd_Argc (cmd) == 2)
1290                 strlcpy (newPath, Cmd_Argv(cmd, 1), sizeof (newPath));
1291         else
1292                 strlcpy (newPath, Cmd_Args(cmd), sizeof (newPath));
1293
1294         for (i = 0, j = 0;newPath[i];i++)
1295                 if (newPath[i] != '\r' && newPath[i] != '\n')
1296                         newPath[j++] = newPath[i];
1297         newPath[j] = 0;
1298
1299         /*
1300         if (host.realtime < host_client->nametime)
1301         {
1302                 SV_ClientPrintf("You can't change playermodel more than once every 5 seconds!\n");
1303                 return;
1304         }
1305
1306         host_client->nametime = host.realtime + 5;
1307         */
1308
1309         // point the string back at updateclient->name to keep it safe
1310         strlcpy (host_client->playerskin, newPath, sizeof (host_client->playerskin));
1311         PRVM_serveredictstring(host_client->edict, playerskin) = PRVM_SetEngineString(prog, host_client->playerskin);
1312         if (strcmp(host_client->old_skin, host_client->playerskin))
1313         {
1314                 //if (host_client->begun)
1315                 //      SV_BroadcastPrintf("%s changed skin to %s\n", host_client->name, host_client->playerskin);
1316                 strlcpy(host_client->old_skin, host_client->playerskin, sizeof(host_client->old_skin));
1317                 /*// send notification to all clients
1318                 MSG_WriteByte (&sv.reliable_datagram, svc_updatepskin);
1319                 MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
1320                 MSG_WriteString (&sv.reliable_datagram, host_client->playerskin);*/
1321         }
1322 }
1323
1324 /*
1325 ======================
1326 SV_PModel_f
1327 LadyHavoc: only supported for Nehahra, I personally think this is dumb, but Mindcrime won't listen.
1328 LadyHavoc: correction, Mindcrime will be removing pmodel in the future, but it's still stuck here for compatibility.
1329 ======================
1330 */
1331 static void SV_PModel_f(cmd_state_t *cmd)
1332 {
1333         prvm_prog_t *prog = SVVM_prog;
1334
1335         if (Cmd_Argc (cmd) == 1)
1336                 return;
1337
1338         PRVM_serveredictfloat(host_client->edict, pmodel) = atoi(Cmd_Argv(cmd, 1));
1339 }
1340
1341 /*
1342 ===============================================================================
1343
1344 DEBUGGING TOOLS
1345
1346 ===============================================================================
1347 */
1348
1349 static prvm_edict_t     *FindViewthing(prvm_prog_t *prog)
1350 {
1351         int             i;
1352         prvm_edict_t    *e;
1353
1354         for (i=0 ; i<prog->num_edicts ; i++)
1355         {
1356                 e = PRVM_EDICT_NUM(i);
1357                 if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
1358                         return e;
1359         }
1360         Con_Print("No viewthing on map\n");
1361         return NULL;
1362 }
1363
1364 /*
1365 ==================
1366 SV_Viewmodel_f
1367 ==================
1368 */
1369 static void SV_Viewmodel_f(cmd_state_t *cmd)
1370 {
1371         prvm_prog_t *prog = SVVM_prog;
1372         prvm_edict_t    *e;
1373         dp_model_t      *m;
1374
1375         if (!sv.active)
1376                 return;
1377
1378         e = FindViewthing(prog);
1379         if (e)
1380         {
1381                 m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
1382                 if (m && m->loaded && m->Draw)
1383                 {
1384                         PRVM_serveredictfloat(e, frame) = 0;
1385                         cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
1386                 }
1387                 else
1388                         Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
1389         }
1390 }
1391
1392 /*
1393 ==================
1394 SV_Viewframe_f
1395 ==================
1396 */
1397 static void SV_Viewframe_f(cmd_state_t *cmd)
1398 {
1399         prvm_prog_t *prog = SVVM_prog;
1400         prvm_edict_t    *e;
1401         int             f;
1402         dp_model_t      *m;
1403
1404         if (!sv.active)
1405                 return;
1406
1407         e = FindViewthing(prog);
1408         if (e)
1409         {
1410                 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
1411
1412                 f = atoi(Cmd_Argv(cmd, 1));
1413                 if (f >= m->numframes)
1414                         f = m->numframes-1;
1415
1416                 PRVM_serveredictfloat(e, frame) = f;
1417         }
1418 }
1419
1420 static void PrintFrameName (dp_model_t *m, int frame)
1421 {
1422         if (m->animscenes)
1423                 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
1424         else
1425                 Con_Printf("frame %i\n", frame);
1426 }
1427
1428 /*
1429 ==================
1430 SV_Viewnext_f
1431 ==================
1432 */
1433 static void SV_Viewnext_f(cmd_state_t *cmd)
1434 {
1435         prvm_prog_t *prog = SVVM_prog;
1436         prvm_edict_t    *e;
1437         dp_model_t      *m;
1438
1439         if (!sv.active)
1440                 return;
1441
1442         e = FindViewthing(prog);
1443         if (e)
1444         {
1445                 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
1446
1447                 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
1448                 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
1449                         PRVM_serveredictfloat(e, frame) = m->numframes - 1;
1450
1451                 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
1452         }
1453 }
1454
1455 /*
1456 ==================
1457 SV_Viewprev_f
1458 ==================
1459 */
1460 static void SV_Viewprev_f(cmd_state_t *cmd)
1461 {
1462         prvm_prog_t *prog = SVVM_prog;
1463         prvm_edict_t    *e;
1464         dp_model_t      *m;
1465
1466         if (!sv.active)
1467                 return;
1468
1469         e = FindViewthing(prog);
1470         if (e)
1471         {
1472                 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
1473
1474                 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
1475                 if (PRVM_serveredictfloat(e, frame) < 0)
1476                         PRVM_serveredictfloat(e, frame) = 0;
1477
1478                 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
1479         }
1480 }
1481
1482 static void SV_SendCvar_f(cmd_state_t *cmd)
1483 {
1484         int i;  
1485         const char *cvarname;
1486         client_t *old;
1487         
1488         if(Cmd_Argc(cmd) != 2)
1489                 return;
1490
1491         if(!sv.active)// || !PRVM_serverfunction(SV_ParseClientCommand))
1492                 return;
1493
1494         cvarname = Cmd_Argv(cmd, 1);
1495
1496         old = host_client;
1497         if (cls.state != ca_dedicated)
1498                 i = 1;
1499         else
1500                 i = 0;
1501         for(;i<svs.maxclients;i++)
1502                 if(svs.clients[i].active && svs.clients[i].netconnection)
1503                 {
1504                         host_client = &svs.clients[i];
1505                         SV_ClientCommands("sendcvar %s\n", cvarname);
1506                 }
1507         host_client = old;
1508 }
1509
1510 static void SV_Ent_Create_f(cmd_state_t *cmd)
1511 {
1512         prvm_prog_t *prog = SVVM_prog;
1513         ddef_t *key;
1514         int i;
1515         qboolean expectval = false, haveorigin = false;
1516         void (*print)(const char *, ...) = (cmd->source == src_client ? SV_ClientPrintf : Con_Printf);
1517
1518         prvm_edict_t *ed = PRVM_ED_Alloc(SVVM_prog);
1519
1520         PRVM_ED_ParseEpair(prog, ed, PRVM_ED_FindField(prog, "classname"), Cmd_Argv(cmd, 1), false);
1521
1522         for(i = 2; i < Cmd_Argc(cmd); i++)
1523         {
1524                 if(!expectval)
1525                 {
1526                         if(!(key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, i))))
1527                         {
1528                                 Con_Printf("Key %s not found!\n", Cmd_Argv(cmd, i));
1529                                 return;
1530                         }
1531
1532                         if(!strcmp(Cmd_Argv(cmd, i), "origin") && !haveorigin)
1533                                 haveorigin = true;
1534
1535                         expectval = true;
1536                 }
1537                 else
1538                 {
1539                         PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, i), false);
1540                         expectval = false;
1541                 }
1542         }
1543         
1544         if(!haveorigin)
1545         {
1546                 print("Missing origin\n");
1547                 if(cmd->source == src_client)
1548                         print("This should never happen if you're a player. Please report this to a developer.\n");
1549                 return;
1550         }
1551
1552         // Spawn it
1553         PRVM_ED_CallPrespawnFunction(prog, ed);
1554         
1555         if(!PRVM_ED_CallSpawnFunction(prog, ed, NULL, NULL))
1556         {
1557                 print("Could not spawn a \"%s\". No such entity or it has no spawn function\n", Cmd_Argv(cmd, 1));
1558                 if(cmd->source == src_client)
1559                         Con_Printf("%s tried to spawn a \"%s\"\n", host_client->name, Cmd_Argv(cmd, 1));
1560                 return;
1561         }
1562
1563         PRVM_ED_CallPostspawnFunction(prog, ed);        
1564
1565         // Make it appear in the world
1566         SV_LinkEdict(ed);
1567
1568         if(cmd->source == src_client)
1569                 Con_Printf("%s spawned a \"%s\"\n", host_client->name, Cmd_Argv(cmd, 1));
1570 }
1571
1572 void SV_InitOperatorCommands(void)
1573 {
1574         Cvar_RegisterVariable(&sv_cheats);
1575         Cvar_RegisterCallback(&sv_cheats, SV_DisableCheats_c);
1576         Cvar_RegisterVariable(&sv_adminnick);
1577         Cvar_RegisterVariable(&sv_status_privacy);
1578         Cvar_RegisterVariable(&sv_status_show_qcstatus);
1579         Cvar_RegisterVariable(&sv_namechangetimer);
1580         
1581         Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "status", SV_Status_f, "print server status information");
1582         Cmd_AddCommand(CMD_SHARED, "map", SV_Map_f, "kick everyone off the server and start a new level");
1583         Cmd_AddCommand(CMD_SHARED, "restart", SV_Restart_f, "restart current level");
1584         Cmd_AddCommand(CMD_SHARED, "changelevel", SV_Changelevel_f, "change to another level, bringing along all connected clients");
1585         Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "say", SV_Say_f, "send a chat message to everyone on the server");
1586         Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "say_team", SV_Say_Team_f, "send a chat message to your team on the server");
1587         Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "tell", SV_Tell_f, "send a chat message to only one person on the server");
1588         Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "pause", SV_Pause_f, "pause the game (if the server allows pausing)");
1589         Cmd_AddCommand(CMD_SHARED, "kick", SV_Kick_f, "kick a player off the server by number or name");
1590         Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "ping", SV_Ping_f, "print ping times of all players on the server");
1591         Cmd_AddCommand(CMD_SHARED, "load", SV_Loadgame_f, "load a saved game file");
1592         Cmd_AddCommand(CMD_SHARED, "save", SV_Savegame_f, "save the game to a file");
1593         Cmd_AddCommand(CMD_SHARED, "viewmodel", SV_Viewmodel_f, "change model of viewthing entity in current level");
1594         Cmd_AddCommand(CMD_SHARED, "viewframe", SV_Viewframe_f, "change animation frame of viewthing entity in current level");
1595         Cmd_AddCommand(CMD_SHARED, "viewnext", SV_Viewnext_f, "change to next animation frame of viewthing entity in current level");
1596         Cmd_AddCommand(CMD_SHARED, "viewprev", SV_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
1597         Cmd_AddCommand(CMD_SHARED, "maxplayers", SV_MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
1598         Cmd_AddCommand(CMD_SHARED, "user", SV_User_f, "prints additional information about a player number or name on the scoreboard");
1599         Cmd_AddCommand(CMD_SHARED, "users", SV_Users_f, "prints additional information about all players on the scoreboard");
1600         Cmd_AddCommand(CMD_SERVER, "sendcvar", SV_SendCvar_f, "sends the value of a cvar to the server as a sentcvar command, for use by QuakeC");
1601
1602         // 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)
1603         Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "prespawn", SV_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
1604         Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "spawn", SV_Spawn_f, "internal use - signon 2 (client has sent player information, and is asking server to send scoreboard rankings)");
1605         Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "begin", SV_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)");
1606         Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "pings", SV_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)");
1607
1608         Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "god", SV_God_f, "god mode (invulnerability)");
1609         Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "notarget", SV_Notarget_f, "notarget mode (monsters do not see you)");
1610         Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "fly", SV_Fly_f, "fly mode (flight)");
1611         Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "noclip", SV_Noclip_f, "noclip mode (flight without collisions, move through walls)");
1612         Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "give", SV_Give_f, "alter inventory");
1613         Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "kill", SV_Kill_f, "die instantly");
1614         
1615         Cmd_AddCommand(CMD_USERINFO, "color", SV_Color_f, "change your player shirt and pants colors");
1616         Cmd_AddCommand(CMD_USERINFO, "name", SV_Name_f, "change your player name");
1617         Cmd_AddCommand(CMD_USERINFO, "rate", SV_Rate_f, "change your network connection speed");
1618         Cmd_AddCommand(CMD_USERINFO, "rate_burstsize", SV_Rate_BurstSize_f, "change your network connection speed");
1619         Cmd_AddCommand(CMD_USERINFO, "pmodel", SV_PModel_f, "(Nehahra-only) change your player model choice");
1620         Cmd_AddCommand(CMD_USERINFO, "playermodel", SV_Playermodel_f, "change your player model");
1621         Cmd_AddCommand(CMD_USERINFO, "playerskin", SV_Playerskin_f, "change your player skin number");
1622
1623         Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "ent_create", SV_Ent_Create_f, "Creates an entity at the specified coordinate, of the specified classname. If executed from a server, origin has to be specified manually.");
1624 }