changeset 15871:792676d7443d draft

(svn r20553) -Feature: allow rate limiting of incoming commands
author rubidium <rubidium@openttd.org>
date Thu, 19 Aug 2010 08:59:36 +0000
parents c9d9786c445d
children 42574c590ecd
files src/lang/english.txt src/network/core/tcp_game.h src/network/network.cpp src/network/network_client.cpp src/network/network_command.cpp src/network/network_server.cpp src/network/network_type.h src/settings_type.h src/table/settings.h
diffstat 9 files changed, 31 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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 */
--- 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;
--- 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;
 	}
--- 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);
 	}
--- 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);
 
--- 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 */
--- 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
--- 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),