From: Mario Date: Wed, 31 Jul 2024 13:26:07 +0000 (+1000) Subject: Implement per-command flood control X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=refs%2Fheads%2FMario%2Ffloodcontrol_per_command;p=xonotic%2Fxonotic-data.pk3dir.git Implement per-command flood control --- diff --git a/commands.cfg b/commands.cfg index 986765185..f43da1fe2 100644 --- a/commands.cfg +++ b/commands.cfg @@ -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" diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 33ceccbb5..0fd198362 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -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 } diff --git a/qcsrc/server/command/cmd.qh b/qcsrc/server/command/cmd.qh index 25c694918..80441c350 100644 --- a/qcsrc/server/command/cmd.qh +++ b/qcsrc/server/command/cmd.qh @@ -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 diff --git a/qcsrc/server/command/reg.qh b/qcsrc/server/command/reg.qh index 59703d700..3edc85290 100644 --- a/qcsrc/server/command/reg.qh +++ b/qcsrc/server/command/reg.qh @@ -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)];