From 1f9ce343da30a20cff2659ca6a269f70d8bbebaa Mon Sep 17 00:00:00 2001 From: "M. Kristall" Date: Thu, 20 Jan 2011 02:12:05 +0000 Subject: * Allow mute and denybuild to target players who are no longer connected --- src/game/g_admin.c | 171 +++++++++++++++++++++++++++++++++++++-------------- src/game/g_cmds.c | 10 +-- src/game/g_local.h | 2 + src/game/g_namelog.c | 6 ++ 4 files changed, 140 insertions(+), 49 deletions(-) diff --git a/src/game/g_admin.c b/src/game/g_admin.c index 038c8cdb..5d9d3fc4 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -1202,6 +1202,15 @@ qboolean G_admin_time( gentity_t *ent ) return qtrue; } +// this should be in one of the headers, but it is only used here for now +namelog_t *G_NamelogFromString( gentity_t *ent, char *s ); + +/* +for consistency, we should be able to target a disconnected player with setlevel +but we can't use namelog and remain consistent, so the solution would be to make +everyone a real level 0 admin so they can be targeted until the next level +but that seems kind of stupid +*/ qboolean G_admin_setlevel( gentity_t *ent ) { char name[ MAX_NAME_LENGTH ] = {""}; @@ -1551,20 +1560,7 @@ qboolean G_admin_ban( gentity_t *ent ) } } - // ban by clientnum - for( i = 0; search[ i ] && isdigit( search[ i ] ); i++ ); - if( !search[ i ] ) - { - i = atoi( search ); - if( i < MAX_CLIENTS && - level.clients[ i ].pers.connected != CON_DISCONNECTED ) - { - logmatches = 1; - exactmatch = qtrue; - for( match = level.namelogs; match->slot != i; match = match->next ); - } - } - else if( G_AddressParse( search, &ip ) ) + if( G_AddressParse( search, &ip ) ) { int max = ip.type == IPv4 ? 32 : 128; int min = ent ? max / 2 : 1; @@ -1576,6 +1572,11 @@ qboolean G_admin_ban( gentity_t *ent ) } ipmatch = qtrue; } + else if( ( match = G_NamelogFromString( ent, search ) ) && !match->banned ) + { + logmatches = 1; + exactmatch = qtrue; + } for( namelog = level.namelogs; namelog && !exactmatch; namelog = namelog->next ) { @@ -1638,7 +1639,9 @@ qboolean G_admin_ban( gentity_t *ent ) if( i < MAX_NAMELOG_NAMES && namelog->name[ i ][ 0 ] ) { ADMBP( namelog->slot > -1 ? - va( S_COLOR_YELLOW "%-2d" S_COLOR_WHITE, namelog->slot ) : "- " ); + va( S_COLOR_YELLOW "%-2d %-2d" S_COLOR_WHITE, namelog->id, + namelog->slot ) : + va( "%-2d - ", namelog->id ) ); for( i = 0; i < MAX_NAMELOG_NAMES && namelog->name[ i ][ 0 ]; i++ ) ADMBP( va( " %s" S_COLOR_WHITE, namelog->name[ i ] ) ); for( i = 0; i < MAX_NAMELOG_ADDRS && namelog->ip[ i ].str[ 0 ]; i++ ) @@ -1657,7 +1660,7 @@ qboolean G_admin_ban( gentity_t *ent ) " level than you\n" ); return qfalse; } - if( !strcmp( match->ip[ 0 ].str, "localhost" ) ) + if( match->slot > -1 && level.clients[ match->slot ].pers.localClient ) { ADMP( "^3ban: ^7disconnecting the host would end the game\n" ); return qfalse; @@ -1959,10 +1962,9 @@ qboolean G_admin_changemap( gentity_t *ent ) qboolean G_admin_mute( gentity_t *ent ) { - int pid; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + char name[ MAX_NAME_LENGTH ]; char command[ MAX_ADMIN_CMD_LEN ]; - gentity_t *vic; + namelog_t *vic; trap_Argv( 0, command, sizeof( command ) ); if( trap_Argc() < 2 ) @@ -1971,29 +1973,30 @@ qboolean G_admin_mute( gentity_t *ent ) return qfalse; } trap_Argv( 1, name, sizeof( name ) ); - if( ( pid = G_ClientNumberFromString( name, err, sizeof( err ) ) ) == -1 ) + if( !( vic = G_NamelogFromString( ent, name ) ) ) { - ADMP( va( "^3%s: ^7%s", command, err ) ); + ADMP( va( "^3%s: ^7no match\n", command ) ); return qfalse; } - vic = &g_entities[ pid ]; - if( !admin_higher( ent, vic ) ) + if( ent && !admin_higher_admin( ent->client->pers.admin, + G_admin_admin( vic->guid ) ) ) { ADMP( va( "^3%s: ^7sorry, but your intended victim has a higher admin" " level than you\n", command ) ); return qfalse; } - if( vic->client->pers.namelog->muted ) + if( vic->muted ) { if( !Q_stricmp( command, "mute" ) ) { ADMP( "^3mute: ^7player is already muted\n" ); return qtrue; } - vic->client->pers.namelog->muted = qfalse; - CPx( pid, "cp \"^1You have been unmuted\"" ); + vic->muted = qfalse; + if( vic->slot > -1 ) + CPx( vic->slot, "cp \"^1You have been unmuted\"" ); AP( va( "print \"^3unmute: ^7%s^7 has been unmuted by %s\n\"", - vic->client->pers.netname, + vic->name[ vic->nameOffset ], ( ent ) ? ent->client->pers.netname : "console" ) ); } else @@ -2003,10 +2006,11 @@ qboolean G_admin_mute( gentity_t *ent ) ADMP( "^3unmute: ^7player is not currently muted\n" ); return qtrue; } - vic->client->pers.namelog->muted = qtrue; - CPx( pid, "cp \"^1You've been muted\"" ); + vic->muted = qtrue; + if( vic->slot > -1 ) + CPx( vic->slot, "cp \"^1You've been muted\"" ); AP( va( "print \"^3mute: ^7%s^7 has been muted by ^7%s\n\"", - vic->client->pers.netname, + vic->name[ vic->nameOffset ], ( ent ) ? ent->client->pers.netname : "console" ) ); } return qtrue; @@ -2014,10 +2018,9 @@ qboolean G_admin_mute( gentity_t *ent ) qboolean G_admin_denybuild( gentity_t *ent ) { - int pid; - char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + char name[ MAX_NAME_LENGTH ]; char command[ MAX_ADMIN_CMD_LEN ]; - gentity_t *vic; + namelog_t *vic; trap_Argv( 0, command, sizeof( command ) ); if( trap_Argc() < 2 ) @@ -2026,30 +2029,31 @@ qboolean G_admin_denybuild( gentity_t *ent ) return qfalse; } trap_Argv( 1, name, sizeof( name ) ); - if( ( pid = G_ClientNumberFromString( name, err, sizeof( err ) ) ) == -1 ) + if( !( vic = G_NamelogFromString( ent, name ) ) ) { - ADMP( va( "^3%s: ^7%s", command, err ) ); + ADMP( va( "^3%s: ^7no match\n", command ) ); return qfalse; } - vic = &g_entities[ pid ]; - if( !admin_higher( ent, vic ) ) + if( ent && !admin_higher_admin( ent->client->pers.admin, + G_admin_admin( vic->guid ) ) ) { ADMP( va( "^3%s: ^7sorry, but your intended victim has a higher admin" " level than you\n", command ) ); return qfalse; } - if( vic->client->pers.namelog->denyBuild ) + if( vic->denyBuild ) { if( !Q_stricmp( command, "denybuild" ) ) { ADMP( "^3denybuild: ^7player already has no building rights\n" ); return qtrue; } - vic->client->pers.namelog->denyBuild = qfalse; - CPx( pid, "cp \"^1You've regained your building rights\"" ); + vic->denyBuild = qfalse; + if( vic->slot > -1 ) + CPx( vic->slot, "cp \"^1You've regained your building rights\"" ); AP( va( "print \"^3allowbuild: ^7building rights for ^7%s^7 restored by %s\n\"", - vic->client->pers.netname, + vic->name[ vic->nameOffset ], ( ent ) ? ent->client->pers.netname : "console" ) ); } else @@ -2059,12 +2063,15 @@ qboolean G_admin_denybuild( gentity_t *ent ) ADMP( "^3allowbuild: ^7player already has building rights\n" ); return qtrue; } - vic->client->pers.namelog->denyBuild = qtrue; - vic->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; - CPx( pid, "cp \"^1You've lost your building rights\"" ); + vic->denyBuild = qtrue; + if( vic->slot > -1 ) + { + level.clients[ vic->slot ].ps.stats[ STAT_BUILDABLE ] = BA_NONE; + CPx( vic->slot, "cp \"^1You've lost your building rights\"" ); + } AP( va( "print \"^3denybuild: ^7building rights for ^7%s^7 revoked by ^7%s\n\"", - vic->client->pers.netname, + vic->name[ vic->nameOffset ], ( ent ) ? ent->client->pers.netname : "console" ) ); } return qtrue; @@ -2742,6 +2749,80 @@ qboolean G_admin_namelog( gentity_t *ent ) return qtrue; } +/* +================== +G_NamelogFromString + +This is similar to G_ClientNumberFromString but for namelog instead +Returns NULL if not exactly 1 match +================== +*/ +namelog_t *G_NamelogFromString( gentity_t *ent, char *s ) +{ + namelog_t *p, *m = NULL; + int i, found = 0; + char n2[ MAX_NAME_LENGTH ] = {""}; + char s2[ MAX_NAME_LENGTH ] = {""}; + + if( !s[ 0 ] ) + return NULL; + + // if a number is provided, it is a clientnum or namelog id + for( i = 0; s[ i ] && isdigit( s[ i ] ); i++ ); + if( !s[ i ] ) + { + i = atoi( s ); + + if( i >= 0 && i < level.maxclients ) + { + if( level.clients[ i ].pers.connected != CON_DISCONNECTED ) + return level.clients[ i ].pers.namelog; + } + else if( i >= MAX_CLIENTS ) + { + for( p = level.namelogs; p; p = p->next ) + { + if( p->id == i ) + break; + } + if( p ) + return p; + } + + return NULL; + } + + // check for a name match + G_SanitiseString( s, s2, sizeof( s2 ) ); + + for( p = level.namelogs; p; p = p->next ) + { + for( i = 0; i < MAX_NAMELOG_NAMES && p->name[ i ][ 0 ]; i++ ) + { + G_SanitiseString( p->name[ i ], n2, sizeof( n2 ) ); + + // if this is an exact match to a current player + if( i == p->nameOffset && p->slot > -1 && !strcmp( s2, n2 ) ) + return p; + + if( strstr( n2, s2 ) ) + m = p; + } + + if( m == p ) + found++; + } + + if( found == 1 ) + return m; + + if( found > 1 ) + admin_search( ent, "namelog", "recent clients", namelog_matchname, + namelog_out, level.namelogs, s2, 0, MAX_CLIENTS, -1 ); + + return NULL; +} + qboolean G_admin_lock( gentity_t *ent ) { char command[ MAX_ADMIN_CMD_LEN ]; diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index b4ac5ec2..c4025ced 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1095,6 +1095,7 @@ void Cmd_CallVote_f( gentity_t *ent ) char reason[ MAX_TOKEN_CHARS ]; char *creason; int clientNum = -1; + int id = -1; team_t team; trap_Argv( 0, cmd, sizeof( cmd ) ); @@ -1161,6 +1162,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } G_DecolorString( level.clients[ clientNum ].pers.netname, name, sizeof( name ) ); + id = level.clients[ clientNum ].pers.namelog->id; if( !Q_stricmp( vote, "kick" ) || !Q_stricmp( vote, "mute" ) || !Q_stricmp( vote, "denybuild" ) ) @@ -1223,7 +1225,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ), - "mute %d", clientNum ); + "mute %d", id ); Com_sprintf( level.voteDisplayString[ team ], sizeof( level.voteDisplayString[ team ] ), "Mute player '%s'", name ); @@ -1244,7 +1246,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ), - "unmute %d", clientNum ); + "unmute %d", id ); Com_sprintf( level.voteDisplayString[ team ], sizeof( level.voteDisplayString[ team ] ), "Unmute player '%s'", name ); @@ -1346,7 +1348,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ), - "denybuild %d", clientNum ); + "denybuild %d", id ); Com_sprintf( level.voteDisplayString[ team ], sizeof( level.voteDisplayString[ team ] ), "Take away building rights from '%s'", name ); @@ -1367,7 +1369,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ), - "allowbuild %d", clientNum ); + "allowbuild %d", id ); Com_sprintf( level.voteDisplayString[ team ], sizeof( level.voteDisplayString[ team ] ), "Allow '%s' to build", name ); diff --git a/src/game/g_local.h b/src/game/g_local.h index f55503c8..277556d9 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -294,6 +294,8 @@ typedef struct namelog_s int score; int credits; team_t team; + + int id; } namelog_t; // client data that stays across multiple respawns, but is cleared diff --git a/src/game/g_namelog.c b/src/game/g_namelog.c index 1e5b7f4f..66136844 100644 --- a/src/game/g_namelog.c +++ b/src/game/g_namelog.c @@ -51,9 +51,15 @@ void G_namelog_connect( gclient_t *client ) n = BG_Alloc( sizeof( namelog_t ) ); strcpy( n->guid, client->pers.guid ); if( p ) + { p->next = n; + n->id = p->id + 1; + } else + { level.namelogs = n; + n->id = MAX_CLIENTS; + } } client->pers.namelog = n; n->slot = client - level.clients; -- cgit