summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/game/g_admin.c6
-rw-r--r--src/game/g_cmds.c38
-rw-r--r--src/game/g_local.h7
-rw-r--r--src/game/g_main.c6
4 files changed, 56 insertions, 1 deletions
diff --git a/src/game/g_admin.c b/src/game/g_admin.c
index b7aae83e..e712dfeb 100644
--- a/src/game/g_admin.c
+++ b/src/game/g_admin.c
@@ -953,6 +953,9 @@ qboolean G_admin_cmd_check( gentity_t *ent, qboolean say )
if( admin_command_permission( ent, cmd ) )
{
+ // flooding say will have already been accounted for in ClientCommand
+ if( !say && G_FloodLimited( ent ) )
+ return qtrue;
trap_SendConsoleCommand( EXEC_APPEND, g_admin_commands[ i ]->exec );
admin_log( ent, cmd, skip );
}
@@ -971,6 +974,9 @@ qboolean G_admin_cmd_check( gentity_t *ent, qboolean say )
if( G_admin_permission( ent, g_admin_cmds[ i ].flag[ 0 ] ) )
{
+ // flooding say will have already been accounted for in ClientCommand
+ if( !say && G_FloodLimited( ent ) )
+ return qtrue;
g_admin_cmds[ i ].handler( ent, skip );
admin_log( ent, cmd, skip );
}
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 57b43c85..86a26483 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -3025,6 +3025,41 @@ void Cmd_Damage_f( gentity_t *ent )
( nonloc ? DAMAGE_NO_LOCDAMAGE : 0 ), MOD_TARGET_LASER );
}
+/*
+==================
+G_FloodLimited
+
+Determine whether a user is flood limited, and adjust their flood demerits
+Notify them if this is the first time they were over the limit
+==================
+*/
+qboolean G_FloodLimited( gentity_t *ent )
+{
+ int deltatime = level.time - ent->client->pers.floodTime;
+ int flooding;
+
+ if( g_floodMinTime.integer <= 0 )
+ return qfalse;
+
+ if( G_admin_permission( ent, ADMF_NOCENSORFLOOD ) )
+ return qfalse;
+
+ ent->client->pers.floodDemerits += g_floodMinTime.integer - deltatime;
+ if( ent->client->pers.floodDemerits < 0 )
+ ent->client->pers.floodDemerits = 0;
+ ent->client->pers.floodTime = level.time;
+
+ flooding = ent->client->pers.floodDemerits - g_floodMaxDemerits.integer;
+ if( flooding <= 0 )
+ return qfalse;
+ // seconds (rounded up)
+ flooding = ( flooding + 999 ) / 1000;
+ trap_SendServerCommand( ent - g_entities, va( "print \"You are flooding: "
+ "please wait %d second%s before trying again\n",
+ flooding, ( flooding != 1 ) ? "s" : "" ) );
+ return qtrue;
+}
+
commands_t cmds[ ] = {
// normal commands
{ "team", 0, Cmd_Team_f },
@@ -3125,7 +3160,8 @@ void ClientCommand( int clientNum )
return;
}
- if( cmds[ i ].cmdFlags & CMD_MESSAGE && ent->client->pers.muted )
+ if( cmds[ i ].cmdFlags & CMD_MESSAGE && ( ent->client->pers.muted ||
+ G_FloodLimited( ent ) ) )
return;
if( cmds[ i ].cmdFlags & CMD_TEAM &&
diff --git a/src/game/g_local.h b/src/game/g_local.h
index bd5cdc78..da99d44e 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -330,6 +330,10 @@ typedef struct
qboolean vote;
qboolean teamVote;
+ // flood protection
+ int floodDemerits;
+ int floodTime;
+
vec3_t lastDeathLocation;
char guid[ 33 ];
char ip[ 40 ];
@@ -1140,6 +1144,9 @@ extern vmCvar_t g_chatTeamPrefix;
extern vmCvar_t g_debugVoices;
extern vmCvar_t g_voiceChats;
+extern vmCvar_t g_floodMaxDemerits;
+extern vmCvar_t g_floodMinTime;
+
extern vmCvar_t g_shove;
extern vmCvar_t g_mapConfigs;
diff --git a/src/game/g_main.c b/src/game/g_main.c
index 0c8aa33f..0a2cbedb 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -121,6 +121,9 @@ vmCvar_t g_shove;
vmCvar_t g_mapConfigs;
vmCvar_t g_chatTeamPrefix;
+vmCvar_t g_floodMaxDemerits;
+vmCvar_t g_floodMinTime;
+
vmCvar_t g_layouts;
vmCvar_t g_layoutAuto;
@@ -232,6 +235,9 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_chatTeamPrefix, "g_chatTeamPrefix", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_floodMaxDemerits, "g_floodMaxDemerits", "5000", CVAR_ARCHIVE, 0, qfalse },
+ { &g_floodMinTime, "g_floodMinTime", "2000", CVAR_ARCHIVE, 0, qfalse },
+
{ &g_markDeconstruct, "g_markDeconstruct", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse },
{ &g_debugMapRotation, "g_debugMapRotation", "0", 0, 0, qfalse },