# HG changeset patch # User Adam Kaminski # Date 1638334006 18000 # Tue Nov 30 23:46:46 2021 -0500 # Node ID 7a59b76719fb30532b3b2ba3af59bbd032332432 # Parent 1106f2a4c6ed86fb6fb5e7e12e85772929a12814 The server no longer ignores commands that arrived too late anymore, but rather only ignores duplicates of commands it already received from a player. diff -r 1106f2a4c6ed -r 7a59b76719fb src/sv_main.cpp --- a/src/sv_main.cpp Tue Nov 30 22:54:42 2021 -0500 +++ b/src/sv_main.cpp Tue Nov 30 23:46:46 2021 -0500 @@ -2038,6 +2038,10 @@ // should be able to go back is the gametic they connected with. g_aClients[lClient].lLastServerGametic = gametic; + // [AK] Clear any recent command gametics from the client. + g_aClients[lClient].recentMoveCMDs.clear(); + g_aClients[lClient].recentSelectCMDs.clear(); + // [AK] Reset the client's tic buffer. SERVER_ResetClientTicBuffer( lClient ); @@ -5200,13 +5204,27 @@ const ULONG ulClientTic = cmd->getClientTic( ); const bool bIsMoveCMD = cmd->isMoveCmd( ); - // [AK] Ignore commands that arrived too late or are duplicates of commands we already processed, - // neither of which we can account for anymore. This way, we aren't processing the same commands - // again, or that happened too far into the past. - if (( ulClientTic != 0 ) && ( ulClientTic <= g_aClients[g_lCurrentClient].ulClientGameTic )) - { - delete cmd; - return false; + // [AK] Ignore commands that are duplicates of commands we already received and/or processed. + // This way, we won't process the exact same commands multiple times. + if ( ulClientTic != 0 ) + { + RingBuffer *recentCMDs; + + // [AK] Move commands and weapon select commands each have their own ring buffers, since + // a movement and weapon select commands with the same gametic can co-exist. + recentCMDs = bIsMoveCMD ? &g_aClients[g_lCurrentClient].recentMoveCMDs : &g_aClients[g_lCurrentClient].recentSelectCMDs; + + for ( unsigned int i = 0; i < MAX_RECENT_COMMANDS; i++ ) + { + if ( recentCMDs->getOldestEntry( i ) == ulClientTic ) + { + delete cmd; + return false; + } + } + + // [AK] Save this gametic into the ring buffer for later use. + recentCMDs->put( ulClientTic ); } if ( sv_useticbuffer ) @@ -5236,25 +5254,11 @@ { ULONG ulBufferClientTic = (*buffer)[i]->getClientTic( ); - if ( ulBufferClientTic != 0 ) + // [AK] Reorganize the commands in case they arrived in the wrong order. + if (( ulBufferClientTic != 0 ) && ( ulClientTic < ulBufferClientTic )) { - // [AK] Double-check to make sure we don't already have this command stored anywhere in the buffer. - // If it's not a late command, we also need to make sure there aren't any newer move commands already - // in the tic buffer. If any of these conditions fail, we have to ignore this command. - // Movement and weapon select commands with the same gametic can co-exist in the tic buffer, as - // they're not the same command, but two movement or weapon select commands cannot. - if (( ulBufferClientTic == ulClientTic ) && ( bIsMoveCMD == (*buffer)[i]->isMoveCmd( ))) - { - delete cmd; - return false; - } - - // [AK] Reorganize the commands in case they arrived in the wrong order. - if ( ulClientTic < ulBufferClientTic ) - { - buffer->Insert( i, cmd ); - return false; - } + buffer->Insert( i, cmd ); + return false; } } } @@ -5463,14 +5467,7 @@ // [AK] Clear all stored commands in the tic buffer. for ( unsigned int i = 0; i < g_aClients[ulClient].MoveCMDs.Size( ); i++ ) - { - // [AK] Set the client's gametic to the last command in their tic buffer that has a non-zero - // gametic. This way, any old and therefore invalid backup commands will be rejected. - if ( g_aClients[ulClient].MoveCMDs[i]->getClientTic( ) != 0 ) - g_aClients[ulClient].ulClientGameTic = g_aClients[ulClient].MoveCMDs[i]->getClientTic( ); - delete g_aClients[ulClient].MoveCMDs[i]; - } g_aClients[ulClient].MoveCMDs.Clear( ); @@ -5500,12 +5497,9 @@ // to the last late command we received from them and update their gametic. if ( bUpdatedLastMoveCMD == false ) { - ULONG ulNewClientGametic = g_aClients[ulClient].LastMoveCMD->getClientTic( ) + g_aClients[ulClient].ulExtrapolatedTics; - delete g_aClients[ulClient].LastMoveCMD; g_aClients[ulClient].LastMoveCMD = static_cast( g_aClients[ulClient].LateMoveCMDs[i] ); - g_aClients[ulClient].LastMoveCMD->setClientTic( ulNewClientGametic ); bUpdatedLastMoveCMD = true; continue; } diff -r 1106f2a4c6ed -r 7a59b76719fb src/sv_main.h --- a/src/sv_main.h Tue Nov 30 22:54:42 2021 -0500 +++ b/src/sv_main.h Tue Nov 30 23:46:46 2021 -0500 @@ -82,6 +82,9 @@ // Amount of time the client has to report his checksum of the level. #define CLIENT_CHECKSUM_WAITTIME ( 15 * TICRATE ) +// [AK] Maximum amount of gametics of recent commands from a client that we can store. +#define MAX_RECENT_COMMANDS 15 + // This is for the server console, but since we normally can't include that (win32 stuff), // we can just put it here. #define UDF_NAME 0x00000001 @@ -392,6 +395,12 @@ // [BB] A record of the gametics the client called protected minor commands, e.g. toggleconsole. RingBuffer minorCommandInstances; + // [AK] A list of gametics of the last few movement commands the client sent us. + RingBuffer recentMoveCMDs; + + // [AK] A list of (non-zero) gametics of the last few weapon select commands the client sent us. + RingBuffer recentSelectCMDs; + // A record of the gametic the client spoke at. We store the last MAX_CHATINSTANCE_STORAGE // times the client chatted. This is used to chat spam protection. LONG lChatInstances[MAX_CHATINSTANCE_STORAGE];