diff options
author | Paweł Redman <pawel@redman.xyz> | 2017-04-13 11:30:00 +0000 |
---|---|---|
committer | /dev/humancontroller <devhc@example.com> | 2017-04-15 12:06:43 +0200 |
commit | 5ad9e26c3be1f2ebc6cdb340b685aef30ae16db7 (patch) | |
tree | 5ee97c52196122bd8356ad8e09403332e7712fcd /src/game/g_admin.c | |
parent | 45973dc48641365b31475733bce7af9c3b8603a6 (diff) |
import the cQVM game module
replacing the existing one
Diffstat (limited to 'src/game/g_admin.c')
-rw-r--r-- | src/game/g_admin.c | 2907 |
1 files changed, 1945 insertions, 962 deletions
diff --git a/src/game/g_admin.c b/src/game/g_admin.c index f9c5f84..de59a47 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -80,6 +80,16 @@ g_admin_cmd_t g_admin_cmds[ ] = "[^3name|slot#|IP^7] (^5time^7) (^5reason^7)" }, + {"bring", G_admin_bring, "bring", + "This will bring you to a players location.", + "[^3name|slot#^7]" + }, + + {"bubble", G_admin_bubble, "bubble", + "draw attention to a player with bubbles", + "[^3name|slot#^7]" + }, + {"buildlog", G_admin_buildlog, "buildlog", "display a list of recent builds and deconstructs, optionally specifying" " a team", @@ -108,26 +118,31 @@ g_admin_cmd_t g_admin_cmds[ ] = "[^3name|slot#^7]" }, + {"denyweapon", G_admin_denyweapon, "denyweapon", + "take away a player's ability to use a weapon or class", + "[^3name|slot#^7] [^3class|weapon^7]" + }, + {"designate", G_admin_designate, "designate", "give the player designated builder privileges", "[^3name|slot#^7]" }, - + {"devmap", G_admin_devmap, "devmap", "load a map with cheats (and optionally force layout)", "[^3mapname^7] (^5layout^7)" }, - {"denyweapon", G_admin_denyweapon, "denyweapon", - "take away a player's ability to use a weapon or class", - "[^3name|slot#^7] [^3class|weapon^7]" - }, - {"drop", G_admin_drop, "drop", "kick a client from the server without log", "[^3name|slot#^7] [^3message^7]" }, + {"expire", G_admin_expire, "expire", + "Expires up to 5 level 1 admins with !seen times older than g_adminExpireTime", + "[^5confirm^7]" + }, + {"flag", G_admin_flag, "flag", "add an admin flag to a player, prefix flag with '-' to disallow the flag. " "console can use this command on admin levels by prefacing a '*' to the admin level value.", @@ -138,7 +153,7 @@ g_admin_cmd_t g_admin_cmds[ ] = "list all flags understood by this server", "" }, - + {"help", G_admin_help, "help", "display commands available to you or help on a specific command", "(^5command^7)" @@ -148,12 +163,17 @@ g_admin_cmd_t g_admin_cmds[ ] = "display the contents of server info files", "(^5subject^7)" }, - + + {"immunity", G_admin_immunity, "immunity", + "give a player ban immunity", + "[^3+|-^7](^5slot#^7)" + }, + {"invisible", G_admin_invisible, "invisible", "hides a player so they cannot be seen in playerlists", "" }, - + {"kick", G_admin_kick, "kick", "kick a player with an optional reason", "[^3name|slot#^7] (^5reason^7)" @@ -224,9 +244,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "" }, - {"nobuild", G_admin_nobuild, "nobuild", - "set nobuild markers to prevent players from building in an area", - "(^5area^7) (^5height^7)" + {"outlaw", G_admin_outlaw, "outlaw", + "adjust a player's bleed counter, usually to make their base turn on them." + " bleed value can be '?' to query their current value, a number to add and activate bleed status," + " +num or -num will silently adjust their current bleed value, 0 will pardon them", + "(^5name|slot^7) (^5bleed value)" }, {"passvote", G_admin_passvote, "passvote", @@ -240,6 +262,12 @@ g_admin_cmd_t g_admin_cmds[ ] = "(^5name|slot|*^7)" }, + {"practice", G_admin_practice, "practice", + "set practice mode for player names with 'clan tag', " + "these players will be allowed to join any team regardless of balance. " + "'map count' is number of maps to maintain practice mode", + "[^3clan tag|off^7] [^3map count^7])" + }, {"putteam", G_admin_putteam, "putteam", "move a player to a specified team", @@ -253,7 +281,7 @@ g_admin_cmd_t g_admin_cmds[ ] = {"register", G_admin_register, "register", "Registers your name to protect it from being used by others or updates your admin name to your current name.", - "" + "[^3level^7] [^3password^7]" }, {"rename", G_admin_rename, "rename", @@ -299,12 +327,13 @@ g_admin_cmd_t g_admin_cmds[ ] = {"spec999", G_admin_spec999, "spec999", "move 999 pingers to the spectator team", - "" - }, - + ""}, + + //kev: a bit of a hack, but there is no real point to + //creating a new admin flag for this, so i stole it from !help {"specme", G_admin_putmespec, "specme", - "moves you to the spectators", - "" + "moves you to the spectators (can be done silently with the 's' argument)", + "(^5s^7)" }, {"subnetban", G_admin_subnetban, "subnetban", @@ -316,7 +345,7 @@ g_admin_cmd_t g_admin_cmds[ ] = "\n ^1WARNING:^7 Use of this command may make your admin.dat incompatible with other game.qvms" }, - {"suspendban", G_admin_suspendban, "ban", + {"suspendban", G_admin_suspendban, "suspendban", "suspend a ban for a length of time. time is specified as numbers " "followed by units 'w' (weeks), 'd' (days), 'h' (hours) or 'm' (minutes)," " or seconds if no units are specified", @@ -325,7 +354,11 @@ g_admin_cmd_t g_admin_cmds[ ] = {"time", G_admin_time, "time", "show the current local server time", - "" + ""}, + + {"tklog", G_admin_tklog, "tklog", + "list recent teamkill activity", + "(^5start id#|name|-skip#^7) (^5search skip#^7)" }, {"unban", G_admin_unban, "ban", @@ -337,13 +370,13 @@ g_admin_cmd_t g_admin_cmds[ ] = "revoke designated builder privileges", "[^3name|slot#^7]" }, - + {"unflag", G_admin_flag, "flag", "clears an admin flag from a player. " "console can use this command on admin levels by prefacing a '*' to the admin level value.", "[^3name|slot#|admin#|*adminlevel^7] (^5+^7|^5-^7)[^3flag^7]" }, - + {"unlock", G_admin_unlock, "lock", "unlock a locked team", "[^3a|h^7]" @@ -354,6 +387,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "[^3name|slot#^7]" }, + {"nobuild", G_admin_nobuild, "nobuild", + "Enable and control nobuild mode.", + "[^3on|off|save|add|del|list|mode|zone|+|-|go^7]" + }, + {"unpause", G_admin_pause, "pause", "allow a player to interact with the game." " * will unpause all players, using no argument will unpause game clock", @@ -376,6 +414,12 @@ g_admin_ban_t *g_admin_bans[ MAX_ADMIN_BANS ]; g_admin_command_t *g_admin_commands[ MAX_ADMIN_COMMANDS ]; g_admin_namelog_t *g_admin_namelog[ MAX_ADMIN_NAMELOGS ]; +static int admin_adminlog_index = 0; +g_admin_adminlog_t *g_admin_adminlog[ MAX_ADMIN_ADMINLOGS ]; + +static int admin_tklog_index = 0; +g_admin_tklog_t *g_admin_tklog[ MAX_ADMIN_TKLOGS ]; + // match a certain flag within these flags // return state of whether flag was found or not, // set *perm to indicate whether found flag was + or - @@ -404,16 +448,12 @@ static qboolean admin_permission( char *flags, const char *flag, qboolean *perm *perm = base_perm; return qtrue; } - return qfalse; } -static int admin_adminlog_index = 0; -g_admin_adminlog_t *g_admin_adminlog[ MAX_ADMIN_ADMINLOGS ]; - // This function should only be used directly when the client is connecting and thus has no GUID. // Else, use G_admin_permission() -qboolean G_admin_permission_guid( char *guid, const char* flag ) +qboolean G_admin_permission_guid( char *guid, const char *flag ) { int i; int l = 0; @@ -436,8 +476,7 @@ qboolean G_admin_permission_guid( char *guid, const char* flag ) for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) { if( g_admin_levels[ i ]->level == l ) - return admin_permission( g_admin_levels[ i ]->flags, flag, &perm ) && - perm; + return admin_permission( g_admin_levels[ i ]->flags, flag, &perm ) && perm; } return qfalse; } @@ -542,18 +581,13 @@ static qboolean admin_higher_guid( char *admin_guid, char *victim_guid ) { int i; int alevel = 0; - int alevel2 = 0; + qboolean perm = qfalse; for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { if( !Q_stricmp( admin_guid, g_admin_admins[ i ]->guid ) ) { alevel = g_admin_admins[ i ]->level; - - // Novelty Levels should be equivelant to level 1 - if( alevel > 9 ) - alevel = 1; - break; } } @@ -561,17 +595,9 @@ static qboolean admin_higher_guid( char *admin_guid, char *victim_guid ) { if( !Q_stricmp( victim_guid, g_admin_admins[ i ]->guid ) ) { - alevel2 = g_admin_admins[ i ]->level; - - // Novelty Levels should be equivelant to level 1 - if( alevel2 > 9 ) - alevel2 = 1; - - if( alevel < alevel2 ) - return qfalse; - - if( strstr( g_admin_admins[ i ]->flags, va( "%s", ADMF_IMMUTABLE ) ) ) + if( alevel < g_admin_admins[ i ]->level ) return qfalse; + return ( !admin_permission( g_admin_admins[ i ]->flags, ADMF_IMMUTABLE, &perm ) || !perm ); } } return qtrue; @@ -619,7 +645,7 @@ static void admin_writeconfig( void ) { fileHandle_t f; int len, i; - int t, expiretime; + int t; char levels[ MAX_STRING_CHARS ] = {""}; if( !g_admin.string[ 0 ] ) @@ -650,19 +676,9 @@ static void admin_writeconfig( void ) for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { // don't write level 0 users - if( g_admin_admins[ i ]->level < 1 ) + if( g_admin_admins[ i ]->level == 0 ) continue; - //if set dont write admins that havent been seen in a while - expiretime = G_admin_parse_time( g_adminExpireTime.string ); - if( expiretime > 0 ) { - //only expire level 1 people - if( t - expiretime > g_admin_admins[ i ]->seen && g_admin_admins[ i ]->level == 1 ) { - G_Printf("Admin %s has been expired.\n", g_admin_admins[ i ]->name ); - continue; - } - } - trap_FS_Write( "[admin]\n", 8, f ); trap_FS_Write( "name = ", 10, f ); admin_writeconfig_string( g_admin_admins[ i ]->name, f ); @@ -672,8 +688,6 @@ static void admin_writeconfig( void ) admin_writeconfig_int( g_admin_admins[ i ]->level, f ); trap_FS_Write( "flags = ", 10, f ); admin_writeconfig_string( g_admin_admins[ i ]->flags, f ); - trap_FS_Write( "seen = ", 10, f ); - admin_writeconfig_int( g_admin_admins[ i ]->seen, f ); trap_FS_Write( "\n", 1, f ); } for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) @@ -697,7 +711,8 @@ static void admin_writeconfig( void ) admin_writeconfig_string( g_admin_bans[ i ]->made, f ); trap_FS_Write( "expires = ", 10, f ); admin_writeconfig_int( g_admin_bans[ i ]->expires, f ); - if( g_admin_bans[ i ]->suspend > t ) { + if( g_admin_bans[ i ]->suspend > t ) + { trap_FS_Write( "suspend = ", 10, f ); admin_writeconfig_int( g_admin_bans[ i ]->suspend, f ); } @@ -778,6 +793,253 @@ static void admin_readconfig_int( char **cnf, int *v ) *v = atoi( t ); } +void G_admin_chat_writeconfig( void ) +{ + fileHandle_t f; + int len; + char keybuf[ 16 ]; + int i, j; + qboolean found; + + G_admin_karma_sync( ); + + if( !g_chat.string[ 0 ] ) + { + G_Printf( "WARNING: g_chat is not set. " + " channel subscriptions will not be saved to a file.\n" ); + return; + } + + // check that there is something to save + found = qfalse; + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && !found; i++ ) + { + if( g_admin_admins[ i ]->level == 0 ) + continue; + for( j = 0; j < CHAT_MAXCHAN; j++ ) + { + if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) + found = qtrue; + } + if(g_admin_admins[ i ]->seen ) + found = qtrue; + } + if( !found ) + { + G_Printf( "Cowardly refusing to create an empty chat file.\n" ); + return; + } + + len = trap_FS_FOpenFile( g_chat.string, &f, FS_WRITE ); + if( len < 0 ) + { + G_Printf( "chat_writeconfig: could not open g_chat file \"%s\"\n", + g_chat.string ); + return; + } + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + { + // don't write level 0 users + if( g_admin_admins[ i ]->level == 0 ) + continue; + + // don't write users not joined to a channel + found = qfalse; + for( j = 0; j < CHAT_MAXCHAN; j++ ) + { + if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) + found = qtrue; + } + if( !found && !g_admin_admins[ i ]->seen ) + continue; + + trap_FS_Write( "[chat]\n", 7, f ); + trap_FS_Write( "guid = ", 10, f ); + admin_writeconfig_string( g_admin_admins[ i ]->guid, f ); + trap_FS_Write( "seen = ", 10, f ); + admin_writeconfig_int( g_admin_admins[ i ]->seen, f ); + trap_FS_Write( "karma = ", 10, f ); + admin_writeconfig_int( g_admin_admins[ i ]->karma, f ); + + for( j = 0 ; j < CHAT_MAXCHAN; j++ ) + { + if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) + { + Com_sprintf( keybuf, sizeof( keybuf ), "%d = ", j ); + trap_FS_Write( keybuf, 10, f ); + admin_writeconfig_string( g_admin_admins[ i ]->chat[ j ], f ); + } + } + + trap_FS_Write( "\n", 1, f ); + } + trap_FS_FCloseFile( f ); +} + +qboolean G_admin_chat_readconfig( gentity_t *ent ) +{ + g_admin_admin_t *a = NULL; + fileHandle_t f; + int len; + char *cnf, *cnf2; + char *t; + int uc = 0, cc = 0; + qboolean chat_open; + char guid[ 33 ]; + int i; + + if( !g_chat.string[ 0 ] ) + { + ADMP( "chat_readconfig: g_chat is not set, not loading channel subscriptions " + "from a file\n" ); + return qfalse; + } + + len = trap_FS_FOpenFile( g_chat.string, &f, FS_READ ) ; + if( len < 0 ) + { + ADMP( va( "chat_readconfig: could not open chat config file %s\n", + g_chat.string ) ); + return qfalse; + } + cnf = G_Alloc( len + 1 ); + cnf2 = cnf; + trap_FS_Read( cnf, len, f ); + *( cnf + len ) = '\0'; + trap_FS_FCloseFile( f ); + + t = COM_Parse( &cnf ); + chat_open = qfalse; + while( *t ) + { + if( !Q_stricmp( t, "[chat]" ) ) + { + chat_open = qtrue; + a = NULL; + } + else if( chat_open ) + { + int chan; + + if( !Q_stricmp( t, "guid" ) ) + { + admin_readconfig_string( &cnf, guid, sizeof( guid ) ); + for( i = 0 ; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + { + if( !Q_stricmp( guid, g_admin_admins[ i ]->guid ) ) + { + a = g_admin_admins[ i ]; + memset( a->chat, 0, sizeof( a->chat) ); + uc++; + break; + } + } + } + else if( a == NULL ) + { + // no guid match, ignored + } + else if( !Q_stricmp( t, "seen" ) ) + { + admin_readconfig_int( &cnf, &a->seen ); + } + else if( !Q_stricmp( t, "karma" ) ) + { + admin_readconfig_int( &cnf, &a->karma ); + } + else if( *t >= '0' && *t <= '9' ) + { + chan = atoi( t ); + if( chan >= 0 && chan < CHAT_MAXCHAN && a ) + { + admin_readconfig_string( &cnf, a->chat[ chan ], sizeof( a->chat[ chan ] ) ); + cc++; + } + } + else + { + ADMP( va( "chat_readconfig: [chat] parse error near %s on line %d\n", + t, COM_GetCurrentParseLine() ) ); + } + } + + t = COM_Parse( &cnf ); + } + + G_Free( cnf2 ); + ADMP( va( "chat_readconfig: loaded %d users with %d channels\n", uc, cc ) ); + return qtrue; +} + +void G_admin_chat_sync( gentity_t *ent ) +{ + gentity_t *target; + int i, j; + qboolean rejoin = qfalse; + + if( !ent || !ent->client || ent->client->pers.adminLevel < 1 ) + return; + + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) + { + if( !Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) + { + ent->client->pers.karma = g_admin_admins[ i ]->karma; + for( j = 0; j < CHAT_MAXCHAN; j++ ) + { + Q_strncpyz( ent->client->pers.chat[ j ], + g_admin_admins[ i ]->chat[ j ], + sizeof( g_admin_admins[ i ]->chat[ j ] ) ); + } + rejoin = qtrue; + break; + } + } + + if( !rejoin ) + return; + + for( j = 0; j < CHAT_MAXCHAN; j++ ) + { + if( !ent->client->pers.chat[ j ][ 0 ] ) + continue; + + for( i = 0; i < level.maxclients; i++ ) + { + target = &g_entities[ i ]; + if( target && target->client && + target->client->pers.connected == CON_CONNECTED && + !Q_stricmp( target->client->pers.chat[ j ], ent->client->pers.chat[ j ] ) ) + { + trap_SendServerCommand( i, va( "print \"join: %s^7 has rejoined channel #%d\n\"", + ent->client->pers.netname, j ) ); + } + } + } +} + +void G_admin_chat_update( gentity_t *ent, int chan ) +{ + int i; + + if( !ent || !ent->client || ent->client->pers.adminLevel < 1 ) + return; + + if( chan < 0 || chan > CHAT_MAXCHAN - 1 ) + return; + + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) + { + if( !Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) + { + Q_strncpyz( g_admin_admins[ i ]->chat[ chan ], + ent->client->pers.chat[ chan ], + sizeof( g_admin_admins[ i ]->chat[ chan ] ) ); + return; + } + } +} + // if we can't parse any levels from readconfig, set up default // ones to make new installs easier for admins static void admin_default_levels( void ) @@ -828,7 +1090,7 @@ static void admin_default_levels( void ) sizeof( l->name ) ); Q_strncpyz( g_admin_levels[ 4 ]->flags, "listplayers admintest help specme time putteam spec999 kick mute showbans " - "ban namelog warn denybuild ADMINCHAT SEESFULLLISTPLAYERS", + "ban namelog warn denybuild invisible ADMINCHAT SEESFULLLISTPLAYERS", sizeof( l->flags ) ); Q_strncpyz( g_admin_levels[ 5 ]->name, "^1Server Operator", @@ -897,7 +1159,7 @@ void G_admin_set_adminname( gentity_t *ent ) } } -// Get an admin's registered name +// get an admin's realname const char *G_admin_get_adminname( gentity_t *ent ) { int i; @@ -914,8 +1176,6 @@ const char *G_admin_get_adminname( gentity_t *ent ) return ent->client->pers.netname; } -// Get an admin's name to print as the user of various commands, -// obeying admin stealth settings when necessary char* G_admin_adminPrintName( gentity_t *ent ) { char *out; @@ -1110,26 +1370,27 @@ static int admin_listadmins( gentity_t *ent, int start, char *search, int minlev break; } } - ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", + ADMBP( va( "%4i %4i %s^7 (*%s) ^1%1s ^7%s^7\n", i, l, lname, guid_stub, + ( G_admin_permission( &g_entities[ i ], ADMF_BAN_IMMUNITY ) ) ? "I" : "", vic->client->pers.netname ) ); drawn++; } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && drawn < MAX_ADMIN_LISTITEMS; i++ ) - if( g_admin_admins[ i ]->level >= minlevel ) + if( ( minlevel > 0 && g_admin_admins[ i ]->level >= minlevel ) || + ( minlevel < 0 && g_admin_admins[ i ]->level <= minlevel ) || + ( minlevel == 0 && g_admin_admins[ i ]->level != 0 ) ) { - if( start ) { start--; continue; } - if( search[ 0 ] ) { G_SanitiseString( g_admin_admins[ i ]->name, name, sizeof( name ) ); @@ -1174,11 +1435,12 @@ static int admin_listadmins( gentity_t *ent, int start, char *search, int minlev break; } } - ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n", + ADMBP( va( "%4i %4i %s^7 (*%s) ^1%1s ^7%s^7\n", ( i + MAX_CLIENTS ), g_admin_admins[ i ]->level, lname, guid_stub, + ( G_admin_permission_guid( g_admin_admins[ i ]->guid, ADMF_BAN_IMMUNITY ) ) ? "I" : "", g_admin_admins[ i ]->name ) ); drawn++; } @@ -1223,6 +1485,7 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) int t; char notice[51]; qboolean ignoreIP = qfalse; + qboolean banned = qfalse; trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); @@ -1252,17 +1515,15 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) if(!IP[k]) continue; userIP |= IP[k] << 8*(k-1); } - ignoreIP = G_admin_permission_guid( guid , ADMF_BAN_IMMUNITY ); + ignoreIP = G_admin_permission_guid( guid, ADMF_BAN_IMMUNITY ); for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { // 0 is for perm ban if( g_admin_bans[ i ]->expires != 0 && ( g_admin_bans[ i ]->expires - t ) < 1 ) continue; - //if suspend time is over continue if( g_admin_bans[ i ]->suspend >= t ) continue; - if( !ignoreIP ) { tempIP = userIP; @@ -1295,47 +1556,38 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) if( intIP == tempIP || mask == 0 ) { - char duration[ 32 ]; - G_admin_duration( ( g_admin_bans[ i ]->expires - t ), - duration, sizeof( duration ) ); - - // flood protected - if( t - lastConnectTime >= 300 || - Q_stricmp( lastConnectIP, ip ) ) - { - lastConnectTime = t; - Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) ); - - G_WarningsPrintf( - "ban", - "Banned player %s^7 (%s^7) tried to connect (ban #%i on %s by %s^7 expires %s reason: %s^7 )\n", - Info_ValueForKey( userinfo, "name" ), - g_admin_bans[ i ]->name, - i+1, - ip, - g_admin_bans[ i ]->banner, - duration, - g_admin_bans[ i ]->reason ); - } - - Com_sprintf( - reason, - rlen, - "You have been banned by %s^7 reason: %s^7 expires: %s %s", - g_admin_bans[ i ]->banner, - g_admin_bans[ i ]->reason, - duration, - notice - ); G_LogPrintf("Banned player tried to connect from IP %s\n", ip); - return qtrue; + banned = qtrue; } } - if( *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) + if( !banned && *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) + { + G_LogPrintf("Banned player tried to connect with GUID %s\n", guid); + banned = qtrue; + } + if( banned ) { char duration[ 32 ]; G_admin_duration( ( g_admin_bans[ i ]->expires - t ), duration, sizeof( duration ) ); + + // flood protected + if( t - lastConnectTime >= 300 || + Q_stricmp( lastConnectIP, ip ) ) + { + lastConnectTime = t; + Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) ); + + G_AdminsPrintf( + "Banned player %s^7 (%s^7) tried to connect (ban #%i on %s by %s^7 expires %s reason: %s^7 )\n", + Info_ValueForKey( userinfo, "name" ), + g_admin_bans[ i ]->name, + i+1, + ip, + g_admin_bans[ i ]->banner, + duration, + g_admin_bans[ i ]->reason ); + } Com_sprintf( reason, rlen, @@ -1344,7 +1596,6 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) g_admin_bans[ i ]->reason, duration ); - G_Printf("Banned player tried to connect with GUID %s\n", guid); return qtrue; } } @@ -1469,10 +1720,7 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect ) char n2[ MAX_NAME_LENGTH ]; int clientNum = ( client - level.clients ); - if ( client->sess.invisible == qfalse ) - { - G_admin_seen_update( client->pers.guid ); - } + G_admin_seen_update( client, disconnect ); G_SanitiseString( client->pers.netname, n1, sizeof( n1 ) ); for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) @@ -1539,11 +1787,6 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect ) G_AdminsPrintf( "^7%s^7's Putteam spectator has been restored\n", client->pers.netname ); g_admin_namelog[ i ]->specExpires = 0; } - if( g_admin_namelog[ i ]->voteCount > 0 ) - { - client->pers.voteCount = g_admin_namelog[ i ]->voteCount; - g_admin_namelog[ i ]->voteCount = 0; - } } else { @@ -1573,12 +1816,9 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect ) { g_admin_namelog[ i ]->specExpires = client->pers.specExpires; } - if( client->pers.voteCount > 0 ) - { - g_admin_namelog[ i ]->voteCount = client->pers.voteCount; - } } + return; } } @@ -1698,10 +1938,6 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) { admin_readconfig_string( &cnf, a->flags, sizeof( a->flags ) ); } - else if( !Q_stricmp( t, "seen" ) ) - { - admin_readconfig_int( &cnf, &a->seen ); - } else { ADMP( va( "^3!readconfig: ^7[admin] parse error near %s on line %d\n", @@ -1863,6 +2099,9 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) for( i = 0; i < level.maxclients; i++ ) if( level.clients[ i ].pers.connected != CON_DISCONNECTED ) level.clients[ i ].pers.adminLevel = G_admin_level( &g_entities[ i ] ); + + G_admin_chat_readconfig( ent ); + return qtrue; } @@ -1874,7 +2113,6 @@ qboolean G_admin_time( gentity_t *ent, int skiparg ) t = trap_RealTime( &qt ); ADMP( va( "^3!time: ^7local time is %02i:%02i:%02i\n", qt.tm_hour, qt.tm_min, qt.tm_sec ) ); - return qtrue; } @@ -2138,379 +2376,6 @@ qboolean G_admin_setlevel( gentity_t *ent, int skiparg ) return qtrue; } -static int SortFlags( const void *pa, const void *pb ) -{ - char *a = (char *)pa; - char *b = (char *)pb; - - if( *a == '-' || *a == '+' ) - a++; - if( *b == '-' || *b == '+' ) - b++; - return strcmp(a, b); -} - -#define MAX_USER_FLAGS 200 -const char *G_admin_user_flag( char *oldflags, char *flag, qboolean add, qboolean clear, - char *newflags, int size ) -{ - char *token, *token_p; - char *key; - char head_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; - char tail_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; - char allflag[ MAX_ADMIN_FLAG_LEN ]; - char newflag[ MAX_ADMIN_FLAG_LEN ]; - int head_count = 0; - int tail_count = 0; - qboolean flagset = qfalse; - int i; - - if( !flag[ 0 ] ) - { - return "invalid admin flag"; - } - - allflag[ 0 ] = '\0'; - token_p = oldflags; - while( *( token = COM_Parse( &token_p ) ) ) - { - key = token; - if( *key == '-' || *key == '+' ) - key++; - - if( !strcmp( key, flag ) ) - { - if( flagset ) - continue; - flagset = qtrue; - if( clear ) - { - // clearing ALLFLAGS will result in clearing any following flags - if( !strcmp( key, ADMF_ALLFLAGS ) ) - break; - else - continue; - } - Com_sprintf( newflag, sizeof( newflag ), "%s%s", - ( add ) ? "+" : "-", key ); - } - else - { - Q_strncpyz( newflag, token, sizeof( newflag ) ); - } - - if( !strcmp( key, ADMF_ALLFLAGS ) ) - { - if( !allflag[ 0 ] ) - Q_strncpyz( allflag, newflag, sizeof( allflag ) ); - continue; - } - - if( !allflag[ 0 ] ) - { - if( head_count < MAX_USER_FLAGS ) - { - Q_strncpyz( head_flags[ head_count ], newflag, - sizeof( head_flags[ head_count ] ) ); - head_count++; - } - } - else - { - if( tail_count < MAX_USER_FLAGS ) - { - Q_strncpyz( tail_flags[ tail_count ], newflag, - sizeof( tail_flags[ tail_count ] ) ); - tail_count++; - } - } - } - - if( !flagset && !clear ) - { - if( !strcmp( flag, ADMF_ALLFLAGS ) ) - { - Com_sprintf( allflag, sizeof( allflag ), "%s%s", - ( add ) ? "" : "-", ADMF_ALLFLAGS ); - } - else if( !allflag[ 0 ] ) - { - if( head_count < MAX_USER_FLAGS ) - { - Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ), - "%s%s", ( add ) ? "" : "-", flag ); - head_count++; - } - } - else - { - if( tail_count < MAX_USER_FLAGS ) - { - Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ), - "%s%s", ( add ) ? "+" : "-", flag ); - tail_count++; - } - } - } - - qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags ); - qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags ); - - // rebuild flags - newflags[ 0 ] = '\0'; - for( i = 0; i < head_count; i++ ) - { - Q_strcat( newflags, size, - va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) ); - } - if( allflag[ 0 ] ) - { - Q_strcat( newflags, size, - va( "%s%s", ( newflags[ 0 ] ) ? " ": "", allflag ) ); - - for( i = 0; i < tail_count; i++ ) - { - Q_strcat( newflags, size, - va( " %s", tail_flags[ i ] ) ); - } - } - - return NULL; -} - -typedef struct { - char *flag; - char *description; -} AdminFlagListEntry_t; -static AdminFlagListEntry_t adminFlagList[] = -{ - { ADMF_ACTIVITY, "inactivity rules do not apply" }, - { ADMF_ADMINCHAT, "can see and use admin chat" }, - { ADMF_ALLFLAGS, "has all flags and can use any command" }, - { ADMF_BAN_IMMUNITY, "immune from IP bans" }, - { ADMF_CAN_PERM_BAN, "can permanently ban players" }, - { ADMF_DBUILDER, "permanent designated builder" }, - { ADMF_FORCETEAMCHANGE, "team balance rules do not apply" }, - { ADMF_INCOGNITO, "does not show as admin in !listplayers" }, - { ADMF_IMMUNITY, "cannot be vote kicked or muted" }, - { ADMF_IMMUTABLE, "admin commands cannot be used on them" }, - { ADMF_NOCENSORFLOOD, "no flood protection" }, - { ADMF_NO_VOTE_LIMIT, "vote limitations do not apply" }, - { ADMF_SEESFULLLISTPLAYERS, "sees all info in !listplayers" }, - { ADMF_SPEC_ALLCHAT, "can see team chat as spectator" }, - { ADMF_ADMINSTEALTH, "uses admin stealth" }, - { ADMF_TEAMCHANGEFREE, "keeps credits on team switch" }, - { ADMF_TEAMCHAT_CMD, "can run commands from team chat" }, - { ADMF_UNACCOUNTABLE, "does not need to specify reason for kick/ban" }, - { ADMF_NO_CHAT, "can not talk" }, - { ADMF_NO_VOTE, "can not call votes" } -}; -static int adminNumFlags= sizeof( adminFlagList ) / sizeof( adminFlagList[ 0 ] ); - -#define MAX_LISTCOMMANDS 128 -qboolean G_admin_flaglist( gentity_t *ent, int skiparg ) -{ - qboolean shown[ MAX_LISTCOMMANDS ]; - int i, j; - int count = 0; - - ADMBP_begin(); - - ADMBP( "^3Ability flags:\n" ); - - for( i = 0; i < adminNumFlags; i++ ) - { - ADMBP( va( " %s%-20s ^7%s\n", - ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1", - adminFlagList[ i ].flag, - adminFlagList[ i ].description ) ); - } - - ADMBP( "^3Command flags:\n" ); - - memset( shown, 0, sizeof( shown ) ); - for( i = 0; i < adminNumCmds; i++ ) - { - if( i < MAX_LISTCOMMANDS && shown[ i ] ) - continue; - ADMBP( va( " ^5%-20s^7", g_admin_cmds[ i ].flag ) ); - for( j = i; j < adminNumCmds; j++ ) - { - if( !strcmp( g_admin_cmds[ j ].flag, g_admin_cmds[ i ].flag ) ) - { - ADMBP( va( " %s", g_admin_cmds[ j ].keyword ) ); - if( j < MAX_LISTCOMMANDS ) - shown[ j ] = qtrue; - } - } - ADMBP( "\n" ); - count++; - } - - ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n", - adminNumFlags, count ) ); - - ADMBP_end(); - - return qtrue; -} - -qboolean G_admin_flag( gentity_t *ent, int skiparg ) -{ - char command[ MAX_ADMIN_CMD_LEN ], *cmd; - char name[ MAX_NAME_LENGTH ]; - char flagbuf[ MAX_ADMIN_FLAG_LEN ]; - char *flag; - int id; - char adminname[ MAX_NAME_LENGTH ] = {""}; - const char *result; - qboolean add = qtrue; - qboolean clear = qfalse; - int admin_level = -1; - int i, level; - - G_SayArgv( skiparg, command, sizeof( command ) ); - cmd = command; - if( *cmd == '!' ) - cmd++; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) ); - return qfalse; - } - - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( name[ 0 ] == '*' ) - { - if( ent ) - { - ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) ); - return qfalse; - } - id = atoi( name + 1 ); - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - if( g_admin_levels[ i ]->level == id ) - { - admin_level = i; - break; - } - } - if( admin_level < 0 ) - { - ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) ); - return qfalse; - } - Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id ); - } - else - { - id = G_admin_find_admin_slot( ent, name, cmd, adminname, sizeof( adminname ) ); - if( id < 0 ) - return qfalse; - - if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) - { - ADMP( va( "^3%s:^7 your intended victim has a higher admin level than you\n", cmd ) ); - return qfalse; - } - } - - if( G_SayArgc() < 3 + skiparg ) - { - flag = ""; - level = 0; - if( admin_level < 0 ) - { - for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) - { - if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level ) - { - flag = g_admin_levels[ i ]->flags; - level = g_admin_admins[ id ]->level; - break; - } - } - ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n", - cmd, adminname, g_admin_admins[ id ]->flags) ); - } - else - { - flag = g_admin_levels[ admin_level ]->flags; - level = g_admin_levels[ admin_level ]->level; - } - ADMP( va( "^3%s:^7 level %d flags are '%s'\n", - cmd, level, flag ) ); - - return qtrue; - } - - G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) ); - flag = flagbuf; - if( flag[ 0 ] == '-' || flag[ 0 ] == '+' ) - { - add = ( flag[ 0 ] == '+' ); - flag++; - } - if( ent && !Q_stricmp( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) - { - ADMP( va( "^3%s:^7 you may not change your own flags (use rcon)\n", cmd ) ); - return qfalse; - } - if( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) ) - { - ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) ); - return qfalse; - } - - if( !Q_stricmp( cmd, "unflag" ) ) - { - clear = qtrue; - } - - if( admin_level < 0 ) - { - result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear, - g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) ); - } - else - { - result = G_admin_user_flag( g_admin_levels[ admin_level ]->flags, flag, add, clear, - g_admin_levels[ admin_level ]->flags, - sizeof( g_admin_levels[ admin_level ]->flags ) ); - } - if( result ) - { - ADMP( va( "^3!flag: ^7an error occured setting flag '^3%s^7', %s\n", - flag, result ) ); - return qfalse; - } - - if( !Q_stricmp( cmd, "flag" ) ) - { - G_AdminsPrintf( "^3!%s: ^7%s^7 was %s admin flag '%s' by %s\n", - cmd, adminname, - ( add ) ? "given" : "denied", - flag, - ( ent ) ? ent->client->pers.netname : "console" ); - } - else - { - G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n", - cmd, flag, adminname, - ( ent ) ? ent->client->pers.netname : "console" ); - } - - if( !g_admin.string[ 0 ] ) - ADMP( va( "^3!%s: ^7WARNING g_admin not set, not saving admin record " - "to a file\n", cmd ) ); - else - admin_writeconfig(); - - return qtrue; -} - int G_admin_parse_time( const char *time ) { int seconds = 0, num = 0; @@ -2544,6 +2409,49 @@ int G_admin_parse_time( const char *time ) return seconds; } +static void admin_check_duplicate_ban( int ban ) +{ + qtime_t qt; + int t; + int i, j; + qboolean immune; + + if ( ban < 0 || ban >= MAX_ADMIN_BANS || !g_admin_bans[ ban ] ) + return; + + t = trap_RealTime( &qt ); + for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) + { + if( g_admin_bans[ i ]->expires != 0 + && ( g_admin_bans[ i ]->expires - t ) < 1 ) + { + continue; + } + + if( i != ban && + strstr( g_admin_bans[ ban ]->ip, g_admin_bans[ i ]->ip ) == g_admin_bans[ ban ]->ip ) + { + immune = qfalse; + + for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) + { + if( !Q_stricmp( g_admin_bans[ ban ]->guid, g_admin_admins[ j ]->guid ) && + G_admin_permission_guid( g_admin_admins[ j ]->guid, ADMF_BAN_IMMUNITY ) ) + { + immune = qtrue; + } + } + + G_AdminsPrintf( "new ban #%d duplicates %sban #%d (%s^7)%s.\n", + ban + 1, + ( g_admin_bans[ i ]->suspend > t ) ? "SUSPENDED " : "", + i + 1, + g_admin_bans[ i ]->name, + ( immune ) ? ", player has immunity" : "" ); + } + } +} + static qboolean admin_create_ban( gentity_t *ent, char *netname, char *guid, @@ -2597,6 +2505,82 @@ static qboolean admin_create_ban( gentity_t *ent, return qfalse; } g_admin_bans[ i ] = b; + + admin_check_duplicate_ban( i ); + + return qtrue; +} + +static qboolean admin_create_ban_check_repeats( gentity_t *ent, + char *netname, + char *guid, + char *ip, + int seconds, + char *reason ) +{ + qboolean repeatBan = qfalse; + qboolean isKick = qfalse; + qtime_t qt; + int t; + int i; + + t = trap_RealTime( &qt ); + + if( seconds >= G_admin_parse_time( g_adminTempBan.string ) - 60 && + seconds <= G_admin_parse_time( g_adminTempBan.string ) + 60 ) + isKick = qtrue; + + if( g_adminBanRepeatKicks.integer && isKick ) + { + for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ] && !repeatBan; i++ ) + { + if( g_admin_bans[ i ]->expires != 0 && + g_admin_bans[ i ]->expires - t < 1 ) + continue; + + if( !Q_stricmp( ip, g_admin_bans[ i ]->ip ) || + ( guid[0] != 'X' && !Q_stricmp( guid, g_admin_bans[ i ]->guid) )) + { + char duration[ 32 ]; + + g_admin_bans[ i ]->suspend = 0; + g_admin_bans[ i ]->expires += g_adminBanRepeatKicks.integer * 60 * 60; + repeatBan = qtrue; + + G_admin_duration( ( g_admin_bans[ i ]->expires - t ), duration, sizeof( duration ) ); + trap_SendServerCommand( -1, + va( "print \"^3autoban: ^7%s^7 has been auto-banned. duration: %s, reason: repeated kicks\n\"", + netname, duration ) ); + + return qtrue; + } + } + } + + if( !admin_create_ban( ent, netname, guid, ip, seconds, reason ) ) + return qfalse; + + if( g_adminBanRepeatKicks.integer && seconds > 0 && isKick ) + { + int newestBan = -1; + int length; + + length = g_adminBanRepeatKicks.integer * 60 * 60; + if( admin_create_ban( ent, netname, guid, ip, length, reason ) ) + { + for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) + { + newestBan = i; + } + if( newestBan >= 0 && + !Q_stricmp( ip, g_admin_bans[ newestBan ]->ip ) ) + { + g_admin_bans[ newestBan ]->suspend = t + length; + Q_strncpyz( g_admin_bans[ newestBan ]->banner, "^3auto-banner", sizeof( g_admin_bans[ newestBan ]->banner ) ); + } + } + } + return qtrue; } @@ -2634,7 +2618,8 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg ) return qfalse; } vic = &g_entities[ pids[ 0 ] ]; - admin_create_ban( ent, + G_admin_autorevert( vic ); + admin_create_ban_check_repeats( ent, vic->client->pers.netname, vic->client->pers.guid, vic->client->pers.ip, G_admin_parse_time( g_adminTempBan.string ), @@ -2642,10 +2627,20 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg ) if( g_admin.string[ 0 ] ) admin_writeconfig(); + vic->client->pers.karma -= 5000; + trap_SendServerCommand( pids[ 0 ], va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\n%s\"", ( ent ) ? va( "admin:\n%s", G_admin_adminPrintName( ent ) ) : "admin\nconsole", ( *reason ) ? reason : "kicked by admin", notice ) ); + + G_LogPrintf( "kick: %i %i [%s] (%s) %s^7 %s^7\n", + vic->client->ps.clientNum, + G_admin_parse_time( g_adminTempBan.string ), + vic->client->pers.ip, + vic->client->pers.guid, + vic->client->pers.netname, + ( *reason ) ? reason : "automatic temp ban created by kick" ); trap_DropClient( pids[ 0 ], va( "kicked%s^7, reason: %s", ( ent ) ? va( " by %s", G_admin_adminPrintName( ent ) ) : " by console", @@ -2669,6 +2664,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) char s2[ MAX_NAME_LENGTH ]; char guid_stub[ 9 ]; char notice[51]; + gentity_t *vic; trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); @@ -2823,7 +2819,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) return qfalse; } - admin_create_ban( ent, + admin_create_ban_check_repeats( ent, g_admin_namelog[ logmatch ]->name[ 0 ], g_admin_namelog[ logmatch ]->guid, g_admin_namelog[ logmatch ]->ip, @@ -2836,6 +2832,13 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) else admin_writeconfig(); + G_LogPrintf( "ban: %i %i [%s] (%s) %s^7 %s^7\n", + g_admin_namelog[ logmatch ]->slot, seconds, + g_admin_namelog[ logmatch ]->ip, + g_admin_namelog[ logmatch ]->guid, + g_admin_namelog[ logmatch ]->name[ 0 ], + ( *reason ) ? reason : "banned by admin" ); + if( g_admin_namelog[ logmatch ]->slot == -1 ) { // client is already disconnected so stop here @@ -2847,6 +2850,11 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) ( *reason ) ? reason : "banned by admin" ) ); return qtrue; } + vic = &g_entities[ g_admin_namelog[ logmatch ]->slot ]; + G_admin_autorevert( vic ); + + if( g_karma.integer ) + vic->client->pers.karma -= 5000; trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot, va( "disconnect \"You have been banned.\n" @@ -3022,6 +3030,7 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg ) ADMP( "^3!subnetban: ^7Only console may ban such a large network. Regular admins may only ban >=16.\n" ); return qfalse; } + if( strcmp(exl, "!") ) { ADMP( "^3!subnetban: ^1WARNING:^7 you are about to ban a large network, use !subnetban [ban] [mask] ! to force^7\n" ); @@ -3065,12 +3074,12 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg ) { Q_strncpyz( cIPRlow, - va("%u.%u.%u.%u", (IPRlow & (255 << 24)) >> 24, (IPRlow & (255 << 16)) >> 16, (IPRlow & (255 << 8)) >> 8, IPRlow & 255), + va("%i.%i.%i.%i", (IPRlow & (255 << 24)) >> 24, (IPRlow & (255 << 16)) >> 16, (IPRlow & (255 << 8)) >> 8, IPRlow & 255), sizeof( cIPRlow ) ); Q_strncpyz( cIPRhigh, - va("%u.%u.%u.%u", (IPRhigh & (255 << 24)) >> 24, (IPRhigh & (255 << 16)) >> 16, (IPRhigh & (255 << 8)) >> 8, IPRhigh & 255), + va("%i.%i.%i.%i", (IPRhigh & (255 << 24)) >> 24, (IPRhigh & (255 << 16)) >> 16, (IPRhigh & (255 << 8)) >> 8, IPRhigh & 255), sizeof( cIPRhigh ) ); } @@ -3097,16 +3106,15 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg ) return qtrue; } + qboolean G_admin_suspendban( gentity_t *ent, int skiparg ) { int bnum; int length; - int timenow = 0; int expires = 0; char *arg; char bs[ 5 ]; char duration[ 32 ]; - qtime_t qt; if( G_SayArgc() < 3 + skiparg ) { @@ -3127,9 +3135,7 @@ qboolean G_admin_suspendban( gentity_t *ent, int skiparg ) } arg = G_SayConcatArgs( 2 + skiparg ); - timenow = trap_RealTime( &qt ); length = G_admin_parse_time( arg ); - if( length < 0 ) { ADMP( "^3!suspendban: ^7invalid length\n" ); @@ -3140,16 +3146,13 @@ qboolean G_admin_suspendban( gentity_t *ent, int skiparg ) length = MAX_ADMIN_BANSUSPEND_DAYS * 24 * 60 * 60; ADMP( va( "^3!suspendban: ^7maximum ban suspension is %d days\n", MAX_ADMIN_BANSUSPEND_DAYS ) ); - } else if( g_admin_bans[ bnum - 1 ]->expires > 0 && length + timenow > g_admin_bans[ bnum - 1 ]->expires ) { - length = g_admin_bans[ bnum - 1 ]->expires - timenow; - G_admin_duration( length , duration, sizeof( duration ) ); - ADMP( va( "^3!suspendban: ^7Suspension Duration trimmed to Ban duration: %s\n", - duration ) ); } if ( length > 0 ) { - expires = timenow + length; + qtime_t qt; + + expires = trap_RealTime( &qt ) + length; } if( g_admin_bans[ bnum - 1 ]->suspend == expires ) { @@ -3225,93 +3228,6 @@ qboolean G_admin_unban( gentity_t *ent, int skiparg ) return qtrue; } -qboolean G_admin_putteam( gentity_t *ent, int skiparg ) -{ - int pids[ MAX_CLIENTS ]; - char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ]; - gentity_t *vic; - pTeam_t teamnum = PTE_NONE; - char teamdesc[ 32 ] = {"spectators"}; - char secs[ 7 ]; - int seconds = 0; - qboolean useDuration = qfalse; - - G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - G_SayArgv( 2 + skiparg, team, sizeof( team ) ); - if( G_SayArgc() < 3 + skiparg ) - { - ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s] (duration)\n" ); - return qfalse; - } - - if( G_ClientNumbersFromString( name, pids ) != 1 ) - { - G_MatchOnePlayer( pids, err, sizeof( err ) ); - ADMP( va( "^3!putteam: ^7%s\n", err ) ); - return qfalse; - } - if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) - { - ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher " - " admin level than you\n" ); - return qfalse; - } - vic = &g_entities[ pids[ 0 ] ]; - - if ( vic->client->sess.invisible == qtrue ) - { - ADMP( "^3!putteam: ^7invisible players cannot join a team\n" ); - return qfalse; - } - - switch( team[ 0 ] ) - { - case 'a': - teamnum = PTE_ALIENS; - Q_strncpyz( teamdesc, "aliens", sizeof( teamdesc ) ); - break; - case 'h': - teamnum = PTE_HUMANS; - Q_strncpyz( teamdesc, "humans", sizeof( teamdesc ) ); - break; - case 's': - teamnum = PTE_NONE; - break; - default: - ADMP( va( "^3!putteam: ^7unknown team %c\n", team[ 0 ] ) ); - return qfalse; - } - //duration code - if( G_SayArgc() > 3 + skiparg ) { - //can only lock players in spectator - if ( teamnum != PTE_NONE ) - { - ADMP( "^3!putteam: ^7You can only lock a player into the spectators team\n" ); - return qfalse; - } - G_SayArgv( 3 + skiparg, secs, sizeof( secs ) ); - seconds = G_admin_parse_time( secs ); - useDuration = qtrue; - } - - if( vic->client->pers.teamSelection == teamnum && teamnum != PTE_NONE ) - { - ADMP( va( "^3!putteam: ^7%s ^7is already on the %s team\n", vic->client->pers.netname, teamdesc ) ); - return qfalse; - } - - if( useDuration == qtrue && seconds > 0 ) { - vic->client->pers.specExpires = level.time + ( seconds * 1000 ); - } - G_ChangeTeam( vic, teamnum ); - - AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team%s\n\"", - ( ent ) ? G_admin_adminPrintName( ent ) : "console", - vic->client->pers.netname, teamdesc, - ( seconds ) ? va( " for %i seconds", seconds ) : "" ) ); - return qtrue; -} - qboolean G_admin_seen(gentity_t *ent, int skiparg ) { char name[ MAX_NAME_LENGTH ]; @@ -3362,11 +3278,8 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg ) if( i == id || (search[ 0 ] && strstr( name, search ) ) ) { - if ( vic->client->sess.invisible == qfalse ) - { - ADMBP( va( "^3%4d ^7%s^7 is currently playing\n", i, vic->client->pers.netname ) ); - count++; - } + ADMBP( va( "%4d %s^7 is currently playing\n", i, vic->client->pers.netname ) ); + count++; } } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && count < 10; i++ ) @@ -3384,11 +3297,8 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg ) if( !Q_stricmp( vic->client->pers.guid, g_admin_admins[ i ]->guid ) && strstr( name, search ) ) { - if ( vic->client->sess.invisible == qfalse ) - { - ison = qtrue; - break; - } + ison = qtrue; + break; } } @@ -3396,7 +3306,7 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg ) { if( id == -1 ) continue; - ADMBP( va( "^3%4d ^7%s^7 is currently playing\n", + ADMBP( va( "%4d %s^7 is currently playing\n", i + MAX_CLIENTS, g_admin_admins[ i ]->name ) ); } else @@ -3422,10 +3332,42 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg ) return qtrue; } -void G_admin_seen_update( char *guid ) +void G_admin_karma_sync( void ) { + gclient_t *p; + int i, j; + + if( !g_karma.integer ) + return; + + for( i = 0; i < level.maxclients; i++ ) + { + p = &level.clients[ i ]; + + if( p->pers.connected != CON_CONNECTED && + p->pers.connected != CON_CONNECTING ) + continue; + if( p->pers.adminLevel < 1 ) + continue; + + for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) + { + if( !Q_stricmp( g_admin_admins[ j ]->guid, p->pers.guid ) ) + { + if( p->pers.karma ) + g_admin_admins[ j ]->karma = p->pers.karma; + break; + } + } + } +} + +void G_admin_seen_update( gclient_t *client, qboolean disconnect ) +{ + char *guid; int i; + guid = client->pers.guid; for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) { if( !Q_stricmp( g_admin_admins[ i ]->guid, guid ) ) @@ -3433,11 +3375,198 @@ void G_admin_seen_update( char *guid ) qtime_t qt; g_admin_admins[ i ]->seen = trap_RealTime( &qt ); + if ( disconnect && client->pers.karma ) + g_admin_admins[ i ]->karma = client->pers.karma; return; } } } +typedef struct +{ + g_admin_admin_t *admin; + int id; +} +g_admin_sort_t; + +static int SortSeenTimes( const void *av, const void *bv) +{ + const g_admin_sort_t *a = av; + const g_admin_sort_t *b = bv; + + if( !a->admin || !b->admin ) + return 0; + + if( a->admin->seen > b->admin->seen ) + return 1; + if( a->admin->seen < b->admin->seen ) + return -1; + + return 0; +} + +qboolean G_admin_expire( gentity_t *ent, int skiparg ) +{ + g_admin_sort_t sort_list[ MAX_ADMIN_ADMINS ]; + char arg[ MAX_ADMIN_CMD_LEN ]; + qboolean confirm = qfalse; + qtime_t qt; + int t; + int count = 0; + int max = 5; + int i; + + if( g_adminExpireTime.integer < 1 ) + { + ADMP( "^3!expire: ^7expire is disabled\n" ); + return qfalse; + } + + if( G_SayArgc() > 1 + skiparg ) + { + G_SayArgv( skiparg + 1, arg, sizeof( arg ) ); + if( !Q_stricmp( arg, "confirm" ) ) + confirm = qtrue; + } + + t = trap_RealTime( &qt ); + + memset( sort_list, 0, sizeof( sort_list ) ); + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) + { + sort_list[ i ].admin = g_admin_admins[ i ]; + sort_list[ i ].id = MAX_CLIENTS + i; + count++; + } + + qsort( sort_list, count, sizeof( sort_list[ 0 ] ), SortSeenTimes ); + + ADMBP_begin(); + for( i = 0; i < count && max > 0 ; i++ ) + { + if( !sort_list[ i ].admin ) + continue; + if( sort_list[ i ].admin->level != 1 ) + continue; + if( t - sort_list[ i ].admin->seen > g_adminExpireTime.integer * 86400 ) + { + char sduration[ 32 ]; + + max--; + G_admin_duration( t - sort_list[ i ].admin->seen, sduration, sizeof( sduration ) ); + + if( confirm ) + { + trap_SendConsoleCommand( EXEC_APPEND, va( "!setlevel %d 0;", sort_list[ i ].id ) ); + } + else + { + ADMBP( va( " ^7%d %s^7 last seen %s%s\n", + sort_list[ i ].id, + sort_list[ i ].admin->name, + (sort_list[ i ].admin->seen ) ? sduration : "", + (sort_list[ i ].admin->seen ) ? " ago" : "time is unknown" ) ); + } + } + } + + ADMBP( va( "^3!expire: ^7%s %d level 1 admin(s) older than %d days\n", + ( confirm ) ? "expired" : "listed", + 5 - max, g_adminExpireTime.integer ) ); + if ( !confirm ) + ADMBP( "^3!expire: ^7to make this permanent use '^2!expire confirm^7'\n" ); + + ADMBP_end(); + return qtrue; +} + +qboolean G_admin_putteam( gentity_t *ent, int skiparg ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ]; + gentity_t *vic; + pTeam_t teamnum = PTE_NONE; + char teamdesc[ 32 ] = {"spectators"}; + char secs[ 7 ]; + int seconds = 0; + qboolean useDuration = qfalse; + + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + G_SayArgv( 2 + skiparg, team, sizeof( team ) ); + if( G_SayArgc() < 3 + skiparg ) + { + ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s] (duration)\n" ); + return qfalse; + } + + if( G_ClientNumbersFromString( name, pids ) != 1 ) + { + G_MatchOnePlayer( pids, err, sizeof( err ) ); + ADMP( va( "^3!putteam: ^7%s\n", err ) ); + return qfalse; + } + if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + { + ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher " + " admin level than you\n" ); + return qfalse; + } + vic = &g_entities[ pids[ 0 ] ]; + + if ( vic->client->sess.invisible == qtrue ) + { + ADMP( "^3!putteam: ^7invisible players cannot join a team\n" ); + return qfalse; + } + + switch( team[ 0 ] ) + { + case 'a': + teamnum = PTE_ALIENS; + Q_strncpyz( teamdesc, "aliens", sizeof( teamdesc ) ); + break; + case 'h': + teamnum = PTE_HUMANS; + Q_strncpyz( teamdesc, "humans", sizeof( teamdesc ) ); + break; + case 's': + teamnum = PTE_NONE; + break; + default: + ADMP( va( "^3!putteam: ^7unknown team %c\n", team[ 0 ] ) ); + return qfalse; + } + //duration code + if( G_SayArgc() > 3 + skiparg ) { + //can only lock players in spectator + if ( teamnum != PTE_NONE ) + { + ADMP( "^3!putteam: ^7You can only lock a player into the spectators team\n" ); + return qfalse; + } + G_SayArgv( 3 + skiparg, secs, sizeof( secs ) ); + seconds = G_admin_parse_time( secs ); + useDuration = qtrue; + } + + if( vic->client->pers.teamSelection == teamnum && teamnum != PTE_NONE ) + { + ADMP( va( "^3!putteam: ^7%s ^7is already on the %s team\n", vic->client->pers.netname, teamdesc ) ); + return qfalse; + } + + if( useDuration == qtrue && seconds > 0 ) { + vic->client->pers.specExpires = level.time + ( seconds * 1000 ); + } + G_ChangeTeam( vic, teamnum ); + + AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team%s\n\"", + ( ent ) ? G_admin_adminPrintName( ent ) : "console", + vic->client->pers.netname, teamdesc, + ( seconds ) ? va( " for %i seconds", seconds ) : "" ) ); + return qtrue; +} + void G_admin_adminlog_cleanup( void ) { int i; @@ -3469,7 +3598,6 @@ void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skipar !Q_stricmp( command, "listplayers" ) || !Q_stricmp( command, "namelog" ) || !Q_stricmp( command, "showbans" ) || - !Q_stricmp( command, "seen" ) || !Q_stricmp( command, "time" ) ) return; @@ -3722,6 +3850,261 @@ qboolean G_admin_adminlog( gentity_t *ent, int skiparg ) return qtrue; } +void G_admin_tklog_cleanup( void ) +{ + int i; + + for( i = 0; i < MAX_ADMIN_TKLOGS && g_admin_tklog[ i ]; i++ ) + { + G_Free( g_admin_tklog[ i ] ); + g_admin_tklog[ i ] = NULL; + } + + admin_tklog_index = 0; +} + +void G_admin_tklog_log( gentity_t *attacker, gentity_t *victim, int meansOfDeath ) +{ + g_admin_tklog_t *tklog; + int previous; + int count = 1; + + if( !attacker ) + return; + + previous = admin_tklog_index - 1; + if( previous < 0 ) + previous = MAX_ADMIN_TKLOGS - 1; + + if( g_admin_tklog[ previous ] ) + count = g_admin_tklog[ previous ]->id + 1; + + if( g_admin_tklog[ admin_tklog_index ] ) + tklog = g_admin_tklog[ admin_tklog_index ]; + else + tklog = G_Alloc( sizeof( g_admin_tklog_t ) ); + + memset( tklog, 0, sizeof( g_admin_tklog_t ) ); + tklog->id = count; + tklog->time = level.time - level.startTime; + Q_strncpyz( tklog->name, attacker->client->pers.netname, sizeof( tklog->name ) ); + + if( victim ) + { + Q_strncpyz( tklog->victim, victim->client->pers.netname, sizeof( tklog->victim ) ); + tklog->damage = victim->client->tkcredits[ attacker->s.number ]; + tklog->value = victim->client->ps.stats[ STAT_MAX_HEALTH ]; + } + else + { + Q_strncpyz( tklog->victim, "^3BLEEDING", sizeof( tklog->victim ) ); + tklog->damage = attacker->client->pers.statscounters.spreebleeds; + tklog->value = g_bleedingSpree.integer * 100; + } + + tklog->team = attacker->client->ps.stats[ STAT_PTEAM ]; + if( meansOfDeath == MOD_GRENADE ) + tklog->weapon = WP_GRENADE; + else if( tklog->team == PTE_HUMANS ) + tklog->weapon = attacker->s.weapon; + else + tklog->weapon = attacker->client->ps.stats[ STAT_PCLASS ]; + + g_admin_tklog[ admin_tklog_index ] = tklog; + admin_tklog_index++; + if( admin_tklog_index >= MAX_ADMIN_TKLOGS ) + admin_tklog_index = 0; +} + +qboolean G_admin_tklog( gentity_t *ent, int skiparg ) +{ + g_admin_tklog_t *results[ 10 ]; + int result_index = 0; + char *search_name = NULL; + int index; + int skip = 0; + int skipped = 0; + int checked = 0; + char n1[ MAX_NAME_LENGTH ]; + char fmt_name[ 16 ]; + char argbuf[ 32 ]; + char *weaponName; + int name_length = 10; + int max_id = 0; + int i; + qboolean match; + + memset( results, 0, sizeof( results ) ); + + index = admin_tklog_index; + for( i = 0; i < 10; i++ ) + { + int prev; + + prev = index - 1; + if( prev < 0 ) + prev = MAX_ADMIN_TKLOGS - 1; + if( !g_admin_tklog[ prev ] ) + break; + if( g_admin_tklog[ prev ]->id > max_id ) + max_id = g_admin_tklog[ prev ]->id; + index = prev; + } + + if( G_SayArgc() > 1 + skiparg ) + { + G_SayArgv( 1 + skiparg, argbuf, sizeof( argbuf ) ); + if( ( *argbuf >= '0' && *argbuf <= '9' ) || *argbuf == '-' ) + { + int id; + + id = atoi( argbuf ); + if( id < 0 ) + id += ( max_id - 9 ); + else if( id <= max_id - MAX_ADMIN_TKLOGS ) + id = max_id - MAX_ADMIN_TKLOGS + 1; + + if( id + 9 >= max_id ) + id = max_id - 9; + if( id < 1 ) + id = 1; + for( i = 0; i < MAX_ADMIN_TKLOGS; i++ ) + { + if( g_admin_tklog[ i ]->id == id ) + { + index = i; + break; + } + } + } + else + { + search_name = argbuf; + } + + if( G_SayArgc() > 2 + skiparg && ( search_name ) ) + { + char skipbuf[ 4 ]; + G_SayArgv( 2 + skiparg, skipbuf, sizeof( skipbuf ) ); + skip = atoi( skipbuf ); + } + } + + if( search_name ) + { + g_admin_tklog_t *result_swap[ 10 ]; + + memset( &result_swap, 0, sizeof( result_swap ) ); + + index = admin_tklog_index - 1; + if( index < 0 ) + index = MAX_ADMIN_TKLOGS - 1; + + while( g_admin_tklog[ index ] && + checked < MAX_ADMIN_TKLOGS && + result_index < 10 ) + { + match = qfalse; + + G_SanitiseString( g_admin_tklog[ index ]->name, n1, sizeof( n1 ) ); + if( strstr( n1, search_name ) ) + match = qtrue; + + if( match && skip > 0 ) + { + match = qfalse; + skip--; + skipped++; + } + if( match ) + { + result_swap[ result_index ] = g_admin_tklog[ index ]; + result_index++; + } + + checked++; + index--; + if( index < 0 ) + index = MAX_ADMIN_TKLOGS - 1; + } + // search runs backwards, turn it around + for( i = 0; i < result_index; i++ ) + results[ i ] = result_swap[ result_index - i - 1 ]; + } + else + { + while( g_admin_tklog[ index ] && result_index < 10 ) + { + results[ result_index ] = g_admin_tklog[ index ]; + result_index++; + index++; + if( index >= MAX_ADMIN_TKLOGS ) + index = 0; + } + } + + for( i = 0; results[ i ] && i < 10; i++ ) + { + int l; + + G_DecolorString( results[ i ]->name, n1 ); + l = strlen( n1 ); + if( l > name_length ) + name_length = l; + } + ADMBP_begin( ); + for( i = 0; results[ i ] && i < 10; i++ ) + { + int t; + + t = results[ i ]->time / 1000; + + G_DecolorString( results[ i ]->name, n1 ); + Com_sprintf( fmt_name, sizeof( fmt_name ), "%%%ds", + ( name_length + strlen( results[ i ]->name ) - strlen( n1 ) ) ); + Com_sprintf( n1, sizeof( n1 ), fmt_name, results[ i ]->name ); + + if( results[ i ]->team == PTE_HUMANS ) + weaponName = BG_FindNameForWeapon( results[ i ]->weapon ); + else + weaponName = BG_FindNameForClassNum( results[ i ]->weapon ); + + ADMBP( va( "^7%3d %3d:%02d %s^7 %3d / %3d %10s %s^7\n", + results[ i ]->id, + t / 60, t % 60, + n1, + results[ i ]->damage, + results[ i ]->value, + weaponName, + results[ i ]->victim ) ); + } + if( search_name ) + { + ADMBP( va( "^3!tklog:^7 Showing %d matches for '%s^7'.", + result_index, + argbuf ) ); + if( checked < MAX_ADMIN_TKLOGS && g_admin_tklog[ checked ] ) + ADMBP( va( " run '!tklog %s^7 %d' to see more", + argbuf, + skipped + result_index ) ); + ADMBP( "\n" ); + } + else if ( results[ 0 ] ) + { + ADMBP( va( "^3!tklog:^7 Showing %d - %d of %d.\n", + results[ 0 ]->id, + results[ result_index - 1 ]->id, + max_id ) ); + } + else + { + ADMBP( "^3!tklog:^7 log is empty.\n" ); + } + ADMBP_end( ); + + return qtrue; +} + qboolean G_admin_map( gentity_t *ent, int skiparg ) { char map[ MAX_QPATH ]; @@ -3947,6 +4330,9 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg ) case 'M': result = "^6admin changed map"; break; + case 'l': + result = "^2layout vote"; + break; case 'D': result = "^6admin loaded devmap"; break; @@ -3986,6 +4372,10 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg ) ptr = end; count++; } + + if( g_nextMap.string[ 0 ] ) + ADMBP( va( "^5NextMap override: %s\n", g_nextMap.string ) ); + ADMBP_end( ); return qtrue; @@ -4013,7 +4403,7 @@ qboolean G_admin_demo( gentity_t *ent, int skiparg ) { if( !ent ) { - ADMP( "!demo: console can not use demo.\n" ); + ADMP( "!demo: console can not use demo.\n"); return qfalse; } @@ -4415,29 +4805,34 @@ qboolean G_admin_listadmins( gentity_t *ent, int skiparg ) int start = 0; qboolean numeric = qtrue; int drawn = 0; - int minlevel = 1; + int minlevel = 0; + char *direction = "(all levels)"; if( G_SayArgc() == 3 + skiparg ) { G_SayArgv( 2 + skiparg, s, sizeof( s ) ); - if( s[ 0 ] >= '0' && s[ 0] <= '9' ) + if( ( s[ 0 ] >= '0' && s[ 0] <= '9' ) || s[ 0 ] == '-' ) { minlevel = atoi( s ); - if( minlevel < 1 ) - minlevel = 1; + if( minlevel > 0 ) + direction = "or greater"; + else if( minlevel < 0) + direction = "or lesser"; } } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) { - if( g_admin_admins[ i ]->level >= minlevel ) + if( ( minlevel == 0 && g_admin_admins[ i ]->level != 0 ) || + ( minlevel > 0 && g_admin_admins[ i ]->level >= minlevel ) || + ( minlevel < 0 && g_admin_admins[ i ]->level <= minlevel ) ) found++; } if( !found ) { - if( minlevel > 1 ) + if( minlevel != 0 ) { - ADMP( va( "^3!listadmins: ^7no admins level %i or greater found\n", minlevel ) ); + ADMP( va( "^3!listadmins: ^7no admins level %i %s found\n", minlevel, direction ) ); } else { @@ -4476,27 +4871,28 @@ qboolean G_admin_listadmins( gentity_t *ent, int skiparg ) { if( drawn <= 20 ) { - ADMP( va( "^3!listadmins:^7 found %d admins level %i or greater matching '%s^7'\n", - drawn, minlevel, search ) ); + ADMP( va( "^3!listadmins:^7 found %d admins level %i %s matching '%s^7'\n", + drawn, minlevel, direction, search ) ); } else { - ADMP( va( "^3!listadmins:^7 found >20 admins level %i or greater matching '%s^7. Try a more narrow search.'\n", - minlevel, search ) ); + ADMP( va( "^3!listadmins:^7 found >20 admins level %i %s matching '%s^7. Try a more narrow search.'\n", + minlevel, direction, search ) ); } } else { ADMBP_begin(); - ADMBP( va( "^3!listadmins:^7 showing admins level %i or greater %d - %d of %d. ", + ADMBP( va( "^3!listadmins:^7 showing admins level %i %s %d - %d of %d. ", minlevel, + direction, ( found ) ? ( start + 1 ) : 0, ( ( start + MAX_ADMIN_LISTITEMS ) > found ) ? found : ( start + MAX_ADMIN_LISTITEMS ), found ) ); if( ( start + MAX_ADMIN_LISTITEMS ) < found ) { - if( minlevel > 1) + if( minlevel != 0 ) { ADMBP( va( "run '!listadmins %d %d' to see more", ( start + MAX_ADMIN_LISTITEMS + 1 ), minlevel ) ); @@ -4564,27 +4960,29 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg ) char lname[ MAX_NAME_LENGTH ]; char lname2[ MAX_NAME_LENGTH ]; char guid_stub[ 9 ]; - char muted[ 2 ], denied[ 2 ], dbuilder[ 2 ], misc[ 2 ]; + char muted[ 2 ], denied[ 2 ], dbuilder[ 2 ], immune[ 2 ]; int l; char lname_fmt[ 5 ]; + char karma[ 8 ]; + //get amount of invisible players for( i = 0; i < level.maxclients; i++ ) { p = &level.clients[ i ]; if ( p->sess.invisible == qtrue ) invisiblePlayers++; } - + ADMBP_begin(); - ADMBP( va( "^3!listplayers^7: %i players connected:\n", + ADMBP( va( "^3!listplayers^7: %d players connected:\n", level.numConnectedClients - invisiblePlayers ) ); for( i = 0; i < level.maxclients; i++ ) { p = &level.clients[ i ]; - + // Ignore invisible players if ( p->sess.invisible == qtrue ) continue; - + Q_strncpyz( t, "S", sizeof( t ) ); Q_strncpyz( c, S_COLOR_YELLOW, sizeof( c ) ); if( p->pers.teamSelection == PTE_HUMANS ) @@ -4613,6 +5011,10 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg ) guid_stub[ j ] = '\0'; muted[ 0 ] = '\0'; + if( G_admin_permission( &g_entities[ i ], ADMF_NO_VOTE ) ) + { + Q_strncpyz( muted, "V", sizeof( muted ) ); + } if( p->pers.muted ) { Q_strncpyz( muted, "M", sizeof( muted ) ); @@ -4641,15 +5043,16 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg ) Q_strncpyz( dbuilder, "D", sizeof( dbuilder ) ); } } - - misc[ 0 ] = '\0'; + immune[ 0 ] = '\0'; if( G_admin_permission( &g_entities[ i ], ADMF_BAN_IMMUNITY ) ) { - // use Misc slot for Immunity player status - Q_strncpyz( misc, "I", sizeof( misc ) ); - } else if( p->pers.paused ) { - // use Misc slot for paused player status - Q_strncpyz( misc, "P", sizeof( misc ) ); + Q_strncpyz( immune, "I", sizeof( immune ) ); + } + + if( p->pers.paused ) + { + // use immunity slot for paused player status + Q_strncpyz( immune, "L", sizeof( immune ) ); } l = 0; @@ -4693,19 +5096,25 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg ) } + if( g_karma.integer ) + Com_sprintf( karma, sizeof( karma ), " %4i", p->pers.karma / 1000 ); + else + karma[ 0 ] = '\0'; + if( G_admin_permission(ent, ADMF_SEESFULLLISTPLAYERS ) ) { - ADMBP( va( "%2i %s%s^7 %-2i %s^7 (*%s) ^1%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n", + ADMBP( va( "%2i %s%s^7%s %-2i %s^7 (*%s) ^1%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n", i, c, t, + karma, l, ( *lname ) ? lname2 : "", guid_stub, + immune, muted, dbuilder, denied, - misc, p->pers.netname, ( *n ) ? "(a.k.a. " : "", n, @@ -4714,10 +5123,11 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg ) } else { - ADMBP( va( "%2i %s%s^7 ^1%1s%1s%1s^7 %s^7\n", + ADMBP( va( "%2i %s%s^7%s ^1%1s%1s%1s^7 %s^7\n", i, c, t, + karma, muted, dbuilder, denied, @@ -4870,52 +5280,6 @@ qboolean G_admin_listrotation( gentity_t *ent, int skiparg ) statusColor = 7; status = "vote"; } - } else if( !Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*RANDOM*" ) ) - { - char slotMap[ 64 ]; - int lineLen = 0; - int k; - - trap_Cvar_VariableStringBuffer( "mapname", slotMap, sizeof( slotMap ) ); - mapnames[ 0 ] = '\0'; - for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ ) - { - char *thisMap; - int mc = 7; - - if( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].lhs != MCV_SELECTEDRANDOM ) - continue; - - thisMap = mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest; - lineLen += strlen( thisMap ) + 1; - - if( currentMap == j && !Q_stricmp( thisMap, slotMap ) ) - mc = 3; - Q_strcat( mapnames, sizeof( mapnames ), va( "^7%s%s^%i%s", - ( k ) ? ", " : "", - ( lineLen > 50 ) ? "\n " : "", - mc, thisMap ) ); - if( lineLen > 50 ) - lineLen = strlen( thisMap ) + 2; - else - lineLen++; - } - - if( currentMap == j ) - { - statusColor = 3; - status = "current slot"; - } - else if( !k ) - { - statusColor = 1; - status = "empty Random"; - } - else - { - statusColor = 7; - status = "Random"; - } } else if ( currentMap == j ) { @@ -4980,16 +5344,13 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) char name_match[ MAX_NAME_LENGTH ] = {""}; int show_count = 0; qboolean subnetfilter = qfalse; + int line_color; + int dummy; t = trap_RealTime( NULL ); for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { - if( g_admin_bans[ i ]->expires != 0 - && ( g_admin_bans[ i ]->expires - t ) < 1 ) - { - continue; - } found++; } @@ -5067,7 +5428,6 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) else { int mask = -1; - int dummy; int scanflen = 0; scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask ); if( scanflen == 5 && mask < 32 ) @@ -5114,7 +5474,6 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) else { int mask = -1; - int dummy; int scanflen = 0; scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask ); if( scanflen != 5 || mask >= 32 ) @@ -5141,42 +5500,54 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) made++; } - if( g_admin_bans[ i ]->expires != 0 - && ( g_admin_bans[ i ]->expires - t ) < 1 ) + if( g_admin_bans[ i ]->expires == 0 || + g_admin_bans[ i ]->expires > t ) { - Com_sprintf( duration, sizeof( duration ), "^1*EXPIRED*^7" ); - } else { secs = ( g_admin_bans[ i ]->expires - t ); G_admin_duration( secs, duration, sizeof( duration ) ); + if( sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &dummy ) != 4 ) + line_color = 1; + else if( g_admin_bans[ i ]->suspend > t ) + line_color = 5; + else + line_color = 7; + } + else + { + Q_strncpyz( duration, "expired", sizeof( duration ) ); + line_color = 5; } suspended[ 0 ] = '\0'; if( g_admin_bans[ i ]->suspend > t ) { G_admin_duration( g_admin_bans[ i ]->suspend - t, sduration, sizeof( sduration ) ); - Com_sprintf( suspended, sizeof( suspended ), "^3*SUSPENDED*^7 for %s^7", + Com_sprintf( suspended, sizeof( suspended ), "^5 | - SUSPENDED for %s\n", sduration ); } G_DecolorString( g_admin_bans[ i ]->name, n1 ); - Com_sprintf( name_fmt, sizeof( name_fmt ), "%%%is", + Com_sprintf( name_fmt, sizeof( name_fmt ), "%%-%is", ( max_name + strlen( g_admin_bans[ i ]->name ) - strlen( n1 ) ) ); Com_sprintf( n1, sizeof( n1 ), name_fmt, g_admin_bans[ i ]->name ); G_DecolorString( g_admin_bans[ i ]->banner, n2 ); - Com_sprintf( banner_fmt, sizeof( banner_fmt ), "%%%is", + Com_sprintf( banner_fmt, sizeof( banner_fmt ), "%%-%is", ( max_banner + strlen( g_admin_bans[ i ]->banner ) - strlen( n2 ) ) ); - Com_sprintf( n2, sizeof( n2 ), banner_fmt, g_admin_bans[ i ]->banner ); + Com_sprintf( n2, sizeof( n2 ), banner_fmt, g_admin_bans[ i ]->banner ); bannerslevel = g_admin_bans[ i ]->bannerlevel; - ADMBP( va( "%4i %s^7 %-15s %-8s %-10s\n | %-15s^7 Level:%2i\n | %s\n \\__ %s\n", + ADMBP( va( "^%i%4i ^7%s^%i %-15s %-8s ^7%s^7 %2i^%i %-10s\n%s^5 \\__ %s\n", + line_color, ( i + 1 ), n1, + line_color, g_admin_bans[ i ]->ip, date, - duration, n2, bannerslevel, + line_color, + duration, suspended, g_admin_bans[ i ]->reason ) ); @@ -5209,8 +5580,9 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) if( ( start + MAX_ADMIN_SHOWBANS ) < found ) { - ADMBP( va( "run !showbans %d %s to see more", + ADMBP( va( "run !showbans %d%s%s to see more", ( start + MAX_ADMIN_SHOWBANS + 1 ), + (filter[0]) ? " " : "", (filter[0]) ? filter : "" ) ); } ADMBP( "\n" ); @@ -5221,8 +5593,7 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg ) qboolean G_admin_help( gentity_t *ent, int skiparg ) { int i; - int count = 0; - int commandsPerLine = 6; + char additional[ MAX_STRING_CHARS ] = ""; if( G_SayArgc() < 2 + skiparg ) { @@ -5259,102 +5630,39 @@ qboolean G_admin_help( gentity_t *ent, int skiparg ) j = 0; } } - + + if( ent && g_markDeconstruct.integer == 2 ) + strcat( additional, " /mark" ); + if( ent ) + strcat( additional, " /builder /say_area" ); + if( g_publicSayadmins.integer || G_admin_permission( ent, ADMF_ADMINCHAT ) ) + strcat( additional, " /a /say_admins" ); + if( g_privateMessages.integer ) + strcat( additional, " /m" ); + if( ent && g_actionPrefix.string[0] ) + strcat( additional, " /me /mt /me_team" ); + if( ent && g_myStats.integer ) + strcat( additional, " /mystats" ); + if( ent && g_teamStatus.integer ) + strcat( additional, " /teamstatus" ); + if( ent && ent->client ) + { + if( ent->client->pers.designatedBuilder ) + { + strcat( additional, " /protect /resign" ); + } + } + if( ent && g_allowShare.integer ) + strcat( additional, " /share /donate" ); + if( count ) ADMBP( "\n" ); ADMBP( va( "^3!help: ^7%i available commands\n", count ) ); ADMBP( "run !help [^3command^7] for help with a specific command.\n" ); - ADMBP( "The following non-standard /commands may also be available to you: \n" ); - count = 1; - - if( ent && g_AllStats.integer ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "allstats" ) ); - count++; - } - if ( ent ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "builder" ) ); - count++; - } - if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "callvote" ) ); - count++; - } - if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "callteamvote" ) ); - count++; - } - if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "donate" ) ); - count++; - } - if( g_privateMessages.integer ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "m" ) ); - count++; - } - if( ent && g_markDeconstruct.integer == 2 && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "mark" ) ); - count++; - } - if( ent && g_actionPrefix.string[0] ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "me" ) ); - count++; - } - if( ent && g_actionPrefix.string[0] ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "me_team" ) ); - count++; - } - if( ent && g_actionPrefix.string[0] && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "mt" ) ); - count++; - } - if( ent && g_myStats.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "mystats" ) ); - count++; - } - if( ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "protect" ) ); - count++; - } - if( ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "resign" ) ); - count++; - } - if( g_publicSayadmins.integer || G_admin_permission( ent, ADMF_ADMINCHAT ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "say_admins" ) ); - count++; - } - if( ent && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "say_area" ) ); - count++; - } - if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "share" ) ); - count++; - } - if( ent && g_teamStatus.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) { - if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" ); - ADMBP( va( "^5/%-12s", "teamstatus" ) ); - count++; - } - ADMBP( "\n" ); + ADMBP( va( "\nThe following non-standard /commands may also be available to you: \n^3%s\n", + additional ) ); ADMBP_end(); - + return qtrue; } else @@ -5490,6 +5798,10 @@ qboolean G_admin_cancelvote( gentity_t *ent, int skiparg ) ADMP( "^3!cancelvote^7: no vote in progress\n" ); return qfalse; } + if( !Q_strncmp( level.voteDisplayString, "Extend", 6 ) && + level.extend_vote_count > 0 ) + level.extend_vote_count--; + level.voteNo = level.numConnectedClients; level.voteYes = 0; CheckVote( ); @@ -5679,11 +5991,81 @@ qboolean G_admin_pause( gentity_t *ent, int skiparg ) vic->client->pers.netname, ( ent ) ? ent->client->pers.netname : "console" ) ); } - ClientUserinfoChanged( pids[ i ], qfalse ); } return qtrue; } +qboolean G_admin_practice( gentity_t *ent, int skiparg ) +{ + char clantag[ MAX_NAME_LENGTH ]; + char mapsarg[ 8 ]; + int maps; + + if( G_SayArgc() < 2 + skiparg ) + { + if( g_practiceCount.integer ) + { + AP( va( "print \"^3practice:^7 practice mode is in effect for the next %d maps\n\"", + g_practiceCount.integer ) ); + ADMP( va( "^3!practice: ^7practice mode set to %s^7 for next %d maps\n", + g_practiceText.string, g_practiceCount.integer ) ); + } + else + { + ADMP( "^3!practice: ^7practice mode is off\n" ); + } + return qfalse; + } + + G_SayArgv( 1 + skiparg, clantag, sizeof( clantag ) ); + + if( G_SayArgc() < 3 + skiparg ) + { + if( !Q_stricmp( clantag, "off" ) ) + { + if( g_practiceCount.integer ) + { + trap_Cvar_Set( "g_practiceCount", "0" ); + AP( va ("print \"^3!practice: ^7 practice mode turned off by %s^7\n\"", + ( ent ) ? ent->client->pers.netname : "console" ) ); + ADMP( "^3!practice: ^7practice mode set to off\n" ); + return qtrue; + } + else + { + ADMP( "^3!practice: ^7practice mode already off\n" ); + } + } + else + { + ADMP( "^3!practice: ^7usage: practice [clan tag] [map count]\n" ); + } + return qfalse; + } + G_SayArgv( 2 + skiparg, mapsarg, sizeof( mapsarg ) ); + maps = atoi( mapsarg ); + + if( !clantag[ 0 ] ) + { + ADMP( "^3!practice: ^7no clan tag specified\n" ); + return qfalse; + } + if( maps < 1 || maps > 8 ) + { + ADMP( "^3!practice: ^7map count must be between 1 and 8\n" ); + return qfalse; + } + + trap_Cvar_Set( "g_practiceText", clantag ); + trap_Cvar_Set( "g_practiceCount", va( "%d", maps ) ); + + AP( va( "print \"^3practice:^7 %s^7 has activated practice mode for the next %d maps\n\"", + ( ent ) ? ent->client->pers.netname : "console", + maps ) ); + + return qtrue; +} + qboolean G_admin_spec999( gentity_t *ent, int skiparg ) { int i; @@ -5711,13 +6093,47 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg ) qboolean G_admin_register(gentity_t *ent, int skiparg ){ int level = 0; + int max; + char buffer [ 64 ]; if( !ent ) return qtrue; level = G_admin_level(ent); - if( level == 0 ) - level = 1; + if( G_SayArgc() > 1 + skiparg ) + { + G_SayArgv( 1 + skiparg, buffer, sizeof( buffer ) ); + max = atoi( buffer ); + + if( G_SayArgc() > 2 + skiparg ) + { + G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); + if( g_adminRegisterAdminPass.string[ 0 ] && Q_stricmp( g_adminRegisterAdminPass.string, "none" ) && + Q_stricmp( g_adminRegisterAdminPass.string, buffer) ) + { + ADMP( "Invalid password.\n" ); + return qfalse; + } + if (max > g_adminRegisterAdminLevel.integer) + max = g_adminRegisterAdminLevel.integer; + if( level <= g_adminRegisterAdminLevel.integer ) + level = max; + } + else + { + if( max > g_adminRegisterLevel.integer ) + max = g_adminRegisterLevel.integer; + if( max >= 0 && level >= 0 && level <= g_adminRegisterLevel.integer ) + level = max; + } + } + else + { + max = 1; + } + + if( level >= 0 && level < max ) + level = max; if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) ) { @@ -5886,140 +6302,6 @@ qboolean G_admin_restart( gentity_t *ent, int skiparg ) return qtrue; } -qboolean G_admin_nobuild( gentity_t *ent, int skiparg ) -{ - char buffer[ MAX_STRING_CHARS ]; - int i, tmp; - - if( G_SayArgc() < 2 + skiparg ) - { - ADMP( "^3!nobuild: ^7usage: !nobuild (^5enable / disable / log / remove / save^7)\n" ); - return qfalse; - } - - G_SayArgv( 1 + skiparg, buffer, sizeof( buffer ) ); - - if( !Q_stricmp( buffer, "enable" ) || !Q_stricmp( buffer, "Enable" ) ) - { - if( G_SayArgc() < 4 + skiparg ) - { - ADMP( "^3!nobuild: ^7usage: !nobuild enable (^5area^7) (^5height^7)\n" ); - return qfalse; - } - - if( !level.noBuilding ) - { - - level.noBuilding = qtrue; - - // Grab and set the area - G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); - tmp = atoi( buffer ); - level.nbArea = tmp; - - // Grab and set the height - G_SayArgv( 3 + skiparg, buffer, sizeof( buffer ) ); - tmp = atoi( buffer ); - level.nbHeight = tmp; - - ADMP( "^3!nobuild: ^7nobuild is now enabled, please place a buildable to spawn a marker\n" ); - return qtrue; - } - else - { - ADMP( "^3!nobuild: ^7nobuild is already enabled. type !nobuild disable to disable nobuild mode.\n" ); - return qfalse; - } - } - - if( !Q_stricmp( buffer, "disable" ) || !Q_stricmp( buffer, "Disable" ) ) - { - if( level.noBuilding ) - { - level.noBuilding = qfalse; - level.nbArea = 0.0f; - level.nbHeight = 0.0f; - ADMP( "^3!nobuild: ^7nobuild is now disabled\n" ); - return qtrue; - } - else - { - ADMP( "^3!nobuild: ^7nobuild is disabled. type !nobuild enable (^5area^7) (^5height^7) to enable nobuild mode.\n" ); - return qfalse; - } - } - - if( !Q_stricmp( buffer, "log" ) || !Q_stricmp( buffer, "Log" ) ) - { - ADMBP_begin(); - - tmp = 0; - for( i = 0; i < MAX_GENTITIES; i++ ) - { - if( level.nbMarkers[ i ].Marker == NULL ) - continue; - - // This will display a start at 1, and not 0. This is to stop confusion by server ops - ADMBP( va( "^7#%i at origin (^5%f %f %f^7)^7\n", i + 1, level.nbMarkers[ i ].Origin[0], level.nbMarkers[ i ].Origin[1], level.nbMarkers[ i ].Origin[2] ) ); - tmp++; - } - ADMBP( va( "^3!nobuild:^7 displaying %i marker(s)\n", tmp ) ); - - ADMBP_end(); - return qtrue; - } - - if( !Q_stricmp( buffer, "remove" ) || !Q_stricmp( buffer, "Remove" ) ) - { - if( G_SayArgc() < 3 + skiparg ) - { - ADMP( "^3!nobuild: ^7usage: !nobuild remove (^5marker #^7)\n" ); - return qfalse; - } - - G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) ); - tmp = atoi( buffer ) - 1; - // ^ that was to work with the display number... - - if( level.nbMarkers[ tmp ].Marker == NULL ) - { - ADMP( "^3!nobuild: ^7that is not a valid marker number\n" ); - return qfalse; - } - // Donno why im doing this, but lets clear this... - level.nbMarkers[ tmp ].Marker->noBuild.isNB = qfalse; - level.nbMarkers[ tmp ].Marker->noBuild.Area = 0.0f; - level.nbMarkers[ tmp ].Marker->noBuild.Height = 0.0f; - - // Free the entitiy and null it out... - G_FreeEntity( level.nbMarkers[ tmp ].Marker ); - level.nbMarkers[ tmp ].Marker = NULL; - - // That is to work with the display number... - ADMP( va( "^3!nobuild:^7 marker %i has been removed\n", tmp + 1 ) ); - return qtrue; - } - - if( !Q_stricmp( buffer, "save" ) || !Q_stricmp( buffer, "Save" ) ) - { - int i, tmp; - - G_NobuildSave( ); - - tmp = 0; - for( i = 0; i < MAX_GENTITIES; i++ ) - { - if( level.nbMarkers[ i ].Marker == NULL ) - continue; - - tmp++; - } - ADMP( va( "^3!nobuild:^7 %i nobuild markers have been saved\n", tmp ) ); - return qtrue; - } - return qfalse; -} - qboolean G_admin_nextmap( gentity_t *ent, int skiparg ) { AP( va( "print \"^3!nextmap: ^7%s^7 decided to load the next map\n\"", @@ -6244,6 +6526,441 @@ qboolean G_admin_designate( gentity_t *ent, int skiparg ) return qtrue; } +static int SortFlags( const void *pa, const void *pb ) +{ + char *a = (char *)pa; + char *b = (char *)pb; + + if( *a == '-' || *a == '+' ) + a++; + if( *b == '-' || *b == '+' ) + b++; + return strcmp(a, b); +} + +#define MAX_USER_FLAGS 200 +const char *G_admin_user_flag( char *oldflags, char *flag, qboolean add, qboolean clear, + char *newflags, int size ) +{ + char *token, *token_p; + char *key; + char head_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; + char tail_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ]; + char allflag[ MAX_ADMIN_FLAG_LEN ]; + char newflag[ MAX_ADMIN_FLAG_LEN ]; + int head_count = 0; + int tail_count = 0; + qboolean flagset = qfalse; + int i; + + if( !flag[ 0 ] ) + { + return "invalid admin flag"; + } + + allflag[ 0 ] = '\0'; + token_p = oldflags; + while( *( token = COM_Parse( &token_p ) ) ) + { + key = token; + if( *key == '-' || *key == '+' ) + key++; + + if( !strcmp( key, flag ) ) + { + if( flagset ) + continue; + flagset = qtrue; + if( clear ) + { + // clearing ALLFlAGS will result in clearing any following flags + if( !strcmp( key, ADMF_ALLFLAGS ) ) + break; + else + continue; + } + Com_sprintf( newflag, sizeof( newflag ), "%s%s", + ( add ) ? "+" : "-", key ); + } + else + { + Q_strncpyz( newflag, token, sizeof( newflag ) ); + } + + if( !strcmp( key, ADMF_ALLFLAGS ) ) + { + if( !allflag[ 0 ] ) + Q_strncpyz( allflag, newflag, sizeof( allflag ) ); + continue; + } + + if( !allflag[ 0 ] ) + { + if( head_count < MAX_USER_FLAGS ) + { + Q_strncpyz( head_flags[ head_count ], newflag, + sizeof( head_flags[ head_count ] ) ); + head_count++; + } + } + else + { + if( tail_count < MAX_USER_FLAGS ) + { + Q_strncpyz( tail_flags[ tail_count ], newflag, + sizeof( tail_flags[ tail_count ] ) ); + tail_count++; + } + } + } + + if( !flagset && !clear ) + { + if( !strcmp( flag, ADMF_ALLFLAGS ) ) + { + Com_sprintf( allflag, sizeof( allflag ), "%s%s", + ( add ) ? "" : "-", ADMF_ALLFLAGS ); + } + else if( !allflag[ 0 ] ) + { + if( head_count < MAX_USER_FLAGS ) + { + Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ), + "%s%s", ( add ) ? "" : "-", flag ); + head_count++; + } + } + else + { + if( tail_count < MAX_USER_FLAGS ) + { + Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ), + "%s%s", ( add ) ? "+" : "-", flag ); + tail_count++; + } + } + } + + qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags ); + qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags ); + + // rebuild flags + newflags[ 0 ] = '\0'; + for( i = 0; i < head_count; i++ ) + { + Q_strcat( newflags, size, + va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) ); + } + if( allflag[ 0 ] ) + { + Q_strcat( newflags, size, + va( "%s%s", ( newflags[ 0 ] ) ? " ": "", allflag ) ); + + for( i = 0; i < tail_count; i++ ) + { + Q_strcat( newflags, size, + va( " %s", tail_flags[ i ] ) ); + } + } + + return NULL; +} + + +typedef struct { + char *flag; + char *description; +} AdminFlagListEntry_t; +static AdminFlagListEntry_t adminFlagList[] = +{ + { ADMF_ACTIVITY, "inactivity rules do not apply" }, + { ADMF_ADMINCHAT, "can see and use admin chat" }, + { ADMF_ALLFLAGS, "has all flags and can use any command" }, + { ADMF_BAN_IMMUNITY, "immune from IP bans" }, + { ADMF_CAN_PERM_BAN, "can permanently ban players" }, + { ADMF_DBUILDER, "permanent designated builder" }, + { ADMF_FORCETEAMCHANGE, "team balance rules do not apply" }, + { ADMF_INCOGNITO, "does not show as admin in !listplayers" }, + { ADMF_IMMUNITY, "cannot be vote kicked or muted" }, + { ADMF_IMMUTABLE, "admin commands cannot be used on them" }, + { ADMF_NOCENSORFLOOD, "no flood protection" }, + { ADMF_NO_VOTE_LIMIT, "vote limitations do not apply" }, + { ADMF_SEESFULLLISTPLAYERS, "sees all info in !listplayers" }, + { ADMF_SPEC_ALLCHAT, "can see team chat as spectator" }, + { ADMF_ADMINSTEALTH, "uses admin stealth" }, + { ADMF_TEAMCHANGEFREE, "keeps credits on team switch" }, + { ADMF_TEAMCHAT_CMD, "can run commands from team chat" }, + { ADMF_UNACCOUNTABLE, "does not need to specify reason for kick/ban" }, + { ADMF_NO_CHAT, "can not talk" }, + { ADMF_NO_VOTE, "can not call votes" } +}; +static int adminNumFlags= sizeof( adminFlagList ) / sizeof( adminFlagList[ 0 ] ); + +#define MAX_LISTCOMMANDS 128 +qboolean G_admin_flaglist( gentity_t *ent, int skiparg ) +{ + qboolean shown[ MAX_LISTCOMMANDS ]; + int i, j; + int count = 0; + + ADMBP_begin(); + + ADMBP( "^3Ability flags:\n" ); + + for( i = 0; i < adminNumFlags; i++ ) + { + ADMBP( va( " %s%-20s ^7%s\n", + ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1", + adminFlagList[ i ].flag, + adminFlagList[ i ].description ) ); + } + + ADMBP( "^3Command flags:\n" ); + + memset( shown, 0, sizeof( shown ) ); + for( i = 0; i < adminNumCmds; i++ ) + { + if( i < MAX_LISTCOMMANDS && shown[ i ] ) + continue; + ADMBP( va( " ^5%-20s^7", g_admin_cmds[ i ].flag ) ); + for( j = i; j < adminNumCmds; j++ ) + { + if( !strcmp( g_admin_cmds[ j ].flag, g_admin_cmds[ i ].flag ) ) + { + ADMBP( va( " %s", g_admin_cmds[ j ].keyword ) ); + if( j < MAX_LISTCOMMANDS ) + shown[ j ] = qtrue; + } + } + ADMBP( "\n" ); + count++; + } + + ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n", + adminNumFlags, count ) ); + + ADMBP_end(); + + return qtrue; +} + +qboolean G_admin_flag( gentity_t *ent, int skiparg ) +{ + char command[ MAX_ADMIN_CMD_LEN ], *cmd; + char name[ MAX_NAME_LENGTH ]; + char flagbuf[ MAX_ADMIN_FLAG_LEN ]; + char *flag; + int id; + char adminname[ MAX_NAME_LENGTH ] = {""}; + const char *result; + qboolean add = qtrue; + qboolean clear = qfalse; + int admin_level = -1; + int i, level; + + G_SayArgv( skiparg, command, sizeof( command ) ); + cmd = command; + if( *cmd == '!' ) + cmd++; + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) ); + return qfalse; + } + + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + if( name[ 0 ] == '*' ) + { + if( ent ) + { + ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) ); + return qfalse; + } + id = atoi( name + 1 ); + for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) + { + if( g_admin_levels[ i ]->level == id ) + { + admin_level = i; + break; + } + } + if( admin_level < 0 ) + { + ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) ); + return qfalse; + } + Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id ); + } + else + { + id = G_admin_find_admin_slot( ent, name, cmd, adminname, sizeof( adminname ) ); + if( id < 0 ) + return qfalse; + + if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) + { + ADMP( va( "^3%s:^7 your intended victim has a higher admin level than you\n", cmd ) ); + return qfalse; + } + } + + if( G_SayArgc() < 3 + skiparg ) + { + flag = ""; + level = 0; + if( admin_level < 0 ) + { + for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ ) + { + if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level ) + { + flag = g_admin_levels[ i ]->flags; + level = g_admin_admins[ id ]->level; + break; + } + } + ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n", + cmd, adminname, g_admin_admins[ id ]->flags) ); + } + else + { + flag = g_admin_levels[ admin_level ]->flags; + level = g_admin_levels[ admin_level ]->level; + } + ADMP( va( "^3%s:^7 level %d flags are '%s'\n", + cmd, level, flag ) ); + + return qtrue; + } + + G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) ); + flag = flagbuf; + if( flag[ 0 ] == '-' || flag[ 0 ] == '+' ) + { + add = ( flag[ 0 ] == '+' ); + flag++; + } + if( ent && !Q_stricmp( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) + { + ADMP( va( "^3%s:^7 you may not change your own flags (use rcon)\n", cmd ) ); + return qfalse; + } + if( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) ) + { + ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) ); + return qfalse; + } + + if( !Q_stricmp( cmd, "unflag" ) ) + { + clear = qtrue; + } + + if( admin_level < 0 ) + { + result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear, + g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) ); + } + else + { + result = G_admin_user_flag( g_admin_levels[ admin_level ]->flags, flag, add, clear, + g_admin_levels[ admin_level ]->flags, + sizeof( g_admin_levels[ admin_level ]->flags ) ); + } + if( result ) + { + ADMP( va( "^3!flag: ^7an error occured setting flag '^3%s^7', %s\n", + flag, result ) ); + return qfalse; + } + + if( !Q_stricmp( cmd, "flag" ) ) + { + G_AdminsPrintf( "^3!%s: ^7%s^7 was %s admin flag '%s' by %s\n", + cmd, adminname, + ( add ) ? "given" : "denied", + flag, + ( ent ) ? ent->client->pers.netname : "console" ); + } + else + { + G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n", + cmd, flag, adminname, + ( ent ) ? ent->client->pers.netname : "console" ); + } + + if( !g_admin.string[ 0 ] ) + ADMP( va( "^3!%s: ^7WARNING g_admin not set, not saving admin record " + "to a file\n", cmd ) ); + else + admin_writeconfig(); + + return qtrue; +} + +qboolean G_admin_immunity( gentity_t *ent, int skiparg ) +{ + char command[ MAX_ADMIN_CMD_LEN ]; + char *cmd, *action; + int id; + char adminname[ MAX_NAME_LENGTH ] = {""}; + const char *result; + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!immunity: ^7usage: immunity [+|-]slot#\n" ); + return qfalse; + } + G_SayArgv( 1 + skiparg, command, sizeof( command ) ); + cmd = command; + action = command; + if( *cmd == '+' || *cmd == '-' ) cmd++; + + id = G_admin_find_admin_slot( ent, cmd, "immunity", adminname, sizeof( adminname ) ); + if( id < 0 ) + return qfalse; + + if( *action != '+' && *action != '-' ) + { + ADMP( va( "^3immunity:^7 ban immunity for %s^7 is %s, prepend + or - to the slot number to change.\n", + adminname, + ( G_admin_permission_guid( g_admin_admins[ id ]->guid, ADMF_BAN_IMMUNITY ) ) ? "on" : "off" ) ); + return qfalse; + } + + result = G_admin_user_flag( g_admin_admins[ id ]->flags, + ADMF_BAN_IMMUNITY, qtrue, ( *action != '+' ), + g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) ); + if( result ) + { + ADMP( va( "^3!immunity: ^7an error occured setting flag, %s\n", result ) ); + return qfalse; + } + + if( *action == '+' ) + { + AP( va( + "print \"^3!immunity: ^7%s^7 was given ban immunity by %s\n\"", + adminname, ( ent ) ? ent->client->pers.netname : "console" ) ); + } + else + { + AP( va( + "print \"^3!immunity: ^7ban immunity for %s^7 removed by %s\n\"", + adminname, ( ent ) ? ent->client->pers.netname : "console" ) ); + } + + if( !g_admin.string[ 0 ] ) + ADMP( "^3!immunity: ^7WARNING g_admin not set, not saving admin record " + "to a file\n" ); + else + admin_writeconfig(); + + return qtrue; +} + //!Warn by Gate (Daniel Evans) qboolean G_admin_warn( gentity_t *ent, int skiparg ) {//mostly copy and paste with the proper lines altered from !mute and !kick @@ -6284,9 +7001,47 @@ qboolean G_admin_warn( gentity_t *ent, int skiparg ) ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );//console announcement return qtrue; } + +qboolean G_admin_bring( gentity_t *ent, int skiparg ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + gentity_t *vic; + + if( !ent ) + { + ADMP( "^3!bring: ^7console cannot use this command\n" ); + return qfalse; + } + if( ent->client->pers.teamSelection != PTE_NONE ) + { + ADMP( "^3!bring: ^7you can only use this command from spectator\n" ); + return qfalse; + } + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!bring: ^7usage: !bring [name|slot#]\n" ); + return qfalse; + } + + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + if( G_ClientNumbersFromString( name, pids ) != 1 ) + { + G_MatchOnePlayer( pids, err, sizeof( err ) ); + ADMP( va( "^3!bring: ^7%s\n", err ) ); + return qfalse; + } + + vic = &g_entities[ pids[ 0 ] ]; + VectorCopy( vic->client->ps.origin, ent->client->ps.origin ); + + return qtrue; +} qboolean G_admin_putmespec( gentity_t *ent, int skiparg ) { + int cs_offset; + if( !ent ) { ADMP( "!specme: sorry, but console isn't allowed on the spectators team\n"); @@ -6302,6 +7057,15 @@ qboolean G_admin_putmespec( gentity_t *ent, int skiparg ) if(ent->client->pers.teamSelection == PTE_NONE) return qfalse; + if( ent->client->pers.teamSelection == PTE_ALIENS ) + cs_offset = 1; + else + cs_offset = 0; + if( level.teamVoteTime[ cs_offset ] ) + { + trap_SendServerCommand( ent-g_entities, "print \"Can not leave team during a team vote\n\"" ); + return qfalse; + } //guard against build timer exploit if( ent->client->pers.teamSelection != PTE_NONE && ent->client->sess.sessionTeam != TEAM_SPECTATOR && ( ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 || @@ -6315,10 +7079,119 @@ qboolean G_admin_putmespec( gentity_t *ent, int skiparg ) } G_ChangeTeam( ent, PTE_NONE ); + + // check for silent '!specme s' - requires !kick permission + if( G_SayArgc() > 1 + skiparg ) + { + char arg[ 2 ]; + + G_SayArgv( 1 + skiparg, arg, sizeof( arg ) ); + if( ( arg[ 0 ] == 's' || arg[ 0 ] == 'S' ) && G_admin_permission( ent, "kick" ) ) + { + ADMP("^3!specme: ^7 You have silently joined the spectators\n"); + return qtrue; + } + } + AP( va("print \"^3!specme: ^7%s^7 decided to join the spectators\n\"", ent->client->pers.netname ) ); return qtrue; } +qboolean G_admin_outlaw( gentity_t *ent, int skiparg ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + char valuebuf[ 8 ]; + gentity_t *vic; + int points; + qboolean activate = qtrue; + + if( !g_bleedingSpree.integer ) + { + ADMP( "^3!outlaw: ^7bleeding sprees are disabled\n" ); + return qfalse; + } + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!outlaw: ^7usage: !outlaw [name|slot#] (value)\n" ); + return qfalse; + } + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + if( G_ClientNumbersFromString( name, pids ) != 1 ) + { + G_MatchOnePlayer( pids, err, sizeof( err ) ); + ADMP( va( "^3!mute: ^7%s\n", err ) ); + return qfalse; + } + vic = &g_entities[ pids[ 0 ] ]; + + if( G_SayArgc() > 2 + skiparg ) + { + G_SayArgv( 2 + skiparg, valuebuf, sizeof( valuebuf ) ); + if( valuebuf[ 0 ] == '?' ) + { + ADMP( va( "^3!outlaw: ^7%s^7's bleeder value is %d\n", + vic->client->pers.netname, + vic->client->pers.statscounters.spreebleeds ) ); + return qtrue; + } + if( valuebuf[ 0 ] == '+' || valuebuf[ 0 ] == '-' ) + { + activate = qfalse; + } + points = atoi( valuebuf ); + } + else + { + points = ( g_bleedingSpree.integer + 1 ) * 100; + } + + if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + { + ADMP( "^3!outlaw: ^7sorry, but your intended victim has a higher admin" + " level than you\n" ); + return qfalse; + } + if( points ) + { + vic->client->pers.statscounters.spreebleeds += points; + if( vic->client->pers.statscounters.spreebleeds < 1 ) + vic->client->pers.statscounters.spreebleeds = 1; + if( activate && !vic->client->pers.bleeder ) + { + vic->client->pers.bleeder = qtrue; + level.bleeders++; + + AP( va( "print \"^3!outlaw: ^7%s^7 has been designated an outlaw by ^7%s\n\"", + vic->client->pers.netname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + else + { + AP( va( "print \"^3!outlaw: ^7%s^7 bleeder value has been adjusted by ^7%s\n\"", + vic->client->pers.netname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + ADMP( va( "^3!outlaw: ^7%s^7's bleeder value is now %d\n", + vic->client->pers.netname, + vic->client->pers.statscounters.spreebleeds ) ); + } + } + else + { + vic->client->pers.statscounters.spreebleeds = 0; + if( vic->client->pers.bleeder ) + vic->client->pers.bleeder = qfalse; + if( level.bleeders ) + level.bleeders--; + + AP( va( "print \"^3!outlaw: ^7%s^7 has been pardoned by ^7%s\n\"", + vic->client->pers.netname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + return qtrue; +} + qboolean G_admin_slap( gentity_t *ent, int skiparg ) { int pids[ MAX_CLIENTS ]; @@ -6452,6 +7325,45 @@ qboolean G_admin_drop( gentity_t *ent, int skiparg ) return qtrue; } +qboolean G_admin_bubble( gentity_t *ent, int skiparg ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + gentity_t *vic; + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!bubble: ^7usage: !bubble [name|slot#]\n" ); + return qfalse; + } + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + if( G_ClientNumbersFromString( name, pids ) != 1 ) + { + G_MatchOnePlayer( pids, err, sizeof( err ) ); + ADMP( va( "^3!bubble: ^7%s\n", err ) ); + return qfalse; + } + if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + { + ADMP( "^3!bubble: ^7sorry, but your intended victim has a higher admin" + " level than you\n" ); + return qfalse; + } + + vic = &g_entities[ pids[ 0 ] ]; + if( vic->client->pers.bubbleTime ) + vic->client->pers.bubbleTime = 0; + else + vic->client->pers.bubbleTime = level.time + 500; + + AP( va( "print \"^3!bubble: ^7bubbles %s for %s^7 by %s\n\"", + ( vic->client->pers.bubbleTime ) ? "enabled" : "disabled", + vic->client->pers.netname, + ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) ); + + return qtrue; +} + qboolean G_admin_buildlog( gentity_t *ent, int skiparg ) { #define LOG_DISPLAY_LENGTH 10 @@ -6663,6 +7575,34 @@ qboolean G_admin_buildlog( gentity_t *ent, int skiparg ) return qtrue; } +int G_admin_autorevert( gentity_t *ent ) +{ + int count = 0; + int max; + buildHistory_t *ptr; + + if( !g_autoRevert.integer ) + return 0; + if( !g_buildLogMaxLength.integer ) + return 0; + + max = g_autoRevert.integer; + ptr = level.buildHistory; + while( ptr && count < max ) + { + if( ptr->ent == ent && ptr->time > level.time - (60000 * 5) && + ( ptr->fate == BF_TEAMKILLED || ptr->fate == BF_DECONNED ) ) + { + trap_SendConsoleCommand( EXEC_APPEND, va("!revert #%d\n", ptr->ID) ); + count++; + } + + ptr = ptr->next; + } + + return count; +} + qboolean G_admin_revert( gentity_t *ent, int skiparg ) { int i = 0, j = 0, repeat = 1, ID = 0, len, matchlen=0; @@ -6805,33 +7745,6 @@ qboolean G_admin_revert( gentity_t *ent, int skiparg ) return qfalse; } } - // Prevent teleport glitch when reverting an occupied hovel - if( targ->s.modelindex == BA_A_HOVEL && - targ->active ) - { - gentity_t *builder = targ->builder; - vec3_t newOrigin; - vec3_t newAngles; - - VectorCopy( targ->s.angles, newAngles ); - newAngles[ ROLL ] = 0; - - VectorCopy( targ->s.origin, newOrigin ); - VectorMA( newOrigin, 1.0f, targ->s.origin2, newOrigin ); - - //prevent lerping - builder->client->ps.eFlags ^= EF_TELEPORT_BIT; - builder->client->ps.eFlags &= ~EF_NODRAW; - G_UnlaggedClear( builder ); - - G_SetOrigin( builder, newOrigin ); - VectorCopy( newOrigin, builder->client->ps.origin ); - G_SetClientViewAngle( builder, newAngles ); - - //client leaves hovel - builder->client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING; - } - // if we haven't returned yet then we're good to go, free it G_FreeEntity( targ ); // put the marked buildables back and mark them again @@ -7225,6 +8138,76 @@ qboolean G_admin_L1(gentity_t *ent, int skiparg ){ return qtrue; } +qboolean G_admin_nobuild(gentity_t *ent, int skiparg ) +{ + char func[ 32 ]; + + if( ent && ent->client->pers.teamSelection != PTE_NONE ) + { + ADMP( "^3!bring: ^7you can only use this command from spectator\n" ); + return qfalse; + } + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!nobuild: ^7usage: !nobuild [on|off|save|add|del|list|mode|zone|+|-|go]\n" ); + return qfalse; + } + G_SayArgv( 1 + skiparg, func, sizeof( func ) ); + + if( !Q_stricmp( func, "on" ) ) + { + nobuild_set( qtrue, ent ); + } + else if( !Q_stricmp( func, "off" ) ) + { + nobuild_set( qfalse, ent ); + } + else if( !Q_stricmp( func, "save" ) ) + { + nobuild_save( ); + } + else if( !Q_stricmp( func, "list" ) ) + { + nobuild_list( ent ); + } + else if( !Q_stricmp( func, "add" ) ) + { + nobuild_add( ent ); + } + else if( !Q_stricmp( func, "del" ) ) + { + nobuild_del( ent ); + } + else if( !Q_stricmp( func, "zone" ) ) + { + nobuild_command( ent, qtrue, qfalse, 0.0f ); + } + else if( !Q_stricmp( func, "mode" ) ) + { + nobuild_command( ent, qfalse, qtrue, 0.0f ); + } + else if( !Q_stricmp( func, "+" ) ) + { + nobuild_command( ent, qfalse, qfalse, 8.0f ); + } + else if( !Q_stricmp( func, "-" ) ) + { + nobuild_command( ent, qfalse, qfalse, -8.0f ); + } + else if( !Q_stricmp( func, "go" ) ) + { + nobuild_go( ent ); + } + else + { + ADMP( "^3!nobuild: ^7usage: !nobuild [on|off|save|add|del|list|mode|zone|+|-|go]\n" ); + return qfalse; + } + + return qtrue; +} + qboolean G_admin_invisible( gentity_t *ent, int skiparg ) { if( !ent ) |