]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Implement per-command flood control Mario/floodcontrol_per_command
authorMario <mario.mario@y7mail.com>
Wed, 31 Jul 2024 13:26:07 +0000 (23:26 +1000)
committerMario <mario.mario@y7mail.com>
Wed, 31 Jul 2024 13:26:07 +0000 (23:26 +1000)
commands.cfg
qcsrc/server/command/cmd.qc
qcsrc/server/command/cmd.qh
qcsrc/server/command/reg.qh

index 98676518533159d1eda4cb6626c45ff1b1c08eb4..f43da1fe226e0d4a78fe18e5fb51829923534b55 100644 (file)
@@ -177,6 +177,7 @@ alias menu_showteamselect team_selection_show
 // ========================================================
 set sv_clientcommand_antispam_time 1 "Amount of seconds after a command before another command can be called again without being considered spam. (Use -1 for no antispam limit)"
 set sv_clientcommand_antispam_count 8 "Amount of commands considered spam before commands are rejected."
+set sv_clientcommand_antispam_percommand 1 "Apply separate antispam timers for each command"
 seta sv_status_privacy 1 "hide IP addresses from \"status\" and \"who\" replies shown to clients"
 seta cl_autoswitch 1 "automatically switch to newly picked up weapons if they are better than what you are carrying"
 
index 33ceccbb5d31b8e9ae7753c7ed9f5723062c8d19..0fd198362ab52649d103d2556348a1fa8640094b 100644 (file)
@@ -975,6 +975,35 @@ void ClientCommand_macro_write_aliases(float fh)
        FOREACH(CLIENT_COMMANDS, true, { CMD_Write_Alias("qc_cmd_cmd", it.m_name, it.m_description); });
 }
 
+bool ClientCommand_antispam(int argc, entity caller, string command)
+{
+       entity store = IS_CLIENT(caller) ? CS(caller) : caller; // unfortunately, we need to store these on the client initially
+       .float floodtime = cmd_floodtime;
+
+       if(autocvar_sv_clientcommand_antispam_percommand)
+       {
+               string c = strtolower(argv(0));
+               FOREACH(CLIENT_COMMANDS, it.m_name == c, {
+                       floodtime = cmd_floodtime_forcommand[it.m_id];
+                       break;
+               });
+       }
+
+       if(time < store.(floodtime))
+       {
+               sprint(caller, strcat("^3CMD FLOOD CONTROL: wait ^1", ftos(store.(floodtime) - time), "^3 seconds, command was: ", command, "\n"));
+               return true;
+       }
+       else
+       {
+               float spamtime = autocvar_sv_clientcommand_antispam_time;
+               int spamcount = autocvar_sv_clientcommand_antispam_count;
+               store.(floodtime) = max(time - spamcount * spamtime, store.(floodtime)) + spamtime;
+       }
+
+       return false;
+}
+
 // ======================================
 //  Main Function Called By Engine (cmd)
 // ======================================
@@ -1045,15 +1074,8 @@ void SV_ParseClientCommand(entity this, string command)
                        LABEL(flood_control)
                        if (!timeout_status)  // not while paused
                        {
-                               entity store = IS_CLIENT(this) ? CS(this) : this; // unfortunately, we need to store these on the client initially
-                               // this is basically the same as the chat flood control
-                               if (time < store.cmd_floodtime)
-                               {
-                                       sprint(this, strcat("^3CMD FLOOD CONTROL: wait ^1", ftos(store.cmd_floodtime - time), "^3 seconds, command was: ", command, "\n"));
-                                       return;  // too much spam, halt
-                               }
-                               else
-                                       store.cmd_floodtime = max(time - autocvar_sv_clientcommand_antispam_count * autocvar_sv_clientcommand_antispam_time, store.cmd_floodtime) + autocvar_sv_clientcommand_antispam_time;
+                               if(ClientCommand_antispam(argc, this, command))
+                                       return; // too much spam, halt
                        }
                        break;  // continue, as we're not flooding yet
        }
index 25c69491851d54afe4d4d75a21dcaa80a259de62..80441c350f463be23fa2b9af11216ef9d6bba5da 100644 (file)
@@ -4,6 +4,7 @@
 
 float autocvar_sv_clientcommand_antispam_time;
 int autocvar_sv_clientcommand_antispam_count;
+bool autocvar_sv_clientcommand_antispam_percommand = true;
 
 .float cmd_floodtime;
 .string ignore_list; // stores player id's, maybe can be upgraded to store net address for reconnect protection
index 59703d7000b22447fee645537dd3d43f788ba921..3edc85290a93960a14f9032506f912dfac5376da 100644 (file)
@@ -31,3 +31,5 @@ REGISTRY_DEFINE_GET(CLIENT_COMMANDS, NULL)
        ENDCLASS(clientcommand_##id) \
        REGISTER(CLIENT_COMMANDS, CMD_SVCL, id, m_id, NEW(clientcommand_##id)); \
        METHOD(clientcommand_##id, m_invokecmd, void(clientcommand_##id this, int request, entity caller, int arguments, string command))
+
+.float cmd_floodtime_forcommand[REGISTRY_MAX(CLIENT_COMMANDS)];