Index: src/game/g_local.h =================================================================== --- src/game/g_local.h (revision 809) +++ src/game/g_local.h (working copy) @@ -340,6 +340,9 @@ int nameChangeTime; int nameChanges; + int lastRLTime; // level.time of last rate-limited command + int rlPoints; // number of rate-limit points accumulated + vec3_t lastDeathLocation; char guid[ 33 ]; char ip[ 16 ]; @@ -1101,6 +1104,9 @@ extern vmCvar_t g_currentMap; extern vmCvar_t g_initialMapRotation; extern vmCvar_t g_chatTeamPrefix; +extern vmCvar_t g_rateLimit; +extern vmCvar_t g_rateLimitMaxPoints; +extern vmCvar_t g_rateLimitSafeTime; extern vmCvar_t g_mapConfigs; Index: src/game/g_main.c =================================================================== --- src/game/g_main.c (revision 809) +++ src/game/g_main.c (working copy) @@ -114,6 +114,9 @@ vmCvar_t g_mapConfigs; vmCvar_t g_chatTeamPrefix; +vmCvar_t g_rateLimit; +vmCvar_t g_rateLimitMaxPoints; +vmCvar_t g_rateLimitSafeTime; vmCvar_t g_admin; vmCvar_t g_adminLog; @@ -212,6 +215,9 @@ { &g_disabledBuildables, "g_disabledBuildables", "", CVAR_ROM, 0, qfalse }, { &g_chatTeamPrefix, "g_chatTeamPrefix", "0", CVAR_ARCHIVE }, + { &g_rateLimit, "g_rateLimit", "0", CVAR_ARCHIVE, 0, qfalse }, + { &g_rateLimitMaxPoints, "g_rateLimitMaxPoints", "3000", CVAR_ARCHIVE, 0, qfalse }, + { &g_rateLimitSafeTime, "g_rateLimitSafeTime", "1500", CVAR_ARCHIVE, 0, qfalse }, { &g_debugMapRotation, "g_debugMapRotation", "0", 0, 0, qfalse }, { &g_currentMapRotation, "g_currentMapRotation", "-1", 0, 0, qfalse }, // -1 = NOT_ROTATING Index: src/game/g_cmds.c =================================================================== --- src/game/g_cmds.c (revision 809) +++ src/game/g_cmds.c (working copy) @@ -389,7 +389,40 @@ return line; } +/* +================== +G_Rate_Limited +Determine whether a user is rate limited, and adjust their rate-limit points if so +================== +*/ + +qboolean G_Rate_Limited( gentity_t *ent ) +{ + int millisSinceLastCommand; + + // This shouldn't be called if g_rateLimit isn't set, but handle it anyway. + if( !g_rateLimit.integer ) + return qfalse; + + millisSinceLastCommand = level.time - ent->client->pers.lastRLTime; + if( millisSinceLastCommand < g_rateLimitSafeTime.integer ) + ent->client->pers.rlPoints += ( g_rateLimitSafeTime.integer - millisSinceLastCommand ); + else + { + ent->client->pers.rlPoints -= ( millisSinceLastCommand - g_rateLimitSafeTime.integer ); + if( ent->client->pers.rlPoints < 0 ) + ent->client->pers.rlPoints = 0; + } + + ent->client->pers.lastRLTime = level.time; + + if( ent->client->pers.rlPoints > g_rateLimitMaxPoints.integer ) + return qtrue; + + return qfalse; +} + /* ================== Cmd_Give_f @@ -750,6 +783,18 @@ char text[ MAX_SAY_TEXT ]; char location[ 64 ]; + // Bail if the text is blank. + if( ! chatText[0] ) + return; + + // Rate limit. If they're talking too fast, determine that and return. + if( g_rateLimit.integer ) + if ( G_Rate_Limited( ent ) ) + { + trap_SendServerCommand( ent-g_entities, "print \"Your chat is rate-limited; wait before chatting again\n\"" ); + return; + } + if (g_chatTeamPrefix.integer) switch( ent->client->pers.teamSelection) {