mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-27 06:02:16 +00:00
dcec305bd9
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@2519 fc73d0e0-1445-4013-8a0c-d673dee63da5
2029 lines
40 KiB
C
2029 lines
40 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
#include "qwsvdef.h"
|
|
|
|
#ifndef CLIENTONLY
|
|
|
|
#ifndef INVALID_SOCKET
|
|
#define INVALID_SOCKET -1
|
|
#endif
|
|
|
|
|
|
|
|
qboolean sv_allow_cheats;
|
|
|
|
extern cvar_t cl_warncmd;
|
|
cvar_t sv_cheats = SCVARF("sv_cheats", "0", CVAR_LATCH);
|
|
extern redirect_t sv_redirected;
|
|
|
|
extern cvar_t sv_public;
|
|
|
|
|
|
//generic helper function for naming players.
|
|
client_t *SV_GetClientForString(char *name, int *id)
|
|
{
|
|
int i;
|
|
char *s, *s2;
|
|
char nicename[80];
|
|
client_t *cl;
|
|
|
|
int first=0;
|
|
if (id && *id != -1)
|
|
first = *id;
|
|
|
|
if (!strcmp(name, "*")) //match with all
|
|
{
|
|
for (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++)
|
|
{
|
|
if (cl->state<=cs_zombie)
|
|
continue;
|
|
|
|
*id=i+1;
|
|
return cl;
|
|
}
|
|
*id=sv.allocated_client_slots;
|
|
return NULL;
|
|
}
|
|
|
|
//check to make sure it's all an int
|
|
|
|
for (s = name; *s; s++)
|
|
{
|
|
if (*s < '0' || *s > '9')
|
|
break;
|
|
}
|
|
|
|
//we got to the end of the string and found only numbers. - it's a uid.
|
|
if (!*s)
|
|
{
|
|
int uid = Q_atoi(name);
|
|
for (i = first, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
|
|
{
|
|
if (cl->state<=cs_zombie)
|
|
continue;
|
|
if (cl->userid == uid)
|
|
{
|
|
if (id)
|
|
*id=sv.allocated_client_slots;
|
|
return cl;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
for (i = first, cl = svs.clients+first; i < sv.allocated_client_slots; i++, cl++)
|
|
{
|
|
if (cl->state<=cs_zombie)
|
|
continue;
|
|
|
|
|
|
s = nicename;
|
|
s2 = cl->name;
|
|
while(*s2)
|
|
{
|
|
*s = *s2 & ~128;
|
|
s2++;
|
|
if (*s == '3')
|
|
*s = 'e';
|
|
else if (*s == '4')
|
|
*s = 'a';
|
|
else if (*s == '1' || *s == '7')
|
|
*s = 'l';
|
|
else if (*s >= 18 && *s < 27)
|
|
*s = *s - 18 + '0';
|
|
else if (*s >= 'A' && *s <= 'Z')
|
|
*s = *s - 'A' + 'a';
|
|
else if (*s<' ' || *s == '~')
|
|
continue;
|
|
|
|
s++;
|
|
}
|
|
*s = '\0';
|
|
|
|
if (strstr(nicename, name))
|
|
{
|
|
if (id)
|
|
*id=i+1;
|
|
return cl;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
OPERATOR CONSOLE ONLY COMMANDS
|
|
|
|
These commands can only be entered from stdin or by a remote operator datagram
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
====================
|
|
SV_SetMaster_f
|
|
|
|
Make a master server current
|
|
====================
|
|
*/
|
|
void Master_ClearAll(void);
|
|
void Master_Add(char *stringadr);
|
|
|
|
void SV_SetMaster_f (void)
|
|
{
|
|
int i;
|
|
|
|
Cvar_Set(&sv_public, "1"); //go public.
|
|
|
|
Master_ClearAll();
|
|
|
|
if (!strcmp(Cmd_Argv(1), "none"))
|
|
{
|
|
Con_Printf ("Entering no-master mode\n");
|
|
return;
|
|
}
|
|
|
|
for (i=1 ; i<Cmd_Argc() ; i++)
|
|
{
|
|
Master_Add(Cmd_Argv(i));
|
|
}
|
|
|
|
svs.last_heartbeat = -99999;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_Quit_f
|
|
==================
|
|
*/
|
|
void SV_Quit_f (void)
|
|
{
|
|
SV_FinalMessage ("server shutdown\n");
|
|
Con_TPrintf (STL_SHUTTINGDOWN);
|
|
SV_Shutdown ();
|
|
Sys_Quit ();
|
|
}
|
|
|
|
/*
|
|
============
|
|
SV_Logfile_f
|
|
============
|
|
*/
|
|
void SV_Logfile_f (void)
|
|
{
|
|
extern cvar_t log_enable, log_dir, log_name;
|
|
extern char gamedirfile[];
|
|
|
|
if (log_enable.value)
|
|
{
|
|
Cvar_SetValue(&log_enable, 0);
|
|
Con_Printf("Logging disabled.\n");
|
|
}
|
|
else
|
|
{
|
|
char *d, *f;
|
|
|
|
d = gamedirfile;
|
|
if (log_dir.string[0])
|
|
d = log_dir.string;
|
|
|
|
f = "qconsole";
|
|
if (log_name.string[0])
|
|
f = log_name.string;
|
|
|
|
Con_Printf(va("Logging to %s/%s.log.\n", d, f));
|
|
Cvar_SetValue(&log_enable, 1);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
============
|
|
SV_Fraglogfile_f
|
|
============
|
|
*/
|
|
void SV_Fraglogfile_f (void)
|
|
{
|
|
char name[MAX_OSPATH];
|
|
int i;
|
|
|
|
if (sv_fraglogfile)
|
|
{
|
|
Con_TPrintf (STL_FLOGGINGOFF);
|
|
VFS_CLOSE (sv_fraglogfile);
|
|
sv_fraglogfile = NULL;
|
|
return;
|
|
}
|
|
|
|
// find an unused name
|
|
for (i=0 ; i<1000 ; i++)
|
|
{
|
|
sprintf (name, "frag_%i.log", i);
|
|
sv_fraglogfile = FS_OpenVFS(name, "rb", FS_GAME);
|
|
if (!sv_fraglogfile)
|
|
{ // can't read it, so create this one
|
|
sv_fraglogfile = FS_OpenVFS (name, "wb", FS_GAME);
|
|
if (!sv_fraglogfile)
|
|
i=1000; // give error
|
|
break;
|
|
}
|
|
VFS_CLOSE (sv_fraglogfile);
|
|
}
|
|
if (i==1000)
|
|
{
|
|
Con_TPrintf (STL_FLOGGINGFAILED);
|
|
sv_fraglogfile = NULL;
|
|
return;
|
|
}
|
|
|
|
Con_TPrintf (STL_FLOGGINGTO, name);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
SV_SetPlayer
|
|
|
|
Sets host_client and sv_player to the player with idnum Cmd_Argv(1)
|
|
==================
|
|
*/
|
|
qboolean SV_SetPlayer (void)
|
|
{
|
|
client_t *cl;
|
|
int i;
|
|
int idnum;
|
|
|
|
idnum = atoi(Cmd_Argv(1));
|
|
|
|
for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
|
|
{
|
|
if (!cl->state)
|
|
continue;
|
|
if (cl->userid == idnum)
|
|
{
|
|
host_client = cl;
|
|
sv_player = host_client->edict;
|
|
return true;
|
|
}
|
|
}
|
|
Con_TPrintf (STL_USERIDNOTONSERVER, idnum);
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
SV_God_f
|
|
|
|
Sets client to godmode
|
|
==================
|
|
*/
|
|
void SV_God_f (void)
|
|
{
|
|
if (!sv_allow_cheats)
|
|
{
|
|
Con_TPrintf (STL_NEEDCHEATPARM);
|
|
return;
|
|
}
|
|
|
|
if (!SV_SetPlayer ())
|
|
return;
|
|
|
|
sv_player->v->flags = (int)sv_player->v->flags ^ FL_GODMODE;
|
|
if ((int)sv_player->v->flags & FL_GODMODE)
|
|
SV_ClientTPrintf (host_client, PRINT_HIGH, STL_GODON);
|
|
else
|
|
SV_ClientTPrintf (host_client, PRINT_HIGH, STL_GODOFF);
|
|
}
|
|
|
|
|
|
void SV_Noclip_f (void)
|
|
{
|
|
if (!sv_allow_cheats)
|
|
{
|
|
Con_TPrintf (STL_NEEDCHEATPARM);
|
|
return;
|
|
}
|
|
|
|
if (!SV_SetPlayer ())
|
|
return;
|
|
|
|
if (sv_player->v->movetype != MOVETYPE_NOCLIP)
|
|
{
|
|
sv_player->v->movetype = MOVETYPE_NOCLIP;
|
|
SV_ClientTPrintf (host_client, PRINT_HIGH, STL_NOCLIPON);
|
|
}
|
|
else
|
|
{
|
|
sv_player->v->movetype = MOVETYPE_WALK;
|
|
SV_ClientTPrintf (host_client, PRINT_HIGH, STL_NOCLIPOFF);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
SV_Give_f
|
|
==================
|
|
*/
|
|
void SV_Give_f (void)
|
|
{
|
|
char *t;
|
|
int v;
|
|
|
|
if (!sv_allow_cheats)
|
|
{
|
|
Con_TPrintf (STL_NEEDCHEATPARM);
|
|
return;
|
|
}
|
|
|
|
if (!SV_SetPlayer ())
|
|
{
|
|
int oldself;
|
|
oldself = pr_global_struct->self;
|
|
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);
|
|
Con_Printf("Result: %s\n", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args()));
|
|
pr_global_struct->self = oldself;
|
|
return;
|
|
}
|
|
|
|
if (!svprogfuncs)
|
|
return;
|
|
|
|
t = Cmd_Argv(2);
|
|
v = atoi (Cmd_Argv(3));
|
|
|
|
switch (t[0])
|
|
{
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
sv_player->v->items = (int)sv_player->v->items | IT_SHOTGUN<< (t[0] - '2');
|
|
break;
|
|
|
|
case 's':
|
|
sv_player->v->ammo_shells = v;
|
|
break;
|
|
case 'n':
|
|
sv_player->v->ammo_nails = v;
|
|
break;
|
|
case 'r':
|
|
sv_player->v->ammo_rockets = v;
|
|
break;
|
|
case 'h':
|
|
sv_player->v->health = v;
|
|
break;
|
|
case 'c':
|
|
sv_player->v->ammo_cells = v;
|
|
break;
|
|
default:
|
|
{
|
|
int oldself;
|
|
oldself = pr_global_struct->self;
|
|
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);
|
|
Con_Printf("Result: %s\n", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args()));
|
|
pr_global_struct->self = oldself;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ShowMapList (char *name, int flags, void *parm)
|
|
{
|
|
if (name[5] == 'b' && name[6] == '_') //skip box models
|
|
return true;
|
|
Con_Printf("%s\n", name+5);
|
|
return true;
|
|
}
|
|
void SV_MapList_f(void)
|
|
{
|
|
COM_EnumerateFiles("maps/*.bsp", ShowMapList, NULL);
|
|
}
|
|
|
|
void gtcallback(struct cvar_s *var, char *oldvalue)
|
|
{
|
|
Con_Printf("g_gametype changed\n");
|
|
}
|
|
|
|
/*
|
|
======================
|
|
SV_Map_f
|
|
|
|
handle a
|
|
map <mapname>
|
|
command from the console or progs.
|
|
======================
|
|
*/
|
|
void SV_Map_f (void)
|
|
{
|
|
char level[MAX_QPATH];
|
|
char spot[MAX_QPATH];
|
|
char expanded[MAX_QPATH];
|
|
char *nextserver;
|
|
qboolean issamelevel = false;
|
|
qboolean newunit = false;
|
|
qboolean cinematic = false;
|
|
qboolean waschangelevel = false;
|
|
qboolean wasspmap = false;
|
|
int i;
|
|
char *startspot;
|
|
|
|
#ifndef SERVERONLY
|
|
if (qrenderer == -1)
|
|
{
|
|
Cbuf_AddText(va("wait;map %s\n", Cmd_Args()), Cmd_ExecLevel);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (Cmd_Argc() != 2 && Cmd_Argc() != 3)
|
|
{
|
|
Con_TPrintf (STL_MAPCOMMANDUSAGE);
|
|
return;
|
|
}
|
|
|
|
sv.mapchangelocked = false;
|
|
|
|
Q_strncpyz (level, Cmd_Argv(1), sizeof(level));
|
|
startspot = ((Cmd_Argc() == 2)?NULL:Cmd_Argv(2));
|
|
|
|
waschangelevel = !strcmp(Cmd_Argv(0), "changelevel");
|
|
wasspmap = !strcmp(Cmd_Argv(0), "spmap");
|
|
|
|
nextserver = strchr(level, '+');
|
|
if (nextserver)
|
|
{
|
|
*nextserver = '\0';
|
|
nextserver++;
|
|
}
|
|
|
|
if (startspot)
|
|
{
|
|
strcpy(spot, startspot);
|
|
startspot = spot;
|
|
}
|
|
else if ((startspot = strchr(level, '$')))
|
|
{
|
|
strcpy(spot, startspot+1);
|
|
*startspot = '\0';
|
|
startspot = spot;
|
|
}
|
|
else
|
|
startspot = NULL;
|
|
|
|
if (!strcmp(level, ".")) //restart current
|
|
{
|
|
COM_StripExtension(COM_SkipPath(sv.modelname), level, sizeof(level));
|
|
issamelevel = true;
|
|
|
|
Q_strncpyz(spot, Info_ValueForKey(svs.info, "*startspot"), sizeof(spot));
|
|
startspot = spot;
|
|
}
|
|
|
|
// check to make sure the level exists
|
|
if (*level == '*')
|
|
{
|
|
memmove(level, level+1, strlen(level));
|
|
newunit=true;
|
|
}
|
|
#ifndef SERVERONLY
|
|
SCR_ImageName(level);
|
|
#endif
|
|
|
|
COM_FlushFSCache();
|
|
|
|
if (strlen(level) > 4 && !strcmp(level + strlen(level)-4, ".cin"))
|
|
{
|
|
cinematic = true;
|
|
}
|
|
else
|
|
{
|
|
snprintf (expanded, sizeof(expanded), "maps/%s.bsp", level);
|
|
if (!COM_FCheckExists (expanded))
|
|
{
|
|
//doesn't exist, so try lowercase. Q3 does this.
|
|
for (i = 0; i < sizeof(level); i++)
|
|
{
|
|
if (level[i] >= 'A' && level[i] <= 'Z')
|
|
level[i] = level[i] - 'A' + 'a';
|
|
}
|
|
snprintf (expanded, sizeof(expanded), "maps/%s.bsp", level);
|
|
if (!COM_FCheckExists (expanded))
|
|
{
|
|
Con_TPrintf (STL_CANTFINDMAP, expanded);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sv.mvdrecording)
|
|
SV_MVDStop_f();
|
|
|
|
#ifndef SERVERONLY
|
|
if (!isDedicated) //otherwise, info used on map loading isn't present
|
|
Cmd_ExecuteString(va("fullserverinfo \"%s\"\n", svs.info), RESTRICT_SERVER);
|
|
|
|
if (!sv.state && cls.state)
|
|
CL_Disconnect();
|
|
#endif
|
|
|
|
SV_SaveSpawnparms (issamelevel);
|
|
|
|
if (startspot && !issamelevel && !newunit)
|
|
{
|
|
#ifdef Q2SERVER
|
|
if (ge)
|
|
{
|
|
qboolean savedinuse[MAX_CLIENTS];
|
|
for (i=0 ; i<sv.allocated_client_slots; i++)
|
|
{
|
|
savedinuse[i] = svs.clients[i].q2edict->inuse;
|
|
svs.clients[i].q2edict->inuse = false;
|
|
}
|
|
SV_SaveLevelCache(false);
|
|
for (i=0 ; i<sv.allocated_client_slots; i++)
|
|
{
|
|
svs.clients[i].q2edict->inuse = savedinuse[i];
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
SV_SaveLevelCache(false);
|
|
}
|
|
|
|
#ifdef Q3SERVER
|
|
{
|
|
cvar_t *gametype;
|
|
|
|
gametype = Cvar_Get("mapname", "0", CVAR_LATCH|CVAR_SERVERINFO, "Q3 compatability");
|
|
gametype->flags |= CVAR_SERVERINFO;
|
|
Cvar_ForceSet(gametype, level);
|
|
|
|
gametype = Cvar_Get("g_gametype", "0", CVAR_LATCH|CVAR_SERVERINFO, "Q3 compatability");
|
|
gametype->callback = gtcallback;
|
|
if (wasspmap)
|
|
Cvar_ForceSet(gametype, "2");//singleplayer
|
|
else if (gametype->value == 2)
|
|
Cvar_ForceSet(gametype, "0");//force to ffa deathmatch
|
|
}
|
|
#endif
|
|
|
|
for (i=0 ; i<MAX_CLIENTS ; i++) //we need to drop all q2 clients. We don't mix q1w with q2.
|
|
{
|
|
if (svs.clients[i].state>cs_connected) //so that we don't send a datagram
|
|
svs.clients[i].state=cs_connected;
|
|
}
|
|
|
|
#ifndef SERVERONLY
|
|
S_StopAllSounds (true);
|
|
// SCR_BeginLoadingPlaque();
|
|
SCR_ImageName(level);
|
|
#endif
|
|
|
|
SV_BroadcastCommand ("changing \"%s\"\n", level);
|
|
SV_SendMessagesToAll ();
|
|
|
|
if (newunit || !startspot || !SV_LoadLevelCache(level, startspot, false))
|
|
{
|
|
if (waschangelevel && !startspot)
|
|
startspot = "";
|
|
SV_SpawnServer (level, startspot, false, cinematic);
|
|
}
|
|
|
|
//SV_BroadcastCommand ("cmd new\n");
|
|
for (i=0, host_client = svs.clients ; i<MAX_CLIENTS ; i++, host_client++)
|
|
{ //this expanded code cuts out a packet when changing maps...
|
|
//but more usefully, it stops dp(and probably nq too) from timing out.
|
|
if (host_client->controller)
|
|
continue;
|
|
if (host_client->state>=cs_connected)
|
|
{
|
|
if (ISNQCLIENT(host_client))
|
|
SVNQ_New_f();
|
|
else
|
|
SV_New_f();
|
|
}
|
|
}
|
|
|
|
if (!issamelevel)
|
|
{
|
|
cvar_t *nsv;
|
|
nsv = Cvar_Get("nextserver", "", 0, "");
|
|
if (nextserver)
|
|
Cvar_Set(nsv, va("gamemap \"%s\"", nextserver));
|
|
else
|
|
Cvar_Set(nsv, "");
|
|
}
|
|
}
|
|
|
|
void SV_KillServer_f(void)
|
|
{
|
|
SV_UnspawnServer();
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
SV_Kick_f
|
|
|
|
Kick a user off of the server
|
|
==================
|
|
*/
|
|
void SV_Kick_f (void)
|
|
{
|
|
client_t *cl;
|
|
int clnum=-1;
|
|
|
|
while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
|
|
{
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASKICKED, cl->name);
|
|
// print directly, because the dropped client won't get the
|
|
// SV_BroadcastPrintf message
|
|
SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREKICKED);
|
|
SV_DropClient (cl);
|
|
}
|
|
|
|
if (clnum == -1)
|
|
Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1));
|
|
}
|
|
|
|
void SV_BanName_f (void)
|
|
{
|
|
client_t *cl;
|
|
int clnum=-1;
|
|
char *reason = NULL;
|
|
int reasonsize = 0;
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("%s userid|nick [reason]\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() > 2)
|
|
{
|
|
reason = Cmd_Argv(2);
|
|
reasonsize = strlen(reason);
|
|
}
|
|
|
|
while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
|
|
if (cl)
|
|
{
|
|
bannedips_t *nb;
|
|
|
|
if (NET_IsLoopBackAddress(cl->netchan.remote_address))
|
|
{
|
|
Con_Printf("You're not allowed to ban loopback!\n");
|
|
continue;
|
|
}
|
|
|
|
nb = Z_Malloc(sizeof(bannedips_t)+reasonsize);
|
|
nb->next = svs.bannedips;
|
|
nb->adr = cl->netchan.remote_address;
|
|
NET_IntegerToMask(&nb->adr, &nb->adrmask, -1); // fill mask
|
|
if (*Cmd_Argv(2)) //explicit blocking of all ports of a client ip
|
|
nb->adr.port = 0;
|
|
svs.bannedips = nb;
|
|
if (reasonsize)
|
|
Q_strcpy(nb->reason, reason);
|
|
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name);
|
|
// print directly, because the dropped client won't get the
|
|
// SV_BroadcastPrintf message
|
|
SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREBANNED);
|
|
SV_DropClient (cl);
|
|
}
|
|
|
|
if (clnum == -1)
|
|
Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1));
|
|
}
|
|
|
|
void SV_BanIP_f (void)
|
|
{
|
|
netadr_t banadr;
|
|
netadr_t banmask;
|
|
int i;
|
|
client_t *cl;
|
|
bannedips_t *nb;
|
|
char *reason = NULL;
|
|
int reasonsize = 0;
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("%s address/mask|adress/maskbits [reason]\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
if (!NET_StringToAdrMasked(Cmd_Argv(1), &banadr, &banmask))
|
|
{
|
|
Con_Printf("invalid address or mask\n");
|
|
return;
|
|
}
|
|
|
|
if (NET_IsLoopBackAddress(banadr))
|
|
{
|
|
Con_Printf("You're not allowed to ban loopback!\n");
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() > 2)
|
|
{
|
|
reason = Cmd_Argv(2);
|
|
reasonsize = strlen(reason);
|
|
}
|
|
|
|
// loop through clients and kick the ones that match
|
|
for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
|
|
{
|
|
if (cl->state<=cs_zombie)
|
|
continue;
|
|
|
|
if (NET_CompareAdrMasked(cl->netchan.remote_address, banadr, banmask))
|
|
{
|
|
// match, so kick
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name);
|
|
// print directly, because the dropped client won't get the
|
|
// SV_BroadcastPrintf message
|
|
SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUWEREBANNED);
|
|
SV_DropClient (cl);
|
|
}
|
|
}
|
|
|
|
// add IP and mask to ban list
|
|
nb = Z_Malloc(sizeof(bannedips_t)+reasonsize);
|
|
nb->next = svs.bannedips;
|
|
nb->adr = banadr;
|
|
nb->adrmask = banmask;
|
|
svs.bannedips = nb;
|
|
if (reasonsize)
|
|
Q_strcpy(nb->reason, reason);
|
|
}
|
|
|
|
void SV_FilterIP_f (void)
|
|
{
|
|
netadr_t banadr;
|
|
netadr_t banmask;
|
|
int i;
|
|
client_t *cl;
|
|
filteredips_t *nb;
|
|
extern cvar_t filterban;
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("%s address/mask|adress/maskbits\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
if (!NET_StringToAdrMasked(Cmd_Argv(1), &banadr, &banmask))
|
|
{
|
|
Con_Printf("invalid address or mask\n");
|
|
return;
|
|
}
|
|
|
|
if (NET_IsLoopBackAddress(banadr))
|
|
{
|
|
Con_Printf("You're not allowed to filter loopback!\n");
|
|
return;
|
|
}
|
|
|
|
// loop through clients and kick the ones that match
|
|
for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
|
|
{
|
|
if (cl->state<=cs_zombie)
|
|
continue;
|
|
|
|
if (filterban.value && NET_CompareAdrMasked(cl->netchan.remote_address, banadr, banmask))
|
|
SV_DropClient (cl);
|
|
}
|
|
|
|
// add IP and mask to filter list
|
|
nb = Z_Malloc(sizeof(filteredips_t));
|
|
nb->next = svs.filteredips;
|
|
nb->adr = banadr;
|
|
nb->adrmask = banmask;
|
|
svs.filteredips = nb;
|
|
}
|
|
|
|
void SV_BanList_f (void)
|
|
{
|
|
int bancount = 0;
|
|
bannedips_t *nb = svs.bannedips;
|
|
|
|
while (nb)
|
|
{
|
|
if (nb->reason[0])
|
|
Con_Printf("%s, %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask), nb->reason);
|
|
else
|
|
Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
|
|
bancount++;
|
|
nb = nb->next;
|
|
}
|
|
|
|
Con_Printf("%i total entries in ban list\n", bancount);
|
|
}
|
|
|
|
void SV_FilterList_f (void)
|
|
{
|
|
int filtercount = 0;
|
|
filteredips_t *nb = svs.filteredips;
|
|
|
|
while (nb)
|
|
{
|
|
Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
|
|
filtercount++;
|
|
nb = nb->next;
|
|
}
|
|
|
|
Con_Printf("%i total entries in filter list\n", filtercount);
|
|
}
|
|
|
|
void SV_Unban_f (void)
|
|
{
|
|
qboolean all = false;
|
|
bannedips_t *nb = svs.bannedips;
|
|
bannedips_t *nbnext;
|
|
netadr_t unbanadr = {0};
|
|
netadr_t unbanmask = {0};
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("%s address/mask|address/maskbits|all\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
if (!Q_strcasecmp(Cmd_Argv(1), "all"))
|
|
all = true;
|
|
else if (!NET_StringToAdrMasked(Cmd_Argv(1), &unbanadr, &unbanmask))
|
|
{
|
|
Con_Printf("invalid address or mask\n");
|
|
return;
|
|
}
|
|
|
|
while (nb)
|
|
{
|
|
nbnext = nb->next;
|
|
if (all || (NET_CompareAdr(nb->adr, unbanadr) && NET_CompareAdr(nb->adrmask, unbanmask)))
|
|
{
|
|
if (!all)
|
|
Con_Printf("unbanned %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
|
|
if (svs.bannedips == nb)
|
|
svs.bannedips = nbnext;
|
|
Z_Free(nb);
|
|
break;
|
|
}
|
|
|
|
nb = nbnext;
|
|
}
|
|
}
|
|
|
|
void SV_Unfilter_f (void)
|
|
{
|
|
qboolean all = false;
|
|
filteredips_t *nb = svs.filteredips;
|
|
filteredips_t *nbnext;
|
|
netadr_t unbanadr = {0};
|
|
netadr_t unbanmask = {0};
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("%s address/mask|address/maskbits|all\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
if (!Q_strcasecmp(Cmd_Argv(1), "all"))
|
|
all = true;
|
|
else if (!NET_StringToAdrMasked(Cmd_Argv(1), &unbanadr, &unbanmask))
|
|
{
|
|
Con_Printf("invalid address or mask\n");
|
|
return;
|
|
}
|
|
|
|
while (nb)
|
|
{
|
|
nbnext = nb->next;
|
|
if (all || (NET_CompareAdr(nb->adr, unbanadr) && NET_CompareAdr(nb->adrmask, unbanmask)))
|
|
{
|
|
if (!all)
|
|
Con_Printf("unfiltered %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
|
|
if (svs.filteredips == nb)
|
|
svs.filteredips = nbnext;
|
|
Z_Free(nb);
|
|
break;
|
|
}
|
|
|
|
nb = nbnext;
|
|
}
|
|
}
|
|
|
|
void SV_WriteIP_f (void)
|
|
{
|
|
vfsfile_t *f;
|
|
char name[MAX_OSPATH];
|
|
bannedips_t *bi;
|
|
filteredips_t *fi;
|
|
char *s;
|
|
|
|
strcpy (name, "listip.cfg");
|
|
|
|
Con_Printf ("Writing %s.\n", name);
|
|
|
|
f = FS_OpenVFS(name, "wb", FS_GAME);
|
|
if (!f)
|
|
{
|
|
Con_Printf ("Couldn't open %s\n", name);
|
|
return;
|
|
}
|
|
|
|
s = "// banned ip addresses\n";
|
|
VFS_WRITE(f, s, strlen(s));
|
|
|
|
bi = svs.bannedips;
|
|
while (bi)
|
|
{
|
|
if (bi->reason[0])
|
|
s = va("banip %s \"%s\"\n", NET_AdrToStringMasked(bi->adr, bi->adrmask), bi->reason);
|
|
else
|
|
s = va("banip %s\n", NET_AdrToStringMasked(bi->adr, bi->adrmask));
|
|
VFS_WRITE(f, s, strlen(s));
|
|
bi = bi->next;
|
|
}
|
|
|
|
s = "\n// filtered ip addresses\n";
|
|
VFS_WRITE(f, s, strlen(s));
|
|
|
|
fi = svs.filteredips;
|
|
while (fi)
|
|
{
|
|
s = va("addip %s\n", NET_AdrToStringMasked(fi->adr, fi->adrmask));
|
|
VFS_WRITE(f, s, strlen(s));
|
|
fi = fi->next;
|
|
}
|
|
|
|
VFS_CLOSE (f);
|
|
}
|
|
|
|
|
|
void SV_ForceName_f (void)
|
|
{
|
|
client_t *cl;
|
|
int clnum=-1;
|
|
int i;
|
|
|
|
while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
|
|
{
|
|
Info_SetValueForKey(cl->userinfo, "name", Cmd_Argv(2), MAX_INFO_STRING);
|
|
SV_ExtractFromUserinfo(cl);
|
|
Q_strncpyz(cl->name, Cmd_Argv(2), sizeof(cl->namebuf));
|
|
i = cl - svs.clients;
|
|
MSG_WriteByte (&sv.reliable_datagram, svc_setinfo);
|
|
MSG_WriteByte (&sv.reliable_datagram, i);
|
|
MSG_WriteString (&sv.reliable_datagram, "name");
|
|
MSG_WriteString (&sv.reliable_datagram, cl->name);
|
|
|
|
return;
|
|
}
|
|
|
|
if (clnum == -1)
|
|
Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1));
|
|
}
|
|
|
|
void SV_CripplePlayer_f (void)
|
|
{
|
|
client_t *cl;
|
|
int clnum=-1;
|
|
|
|
int persist = *Cmd_Argv(2);
|
|
|
|
while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
|
|
{
|
|
if (!cl->iscrippled)
|
|
{
|
|
if (persist)
|
|
{
|
|
cl->iscrippled = 2;
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCRIPPLEDPERMANENTLY, cl->name);
|
|
}
|
|
else
|
|
{
|
|
cl->iscrippled = true;
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCRIPPLED, cl->name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cl->iscrippled = false;
|
|
SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUARNTCRIPPLED);
|
|
}
|
|
}
|
|
|
|
if (clnum == -1)
|
|
Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1));
|
|
}
|
|
|
|
void SV_Mute_f (void)
|
|
{
|
|
client_t *cl;
|
|
int clnum=-1;
|
|
|
|
int persist = *Cmd_Argv(2);
|
|
|
|
while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
|
|
{
|
|
if (!cl->ismuted)
|
|
{
|
|
if (persist)
|
|
{
|
|
cl->ismuted = 2;
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISMUTEDPERMANENTLY, cl->name);
|
|
}
|
|
else
|
|
{
|
|
cl->ismuted = true;
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISMUTED, cl->name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cl->ismuted = false;
|
|
SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUARNTMUTED);
|
|
}
|
|
}
|
|
|
|
if (clnum == -1)
|
|
Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1));
|
|
}
|
|
|
|
void SV_Cuff_f (void)
|
|
{
|
|
client_t *cl;
|
|
int clnum=-1;
|
|
|
|
int persist = *Cmd_Argv(2);
|
|
|
|
while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
|
|
{
|
|
if (!cl->iscuffed)
|
|
{
|
|
if (persist)
|
|
{
|
|
cl->iscuffed = 2;
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCUFFEDPERMANENTLY, cl->name);
|
|
}
|
|
else
|
|
{
|
|
cl->iscuffed = true;
|
|
SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTISCUFFED, cl->name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cl->iscuffed = false;
|
|
SV_ClientTPrintf (cl, PRINT_HIGH, STL_YOUARNTCUFFED);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (clnum == -1)
|
|
Con_TPrintf (STL_USERDOESNTEXIST, Cmd_Argv(1));
|
|
}
|
|
|
|
void SV_Floodprot_f(void)
|
|
{
|
|
extern cvar_t sv_floodprotect;
|
|
extern cvar_t sv_floodprotect_messages;
|
|
extern cvar_t sv_floodprotect_interval;
|
|
extern cvar_t sv_floodprotect_silencetime;
|
|
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
if (sv_floodprotect_messages.value <= 0 || !sv_floodprotect.value)
|
|
Con_Printf("Flood protection is off.\n");
|
|
else
|
|
Con_Printf("Current flood protection settings: \nAfter %g msgs for %g seconds, silence for %g seconds\n",
|
|
sv_floodprotect_messages.value,
|
|
sv_floodprotect_interval.value,
|
|
sv_floodprotect_silencetime.value);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() != 4)
|
|
{
|
|
Con_Printf("Usage: %s <messagerate> <ratepersecond> <silencetime>\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
Cvar_SetValue(&sv_floodprotect_messages, atof(Cmd_Argv(1)));
|
|
Cvar_SetValue(&sv_floodprotect_interval, atof(Cmd_Argv(2)));
|
|
Cvar_SetValue(&sv_floodprotect_silencetime, atof(Cmd_Argv(3)));
|
|
}
|
|
|
|
void SV_StuffToClient_f(void)
|
|
{ //with this we emulate the progs 'stuffcmds' builtin
|
|
|
|
client_t *cl;
|
|
|
|
int clnum=-1;
|
|
char *clientname = Cmd_Argv(1);
|
|
char *str;
|
|
char *c;
|
|
char *key;
|
|
|
|
Cmd_ShiftArgs(1, Cmd_ExecLevel==RESTRICT_LOCAL);
|
|
if (!strcmp(Cmd_Argv(1), "bind"))
|
|
{
|
|
key = Z_Malloc(strlen(Cmd_Argv(2))+1);
|
|
strcpy(key, Cmd_Argv(2));
|
|
Cmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL);
|
|
}
|
|
else
|
|
key = NULL;
|
|
str = Cmd_Args();
|
|
|
|
while(*str <= ' ') //strim leading spaces
|
|
{
|
|
if (!*str)
|
|
break;
|
|
str++;
|
|
}
|
|
|
|
//a list of safe, allowed commands. Allows any extention of this.
|
|
if (strchr(str, '\n') || strchr(str, ';') || (
|
|
!strncmp(str, "setinfo", 7) &&
|
|
!strncmp(str, "quit", 4) &&
|
|
!strncmp(str, "gl_fb", 5) &&
|
|
!strncmp(str, "r_fb", 4) &&
|
|
!strncmp(str, "say", 3) && //note that the say parsing could be useful here.
|
|
!strncmp(str, "echo", 4) &&
|
|
!strncmp(str, "name", 4) &&
|
|
!strncmp(str, "skin", 4) &&
|
|
!strncmp(str, "color", 5) &&
|
|
!strncmp(str, "cmd", 3) &&
|
|
!strncmp(str, "fov", 3) &&
|
|
!strncmp(str, "connect", 7) &&
|
|
!strncmp(str, "rate", 4) &&
|
|
!strncmp(str, "cd", 2) &&
|
|
!strncmp(str, "easyrecord", 10) &&
|
|
!strncmp(str, "leftisright", 11) &&
|
|
!strncmp(str, "menu_", 5) &&
|
|
!strncmp(str, "r_fullbright", 12) &&
|
|
!strncmp(str, "toggleconsole", 13) &&
|
|
!strncmp(str, "v_i", 3) && //idlescale vars
|
|
!strncmp(str, "bf", 2) &&
|
|
!strncmp(str, "+", 1) &&
|
|
!strncmp(str, "-", 1) &&
|
|
!strncmp(str, "impulse", 7) &&
|
|
1))
|
|
{
|
|
Con_Printf("You're not allowed to stuffcmd that\n");
|
|
|
|
if (key)
|
|
Z_Free(key);
|
|
return;
|
|
}
|
|
|
|
while((cl = SV_GetClientForString(clientname, &clnum)))
|
|
{
|
|
if (cl->protocol == SCP_QUAKE2)
|
|
ClientReliableWrite_Begin (cl, svcq2_stufftext, 3+strlen(str) + (key?strlen(key)+6:0));
|
|
else
|
|
ClientReliableWrite_Begin (cl, svc_stufftext, 3+strlen(str) + (key?strlen(key)+6:0));
|
|
|
|
if (key)
|
|
{
|
|
for (c = "bind "; *c; c++)
|
|
ClientReliableWrite_Byte (cl, *c);
|
|
|
|
for (c = key; *c; c++)
|
|
ClientReliableWrite_Byte (cl, *c);
|
|
|
|
ClientReliableWrite_Byte (cl, ' ');
|
|
}
|
|
|
|
for (c = str; *c; c++)
|
|
ClientReliableWrite_Byte (cl, *c);
|
|
ClientReliableWrite_Byte (cl, '\n');
|
|
ClientReliableWrite_Byte (cl, '\0');
|
|
}
|
|
|
|
if (key)
|
|
Z_Free(key);
|
|
}
|
|
|
|
/*
|
|
================
|
|
SV_Status_f
|
|
================
|
|
*/
|
|
void SV_Status_f (void)
|
|
{
|
|
int i, j, l;
|
|
client_t *cl;
|
|
float cpu, avg, pak;
|
|
char *s;
|
|
|
|
int columns = 80;
|
|
|
|
if (sv_redirected != RD_OBLIVION && (sv_redirected != RD_NONE
|
|
#ifndef SERVERONLY
|
|
|| (vid.width < 68*8 && qrenderer != QR_NONE)
|
|
#endif
|
|
))
|
|
columns = 40;
|
|
|
|
if (!sv.state)
|
|
{
|
|
if (net_local_sv_ipadr.type != NA_LOOPBACK)
|
|
Con_Printf ("ip address : %s\n",NET_AdrToString (net_local_sv_ipadr));
|
|
|
|
Con_Printf("Server is not running\n");
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc()>1)
|
|
columns = atoi(Cmd_Argv(1));
|
|
|
|
cpu = (svs.stats.latched_active+svs.stats.latched_idle);
|
|
if (cpu)
|
|
cpu = 100*svs.stats.latched_active/cpu;
|
|
avg = 1000*svs.stats.latched_active / STATFRAMES;
|
|
pak = (float)svs.stats.latched_packets/ STATFRAMES;
|
|
|
|
if (svs.socketip != INVALID_SOCKET && net_local_sv_ipadr.type != NA_LOOPBACK)
|
|
{
|
|
extern cvar_t pr_imitatemvdsv;
|
|
if (pr_imitatemvdsv.value)
|
|
Con_Printf ("net address : %s\n",NET_AdrToString (net_local_sv_ipadr));
|
|
else
|
|
Con_Printf ("ip address : %s\n",NET_AdrToString (net_local_sv_ipadr));
|
|
}
|
|
if (svs.socketip6 != INVALID_SOCKET && net_local_sv_ip6adr.type != NA_LOOPBACK)
|
|
Con_Printf ("ipv6 address : %s\n",NET_AdrToString (net_local_sv_ip6adr));
|
|
if (svs.socketipx != INVALID_SOCKET && net_local_sv_ipxadr.type != NA_LOOPBACK)
|
|
Con_Printf ("ipx address : %s\n",NET_AdrToString (net_local_sv_ipxadr));
|
|
#ifdef TCPCONNECT
|
|
if (svs.sockettcp != INVALID_SOCKET && net_local_sv_tcpipadr.type != NA_LOOPBACK)
|
|
Con_Printf ("tcp address : %s\n",NET_AdrToString (net_local_sv_tcpipadr));
|
|
#endif
|
|
|
|
Con_Printf ("cpu utilization : %3i%%\n",(int)cpu);
|
|
Con_Printf ("avg response time: %i ms\n",(int)avg);
|
|
Con_Printf ("packets/frame : %5.2f\n", pak); //not relevent as a limit.
|
|
|
|
// min fps lat drp
|
|
if (columns < 80)
|
|
{
|
|
// most remote clients are 40 columns
|
|
// 0123456789012345678901234567890123456789
|
|
Con_Printf ("name userid frags\n");
|
|
Con_Printf (" address rate ping drop\n");
|
|
Con_Printf (" ---------------- ---- ---- -----\n");
|
|
for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
|
|
{
|
|
if (!cl->state)
|
|
continue;
|
|
|
|
Con_Printf ("%-16.16s ", cl->name);
|
|
|
|
Con_Printf ("%6i %5i", cl->userid, (int)cl->old_frags);
|
|
if (cl->spectator)
|
|
Con_Printf(" (s)\n");
|
|
else
|
|
Con_Printf("\n");
|
|
|
|
if (cl->istobeloaded && cl->state == cs_zombie)
|
|
s = "LoadZombie";
|
|
else if (cl->protocol == SCP_BAD)
|
|
s = "bot";
|
|
else
|
|
s = NET_BaseAdrToString ( cl->netchan.remote_address);
|
|
Con_Printf (" %-16.16s", s);
|
|
if (cl->state == cs_connected)
|
|
{
|
|
Con_Printf ("CONNECTING\n");
|
|
continue;
|
|
}
|
|
if (cl->state == cs_zombie)
|
|
{
|
|
Con_Printf ("ZOMBIE\n");
|
|
continue;
|
|
}
|
|
Con_Printf ("%4i %4i %5.2f\n"
|
|
, (int)(1000*cl->netchan.frame_rate)
|
|
, (int)SV_CalcPing (cl)
|
|
, 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Con_Printf ("frags userid address name rate ping drop qport dl%% dls\n");
|
|
Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- ----- --- ----\n");
|
|
for (i=0,cl=svs.clients ; i<MAX_CLIENTS ; i++,cl++)
|
|
{
|
|
if (!cl->state)
|
|
continue;
|
|
Con_Printf ("%5i %6i ", (int)cl->old_frags, cl->userid);
|
|
|
|
if (cl->istobeloaded && cl->state == cs_zombie)
|
|
s = "LoadZombie";
|
|
else if (cl->protocol == SCP_BAD)
|
|
s = "bot";
|
|
else
|
|
s = NET_BaseAdrToString ( cl->netchan.remote_address);
|
|
Con_Printf ("%s", s);
|
|
l = 16 - strlen(s);
|
|
for (j=0 ; j<l ; j++)
|
|
Con_Printf (" ");
|
|
|
|
Con_Printf ("%s", cl->name);
|
|
l = 16 - strlen(cl->name);
|
|
for (j=0 ; j<l ; j++)
|
|
Con_Printf (" ");
|
|
if (cl->state == cs_connected)
|
|
{
|
|
Con_Printf ("CONNECTING ");
|
|
}
|
|
else if (cl->state == cs_zombie)
|
|
{
|
|
Con_Printf ("ZOMBIE ");
|
|
}
|
|
else
|
|
Con_Printf ("%4i %4i %5.1f %4i"
|
|
, (int)(1000*cl->netchan.frame_rate)
|
|
, (int)SV_CalcPing (cl)
|
|
, 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence
|
|
, cl->netchan.qport);
|
|
if (cl->download)
|
|
{
|
|
Con_Printf (" %3i %4i", (cl->downloadcount*100)/cl->downloadsize, cl->downloadsize/1024);
|
|
}
|
|
if (cl->spectator)
|
|
Con_Printf(" (s)\n");
|
|
else
|
|
Con_Printf("\n");
|
|
|
|
|
|
}
|
|
}
|
|
Con_Printf ("\n");
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_ConSay_f
|
|
==================
|
|
*/
|
|
void SV_ConSay_f(void)
|
|
{
|
|
client_t *client;
|
|
int j;
|
|
char *p;
|
|
char text[1024];
|
|
|
|
if (Cmd_Argc () < 2)
|
|
return;
|
|
|
|
Q_strcpy (text, "console: ");
|
|
p = Cmd_Args();
|
|
|
|
if (*p == '"')
|
|
{
|
|
p++;
|
|
p[Q_strlen(p)-1] = 0;
|
|
}
|
|
|
|
Q_strcat(text, p);
|
|
|
|
for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++)
|
|
{
|
|
if (client->state == cs_free)
|
|
continue;
|
|
SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text);
|
|
}
|
|
|
|
if (sv.mvdrecording)
|
|
{
|
|
MVDWrite_Begin (dem_all, 0, strlen(text)+3);
|
|
MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_print);
|
|
MSG_WriteByte ((sizebuf_t*)demo.dbuf, PRINT_CHAT);
|
|
MSG_WriteString ((sizebuf_t*)demo.dbuf, text);
|
|
}
|
|
}
|
|
|
|
void SV_ConSayOne_f (void)
|
|
{
|
|
char text[2048];
|
|
client_t *to;
|
|
int i;
|
|
char *s;
|
|
int clnum=-1;
|
|
|
|
if (Cmd_Argc () < 3)
|
|
return;
|
|
|
|
while((to = SV_GetClientForString(Cmd_Argv(1), &clnum)))
|
|
{
|
|
Q_strcpy (text, "{console}: ");
|
|
|
|
for (i = 2; ; i++)
|
|
{
|
|
s = Cmd_Argv(i);
|
|
if (!*s)
|
|
break;
|
|
|
|
if (strlen(text) + strlen(s) + 2 >= sizeof(text)-1)
|
|
break;
|
|
strcat(text, " ");
|
|
strcat(text, s);
|
|
}
|
|
strcat(text, "\n");
|
|
SV_ClientPrintf(to, PRINT_CHAT, "%s", text);
|
|
}
|
|
if (!clnum)
|
|
Con_TPrintf(STL_USERDOESNTEXIST, Cmd_Argv(1));
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SV_Heartbeat_f
|
|
==================
|
|
*/
|
|
void SV_Heartbeat_f (void)
|
|
{
|
|
svs.last_heartbeat = -9999;
|
|
}
|
|
|
|
void SV_SendServerInfoChange(char *key, const char *value)
|
|
{
|
|
if (!sv.state)
|
|
return;
|
|
#ifdef Q2SERVER
|
|
if (ge)
|
|
return; //FIXME!!!
|
|
#endif
|
|
|
|
MSG_WriteByte (&sv.reliable_datagram, svc_serverinfo);
|
|
MSG_WriteString (&sv.reliable_datagram, key);
|
|
MSG_WriteString (&sv.reliable_datagram, value);
|
|
}
|
|
|
|
/*
|
|
===========
|
|
SV_Serverinfo_f
|
|
|
|
Examine or change the serverinfo string
|
|
===========
|
|
*/
|
|
char *CopyString(char *s);
|
|
extern char *Info_KeyForNumber(char *s, int num);
|
|
void SV_Serverinfo_f (void)
|
|
{
|
|
cvar_t *var;
|
|
char value[512];
|
|
int i;
|
|
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
Con_TPrintf (STL_SERVERINFOSETTINGS);
|
|
Info_Print (svs.info);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() < 3)
|
|
{
|
|
Con_TPrintf (STL_SERVERINFOSYNTAX);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argv(1)[0] == '*')
|
|
{
|
|
if (!strcmp(Cmd_Argv(1), "*"))
|
|
if (!strcmp(Cmd_Argv(2), ""))
|
|
{ //clear it out
|
|
char *k;
|
|
for(i=0;;)
|
|
{
|
|
k = Info_KeyForNumber(svs.info, i);
|
|
if (!*k)
|
|
break; //no more.
|
|
else if (*k == '*')
|
|
i++; //can't remove * keys
|
|
else if ((var = Cvar_FindVar(k)) && var->flags&CVAR_SERVERINFO)
|
|
i++; //this one is a cvar.
|
|
else
|
|
Info_RemoveKey(svs.info, k); //we can remove this one though, so yay.
|
|
}
|
|
|
|
return;
|
|
}
|
|
Con_TPrintf (TL_STARKEYPROTECTED);
|
|
return;
|
|
}
|
|
Q_strncpyz(value, Cmd_Argv(2), sizeof(value));
|
|
value[sizeof(value)-1] = '\0';
|
|
for (i = 3; i < Cmd_Argc(); i++)
|
|
{
|
|
strncat(value, " ", sizeof(value)-1);
|
|
strncat(value, Cmd_Argv(i), sizeof(value)-1);
|
|
}
|
|
|
|
Info_SetValueForKey (svs.info, Cmd_Argv(1), value, MAX_SERVERINFO_STRING);
|
|
|
|
// if this is a cvar, change it too
|
|
var = Cvar_FindVar (Cmd_Argv(1));
|
|
if (var)
|
|
{
|
|
Cvar_Set(var, value);
|
|
/* Z_Free (var->string); // free the old value string
|
|
var->string = CopyString (value);
|
|
var->value = Q_atof (var->string);
|
|
*/ }
|
|
|
|
SV_SendServerInfoChange(Cmd_Argv(1), value);
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
SV_Serverinfo_f
|
|
|
|
Examine or change the serverinfo string
|
|
===========
|
|
*/
|
|
char *CopyString(char *s);
|
|
void SV_Localinfo_f (void)
|
|
{
|
|
char *old;
|
|
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
Con_TPrintf (STL_LOCALINFOSETTINGS);
|
|
Info_Print (localinfo);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() != 3)
|
|
{
|
|
Con_TPrintf (STL_LOCALINFOSYNTAX);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argv(1)[0] == '*')
|
|
{
|
|
if (!strcmp(Cmd_Argv(1), "*"))
|
|
if (!strcmp(Cmd_Argv(2), ""))
|
|
{ //clear it out
|
|
Info_RemoveNonStarKeys(localinfo);
|
|
return;
|
|
}
|
|
Con_TPrintf (TL_STARKEYPROTECTED);
|
|
return;
|
|
}
|
|
old = Info_ValueForKey(localinfo, Cmd_Argv(1));
|
|
Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING);
|
|
|
|
PR_LocalInfoChanged(Cmd_Argv(1), old, Cmd_Argv(2));
|
|
|
|
Con_DPrintf("Localinfo %s changed (%s -> %s)\n", Cmd_Argv(1), old, Cmd_Argv(2));
|
|
}
|
|
|
|
void SV_SaveInfos(vfsfile_t *f)
|
|
{
|
|
VFS_WRITE(f, "\n", 1);
|
|
VFS_WRITE(f, "serverinfo * \"\"\n", 16);
|
|
Info_WriteToFile(f, svs.info, "serverinfo", CVAR_SERVERINFO);
|
|
VFS_WRITE(f, "\n", 1);
|
|
VFS_WRITE(f, "localinfo * \"\"\n", 15);
|
|
Info_WriteToFile(f, localinfo, "localinfo", 0);
|
|
}
|
|
|
|
/*
|
|
void SV_ResetInfos(void)
|
|
{
|
|
// TODO: add me
|
|
}
|
|
*/
|
|
|
|
/*
|
|
===========
|
|
SV_User_f
|
|
|
|
Examine a users info strings
|
|
===========
|
|
*/
|
|
void SV_User_f (void)
|
|
{
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (STL_USERINFOSYNTAX);
|
|
return;
|
|
}
|
|
|
|
if (!SV_SetPlayer ())
|
|
return;
|
|
|
|
Info_Print (host_client->userinfo);
|
|
}
|
|
|
|
/*
|
|
================
|
|
SV_Floodport_f
|
|
|
|
Sets the gamedir and path to a different directory.
|
|
================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
SV_Gamedir
|
|
|
|
Sets the fake *gamedir to a different directory.
|
|
================
|
|
*/
|
|
void SV_Gamedir (void)
|
|
{
|
|
char *dir;
|
|
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
Con_TPrintf (STL_CURRENTGAMEDIR, Info_ValueForKey (svs.info, "*gamedir"));
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (STL_SVGAMEDIRUSAGE);
|
|
return;
|
|
}
|
|
|
|
dir = Cmd_Argv(1);
|
|
|
|
if (strstr(dir, "..") || strstr(dir, "/")
|
|
|| strstr(dir, "\\") || strstr(dir, ":") )
|
|
{
|
|
Con_TPrintf (STL_GAMEDIRCANTBEPATH);
|
|
return;
|
|
}
|
|
|
|
Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
|
|
}
|
|
|
|
/*
|
|
================
|
|
SV_Gamedir_f
|
|
|
|
Sets the gamedir and path to a different directory.
|
|
================
|
|
*/
|
|
void SV_Gamedir_f (void)
|
|
{
|
|
char *dir;
|
|
|
|
if (Cmd_Argc() == 1)
|
|
{
|
|
Con_TPrintf (STL_CURRENTGAMEDIR, com_gamedir);
|
|
return;
|
|
}
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (STL_GAMEDIRUSAGE);
|
|
return;
|
|
}
|
|
|
|
dir = Cmd_Argv(1);
|
|
|
|
if (strstr(dir, "..") || strstr(dir, "/")
|
|
|| strstr(dir, "\\") || strstr(dir, ":") )
|
|
{
|
|
Con_TPrintf (STL_GAMEDIRCANTBEPATH);
|
|
return;
|
|
}
|
|
|
|
COM_Gamedir (dir);
|
|
Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING);
|
|
}
|
|
|
|
|
|
extern char gamedirfile[MAX_OSPATH];
|
|
/*
|
|
================
|
|
SV_Snap
|
|
================
|
|
*/
|
|
void SV_Snap (int uid)
|
|
{
|
|
client_t *cl;
|
|
char pcxname[80];
|
|
char checkname[MAX_OSPATH];
|
|
int i;
|
|
|
|
for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
|
|
{
|
|
if (!cl->state)
|
|
continue;
|
|
if (cl->userid == uid)
|
|
break;
|
|
}
|
|
if (i >= MAX_CLIENTS) {
|
|
Con_TPrintf (STL_USERDOESNTEXIST);
|
|
return;
|
|
}
|
|
if (!ISQWCLIENT(cl))
|
|
{
|
|
Con_Printf("Can only snap QW clients\n");
|
|
return;
|
|
}
|
|
|
|
sprintf(pcxname, "%d-00.pcx", uid);
|
|
|
|
sprintf(checkname, "%s/snap", gamedirfile);
|
|
Sys_mkdir(gamedirfile);
|
|
Sys_mkdir(checkname);
|
|
|
|
for (i=0 ; i<=99 ; i++)
|
|
{
|
|
pcxname[strlen(pcxname) - 6] = i/10 + '0';
|
|
pcxname[strlen(pcxname) - 5] = i%10 + '0';
|
|
sprintf (checkname, "%s/snap/%s", gamedirfile, pcxname);
|
|
if (Sys_FileTime(checkname) == -1)
|
|
break; // file doesn't exist
|
|
}
|
|
if (i==100)
|
|
{
|
|
Con_TPrintf (STL_SNAPTOOMANYFILES);
|
|
return;
|
|
}
|
|
strcpy(cl->uploadfn, checkname);
|
|
|
|
memcpy(&cl->snap_from, &net_from, sizeof(net_from));
|
|
if (sv_redirected != RD_NONE)
|
|
cl->remote_snap = true;
|
|
else
|
|
cl->remote_snap = false;
|
|
|
|
ClientReliableWrite_Begin (cl, svc_stufftext, 24);
|
|
ClientReliableWrite_String (cl, "cmd snap\n");
|
|
Con_TPrintf (STL_SNAPREQUEST, uid);
|
|
}
|
|
|
|
/*
|
|
================
|
|
SV_Snap_f
|
|
================
|
|
*/
|
|
void SV_Snap_f (void)
|
|
{
|
|
int uid;
|
|
|
|
if (Cmd_Argc() != 2)
|
|
{
|
|
Con_TPrintf (STL_SNAPUSAGE);
|
|
return;
|
|
}
|
|
|
|
uid = atoi(Cmd_Argv(1));
|
|
|
|
SV_Snap(uid);
|
|
}
|
|
|
|
/*
|
|
================
|
|
SV_Snap
|
|
================
|
|
*/
|
|
void SV_SnapAll_f (void)
|
|
{
|
|
client_t *cl;
|
|
int i;
|
|
|
|
for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++)
|
|
{
|
|
if (cl->state < cs_connected || cl->spectator)
|
|
continue;
|
|
SV_Snap(cl->userid);
|
|
}
|
|
}
|
|
|
|
float mytimer;
|
|
float lasttimer;
|
|
int ticsleft;
|
|
float timerinterval;
|
|
int timerlevel;
|
|
cvar_t *timercommand;
|
|
void SV_CheckTimer(void)
|
|
{
|
|
float ctime = Sys_DoubleTime();
|
|
// if (ctime < lasttimer) //new map? (shouldn't happen)
|
|
// mytimer = ctime+5; //trigger in a few secs
|
|
lasttimer = ctime;
|
|
|
|
if (ticsleft)
|
|
{
|
|
if (mytimer < ctime)
|
|
{
|
|
mytimer += timerinterval;
|
|
if (ticsleft > 0)
|
|
ticsleft--;
|
|
|
|
if (timercommand)
|
|
{
|
|
Cbuf_AddText(timercommand->string, timerlevel);
|
|
Cbuf_AddText("\n", timerlevel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SV_SetTimer_f(void)
|
|
{
|
|
int count;
|
|
float interval;
|
|
char *command;
|
|
|
|
if (Cmd_Argc() < 2)
|
|
{
|
|
Con_Printf("%s <count> <interval> <command>\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
count = atoi(Cmd_Argv(1));
|
|
interval = atof(Cmd_Argv(2));
|
|
|
|
if (!count && Cmd_Argc() == 2)
|
|
{
|
|
ticsleft = 0;
|
|
return;
|
|
}
|
|
|
|
if (interval <= 0 || (count <= 0 && count != -1)) //makes sure the args are right. :)
|
|
{
|
|
Con_Printf("%s count interval command\n", Cmd_Argv(0));
|
|
return;
|
|
}
|
|
|
|
Cmd_ShiftArgs(2, Cmd_ExecLevel==RESTRICT_LOCAL); //strip the two vars
|
|
command = Cmd_Args();
|
|
|
|
timercommand = Cvar_Get("sv_timer", "", CVAR_NOSET, NULL);
|
|
Cvar_ForceSet(timercommand, command);
|
|
|
|
mytimer = Sys_DoubleTime() + interval;
|
|
ticsleft = count;
|
|
timerinterval = interval;
|
|
|
|
timerlevel = Cmd_ExecLevel;
|
|
}
|
|
|
|
void SV_SendGameCommand_f(void)
|
|
{
|
|
#ifdef Q3SERVER
|
|
if (SVQ3_ConsoleCommand())
|
|
return;
|
|
#endif
|
|
|
|
#ifdef Q2SERVER
|
|
if (ge)
|
|
{
|
|
ge->ServerCommand();
|
|
}
|
|
else
|
|
#endif
|
|
Con_Printf("This command requires a Q2 sever\n");
|
|
}
|
|
|
|
|
|
|
|
|
|
void PIN_LoadMessages(void);
|
|
void PIN_SaveMessages(void);
|
|
void PIN_DeleteOldestMessage(void);
|
|
void PIN_MakeMessage(char *from, char *msg);
|
|
|
|
void SV_Pin_Save_f(void)
|
|
{
|
|
PIN_SaveMessages();
|
|
}
|
|
void SV_Pin_Reload_f(void)
|
|
{
|
|
PIN_LoadMessages();
|
|
}
|
|
void SV_Pin_Delete_f(void)
|
|
{
|
|
PIN_DeleteOldestMessage();
|
|
}
|
|
void SV_Pin_Add_f(void)
|
|
{
|
|
PIN_MakeMessage(Cmd_Argv(1), Cmd_Argv(2));
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
SV_InitOperatorCommands
|
|
==================
|
|
*/
|
|
void SV_InitOperatorCommands (void)
|
|
{
|
|
#ifndef SERVERONLY
|
|
if (isDedicated)
|
|
#endif
|
|
{
|
|
Cmd_AddCommand ("quit", SV_Quit_f);
|
|
Cmd_AddCommand ("say", SV_ConSay_f);
|
|
Cmd_AddCommand ("sayone", SV_ConSayOne_f);
|
|
Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); //commands that conflict with client commands.
|
|
Cmd_AddCommand ("user", SV_User_f);
|
|
|
|
Cmd_AddCommand ("god", SV_God_f);
|
|
Cmd_AddCommand ("give", SV_Give_f);
|
|
Cmd_AddCommand ("noclip", SV_Noclip_f);
|
|
}
|
|
|
|
Cvar_Register(&sv_cheats, "Server Permissions");
|
|
if (COM_CheckParm ("-cheats"))
|
|
{
|
|
Cvar_Set(&sv_cheats, "1");
|
|
}
|
|
|
|
Cmd_AddCommand ("logfile", SV_Logfile_f);
|
|
Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f);
|
|
|
|
Cmd_AddCommand ("snap", SV_Snap_f);
|
|
Cmd_AddCommand ("snapall", SV_SnapAll_f);
|
|
Cmd_AddCommand ("kick", SV_Kick_f);
|
|
Cmd_AddCommand ("mute", SV_Mute_f);
|
|
Cmd_AddCommand ("cuff", SV_Cuff_f);
|
|
Cmd_AddCommand ("renameclient", SV_ForceName_f);
|
|
Cmd_AddCommand ("cripple", SV_CripplePlayer_f);
|
|
Cmd_AddCommand ("banname", SV_BanName_f);
|
|
Cmd_AddCommand ("banlist", SV_BanList_f);
|
|
Cmd_AddCommand ("banip", SV_BanIP_f);
|
|
Cmd_AddCommand ("unban", SV_Unban_f);
|
|
// Cmd_AddCommand ("ban", SV_BanName_f);
|
|
Cmd_AddCommand ("status", SV_Status_f);
|
|
|
|
Cmd_AddCommand ("addip", SV_FilterIP_f);
|
|
Cmd_AddCommand ("removeip", SV_Unfilter_f);
|
|
Cmd_AddCommand ("listip", SV_FilterList_f);
|
|
Cmd_AddCommand ("writeip", SV_WriteIP_f);
|
|
|
|
Cmd_AddCommand ("floodprot", SV_Floodprot_f);
|
|
|
|
// Cmd_AddCommand ("filterip", SV_FilterIP_f);
|
|
// Cmd_AddCommand ("unfilter", SV_Unfilter_f);
|
|
// Cmd_AddCommand ("filterlist", SV_FilterList_f);
|
|
|
|
// Cmd_AddCommand ("writeip", SV_WriteIP_f);
|
|
|
|
Cmd_AddCommand ("sv", SV_SendGameCommand_f);
|
|
|
|
Cmd_AddCommand ("killserver", SV_KillServer_f);
|
|
Cmd_AddCommand ("map", SV_Map_f);
|
|
#ifdef Q3SERVER
|
|
Cmd_AddCommand ("spmap", SV_Map_f);
|
|
#endif
|
|
Cmd_AddCommand ("gamemap", SV_Map_f);
|
|
Cmd_AddCommand ("changelevel", SV_Map_f);
|
|
Cmd_AddCommand ("listmaps", SV_MapList_f);
|
|
Cmd_AddCommand ("setmaster", SV_SetMaster_f);
|
|
|
|
Cmd_AddCommand ("heartbeat", SV_Heartbeat_f);
|
|
|
|
Cmd_AddCommand ("localinfo", SV_Localinfo_f);
|
|
Cmd_AddCommand ("gamedir", SV_Gamedir_f);
|
|
Cmd_AddCommand ("sv_gamedir", SV_Gamedir);
|
|
Cmd_AddCommand ("sv_settimer", SV_SetTimer_f);
|
|
Cmd_AddCommand ("stuffcmd", SV_StuffToClient_f);
|
|
|
|
Cmd_AddCommand ("pin_save", SV_Pin_Save_f);
|
|
Cmd_AddCommand ("pin_reload", SV_Pin_Reload_f);
|
|
Cmd_AddCommand ("pin_delete", SV_Pin_Delete_f);
|
|
Cmd_AddCommand ("pin_add", SV_Pin_Add_f);
|
|
|
|
cl_warncmd.value = 1;
|
|
}
|
|
|
|
#endif
|