Index: src/game/g_local.h =================================================================== --- src/game/g_local.h (revision 1102) +++ src/game/g_local.h (working copy) @@ -312,6 +312,9 @@ qboolean vote; qboolean teamVote; + int lastFloodTime; // level.time of last flood-limited command + int floodDemerits; // number of flood demerits accumulated + vec3_t lastDeathLocation; char guid[ 33 ]; char ip[ 40 ]; @@ -921,6 +924,7 @@ void CheckTeamVote( team_t teamnum ); void LogExit( const char *string ); int G_TimeTilSuddenDeath( void ); +qboolean G_Flood_Limited( gentity_t *ent ); // // g_client.c @@ -1126,6 +1130,8 @@ extern vmCvar_t g_currentMap; extern vmCvar_t g_initialMapRotation; extern vmCvar_t g_chatTeamPrefix; +extern vmCvar_t g_floodMaxDemerits; +extern vmCvar_t g_floodMinTime; extern vmCvar_t g_debugVoices; extern vmCvar_t g_voiceChats; Index: src/game/g_main.c =================================================================== --- src/game/g_main.c (revision 1102) +++ src/game/g_main.c (working copy) @@ -120,6 +120,8 @@ vmCvar_t g_mapConfigs; vmCvar_t g_chatTeamPrefix; +vmCvar_t g_floodMaxDemerits; +vmCvar_t g_floodMinTime; vmCvar_t g_layouts; vmCvar_t g_layoutAuto; @@ -229,6 +231,8 @@ { &g_disabledBuildables, "g_disabledBuildables", "", CVAR_ROM, 0, qfalse }, { &g_chatTeamPrefix, "g_chatTeamPrefix", "0", CVAR_ARCHIVE, 0, qfalse }, + { &g_floodMaxDemerits, "g_floodMaxDemerits", "0", CVAR_ARCHIVE, 0, qfalse }, + { &g_floodMinTime, "g_floodMinTime", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_markDeconstruct, "g_markDeconstruct", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, Index: src/game/g_admin.c =================================================================== --- src/game/g_admin.c (revision 1102) +++ src/game/g_admin.c (working copy) @@ -945,6 +945,14 @@ { return qfalse; } + + // Flood limit. If they're talking too fast, determine that and return. + if( g_floodMinTime.integer ) + if ( G_Flood_Limited( ent ) ) + { + trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); + return qtrue; + } for( i = 0; i < MAX_ADMIN_COMMANDS && g_admin_commands[ i ]; i++ ) { Index: src/game/g_cmds.c =================================================================== --- src/game/g_cmds.c (revision 1102) +++ src/game/g_cmds.c (working copy) @@ -334,7 +334,52 @@ return line; } +/* +================== +G_Flood_Limited +Determine whether a user is flood limited, and adjust their flood demerits +================== +*/ + +qboolean G_Flood_Limited( gentity_t *ent ) +{ + int millisSinceLastCommand; + int maximumDemerits; + + // This shouldn't be called if g_floodMinTime isn't set, but handle it anyway. + if( !g_floodMinTime.integer ) + return qfalse; + + // Do not limit admins with no censor/flood flag + if( G_admin_permission( ent, ADMF_NOCENSORFLOOD ) ) + return qfalse; + + millisSinceLastCommand = level.time - ent->client->pers.lastFloodTime; + if( millisSinceLastCommand < g_floodMinTime.integer ) + ent->client->pers.floodDemerits += ( g_floodMinTime.integer - millisSinceLastCommand ); + else + { + ent->client->pers.floodDemerits -= ( millisSinceLastCommand - g_floodMinTime.integer ); + if( ent->client->pers.floodDemerits < 0 ) + ent->client->pers.floodDemerits = 0; + } + + ent->client->pers.lastFloodTime = level.time; + + // If g_floodMaxDemerits == 0, then we go against g_floodMinTime^2. + + if( !g_floodMaxDemerits.integer ) + maximumDemerits = g_floodMinTime.integer * g_floodMinTime.integer / 1000; + else + maximumDemerits = g_floodMaxDemerits.integer; + + if( ent->client->pers.floodDemerits > maximumDemerits ) + return qtrue; + + return qfalse; +} + /* ================== Cmd_Give_f @@ -821,7 +866,20 @@ // don't let text be too long for malicious reasons char text[ MAX_SAY_TEXT ]; char location[ 64 ]; + + // Bail if the text is blank. + if( ! chatText[0] ) + return; + // Flood limit. If they're talking too fast, determine that and return. + if( g_floodMinTime.integer ) + if ( G_Flood_Limited( ent ) ) + { + trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); + return; + } + + if( g_chatTeamPrefix.integer ) { switch( ent->client->pers.teamSelection ) @@ -3146,6 +3204,13 @@ ADMP( "Sorry, but private messages have been disabled\n" ); return; } + + if( g_floodMinTime.integer ) + if ( G_Flood_Limited( ent ) ) + { + trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); + return; + } G_SayArgv( 0, cmd, sizeof( cmd ) ); if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) )