# HG changeset patch # User rubidium # Date 1282208376 0 # Node ID 792676d7443d4b2e8b343bde3d01a2dbb798d520 # Parent c9d9786c445d413d1b4e9fdf208536505083a78c (svn r20553) -Feature: allow rate limiting of incoming commands diff --git a/src/lang/english.txt b/src/lang/english.txt --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1757,6 +1757,7 @@ STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}You are banned from this server STR_NETWORK_ERROR_KICKED :{WHITE}You were kicked out of the game STR_NETWORK_ERROR_CHEATER :{WHITE}Cheating is not allowed on this server +STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}You were sending too many commands to the server ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error @@ -1774,6 +1775,7 @@ STR_NETWORK_ERROR_CLIENT_KICKED :kicked by server STR_NETWORK_ERROR_CLIENT_CHEATER :was trying to use a cheat STR_NETWORK_ERROR_CLIENT_SERVER_FULL :server full +STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS :was sending too many commands ############ End of leave-in-this-order # Network related errors diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -78,6 +78,7 @@ class CommandQueue { CommandPacket *first; ///< The first packet in the queue. CommandPacket *last; ///< The last packet in the queue; only valid when first != NULL. + uint count; ///< The number of items in the queue. public: /** Initialise the command queue. */ @@ -88,6 +89,8 @@ CommandPacket *Pop(); CommandPacket *Peek(); void Free(); + /** Get the number of items in the queue. */ + uint Count() const { return this->count; } }; /** Status of a client */ diff --git a/src/network/network.cpp b/src/network/network.cpp --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -344,7 +344,8 @@ STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH, STR_NETWORK_ERROR_CLIENT_KICKED, STR_NETWORK_ERROR_CLIENT_CHEATER, - STR_NETWORK_ERROR_CLIENT_SERVER_FULL + STR_NETWORK_ERROR_CLIENT_SERVER_FULL, + STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS }; if (err >= (ptrdiff_t)lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL; @@ -1181,14 +1182,13 @@ f = NULL; } #endif /* DEBUG_DUMP_COMMANDS */ - NetworkDistributeCommands(); - if (_frame_counter >= _frame_counter_max) { /* Only check for active clients just before we're going to send out * the commands so we don't send multiple pause/unpause commands when - * the frame_freq is more than 1 tick. */ + * the frame_freq is more than 1 tick. Same with distributing commands. */ CheckPauseOnJoin(); CheckMinActiveClients(); + NetworkDistributeCommands(); } bool send_frame = false; diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -515,6 +515,9 @@ case NETWORK_ERROR_CHEATER: _switch_mode_errorstr = STR_NETWORK_ERROR_CHEATER; break; + case NETWORK_ERROR_TOO_MANY_COMMANDS: + _switch_mode_errorstr = STR_NETWORK_ERROR_TOO_MANY_COMMANDS; + break; default: _switch_mode_errorstr = STR_NETWORK_ERROR_LOSTCONNECTION; } diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp --- a/src/network/network_command.cpp +++ b/src/network/network_command.cpp @@ -17,6 +17,7 @@ #include "network.h" #include "../command_func.h" #include "../company_func.h" +#include "../settings_type.h" /** Table with all the callbacks we'll use for conversion*/ static CommandCallback * const _callback_table[] = { @@ -68,6 +69,7 @@ this->last->next = add; } this->last = add; + this->count++; } /** @@ -77,7 +79,10 @@ CommandPacket *CommandQueue::Pop() { CommandPacket *ret = this->first; - if (ret != NULL) this->first = this->first->next; + if (ret != NULL) { + this->first = this->first->next; + this->count--; + } return ret; } @@ -97,6 +102,7 @@ while ((cp = this->Pop()) != NULL) { free(cp); } + assert(this->count == 0); } /** Local queue of packets waiting for handling. */ @@ -241,8 +247,10 @@ */ static void DistributeQueue(CommandQueue *queue, const NetworkClientSocket *owner) { + int to_go = _settings_client.network.commands_per_frame; + CommandPacket *cp; - while ((cp = queue->Pop()) != NULL) { + while (--to_go >= 0 && (cp = queue->Pop()) != NULL) { DistributeCommandPacket(*cp, owner); free(cp); } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -897,6 +897,10 @@ return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); } + if (cs->incoming_queue.Count() >= _settings_client.network.max_commands_in_queue) { + return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_TOO_MANY_COMMANDS); + } + CommandPacket cp; const char *err = cs->Recv_Command(p, &cp); diff --git a/src/network/network_type.h b/src/network/network_type.h --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -110,6 +110,7 @@ NETWORK_ERROR_KICKED, NETWORK_ERROR_CHEATER, NETWORK_ERROR_FULL, + NETWORK_ERROR_TOO_MANY_COMMANDS, }; #endif /* ENABLE_NETWORK */ diff --git a/src/settings_type.h b/src/settings_type.h --- a/src/settings_type.h +++ b/src/settings_type.h @@ -128,6 +128,8 @@ #ifdef ENABLE_NETWORK uint16 sync_freq; ///< how often do we check whether we are still in-sync uint8 frame_freq; ///< how often do we send commands to the clients + uint16 commands_per_frame; ///< how many commands may be sent each frame_freq frames? + uint16 max_commands_in_queue; ///< how many commands may there be in the incoming queue before dropping the connection? uint16 max_join_time; ///< maximum amount of time, in game ticks, a client may take to join bool pause_on_join; ///< pause the game when people join uint16 server_port; ///< port the server listens on diff --git a/src/table/settings.h b/src/table/settings.h --- a/src/table/settings.h +++ b/src/table/settings.h @@ -622,6 +622,8 @@ SDTC_VAR(network.sync_freq, SLE_UINT16,C|S,NO, 100, 0, 100, 0, STR_NULL, NULL), SDTC_VAR(network.frame_freq, SLE_UINT8,C|S,NO, 0, 0, 100, 0, STR_NULL, NULL), + SDTC_VAR(network.commands_per_frame, SLE_UINT16, S, NO, 4, 1, 65535, 0, STR_NULL, NULL), + SDTC_VAR(network.max_commands_in_queue,SLE_UINT16, S, NO, 32, 1, 65535, 0, STR_NULL, NULL), SDTC_VAR(network.max_join_time, SLE_UINT16, S, NO, 500, 0, 32000, 0, STR_NULL, NULL), SDTC_BOOL(network.pause_on_join, S, NO, true, STR_NULL, NULL), SDTC_VAR(network.server_port, SLE_UINT16, S, NO,NETWORK_DEFAULT_PORT,0,65535,0,STR_NULL, NULL),