From 840643efa638de9dc44f45dc4f3f1c81c2985498 Mon Sep 17 00:00:00 2001 From: Fathi Boudra Date: Thu, 20 Oct 2011 15:18:11 +0300 Subject: Initial import --- code/server/sv_ccmds.c | 1319 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1319 insertions(+) create mode 100644 code/server/sv_ccmds.c (limited to 'code/server/sv_ccmds.c') diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c new file mode 100644 index 0000000..0b9d578 --- /dev/null +++ b/code/server/sv_ccmds.c @@ -0,0 +1,1319 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code 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. + +Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "server.h" + +/* +=============================================================================== + +OPERATOR CONSOLE ONLY COMMANDS + +These commands can only be entered from stdin or by a remote operator datagram +=============================================================================== +*/ + + +/* +================== +SV_GetPlayerByHandle + +Returns the player with player id or name from Cmd_Argv(1) +================== +*/ +static client_t *SV_GetPlayerByHandle( void ) { + client_t *cl; + int i; + char *s; + char cleanName[64]; + + // make sure server is running + if ( !com_sv_running->integer ) { + return NULL; + } + + if ( Cmd_Argc() < 2 ) { + Com_Printf( "No player specified.\n" ); + return NULL; + } + + s = Cmd_Argv(1); + + // Check whether this is a numeric player handle + for(i = 0; s[i] >= '0' && s[i] <= '9'; i++); + + if(!s[i]) + { + int plid = atoi(s); + + // Check for numeric playerid match + if(plid >= 0 && plid < sv_maxclients->integer) + { + cl = &svs.clients[plid]; + + if(cl->state) + return cl; + } + } + + // check for a name match + for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { + if ( !cl->state ) { + continue; + } + if ( !Q_stricmp( cl->name, s ) ) { + return cl; + } + + Q_strncpyz( cleanName, cl->name, sizeof(cleanName) ); + Q_CleanStr( cleanName ); + if ( !Q_stricmp( cleanName, s ) ) { + return cl; + } + } + + Com_Printf( "Player %s is not on the server\n", s ); + + return NULL; +} + +/* +================== +SV_GetPlayerByNum + +Returns the player with idnum from Cmd_Argv(1) +================== +*/ +static client_t *SV_GetPlayerByNum( void ) { + client_t *cl; + int i; + int idnum; + char *s; + + // make sure server is running + if ( !com_sv_running->integer ) { + return NULL; + } + + if ( Cmd_Argc() < 2 ) { + Com_Printf( "No player specified.\n" ); + return NULL; + } + + s = Cmd_Argv(1); + + for (i = 0; s[i]; i++) { + if (s[i] < '0' || s[i] > '9') { + Com_Printf( "Bad slot number: %s\n", s); + return NULL; + } + } + idnum = atoi( s ); + if ( idnum < 0 || idnum >= sv_maxclients->integer ) { + Com_Printf( "Bad client slot: %i\n", idnum ); + return NULL; + } + + cl = &svs.clients[idnum]; + if ( !cl->state ) { + Com_Printf( "Client %i is not active\n", idnum ); + return NULL; + } + return cl; +} + +//========================================================= + + +/* +================== +SV_Map_f + +Restart the server on a different map +================== +*/ +static void SV_Map_f( void ) { + char *cmd; + char *map; + qboolean killBots, cheat; + char expanded[MAX_QPATH]; + char mapname[MAX_QPATH]; + + map = Cmd_Argv(1); + if ( !map ) { + return; + } + + // make sure the level exists before trying to change, so that + // a typo at the server console won't end the game + Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", map); + if ( FS_ReadFile (expanded, NULL) == -1 ) { + Com_Printf ("Can't find map %s\n", expanded); + return; + } + + // force latched values to get set + Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH ); + + cmd = Cmd_Argv(0); + if( Q_stricmpn( cmd, "sp", 2 ) == 0 ) { + Cvar_SetValue( "g_gametype", GT_SINGLE_PLAYER ); + Cvar_SetValue( "g_doWarmup", 0 ); + // may not set sv_maxclients directly, always set latched + Cvar_SetLatched( "sv_maxclients", "8" ); + cmd += 2; + cheat = qfalse; + killBots = qtrue; + } + else { + if ( !Q_stricmp( cmd, "devmap" ) || !Q_stricmp( cmd, "spdevmap" ) ) { + cheat = qtrue; + killBots = qtrue; + } else { + cheat = qfalse; + killBots = qfalse; + } + if( sv_gametype->integer == GT_SINGLE_PLAYER ) { + Cvar_SetValue( "g_gametype", GT_FFA ); + } + } + + // save the map name here cause on a map restart we reload the q3config.cfg + // and thus nuke the arguments of the map command + Q_strncpyz(mapname, map, sizeof(mapname)); + + // start up the map + SV_SpawnServer( mapname, killBots ); + + // set the cheat value + // if the level was started with "map ", then + // cheats will not be allowed. If started with "devmap " + // then cheats will be allowed + if ( cheat ) { + Cvar_Set( "sv_cheats", "1" ); + } else { + Cvar_Set( "sv_cheats", "0" ); + } +} + +/* +================ +SV_MapRestart_f + +Completely restarts a level, but doesn't send a new gamestate to the clients. +This allows fair starts with variable load times. +================ +*/ +static void SV_MapRestart_f( void ) { + int i; + client_t *client; + char *denied; + qboolean isBot; + int delay; + + // make sure we aren't restarting twice in the same frame + if ( com_frameTime == sv.serverId ) { + return; + } + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( sv.restartTime ) { + return; + } + + if (Cmd_Argc() > 1 ) { + delay = atoi( Cmd_Argv(1) ); + } + else { + delay = 5; + } + if( delay && !Cvar_VariableValue("g_doWarmup") ) { + sv.restartTime = sv.time + delay * 1000; + SV_SetConfigstring( CS_WARMUP, va("%i", sv.restartTime) ); + return; + } + + // check for changes in variables that can't just be restarted + // check for maxclients change + if ( sv_maxclients->modified || sv_gametype->modified ) { + char mapname[MAX_QPATH]; + + Com_Printf( "variable change -- restarting.\n" ); + // restart the map the slow way + Q_strncpyz( mapname, Cvar_VariableString( "mapname" ), sizeof( mapname ) ); + + SV_SpawnServer( mapname, qfalse ); + return; + } + + // toggle the server bit so clients can detect that a + // map_restart has happened + svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT; + + // generate a new serverid + // TTimo - don't update restartedserverId there, otherwise we won't deal correctly with multiple map_restart + sv.serverId = com_frameTime; + Cvar_Set( "sv_serverid", va("%i", sv.serverId ) ); + + // if a map_restart occurs while a client is changing maps, we need + // to give them the correct time so that when they finish loading + // they don't violate the backwards time check in cl_cgame.c + for (i=0 ; iinteger ; i++) { + if (svs.clients[i].state == CS_PRIMED) { + svs.clients[i].oldServerTime = sv.restartTime; + } + } + + // reset all the vm data in place without changing memory allocation + // note that we do NOT set sv.state = SS_LOADING, so configstrings that + // had been changed from their default values will generate broadcast updates + sv.state = SS_LOADING; + sv.restarting = qtrue; + + SV_RestartGameProgs(); + + // run a few frames to allow everything to settle + for (i = 0; i < 3; i++) + { + VM_Call (gvm, GAME_RUN_FRAME, sv.time); + sv.time += 100; + svs.time += 100; + } + + sv.state = SS_GAME; + sv.restarting = qfalse; + + // connect and begin all the clients + for (i=0 ; iinteger ; i++) { + client = &svs.clients[i]; + + // send the new gamestate to all connected clients + if ( client->state < CS_CONNECTED) { + continue; + } + + if ( client->netchan.remoteAddress.type == NA_BOT ) { + isBot = qtrue; + } else { + isBot = qfalse; + } + + // add the map_restart command + SV_AddServerCommand( client, "map_restart\n" ); + + // connect the client again, without the firstTime flag + denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) ); + if ( denied ) { + // this generally shouldn't happen, because the client + // was connected before the level change + SV_DropClient( client, denied ); + Com_Printf( "SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i ); + continue; + } + + client->state = CS_ACTIVE; + + SV_ClientEnterWorld( client, &client->lastUsercmd ); + } + + // run another frame to allow things to look at all the players + VM_Call (gvm, GAME_RUN_FRAME, sv.time); + sv.time += 100; + svs.time += 100; +} + +//=============================================================== + +/* +================== +SV_Kick_f + +Kick a user off of the server FIXME: move to game +================== +*/ +static void SV_Kick_f( void ) { + client_t *cl; + int i; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( Cmd_Argc() != 2 ) { + Com_Printf ("Usage: kick \nkick all = kick everyone\nkick allbots = kick all bots\n"); + return; + } + + cl = SV_GetPlayerByHandle(); + if ( !cl ) { + if ( !Q_stricmp(Cmd_Argv(1), "all") ) { + for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { + if ( !cl->state ) { + continue; + } + if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { + continue; + } + SV_DropClient( cl, "was kicked" ); + cl->lastPacketTime = svs.time; // in case there is a funny zombie + } + } + else if ( !Q_stricmp(Cmd_Argv(1), "allbots") ) { + for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) { + if ( !cl->state ) { + continue; + } + if( cl->netchan.remoteAddress.type != NA_BOT ) { + continue; + } + SV_DropClient( cl, "was kicked" ); + cl->lastPacketTime = svs.time; // in case there is a funny zombie + } + } + return; + } + if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { + SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); + return; + } + + SV_DropClient( cl, "was kicked" ); + cl->lastPacketTime = svs.time; // in case there is a funny zombie +} + +#ifndef STANDALONE +// these functions require the auth server which of course is not available anymore for stand-alone games. + +/* +================== +SV_Ban_f + +Ban a user from being able to play on this server through the auth +server +================== +*/ +static void SV_Ban_f( void ) { + client_t *cl; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( Cmd_Argc() != 2 ) { + Com_Printf ("Usage: banUser \n"); + return; + } + + cl = SV_GetPlayerByHandle(); + + if (!cl) { + return; + } + + if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { + SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); + return; + } + + // look up the authorize server's IP + if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { + Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); + if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress, NA_IP ) ) { + Com_Printf( "Couldn't resolve address\n" ); + return; + } + svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); + Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, + svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], + svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], + BigShort( svs.authorizeAddress.port ) ); + } + + // otherwise send their ip to the authorize server + if ( svs.authorizeAddress.type != NA_BAD ) { + NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, + "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], + cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); + Com_Printf("%s was banned from coming back\n", cl->name); + } +} + +/* +================== +SV_BanNum_f + +Ban a user from being able to play on this server through the auth +server +================== +*/ +static void SV_BanNum_f( void ) { + client_t *cl; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( Cmd_Argc() != 2 ) { + Com_Printf ("Usage: banClient \n"); + return; + } + + cl = SV_GetPlayerByNum(); + if ( !cl ) { + return; + } + if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { + SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); + return; + } + + // look up the authorize server's IP + if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { + Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); + if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress, NA_IP ) ) { + Com_Printf( "Couldn't resolve address\n" ); + return; + } + svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE ); + Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, + svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1], + svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3], + BigShort( svs.authorizeAddress.port ) ); + } + + // otherwise send their ip to the authorize server + if ( svs.authorizeAddress.type != NA_BAD ) { + NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress, + "banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1], + cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] ); + Com_Printf("%s was banned from coming back\n", cl->name); + } +} +#endif + +/* +================== +SV_RehashBans_f + +Load saved bans from file. +================== +*/ +static void SV_RehashBans_f(void) +{ + int index, filelen; + fileHandle_t readfrom; + char *textbuf, *curpos, *maskpos, *newlinepos, *endpos; + char filepath[MAX_QPATH]; + + serverBansCount = 0; + + if(!sv_banFile->string || !*sv_banFile->string) + return; + + if(!(curpos = Cvar_VariableString("fs_game")) || !*curpos) + curpos = BASEGAME; + + Com_sprintf(filepath, sizeof(filepath), "%s/%s", curpos, sv_banFile->string); + + if((filelen = FS_SV_FOpenFileRead(filepath, &readfrom)) >= 0) + { + if(filelen < 2) + { + // Don't bother if file is too short. + FS_FCloseFile(readfrom); + return; + } + + curpos = textbuf = Z_Malloc(filelen); + + filelen = FS_Read(textbuf, filelen, readfrom); + FS_FCloseFile(readfrom); + + endpos = textbuf + filelen; + + for(index = 0; index < SERVER_MAXBANS && curpos + 2 < endpos; index++) + { + // find the end of the address string + for(maskpos = curpos + 2; maskpos < endpos && *maskpos != ' '; maskpos++); + + if(maskpos + 1 >= endpos) + break; + + *maskpos = '\0'; + maskpos++; + + // find the end of the subnet specifier + for(newlinepos = maskpos; newlinepos < endpos && *newlinepos != '\n'; newlinepos++); + + if(newlinepos >= endpos) + break; + + *newlinepos = '\0'; + + if(NET_StringToAdr(curpos + 2, &serverBans[index].ip, NA_UNSPEC)) + { + serverBans[index].isexception = (curpos[0] != '0'); + serverBans[index].subnet = atoi(maskpos); + + if(serverBans[index].ip.type == NA_IP && + (serverBans[index].subnet < 1 || serverBans[index].subnet > 32)) + { + serverBans[index].subnet = 32; + } + else if(serverBans[index].ip.type == NA_IP6 && + (serverBans[index].subnet < 1 || serverBans[index].subnet > 128)) + { + serverBans[index].subnet = 128; + } + } + + curpos = newlinepos + 1; + } + + serverBansCount = index; + + Z_Free(textbuf); + } +} + +/* +================== +SV_WriteBans_f + +Save bans to file. +================== +*/ +static void SV_WriteBans(void) +{ + int index; + fileHandle_t writeto; + char *curpos, filepath[MAX_QPATH]; + + if(!sv_banFile->string || !*sv_banFile->string) + return; + + if(!(curpos = Cvar_VariableString("fs_game")) || !*curpos) + curpos = BASEGAME; + + Com_sprintf(filepath, sizeof(filepath), "%s/%s", curpos, sv_banFile->string); + + if((writeto = FS_SV_FOpenFileWrite(filepath))) + { + char writebuf[128]; + serverBan_t *curban; + + for(index = 0; index < serverBansCount; index++) + { + curban = &serverBans[index]; + + Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d\n", + curban->isexception, NET_AdrToString(curban->ip), curban->subnet); + FS_Write(writebuf, strlen(writebuf), writeto); + } + + FS_FCloseFile(writeto); + } +} + +/* +================== +SV_DelBanEntryFromList + +Remove a ban or an exception from the list. +================== +*/ + +static qboolean SV_DelBanEntryFromList(int index) +{ + if(index == serverBansCount - 1) + serverBansCount--; + else if(index < sizeof(serverBans) / sizeof(*serverBans) - 1) + { + memmove(serverBans + index, serverBans + index + 1, (serverBansCount - index - 1) * sizeof(*serverBans)); + serverBansCount--; + } + else + return qtrue; + + return qfalse; +} + +/* +================== +SV_ParseCIDRNotation + +Parse a CIDR notation type string and return a netadr_t and suffix by reference +================== +*/ + +static qboolean SV_ParseCIDRNotation(netadr_t *dest, int *mask, char *adrstr) +{ + char *suffix; + + suffix = strchr(adrstr, '/'); + if(suffix) + { + *suffix = '\0'; + suffix++; + } + + if(!NET_StringToAdr(adrstr, dest, NA_UNSPEC)) + return qtrue; + + if(suffix) + { + *mask = atoi(suffix); + + if(dest->type == NA_IP) + { + if(*mask < 1 || *mask > 32) + *mask = 32; + } + else + { + if(*mask < 1 || *mask > 128) + *mask = 128; + } + } + else if(dest->type == NA_IP) + *mask = 32; + else + *mask = 128; + + return qfalse; +} + +/* +================== +SV_AddBanToList + +Ban a user from being able to play on this server based on his ip address. +================== +*/ + +static void SV_AddBanToList(qboolean isexception) +{ + char *banstring; + char addy2[NET_ADDRSTRMAXLEN]; + netadr_t ip; + int index, argc, mask; + serverBan_t *curban; + + argc = Cmd_Argc(); + + if(argc < 2 || argc > 3) + { + Com_Printf ("Usage: %s (ip[/subnet] | clientnum [subnet])\n", Cmd_Argv(0)); + return; + } + + if(serverBansCount > sizeof(serverBans) / sizeof(*serverBans)) + { + Com_Printf ("Error: Maximum number of bans/exceptions exceeded.\n"); + return; + } + + banstring = Cmd_Argv(1); + + if(strchr(banstring, '.') || strchr(banstring, ':')) + { + // This is an ip address, not a client num. + + if(SV_ParseCIDRNotation(&ip, &mask, banstring)) + { + Com_Printf("Error: Invalid address %s\n", banstring); + return; + } + } + else + { + client_t *cl; + + // client num. + if(!com_sv_running->integer) + { + Com_Printf("Server is not running.\n"); + return; + } + + cl = SV_GetPlayerByNum(); + + if(!cl) + { + Com_Printf("Error: Playernum %s does not exist.\n", Cmd_Argv(1)); + return; + } + + ip = cl->netchan.remoteAddress; + + if(argc == 3) + { + mask = atoi(Cmd_Argv(2)); + + if(ip.type == NA_IP) + { + if(mask < 1 || mask > 32) + mask = 32; + } + else + { + if(mask < 1 || mask > 128) + mask = 128; + } + } + else + mask = (ip.type == NA_IP6) ? 128 : 32; + } + + if(ip.type != NA_IP && ip.type != NA_IP6) + { + Com_Printf("Error: Can ban players connected via the internet only.\n"); + return; + } + + // first check whether a conflicting ban exists that would supersede the new one. + for(index = 0; index < serverBansCount; index++) + { + curban = &serverBans[index]; + + if(curban->subnet <= mask) + { + if((curban->isexception || !isexception) && NET_CompareBaseAdrMask(curban->ip, ip, curban->subnet)) + { + Q_strncpyz(addy2, NET_AdrToString(ip), sizeof(addy2)); + + Com_Printf("Error: %s %s/%d supersedes %s %s/%d\n", curban->isexception ? "Exception" : "Ban", + NET_AdrToString(curban->ip), curban->subnet, + isexception ? "exception" : "ban", addy2, mask); + return; + } + } + if(curban->subnet >= mask) + { + if(!curban->isexception && isexception && NET_CompareBaseAdrMask(curban->ip, ip, mask)) + { + Q_strncpyz(addy2, NET_AdrToString(curban->ip), sizeof(addy2)); + + Com_Printf("Error: %s %s/%d supersedes already existing %s %s/%d\n", isexception ? "Exception" : "Ban", + NET_AdrToString(ip), mask, + curban->isexception ? "exception" : "ban", addy2, curban->subnet); + return; + } + } + } + + // now delete bans that are superseded by the new one + index = 0; + while(index < serverBansCount) + { + curban = &serverBans[index]; + + if(curban->subnet > mask && (!curban->isexception || isexception) && NET_CompareBaseAdrMask(curban->ip, ip, mask)) + SV_DelBanEntryFromList(index); + else + index++; + } + + serverBans[serverBansCount].ip = ip; + serverBans[serverBansCount].subnet = mask; + serverBans[serverBansCount].isexception = isexception; + + serverBansCount++; + + SV_WriteBans(); + + Com_Printf("Added %s: %s/%d\n", isexception ? "ban exception" : "ban", + NET_AdrToString(ip), mask); +} + +/* +================== +SV_DelBanFromList + +Remove a ban or an exception from the list. +================== +*/ + +static void SV_DelBanFromList(qboolean isexception) +{ + int index, count = 0, todel, mask; + netadr_t ip; + char *banstring; + + if(Cmd_Argc() != 2) + { + Com_Printf ("Usage: %s (ip[/subnet] | num)\n", Cmd_Argv(0)); + return; + } + + banstring = Cmd_Argv(1); + + if(strchr(banstring, '.') || strchr(banstring, ':')) + { + serverBan_t *curban; + + if(SV_ParseCIDRNotation(&ip, &mask, banstring)) + { + Com_Printf("Error: Invalid address %s\n", banstring); + return; + } + + index = 0; + + while(index < serverBansCount) + { + curban = &serverBans[index]; + + if(curban->isexception == isexception && + curban->subnet >= mask && + NET_CompareBaseAdrMask(curban->ip, ip, mask)) + { + Com_Printf("Deleting %s %s/%d\n", + isexception ? "exception" : "ban", + NET_AdrToString(curban->ip), curban->subnet); + + SV_DelBanEntryFromList(index); + } + else + index++; + } + } + else + { + todel = atoi(Cmd_Argv(1)); + + if(todel < 1 || todel > serverBansCount) + { + Com_Printf("Error: Invalid ban number given\n"); + return; + } + + for(index = 0; index < serverBansCount; index++) + { + if(serverBans[index].isexception == isexception) + { + count++; + + if(count == todel) + { + Com_Printf("Deleting %s %s/%d\n", + isexception ? "exception" : "ban", + NET_AdrToString(serverBans[index].ip), serverBans[index].subnet); + + SV_DelBanEntryFromList(index); + + break; + } + } + } + } + + SV_WriteBans(); +} + + +/* +================== +SV_ListBans_f + +List all bans and exceptions on console +================== +*/ + +static void SV_ListBans_f(void) +{ + int index, count; + serverBan_t *ban; + + // List all bans + for(index = count = 0; index < serverBansCount; index++) + { + ban = &serverBans[index]; + if(!ban->isexception) + { + count++; + + Com_Printf("Ban #%d: %s/%d\n", count, + NET_AdrToString(ban->ip), ban->subnet); + } + } + // List all exceptions + for(index = count = 0; index < serverBansCount; index++) + { + ban = &serverBans[index]; + if(ban->isexception) + { + count++; + + Com_Printf("Except #%d: %s/%d\n", count, + NET_AdrToString(ban->ip), ban->subnet); + } + } +} + +/* +================== +SV_FlushBans_f + +Delete all bans and exceptions. +================== +*/ + +static void SV_FlushBans_f(void) +{ + serverBansCount = 0; + + // empty the ban file. + SV_WriteBans(); + + Com_Printf("All bans and exceptions have been deleted.\n"); +} + +static void SV_BanAddr_f(void) +{ + SV_AddBanToList(qfalse); +} + +static void SV_ExceptAddr_f(void) +{ + SV_AddBanToList(qtrue); +} + +static void SV_BanDel_f(void) +{ + SV_DelBanFromList(qfalse); +} + +static void SV_ExceptDel_f(void) +{ + SV_DelBanFromList(qtrue); +} + +/* +================== +SV_KickNum_f + +Kick a user off of the server FIXME: move to game +================== +*/ +static void SV_KickNum_f( void ) { + client_t *cl; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( Cmd_Argc() != 2 ) { + Com_Printf ("Usage: kicknum \n"); + return; + } + + cl = SV_GetPlayerByNum(); + if ( !cl ) { + return; + } + if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) { + SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n"); + return; + } + + SV_DropClient( cl, "was kicked" ); + cl->lastPacketTime = svs.time; // in case there is a funny zombie +} + +/* +================ +SV_Status_f +================ +*/ +static void SV_Status_f( void ) { + int i, j, l; + client_t *cl; + playerState_t *ps; + const char *s; + int ping; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + Com_Printf ("map: %s\n", sv_mapname->string ); + + Com_Printf ("num score ping name lastmsg address qport rate\n"); + Com_Printf ("--- ----- ---- --------------- ------- --------------------- ----- -----\n"); + for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) + { + if (!cl->state) + continue; + Com_Printf ("%3i ", i); + ps = SV_GameClientNum( i ); + Com_Printf ("%5i ", ps->persistant[PERS_SCORE]); + + if (cl->state == CS_CONNECTED) + Com_Printf ("CNCT "); + else if (cl->state == CS_ZOMBIE) + Com_Printf ("ZMBI "); + else + { + ping = cl->ping < 9999 ? cl->ping : 9999; + Com_Printf ("%4i ", ping); + } + + Com_Printf ("%s", cl->name); + // TTimo adding a ^7 to reset the color + // NOTE: colored names in status breaks the padding (WONTFIX) + Com_Printf ("^7"); + l = 16 - strlen(cl->name); + for (j=0 ; jlastPacketTime ); + + s = NET_AdrToString( cl->netchan.remoteAddress ); + Com_Printf ("%s", s); + l = 22 - strlen(s); + for (j=0 ; jnetchan.qport); + + Com_Printf (" %5i", cl->rate); + + Com_Printf ("\n"); + } + Com_Printf ("\n"); +} + +/* +================== +SV_ConSay_f +================== +*/ +static void SV_ConSay_f(void) { + char *p; + char text[1024]; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( Cmd_Argc () < 2 ) { + return; + } + + strcpy (text, "console: "); + p = Cmd_Args(); + + if ( *p == '"' ) { + p++; + p[strlen(p)-1] = 0; + } + + strcat(text, p); + + SV_SendServerCommand(NULL, "chat \"%s\"", text); +} + + +/* +================== +SV_Heartbeat_f + +Also called by SV_DropClient, SV_DirectConnect, and SV_SpawnServer +================== +*/ +void SV_Heartbeat_f( void ) { + svs.nextHeartbeatTime = -9999999; +} + + +/* +=========== +SV_Serverinfo_f + +Examine the serverinfo string +=========== +*/ +static void SV_Serverinfo_f( void ) { + Com_Printf ("Server info settings:\n"); + Info_Print ( Cvar_InfoString( CVAR_SERVERINFO ) ); +} + + +/* +=========== +SV_Systeminfo_f + +Examine or change the serverinfo string +=========== +*/ +static void SV_Systeminfo_f( void ) { + Com_Printf ("System info settings:\n"); + Info_Print ( Cvar_InfoString( CVAR_SYSTEMINFO ) ); +} + + +/* +=========== +SV_DumpUser_f + +Examine all a users info strings FIXME: move to game +=========== +*/ +static void SV_DumpUser_f( void ) { + client_t *cl; + + // make sure server is running + if ( !com_sv_running->integer ) { + Com_Printf( "Server is not running.\n" ); + return; + } + + if ( Cmd_Argc() != 2 ) { + Com_Printf ("Usage: info \n"); + return; + } + + cl = SV_GetPlayerByHandle(); + if ( !cl ) { + return; + } + + Com_Printf( "userinfo\n" ); + Com_Printf( "--------\n" ); + Info_Print( cl->userinfo ); +} + + +/* +================= +SV_KillServer +================= +*/ +static void SV_KillServer_f( void ) { + SV_Shutdown( "killserver" ); +} + +//=========================================================== + +/* +================== +SV_CompleteMapName +================== +*/ +static void SV_CompleteMapName( char *args, int argNum ) { + if( argNum == 2 ) { + Field_CompleteFilename( "maps", "bsp", qtrue ); + } +} + +/* +================== +SV_AddOperatorCommands +================== +*/ +void SV_AddOperatorCommands( void ) { + static qboolean initialized; + + if ( initialized ) { + return; + } + initialized = qtrue; + + Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); + Cmd_AddCommand ("kick", SV_Kick_f); +#ifndef STANDALONE + if(!Cvar_VariableIntegerValue("com_standalone")) + { + Cmd_AddCommand ("banUser", SV_Ban_f); + Cmd_AddCommand ("banClient", SV_BanNum_f); + } +#endif + Cmd_AddCommand ("clientkick", SV_KickNum_f); + Cmd_AddCommand ("status", SV_Status_f); + Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); + Cmd_AddCommand ("systeminfo", SV_Systeminfo_f); + Cmd_AddCommand ("dumpuser", SV_DumpUser_f); + Cmd_AddCommand ("map_restart", SV_MapRestart_f); + Cmd_AddCommand ("sectorlist", SV_SectorList_f); + Cmd_AddCommand ("map", SV_Map_f); + Cmd_SetCommandCompletionFunc( "map", SV_CompleteMapName ); +#ifndef PRE_RELEASE_DEMO + Cmd_AddCommand ("devmap", SV_Map_f); + Cmd_SetCommandCompletionFunc( "devmap", SV_CompleteMapName ); + Cmd_AddCommand ("spmap", SV_Map_f); + Cmd_SetCommandCompletionFunc( "spmap", SV_CompleteMapName ); + Cmd_AddCommand ("spdevmap", SV_Map_f); + Cmd_SetCommandCompletionFunc( "spdevmap", SV_CompleteMapName ); +#endif + Cmd_AddCommand ("killserver", SV_KillServer_f); + if( com_dedicated->integer ) { + Cmd_AddCommand ("say", SV_ConSay_f); + } + + Cmd_AddCommand("rehashbans", SV_RehashBans_f); + Cmd_AddCommand("listbans", SV_ListBans_f); + Cmd_AddCommand("banaddr", SV_BanAddr_f); + Cmd_AddCommand("exceptaddr", SV_ExceptAddr_f); + Cmd_AddCommand("bandel", SV_BanDel_f); + Cmd_AddCommand("exceptdel", SV_ExceptDel_f); + Cmd_AddCommand("flushbans", SV_FlushBans_f); +} + +/* +================== +SV_RemoveOperatorCommands +================== +*/ +void SV_RemoveOperatorCommands( void ) { +#if 0 + // removing these won't let the server start again + Cmd_RemoveCommand ("heartbeat"); + Cmd_RemoveCommand ("kick"); + Cmd_RemoveCommand ("banUser"); + Cmd_RemoveCommand ("banClient"); + Cmd_RemoveCommand ("status"); + Cmd_RemoveCommand ("serverinfo"); + Cmd_RemoveCommand ("systeminfo"); + Cmd_RemoveCommand ("dumpuser"); + Cmd_RemoveCommand ("map_restart"); + Cmd_RemoveCommand ("sectorlist"); + Cmd_RemoveCommand ("say"); +#endif +} + -- cgit v1.2.3