2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
25 cvar_t sv_cheats = {CVAR_SERVER | CVAR_NOTIFY, "sv_cheats", "0", "enables cheat commands in any game, and cheat impulses in dpmod"};
26 cvar_t sv_adminnick = {CVAR_SERVER | CVAR_SAVE, "sv_adminnick", "", "nick name to use for admin messages instead of host name"};
27 cvar_t sv_status_privacy = {CVAR_SERVER | CVAR_SAVE, "sv_status_privacy", "0", "do not show IP addresses in 'status' replies to clients"};
28 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."};
29 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 ===============================================================================
36 ===============================================================================
40 ======================
45 command from the console. Active clients are kicked off.
46 ======================
48 static void SV_Map_f(cmd_state_t *cmd)
50 char level[MAX_QPATH];
52 if (Cmd_Argc(cmd) != 2)
54 Con_Print("map <levelname> : start a new game (kicks off all players)\n");
58 // GAME_DELUXEQUAKE - clear warpmark (used by QC)
59 if (gamemode == GAME_DELUXEQUAKE)
60 Cvar_Set(&cvars_all, "warpmark", "");
62 cls.demonum = -1; // stop demo loop in case this fails
67 if(svs.maxclients != svs.maxclients_next)
69 svs.maxclients = svs.maxclients_next;
71 Mem_Free(svs.clients);
72 svs.clients = (client_t *)Mem_Alloc(sv_mempool, sizeof(client_t) * svs.maxclients);
77 if (key_dest == key_menu || key_dest == key_menu_grabbed)
82 svs.serverflags = 0; // haven't completed an episode yet
83 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
84 SV_SpawnServer(level);
85 if (sv.active && cls.state == ca_disconnected)
86 CL_EstablishConnection("local:1", -2);
93 Goes to a new map, taking all clients along
96 static void SV_Changelevel_f(cmd_state_t *cmd)
98 char level[MAX_QPATH];
100 if (Cmd_Argc(cmd) != 2)
102 Con_Print("changelevel <levelname> : continue game on a new level\n");
113 if (key_dest == key_menu || key_dest == key_menu_grabbed)
118 SV_SaveSpawnparms ();
119 strlcpy(level, Cmd_Argv(cmd, 1), sizeof(level));
120 SV_SpawnServer(level);
121 if (sv.active && cls.state == ca_disconnected)
122 CL_EstablishConnection("local:1", -2);
129 Restarts the current server for a dead player
132 static void SV_Restart_f(cmd_state_t *cmd)
134 char mapname[MAX_QPATH];
136 if (Cmd_Argc(cmd) != 1)
138 Con_Print("restart : restart current level\n");
143 Con_Print("Only the server may restart\n");
149 if (key_dest == key_menu || key_dest == key_menu_grabbed)
154 strlcpy(mapname, sv.name, sizeof(mapname));
155 SV_SpawnServer(mapname);
156 if (sv.active && cls.state == ca_disconnected)
157 CL_EstablishConnection("local:1", -2);
160 //===========================================================================
162 // Disable cheats if sv_cheats is turned off
163 static void SV_DisableCheats_c(char *value)
165 prvm_prog_t *prog = SVVM_prog;
168 if (value[0] == '0' && !value[1])
170 while (svs.clients[i].edict)
172 if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_GODMODE))
173 PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_GODMODE;
174 if (((int)PRVM_serveredictfloat(svs.clients[i].edict, flags) & FL_NOTARGET))
175 PRVM_serveredictfloat(svs.clients[i].edict, flags) = (int)PRVM_serveredictfloat(svs.clients[i].edict, flags) ^ FL_NOTARGET;
176 if (PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_NOCLIP ||
177 PRVM_serveredictfloat(svs.clients[i].edict, movetype) == MOVETYPE_FLY)
179 noclip_anglehack = false;
180 PRVM_serveredictfloat(svs.clients[i].edict, movetype) = MOVETYPE_WALK;
191 Sets client to godmode
194 static void SV_God_f(cmd_state_t *cmd)
196 prvm_prog_t *prog = SVVM_prog;
198 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_GODMODE;
199 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_GODMODE) )
200 SV_ClientPrint("godmode OFF\n");
202 SV_ClientPrint("godmode ON\n");
205 qboolean noclip_anglehack;
207 static void SV_Noclip_f(cmd_state_t *cmd)
209 prvm_prog_t *prog = SVVM_prog;
211 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP)
213 noclip_anglehack = true;
214 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_NOCLIP;
215 SV_ClientPrint("noclip ON\n");
219 noclip_anglehack = false;
220 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
221 SV_ClientPrint("noclip OFF\n");
230 static void SV_Give_f(cmd_state_t *cmd)
232 prvm_prog_t *prog = SVVM_prog;
236 t = Cmd_Argv(cmd, 1);
237 v = atoi (Cmd_Argv(cmd, 2));
251 // MED 01/04/97 added hipnotic give stuff
252 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_QUOTH)
257 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_PROXIMITY_GUN;
259 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | IT_GRENADE_LAUNCHER;
261 else if (t[0] == '9')
262 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_LASER_CANNON;
263 else if (t[0] == '0')
264 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | HIT_MJOLNIR;
265 else if (t[0] >= '2')
266 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
271 PRVM_serveredictfloat(host_client->edict, items) = (int)PRVM_serveredictfloat(host_client->edict, items) | (IT_SHOTGUN << (t[0] - '2'));
276 if (gamemode == GAME_ROGUE)
277 PRVM_serveredictfloat(host_client->edict, ammo_shells1) = v;
279 PRVM_serveredictfloat(host_client->edict, ammo_shells) = v;
282 if (gamemode == GAME_ROGUE)
284 PRVM_serveredictfloat(host_client->edict, ammo_nails1) = v;
285 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
286 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
290 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
294 if (gamemode == GAME_ROGUE)
296 PRVM_serveredictfloat(host_client->edict, ammo_lava_nails) = v;
297 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
298 PRVM_serveredictfloat(host_client->edict, ammo_nails) = v;
302 if (gamemode == GAME_ROGUE)
304 PRVM_serveredictfloat(host_client->edict, ammo_rockets1) = v;
305 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
306 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
310 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
314 if (gamemode == GAME_ROGUE)
316 PRVM_serveredictfloat(host_client->edict, ammo_multi_rockets) = v;
317 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
318 PRVM_serveredictfloat(host_client->edict, ammo_rockets) = v;
322 PRVM_serveredictfloat(host_client->edict, health) = v;
325 if (gamemode == GAME_ROGUE)
327 PRVM_serveredictfloat(host_client->edict, ammo_cells1) = v;
328 if (PRVM_serveredictfloat(host_client->edict, weapon) <= IT_LIGHTNING)
329 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
333 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
337 if (gamemode == GAME_ROGUE)
339 PRVM_serveredictfloat(host_client->edict, ammo_plasma) = v;
340 if (PRVM_serveredictfloat(host_client->edict, weapon) > IT_LIGHTNING)
341 PRVM_serveredictfloat(host_client->edict, ammo_cells) = v;
351 Sets client to flymode
354 static void SV_Fly_f(cmd_state_t *cmd)
356 prvm_prog_t *prog = SVVM_prog;
358 if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_FLY)
360 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_FLY;
361 SV_ClientPrint("flymode ON\n");
365 PRVM_serveredictfloat(host_client->edict, movetype) = MOVETYPE_WALK;
366 SV_ClientPrint("flymode OFF\n");
370 static void SV_Notarget_f(cmd_state_t *cmd)
372 prvm_prog_t *prog = SVVM_prog;
374 PRVM_serveredictfloat(host_client->edict, flags) = (int)PRVM_serveredictfloat(host_client->edict, flags) ^ FL_NOTARGET;
375 if (!((int)PRVM_serveredictfloat(host_client->edict, flags) & FL_NOTARGET) )
376 SV_ClientPrint("notarget OFF\n");
378 SV_ClientPrint("notarget ON\n");
386 static void SV_Kill_f(cmd_state_t *cmd)
388 prvm_prog_t *prog = SVVM_prog;
389 if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
391 SV_ClientPrint("Can't suicide -- already dead!\n");
395 PRVM_serverglobalfloat(time) = sv.time;
396 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
397 prog->ExecuteProgram(prog, PRVM_serverfunction(ClientKill), "QC function ClientKill is missing");
405 static void SV_Pause_f(cmd_state_t *cmd)
407 void (*print) (const char *fmt, ...);
408 if (cmd->source == src_command)
410 // if running a client, try to send over network so the pause is handled by the server
411 if (cls.state == ca_connected)
413 Cmd_ForwardToServer_f(cmd);
419 print = SV_ClientPrintf;
421 if (!pausable.integer)
423 if (cmd->source == src_client)
425 if(cls.state == ca_dedicated || host_client != &svs.clients[0]) // non-admin
427 print("Pause not allowed.\n");
434 if (cmd->source != src_command)
435 SV_BroadcastPrintf("%s %spaused the game\n", host_client->name, sv.paused ? "" : "un");
436 else if(*(sv_adminnick.string))
437 SV_BroadcastPrintf("%s %spaused the game\n", sv_adminnick.string, sv.paused ? "" : "un");
439 SV_BroadcastPrintf("%s %spaused the game\n", hostname.string, sv.paused ? "" : "un");
440 // send notification to all clients
441 MSG_WriteByte(&sv.reliable_datagram, svc_setpause);
442 MSG_WriteByte(&sv.reliable_datagram, sv.paused);
445 static void SV_Say(cmd_state_t *cmd, qboolean teamonly)
447 prvm_prog_t *prog = SVVM_prog;
452 // LadyHavoc: long say messages
454 qboolean fromServer = false;
456 if (cmd->source == src_command)
458 if (cls.state == ca_dedicated)
465 Cmd_ForwardToServer_f(cmd);
470 if (Cmd_Argc (cmd) < 2)
473 if (!teamplay.integer)
483 // note this uses the chat prefix \001
484 if (!fromServer && !teamonly)
485 dpsnprintf (text, sizeof(text), "\001%s: %s", host_client->name, p1);
486 else if (!fromServer && teamonly)
487 dpsnprintf (text, sizeof(text), "\001(%s): %s", host_client->name, p1);
488 else if(*(sv_adminnick.string))
489 dpsnprintf (text, sizeof(text), "\001<%s> %s", sv_adminnick.string, p1);
491 dpsnprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p1);
492 p2 = text + strlen(text);
493 while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)))
495 if (p2[-1] == '\"' && quoted)
500 strlcat(text, "\n", sizeof(text));
502 // note: save is not a valid edict if fromServer is true
504 for (j = 0, host_client = svs.clients;j < svs.maxclients;j++, host_client++)
505 if (host_client->active && (!teamonly || PRVM_serveredictfloat(host_client->edict, team) == PRVM_serveredictfloat(save->edict, team)))
506 SV_ClientPrint(text);
509 if (cls.state == ca_dedicated)
513 static void SV_Say_f(cmd_state_t *cmd)
518 static void SV_Say_Team_f(cmd_state_t *cmd)
523 static void SV_Tell_f(cmd_state_t *cmd)
525 const char *playername_start = NULL;
526 size_t playername_length = 0;
527 int playernumber = 0;
531 char text[MAX_INPUTLINE]; // LadyHavoc: FIXME: temporary buffer overflow fix (was 64)
532 qboolean fromServer = false;
534 if (cmd->source == src_command)
536 if (cls.state == ca_dedicated)
540 Cmd_ForwardToServer_f(cmd);
545 if (Cmd_Argc (cmd) < 2)
548 // note this uses the chat prefix \001
550 dpsnprintf (text, sizeof(text), "\001%s tells you: ", host_client->name);
551 else if(*(sv_adminnick.string))
552 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", sv_adminnick.string);
554 dpsnprintf (text, sizeof(text), "\001<%s tells you> ", hostname.string);
557 p2 = p1 + strlen(p1);
558 // remove the target name
559 while (p1 < p2 && *p1 == ' ')
564 while (p1 < p2 && *p1 == ' ')
566 while (p1 < p2 && isdigit(*p1))
568 playernumber = playernumber * 10 + (*p1 - '0');
576 playername_start = p1;
577 while (p1 < p2 && *p1 != '"')
579 playername_length = p1 - playername_start;
585 playername_start = p1;
586 while (p1 < p2 && *p1 != ' ')
588 playername_length = p1 - playername_start;
590 while (p1 < p2 && *p1 == ' ')
594 // set playernumber to the right client
596 if(playername_length >= sizeof(namebuf))
599 Con_Print("Host_Tell: too long player name/ID\n");
601 SV_ClientPrint("Host_Tell: too long player name/ID\n");
604 memcpy(namebuf, playername_start, playername_length);
605 namebuf[playername_length] = 0;
606 for (playernumber = 0; playernumber < svs.maxclients; playernumber++)
608 if (!svs.clients[playernumber].active)
610 if (strcasecmp(svs.clients[playernumber].name, namebuf) == 0)
614 if(playernumber < 0 || playernumber >= svs.maxclients || !(svs.clients[playernumber].active))
617 Con_Print("Host_Tell: invalid player name/ID\n");
619 SV_ClientPrint("Host_Tell: invalid player name/ID\n");
622 // remove trailing newlines
623 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
625 // remove quotes if present
632 Con_Print("Host_Tell: missing end quote\n");
634 SV_ClientPrint("Host_Tell: missing end quote\n");
636 while (p2 > p1 && (p2[-1] == '\n' || p2[-1] == '\r'))
640 for (j = (int)strlen(text);j < (int)(sizeof(text) - 2) && p1 < p2;)
646 host_client = svs.clients + playernumber;
647 SV_ClientPrint(text);
657 static void SV_Ping_f(cmd_state_t *cmd)
661 void (*print) (const char *fmt, ...);
663 if (cmd->source == src_command)
665 // if running a client, try to send over network so the client's ping report parser will see the report
666 if (cls.state == ca_connected)
668 Cmd_ForwardToServer_f(cmd);
674 print = SV_ClientPrintf;
679 print("Client ping times:\n");
680 for (i = 0, client = svs.clients;i < svs.maxclients;i++, client++)
684 print("%4i %s\n", bound(0, (int)floor(client->ping*1000+0.5), 9999), client->name);
692 Send back ping and packet loss update for all current players to this player
695 static void SV_Pings_f(cmd_state_t *cmd)
697 int i, j, ping, packetloss, movementloss;
700 if (!host_client->netconnection)
703 if (sv.protocol != PROTOCOL_QUAKEWORLD)
705 MSG_WriteByte(&host_client->netconnection->message, svc_stufftext);
706 MSG_WriteUnterminatedString(&host_client->netconnection->message, "pingplreport");
708 for (i = 0;i < svs.maxclients;i++)
712 if (svs.clients[i].netconnection)
714 for (j = 0;j < NETGRAPH_PACKETS;j++)
715 if (svs.clients[i].netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
717 for (j = 0;j < NETGRAPH_PACKETS;j++)
718 if (svs.clients[i].movement_count[j] < 0)
721 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
722 movementloss = (movementloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
723 ping = (int)floor(svs.clients[i].ping*1000+0.5);
724 ping = bound(0, ping, 9999);
725 if (sv.protocol == PROTOCOL_QUAKEWORLD)
727 // send qw_svc_updateping and qw_svc_updatepl messages
728 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updateping);
729 MSG_WriteShort(&host_client->netconnection->message, ping);
730 MSG_WriteByte(&host_client->netconnection->message, qw_svc_updatepl);
731 MSG_WriteByte(&host_client->netconnection->message, packetloss);
735 // write the string into the packet as multiple unterminated strings to avoid needing a local buffer
737 dpsnprintf(temp, sizeof(temp), " %d %d,%d", ping, packetloss, movementloss);
739 dpsnprintf(temp, sizeof(temp), " %d %d", ping, packetloss);
740 MSG_WriteUnterminatedString(&host_client->netconnection->message, temp);
743 if (sv.protocol != PROTOCOL_QUAKEWORLD)
744 MSG_WriteString(&host_client->netconnection->message, "\n");
751 user <name or userid>
753 Dump userdata / masterdata for a user
756 static void SV_User_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
761 if (Cmd_Argc(cmd) != 2)
763 Con_Printf ("Usage: user <username / userid>\n");
767 uid = atoi(Cmd_Argv(cmd, 1));
769 for (i = 0;i < cl.maxclients;i++)
771 if (!cl.scores[i].name[0])
773 if (cl.scores[i].qw_userid == uid || !strcasecmp(cl.scores[i].name, Cmd_Argv(cmd, 1)))
775 InfoString_Print(cl.scores[i].qw_userinfo);
779 Con_Printf ("User not in server.\n");
786 Dump userids for all current players
789 static void SV_Users_f(cmd_state_t *cmd) // credit: taken from QuakeWorld
795 Con_Printf ("userid frags name\n");
796 Con_Printf ("------ ----- ----\n");
797 for (i = 0;i < cl.maxclients;i++)
799 if (cl.scores[i].name[0])
801 Con_Printf ("%6i %4i %s\n", cl.scores[i].qw_userid, cl.scores[i].frags, cl.scores[i].name);
806 Con_Printf ("%i total users\n", c);
814 static void SV_Status_f(cmd_state_t *cmd)
816 prvm_prog_t *prog = SVVM_prog;
819 int seconds = 0, minutes = 0, hours = 0, i, j, k, in, players, ping = 0, packetloss = 0;
820 void (*print) (const char *fmt, ...);
821 char ip[48]; // can contain a full length v6 address with [] and a port
825 if (cmd->source == src_command)
827 // if running a client, try to send over network so the client's status report parser will see the report
828 if (cls.state == ca_connected)
830 Cmd_ForwardToServer_f(cmd);
836 print = SV_ClientPrintf;
842 if (Cmd_Argc(cmd) == 2)
844 if (strcmp(Cmd_Argv(cmd, 1), "1") == 0)
846 else if (strcmp(Cmd_Argv(cmd, 1), "2") == 0)
850 for (players = 0, i = 0;i < svs.maxclients;i++)
851 if (svs.clients[i].active)
853 print ("host: %s\n", Cvar_VariableString (&cvars_all, "hostname", CVAR_SERVER));
854 print ("version: %s build %s (gamename %s)\n", gamename, buildstring, gamenetworkfiltername);
855 print ("protocol: %i (%s)\n", Protocol_NumberForEnum(sv.protocol), Protocol_NameForEnum(sv.protocol));
856 print ("map: %s\n", sv.name);
857 print ("timing: %s\n", Host_TimingReport(vabuf, sizeof(vabuf)));
858 print ("players: %i active (%i max)\n\n", players, svs.maxclients);
861 print ("^2IP %%pl ping time frags no name\n");
863 print ("^5IP no name\n");
865 for (i = 0, k = 0, client = svs.clients;i < svs.maxclients;i++, client++)
872 if (in == 0 || in == 1)
874 seconds = (int)(host.realtime - client->connecttime);
875 minutes = seconds / 60;
878 seconds -= (minutes * 60);
879 hours = minutes / 60;
881 minutes -= (hours * 60);
887 if (client->netconnection)
888 for (j = 0;j < NETGRAPH_PACKETS;j++)
889 if (client->netconnection->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
891 packetloss = (packetloss * 100 + NETGRAPH_PACKETS - 1) / NETGRAPH_PACKETS;
892 ping = bound(0, (int)floor(client->ping*1000+0.5), 9999);
895 if(sv_status_privacy.integer && cmd->source != src_command)
896 strlcpy(ip, client->netconnection ? "hidden" : "botclient", 48);
898 strlcpy(ip, (client->netconnection && *client->netconnection->address) ? client->netconnection->address : "botclient", 48);
900 frags = client->frags;
902 if(sv_status_show_qcstatus.integer)
904 prvm_edict_t *ed = PRVM_EDICT_NUM(i + 1);
905 const char *str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
911 for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
912 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
916 frags = atoi(qcstatus);
920 if (in == 0) // default layout
922 if (sv.protocol == PROTOCOL_QUAKE && svs.maxclients <= 99)
924 // LadyHavoc: this is very touchy because we must maintain ProQuake compatible status output
925 print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
930 // LadyHavoc: no real restrictions here, not a ProQuake-compatible protocol anyway...
931 print ("#%-3u %-16.16s %4i %2i:%02i:%02i\n", i+1, client->name, frags, hours, minutes, seconds);
935 else if (in == 1) // extended layout
937 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);
939 else if (in == 2) // reduced layout
941 print ("%s%-47s #%-3u ^7%s\n", k%2 ? "^3" : "^7", ip, i+1, client->name);
950 Kicks a user off of the server
953 static void SV_Kick_f(cmd_state_t *cmd)
956 const char *message = NULL;
959 qboolean byNumber = false;
966 if (Cmd_Argc(cmd) > 2 && strcmp(Cmd_Argv(cmd, 1), "#") == 0)
968 i = (int)(atof(Cmd_Argv(cmd, 2)) - 1);
969 if (i < 0 || i >= svs.maxclients || !(host_client = svs.clients + i)->active)
975 for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
977 if (!host_client->active)
979 if (strcasecmp(host_client->name, Cmd_Argv(cmd, 1)) == 0)
984 if (i < svs.maxclients)
986 if (cmd->source == src_command)
988 if (cls.state == ca_dedicated)
991 who = cl_name.string;
996 // can't kick yourself!
997 if (host_client == save)
1000 if (Cmd_Argc(cmd) > 2)
1002 message = Cmd_Args(cmd);
1003 COM_ParseToken_Simple(&message, false, false, true);
1006 message++; // skip the #
1007 while (*message == ' ') // skip white space
1009 message += strlen(Cmd_Argv(cmd, 2)); // skip the number
1011 while (*message && *message == ' ')
1015 SV_ClientPrintf("Kicked by %s: %s\n", who, message);
1017 SV_ClientPrintf("Kicked by %s\n", who);
1018 SV_DropClient (false); // kicked
1024 static void SV_MaxPlayers_f(cmd_state_t *cmd)
1028 if (Cmd_Argc(cmd) != 2)
1030 Con_Printf("\"maxplayers\" is \"%u\"\n", svs.maxclients_next);
1036 Con_Print("maxplayers can not be changed while a server is running.\n");
1037 Con_Print("It will be changed on next server startup (\"map\" command).\n");
1040 n = atoi(Cmd_Argv(cmd, 1));
1041 n = bound(1, n, MAX_SCOREBOARD);
1042 Con_Printf("\"maxplayers\" set to \"%u\"\n", n);
1044 svs.maxclients_next = n;
1046 Cvar_Set (&cvars_all, "deathmatch", "0");
1048 Cvar_Set (&cvars_all, "deathmatch", "1");
1052 ===============================================================================
1056 ===============================================================================
1059 static prvm_edict_t *FindViewthing(prvm_prog_t *prog)
1064 for (i=0 ; i<prog->num_edicts ; i++)
1066 e = PRVM_EDICT_NUM(i);
1067 if (!strcmp (PRVM_GetString(prog, PRVM_serveredictstring(e, classname)), "viewthing"))
1070 Con_Print("No viewthing on map\n");
1079 static void SV_Viewmodel_f(cmd_state_t *cmd)
1081 prvm_prog_t *prog = SVVM_prog;
1088 e = FindViewthing(prog);
1091 m = Mod_ForName (Cmd_Argv(cmd, 1), false, true, NULL);
1092 if (m && m->loaded && m->Draw)
1094 PRVM_serveredictfloat(e, frame) = 0;
1095 cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)] = m;
1098 Con_Printf("viewmodel: can't load %s\n", Cmd_Argv(cmd, 1));
1107 static void SV_Viewframe_f(cmd_state_t *cmd)
1109 prvm_prog_t *prog = SVVM_prog;
1117 e = FindViewthing(prog);
1120 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
1122 f = atoi(Cmd_Argv(cmd, 1));
1123 if (f >= m->numframes)
1126 PRVM_serveredictfloat(e, frame) = f;
1130 static void PrintFrameName (dp_model_t *m, int frame)
1133 Con_Printf("frame %i: %s\n", frame, m->animscenes[frame].name);
1135 Con_Printf("frame %i\n", frame);
1143 static void SV_Viewnext_f(cmd_state_t *cmd)
1145 prvm_prog_t *prog = SVVM_prog;
1152 e = FindViewthing(prog);
1155 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
1157 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) + 1;
1158 if (PRVM_serveredictfloat(e, frame) >= m->numframes)
1159 PRVM_serveredictfloat(e, frame) = m->numframes - 1;
1161 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
1170 static void SV_Viewprev_f(cmd_state_t *cmd)
1172 prvm_prog_t *prog = SVVM_prog;
1179 e = FindViewthing(prog);
1182 m = cl.model_precache[(int)PRVM_serveredictfloat(e, modelindex)];
1184 PRVM_serveredictfloat(e, frame) = PRVM_serveredictfloat(e, frame) - 1;
1185 if (PRVM_serveredictfloat(e, frame) < 0)
1186 PRVM_serveredictfloat(e, frame) = 0;
1188 PrintFrameName (m, (int)PRVM_serveredictfloat(e, frame));
1192 void SV_InitOperatorCommands(void)
1194 Cvar_RegisterVariable(&sv_cheats);
1195 Cvar_RegisterCallback(&sv_cheats, SV_DisableCheats_c);
1196 Cvar_RegisterVariable(&sv_adminnick);
1197 Cvar_RegisterVariable(&sv_status_privacy);
1198 Cvar_RegisterVariable(&sv_status_show_qcstatus);
1199 Cvar_RegisterVariable(&sv_namechangetimer);
1201 Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "status", SV_Status_f, "print server status information");
1202 Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "map", SV_Map_f, "kick everyone off the server and start a new level");
1203 Cmd_AddCommand(CMD_SHARED, "restart", SV_Restart_f, "restart current level");
1204 Cmd_AddCommand(CMD_SHARED, "changelevel", SV_Changelevel_f, "change to another level, bringing along all connected clients");
1205 Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "say", SV_Say_f, "send a chat message to everyone on the server");
1206 Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "say_team", SV_Say_Team_f, "send a chat message to your team on the server");
1207 Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "tell", SV_Tell_f, "send a chat message to only one person on the server");
1208 Cmd_AddCommand(CMD_SERVER | CMD_SERVER_FROM_CLIENT, "pause", SV_Pause_f, "pause the game (if the server allows pausing)");
1209 Cmd_AddCommand(CMD_SHARED, "kick", SV_Kick_f, "kick a player off the server by number or name");
1210 Cmd_AddCommand(CMD_SHARED | CMD_SERVER_FROM_CLIENT, "ping", SV_Ping_f, "print ping times of all players on the server");
1211 Cmd_AddCommand(CMD_SHARED | CMD_INITWAIT, "load", SV_Loadgame_f, "load a saved game file");
1212 Cmd_AddCommand(CMD_SHARED, "save", SV_Savegame_f, "save the game to a file");
1213 Cmd_AddCommand(CMD_SHARED, "viewmodel", SV_Viewmodel_f, "change model of viewthing entity in current level");
1214 Cmd_AddCommand(CMD_SHARED, "viewframe", SV_Viewframe_f, "change animation frame of viewthing entity in current level");
1215 Cmd_AddCommand(CMD_SHARED, "viewnext", SV_Viewnext_f, "change to next animation frame of viewthing entity in current level");
1216 Cmd_AddCommand(CMD_SHARED, "viewprev", SV_Viewprev_f, "change to previous animation frame of viewthing entity in current level");
1217 Cmd_AddCommand(CMD_SHARED, "maxplayers", SV_MaxPlayers_f, "sets limit on how many players (or bots) may be connected to the server at once");
1218 Cmd_AddCommand(CMD_SHARED, "user", SV_User_f, "prints additional information about a player number or name on the scoreboard");
1219 Cmd_AddCommand(CMD_SHARED, "users", SV_Users_f, "prints additional information about all players on the scoreboard");
1221 // 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)
1222 Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "prespawn", SV_PreSpawn_f, "internal use - signon 1 (client acknowledges that server information has been received)");
1223 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)");
1224 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)");
1225 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)");
1227 Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "god", SV_God_f, "god mode (invulnerability)");
1228 Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "notarget", SV_Notarget_f, "notarget mode (monsters do not see you)");
1229 Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "fly", SV_Fly_f, "fly mode (flight)");
1230 Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "noclip", SV_Noclip_f, "noclip mode (flight without collisions, move through walls)");
1231 Cmd_AddCommand(CMD_CHEAT | CMD_SERVER_FROM_CLIENT, "give", SV_Give_f, "alter inventory");
1232 Cmd_AddCommand(CMD_SERVER_FROM_CLIENT, "kill", SV_Kill_f, "die instantly");