summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lang/english.txt2
-rw-r--r--src/network/network_client.cpp6
-rw-r--r--src/network/network_server.cpp129
-rw-r--r--src/network/network_type.h2
-rw-r--r--src/settings_type.h2
-rw-r--r--src/table/settings.ini20
6 files changed, 116 insertions, 45 deletions
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 7730f4ecd..744ffdcbf 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1799,6 +1799,8 @@ STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}You are
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
diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp
index 3de63a4e0..3a0f58b83 100644
--- a/src/network/network_client.cpp
+++ b/src/network/network_client.cpp
@@ -670,6 +670,12 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p
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);
}
diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp
index 72f956240..8adff7a13 100644
--- a/src/network/network_server.cpp
+++ b/src/network/network_server.cpp
@@ -1794,55 +1794,94 @@ void NetworkServer_Tick(bool send_frame)
_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;
}
- } 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_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;
+ }
+ 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) {
diff --git a/src/network/network_type.h b/src/network/network_type.h
index ff29492b7..eff46a232 100644
--- a/src/network/network_type.h
+++ b/src/network/network_type.h
@@ -122,6 +122,8 @@ enum NetworkErrorCode {
NETWORK_ERROR_CHEATER,
NETWORK_ERROR_FULL,
NETWORK_ERROR_TOO_MANY_COMMANDS,
+ NETWORK_ERROR_TIMEOUT_PASSWORD,
+ NETWORK_ERROR_TIMEOUT_COMPUTER,
};
#endif /* ENABLE_NETWORK */
diff --git a/src/settings_type.h b/src/settings_type.h
index 541aad2fe..4f8f36a07 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -173,6 +173,8 @@ struct NetworkSettings {
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
diff --git a/src/table/settings.ini b/src/table/settings.ini
index 3a834336a..fddd034be 100644
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -2653,6 +2653,26 @@ def = 500
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