changeset 18915:df0059acdb6c draft

(svn r23764) -Fix [FS#4955]: make default timeouts for certain states lower and configurable
author rubidium <rubidium@openttd.org>
date Fri, 06 Jan 2012 21:49:06 +0000
parents 6442865ba323
children 30da1fd45c76
files src/lang/english.txt src/network/network_client.cpp src/network/network_server.cpp src/network/network_type.h src/settings_type.h src/table/settings.ini
diffstat 6 files changed, 116 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1799,6 +1799,8 @@
 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
+STR_NETWORK_ERROR_TIMEOUT_PASSWORD                              :{WHITE}You took too long to enter the password
+STR_NETWORK_ERROR_TIMEOUT_COMPUTER                              :{WHITE}Your computer took too long to join
 
 ############ Leave those lines in this order!!
 STR_NETWORK_ERROR_CLIENT_GENERAL                                :general error
--- a/src/network/network_client.cpp
+++ b/src/network/network_client.cpp
@@ -670,6 +670,12 @@
 		case NETWORK_ERROR_TOO_MANY_COMMANDS:
 			ShowErrorMessage(STR_NETWORK_ERROR_TOO_MANY_COMMANDS, INVALID_STRING_ID, WL_CRITICAL);
 			break;
+		case NETWORK_ERROR_TIMEOUT_PASSWORD:
+			ShowErrorMessage(STR_NETWORK_ERROR_TIMEOUT_PASSWORD, INVALID_STRING_ID, WL_CRITICAL);
+			break;
+		case NETWORK_ERROR_TIMEOUT_COMPUTER:
+			ShowErrorMessage(STR_NETWORK_ERROR_TIMEOUT_COMPUTER, INVALID_STRING_ID, WL_CRITICAL);
+			break;
 		default:
 			ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL);
 	}
--- a/src/network/network_server.cpp
+++ b/src/network/network_server.cpp
@@ -1794,55 +1794,94 @@
 				_settings_client.network.bytes_per_frame_burst);
 
 		/* Check if the speed of the client is what we can expect from a client */
-		if (cs->status == NetworkClientSocket::STATUS_ACTIVE) {
-			/* 1 lag-point per day */
-			uint lag = NetworkCalculateLag(cs) / DAY_TICKS;
-			if (lag > 0) {
-				if (lag > 3) {
-					/* Client did still not report in after 4 game-day, drop him
-					 *  (that is, the 3 of above, + 1 before any lag is counted) */
-					IConsolePrintF(CC_ERROR, cs->last_packet + 3 * DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick ?
-							/* A packet was received in the last three game days, so the client is likely lagging behind. */
-								"Client #%d is dropped because the client's game state is more than 4 game-days behind" :
-							/* No packet was received in the last three game days; sounds like a lost connection. */
-								"Client #%d is dropped because the client did not respond for more than 4 game-days",
-							cs->client_id);
-					cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
+		uint lag = NetworkCalculateLag(cs);
+		switch (cs->status) {
+			case NetworkClientSocket::STATUS_ACTIVE:
+				/* 1 lag-point per day */
+				lag /= DAY_TICKS;
+				if (lag > 0) {
+					if (lag > 3) {
+						/* Client did still not report in after 4 game-day, drop him
+						 *  (that is, the 3 of above, + 1 before any lag is counted) */
+						IConsolePrintF(CC_ERROR, cs->last_packet + 3 * DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick ?
+								/* A packet was received in the last three game days, so the client is likely lagging behind. */
+									"Client #%d is dropped because the client's game state is more than 4 game-days behind" :
+								/* No packet was received in the last three game days; sounds like a lost connection. */
+									"Client #%d is dropped because the client did not respond for more than 4 game-days",
+								cs->client_id);
+						cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
+						continue;
+					}
+
+					/* Report once per time we detect the lag, and only when we
+					 * received a packet in the last 2000 milliseconds. If we
+					 * did not receive a packet, then the client is not just
+					 * slow, but the connection is likely severed. Mentioning
+					 * frame_freq is not useful in this case. */
+					if (cs->lag_test == 0 && cs->last_packet + DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick) {
+						IConsolePrintF(CC_WARNING,"[%d] Client #%d is slow, try increasing [network.]frame_freq to a higher value!", _frame_counter, cs->client_id);
+						cs->lag_test = 1;
+					}
+				} else {
+					cs->lag_test = 0;
+				}
+				if (cs->last_frame_server - cs->last_token_frame >= 5 * DAY_TICKS) {
+					/* This is a bad client! It didn't send the right token back. */
+					IConsolePrintF(CC_ERROR, "Client #%d is dropped because it fails to send valid acks", cs->client_id);
+					cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
 					continue;
 				}
+				break;
 
-				/* Report once per time we detect the lag, and only when we
-				 * received a packet in the last 2000 milliseconds. If we
-				 * did not receive a packet, then the client is not just
-				 * slow, but the connection is likely severed. Mentioning
-				 * frame_freq is not useful in this case. */
-				if (cs->lag_test == 0 && cs->last_packet + DAY_TICKS * MILLISECONDS_PER_TICK > _realtime_tick) {
-					IConsolePrintF(CC_WARNING,"[%d] Client #%d is slow, try increasing [network.]frame_freq to a higher value!", _frame_counter, cs->client_id);
-					cs->lag_test = 1;
+			case NetworkClientSocket::STATUS_INACTIVE:
+			case NetworkClientSocket::STATUS_NEWGRFS_CHECK:
+			case NetworkClientSocket::STATUS_AUTHORIZED:
+				/* NewGRF check and authorized states should be handled almost instantly.
+				 * So give them some lee-way, likewise for the query with inactive. */
+				if (lag > 4 * DAY_TICKS) {
+					IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks to start the joining process", cs->client_id, 4 * DAY_TICKS);
+					cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
+					continue;
+				}
+				break;
+
+			case NetworkClientSocket::STATUS_MAP:
+				/* Downloading the map... this is the amount of time since starting the saving. */
+				if (lag > _settings_client.network.max_download_time) {
+					IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to download the map", cs->client_id, _settings_client.network.max_download_time);
+					cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
+					continue;
 				}
-			} else {
-				cs->lag_test = 0;
-			}
-			if (cs->last_frame_server - cs->last_token_frame >= 5 * DAY_TICKS) {
-				/* This is a bad client! It didn't send the right token back. */
-				IConsolePrintF(CC_ERROR, "Client #%d is dropped because it fails to send valid acks", cs->client_id);
-				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
-				continue;
-			}
-		} else if (cs->status == NetworkClientSocket::STATUS_PRE_ACTIVE) {
-			uint lag = NetworkCalculateLag(cs);
-			if (lag > _settings_client.network.max_join_time) {
-				IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to join", cs->client_id, _settings_client.network.max_join_time);
-				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
-				continue;
-			}
-		} else if (cs->status == NetworkClientSocket::STATUS_INACTIVE) {
-			uint lag = NetworkCalculateLag(cs);
-			if (lag > 4 * DAY_TICKS) {
-				IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks to start the joining process", cs->client_id, 4 * DAY_TICKS);
-				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
-				continue;
-			}
+				break;
+
+			case NetworkClientSocket::STATUS_DONE_MAP:
+			case NetworkClientSocket::STATUS_PRE_ACTIVE:
+				/* The map has been sent, so this is for loading the map and syncing up. */
+				if (lag > _settings_client.network.max_join_time) {
+					IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to join", cs->client_id, _settings_client.network.max_join_time);
+					cs->SendError(NETWORK_ERROR_TIMEOUT_COMPUTER);
+					continue;
+				}
+				break;
+
+			case NetworkClientSocket::STATUS_AUTH_GAME:
+			case NetworkClientSocket::STATUS_AUTH_COMPANY:
+				/* These don't block? */
+				if (lag > _settings_client.network.max_password_time) {
+					IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d to enter the password", cs->client_id, _settings_client.network.max_password_time);
+					cs->SendError(NETWORK_ERROR_TIMEOUT_PASSWORD);
+					continue;
+				}
+				break;
+
+			case NetworkClientSocket::STATUS_MAP_WAIT:
+				/* This is an internal state where we do not wait
+				 * on the client to move to a different state. */
+				break;
+
+			case NetworkClientSocket::STATUS_END:
+				/* Bad server/code. */
+				NOT_REACHED();
 		}
 
 		if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) {
--- a/src/network/network_type.h
+++ b/src/network/network_type.h
@@ -122,6 +122,8 @@
 	NETWORK_ERROR_CHEATER,
 	NETWORK_ERROR_FULL,
 	NETWORK_ERROR_TOO_MANY_COMMANDS,
+	NETWORK_ERROR_TIMEOUT_PASSWORD,
+	NETWORK_ERROR_TIMEOUT_COMPUTER,
 };
 
 #endif /* ENABLE_NETWORK */
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -173,6 +173,8 @@
 	uint16 bytes_per_frame;                               ///< how many bytes may, over a long period, be received per frame?
 	uint16 bytes_per_frame_burst;                         ///< how many bytes may, over a short period, be received?
 	uint16 max_join_time;                                 ///< maximum amount of time, in game ticks, a client may take to join
+	uint16 max_download_time;                             ///< maximum amount of time, in game ticks, a client may take to download the map
+	uint16 max_password_time;                             ///< maximum amount of time, in game ticks, a client may take to enter the password
 	bool   pause_on_join;                                 ///< pause the game when people join
 	uint16 server_port;                                   ///< port the server listens on
 	uint16 server_admin_port;                             ///< port the server listens on for the admin network
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -2653,6 +2653,26 @@
 min      = 0
 max      = 32000
 
+[SDTC_VAR]
+ifdef    = ENABLE_NETWORK
+var      = network.max_download_time
+type     = SLE_UINT16
+flags    = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
+guiflags = SGF_NETWORK_ONLY
+def      = 1000
+min      = 0
+max      = 32000
+
+[SDTC_VAR]
+ifdef    = ENABLE_NETWORK
+var      = network.max_password_time
+type     = SLE_UINT16
+flags    = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC
+guiflags = SGF_NETWORK_ONLY
+def      = 2000
+min      = 0
+max      = 32000
+
 [SDTC_BOOL]
 ifdef    = ENABLE_NETWORK
 var      = network.pause_on_join