]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Improve SVQC command flood control
authorbones_was_here <bones_was_here@xonotic.au>
Thu, 24 Nov 2022 11:43:40 +0000 (21:43 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Fri, 6 Jan 2023 07:16:34 +0000 (17:16 +1000)
Changes to the same logic used by chat flood control which is more
progressive.  The previous command flood control was only triggered if a
client managed to send more than 8 commands within 1 second, and then
it reset immediately.

Prints an informative message when flood control blocks a command.
Previously this was silent.

Special cases the client's initial connect commands, as these would
otherwise trigger the new flood logic with the current default settings.

Exempts chat commands from command flood control, as these have their
own flood control.

Simplifies the code for max perf.

qcsrc/server/chat.qc
qcsrc/server/client.qh
qcsrc/server/command/cmd.qc
qcsrc/server/command/cmd.qh

index a73de2a1f6c58f2ed97e4303ae5179a7354b07db..41af1de57c1bae1b23c9bbf42111a3f4fcf29a51 100644 (file)
@@ -208,7 +208,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
        {
                if(autocvar_g_chat_flood_notify_flooder)
                {
-                       sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n");
+                       sourcemsgstr = strcat(msgstr, "\n^3CHAT FLOOD CONTROL: ^7message too long, trimmed\n");
                        sourcecmsgstr = "";
                }
                else
@@ -247,7 +247,7 @@ int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodc
        {
                if (autocvar_g_chat_flood_notify_flooder)
                {
-                       sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n"));
+                       sprint(source, strcat("^3CHAT FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n"));
                        ret = 0;
                }
                else
index eebf016f7500716881fcaa6c8fb44f88b02e91df..33b9d4511967c5c0834fdc9f0190c78b78d10df3 100644 (file)
@@ -185,7 +185,6 @@ CLASS(Client, Object)
     ATTRIB(Client, specialcommand_pos, int, this.specialcommand_pos);
     ATTRIB(Client, hitplotfh, int, this.hitplotfh);
     ATTRIB(Client, clientdata, entity, this.clientdata);
-    ATTRIB(Client, cmd_floodcount, int, this.cmd_floodcount);
     ATTRIB(Client, cmd_floodtime, float, this.cmd_floodtime);
     ATTRIB(Client, wasplayer, bool, this.wasplayer);
     ATTRIB(Client, weaponorder_byimpulse, string, this.weaponorder_byimpulse);
index 50b71957bd689efb542bc16afbd154194edd9281..0ee108c7c1cb744fe2509d0ed26860000d407e17 100644 (file)
 //  Last updated: December 28th, 2011
 // =========================================================
 
-bool SV_ParseClientCommand_floodcheck(entity this)
-{
-       entity store = IS_CLIENT(this) ? CS(this) : this; // unfortunately, we need to store these on the client initially
-
-       if (!timeout_status)  // not while paused
-       {
-               if (time <= (store.cmd_floodtime + autocvar_sv_clientcommand_antispam_time))
-               {
-                       store.cmd_floodcount += 1;
-                       if (store.cmd_floodcount > autocvar_sv_clientcommand_antispam_count)   return false;  // too much spam, halt
-               }
-               else
-               {
-                       store.cmd_floodtime = time;
-                       store.cmd_floodcount = 1;
-               }
-       }
-       return true;  // continue, as we're not flooding yet
-}
-
 
 // =======================
 //  Command Sub-Functions
@@ -889,6 +869,7 @@ void SV_ParseClientCommand(entity this, string command)
                case "prespawn": break;                            // handled by engine in host_cmd.c
                case "sentcvar": break;                            // handled by server in this file
                case "spawn": break;                               // handled by engine in host_cmd.c
+               case "say": case "say_team": case "tell": break;   // chat has its own flood control in chat.qc
                case "color": case "topcolor": case "bottomcolor": // handled by engine in host_cmd.c
                        if(!IS_CLIENT(this)) // on connection
                        {
@@ -900,9 +881,24 @@ void SV_ParseClientCommand(entity this, string command)
                        break;
                case "c2s": Net_ClientCommand(this, command); return; // handled by net.qh
 
+               // on connection, client sends all of these
+               case "name": case "rate": case "rate_burstsize": case "playermodel": case "playerskin": case "clientversion":
+                       if(!IS_CLIENT(this)) break;
+                       // else fall through to default: flood control
                default:
-                       if (SV_ParseClientCommand_floodcheck(this)) break; // "true": continue, as we're not flooding yet
-                       else return;                                   // "false": not allowed to continue, halt // print("^1ERROR: ^7ANTISPAM CAUGHT: ", command, ".\n");
+                       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;
+                       }
+                       break;  // continue, as we're not flooding yet
        }
 
        /* NOTE: should this be disabled? It can be spammy perhaps, but hopefully it's okay for now */
index 802afc8bde6f86e1ed3e145df61f111ea51ec715..bb97f0d0b17cf18926d1d0c05a996d4a515e116e 100644 (file)
@@ -4,7 +4,6 @@ float autocvar_sv_clientcommand_antispam_time;
 int autocvar_sv_clientcommand_antispam_count;
 
 .float cmd_floodtime;
-.float cmd_floodcount;
 
 string MapVote_Suggest(entity this, string m);