Index: src/game/g_local.h =================================================================== --- src/game/g_local.h (revision 959) +++ src/game/g_local.h (working copy) @@ -347,6 +347,9 @@ int savedScore; int savedCredit; + int lastFloodTime; // level.time of last flood-limited command + int floodDemerits; // number of flood demerits accumulated + vec3_t lastDeathLocation; char guid[ 33 ]; char ip[ 16 ]; @@ -1168,6 +1171,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_shove; Index: src/game/g_main.c =================================================================== --- src/game/g_main.c (revision 959) +++ src/game/g_main.c (working copy) @@ -119,6 +119,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; @@ -227,6 +229,8 @@ { &g_disabledBuildables, "g_disabledBuildables", "", CVAR_ROM, 0, qfalse }, { &g_chatTeamPrefix, "g_chatTeamPrefix", "0", CVAR_ARCHIVE }, + { &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_cmds.c =================================================================== --- src/game/g_cmds.c (revision 959) +++ src/game/g_cmds.c (working copy) @@ -345,7 +345,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 @@ -825,6 +870,18 @@ 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)