From dc73aa50dd8c3cf70ba23e3f50738377fe46b720 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 11 Feb 2024 22:53:56 +0100 Subject: [PATCH] Fix: as client, try to reconnect more than once on server restart --- src/lang/english.txt | 1 + src/network/core/config.h | 2 ++ src/network/network.cpp | 10 ++++++---- src/network/network_client.cpp | 12 +++++------- src/network/network_func.h | 4 +++- src/network/network_internal.h | 2 -- src/openttd.cpp | 27 +++++++++++++++++++++++---- 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 93dea7e8c4..a8921ed122 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2640,6 +2640,7 @@ STR_NETWORK_MESSAGE_GIVE_MONEY :*** {0:RAW_STRI STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The server closed the session STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait... STR_NETWORK_MESSAGE_KICKED :*** {RAW_STRING} was kicked. Reason: ({RAW_STRING}) +STR_NETWORK_MESSAGE_SERVER_REBOOT_REJOIN_FAILED :{WHITE}Failed to reconnect to server STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Server registration failed STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE :{WHITE}Another server with the same invite-code registered itself. Switching to "local" game-type. diff --git a/src/network/core/config.h b/src/network/core/config.h index df8991ad69..fe98f9d718 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -74,6 +74,8 @@ static const uint NETWORK_TOKEN_LENGTH = 64; ///< The m static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF +static const uint32_t NETWORK_RECONNECT_ATTEMPTS = 8; ///< How many attempts before we give up reconnecting. + /** * Maximum number of GRFs that can be sent. * diff --git a/src/network/network.cpp b/src/network/network.cpp index 2193855661..13944d66a6 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -64,7 +64,9 @@ bool _is_network_server; ///< Does this client wants to be a network-server? NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies. ClientID _network_own_client_id; ///< Our client identifier. ClientID _redirect_console_to_client; ///< If not invalid, redirect the console output to a client. -uint8_t _network_reconnect; ///< Reconnect timeout +uint32_t _network_reconnect_attempts = 0; ///< How many attempts to reconnect left. +std::chrono::seconds _network_reconnect_backoff = {}; ///< How much time to wait for the next reconnect attempt. +std::chrono::steady_clock::time_point _network_reconnect = {}; ///< The time when to attempt another reconnect. StringList _network_bind_list; ///< The addresses to bind on. StringList _network_host_list; ///< The servers we know. StringList _network_ban_list; ///< The banned clients. @@ -619,8 +621,6 @@ static void NetworkInitialize(bool close_admins = true) _sync_frame = 0; _network_first_time = true; - - _network_reconnect = 0; } /** Non blocking connection to query servers for their game info. */ @@ -738,7 +738,9 @@ public: { Debug(net, 9, "Client::OnFailure(): connection_string={}", this->connection_string); - ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION); + if (_network_reconnect_attempts == 0) { + ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION); + } } void OnConnect(SOCKET s) override diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 334b3bc243..0d48a54c43 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -344,6 +344,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin() _network_join_status = NETWORK_JOIN_STATUS_AUTHORIZING; SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); + _network_reconnect_attempts = 0; + auto p = std::make_unique(PACKET_CLIENT_JOIN); p->Send_string(GetNetworkRevisionString()); p->Send_uint32(_openttd_newgrf_version); @@ -1124,14 +1126,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEWGAME(Packet { Debug(net, 9, "Client::Receive_SERVER_NEWGAME()"); - /* Only when we're trying to join we really - * care about the server shutting down. */ if (this->status >= STATUS_JOIN) { - /* To throttle the reconnects a bit, every clients waits its - * Client ID modulo 16 + 1 (value 0 means no reconnect). - * This way reconnects should be spread out a bit. */ - _network_reconnect = _network_own_client_id % 16 + 1; - ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT, INVALID_STRING_ID, WL_CRITICAL); + _network_reconnect_attempts = NETWORK_RECONNECT_ATTEMPTS; + _network_reconnect_backoff = std::chrono::seconds(1); + _network_reconnect = std::chrono::steady_clock::now() + _network_reconnect_backoff; } if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); diff --git a/src/network/network_func.h b/src/network/network_func.h index 369eb0fc30..7289a02d0c 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -28,7 +28,9 @@ extern NetworkCompanyState *_network_company_states; extern ClientID _network_own_client_id; extern ClientID _redirect_console_to_client; -extern uint8_t _network_reconnect; +extern uint32_t _network_reconnect_attempts; +extern std::chrono::seconds _network_reconnect_backoff; +extern std::chrono::steady_clock::time_point _network_reconnect; extern StringList _network_bind_list; extern StringList _network_host_list; extern StringList _network_ban_list; diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 66dea11be4..e1d29f83b0 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -91,8 +91,6 @@ extern std::string _network_server_invite_code; /* Variable available for clients. */ extern std::string _network_server_name; -extern uint8_t _network_reconnect; - extern CompanyMask _network_company_passworded; void NetworkQueryServer(const std::string &connection_string); diff --git a/src/openttd.cpp b/src/openttd.cpp index 31db6b0d60..684f9b5852 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1598,11 +1598,30 @@ void GameLoop() /* Multiplayer */ NetworkGameLoop(); } else { - if (_network_reconnect > 0 && --_network_reconnect == 0) { - /* This means that we want to reconnect to the last host - * We do this here, because it means that the network is really closed */ - NetworkClientConnectGame(_settings_client.network.last_joined, COMPANY_SPECTATOR); + if (_network_reconnect_attempts > 0 && std::chrono::steady_clock::now() > _network_reconnect) { + Debug(net, 3, "Reconnect attempt #{}", _network_reconnect_attempts); + + _network_reconnect_attempts--; + _network_reconnect_backoff = std::min(_network_reconnect_backoff * 2, std::chrono::seconds(30)); + _network_reconnect = std::chrono::steady_clock::now() + _network_reconnect_backoff; + + if (_network_reconnect_attempts == 0) { + ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT_REJOIN_FAILED, INVALID_STRING_ID, WL_ERROR); + } else { + /* Show that we are reconnecting the first time we do an attempt. + * We have to do it this late, as switching to the main menu closes + * all errors (except critical, but this isn't critical). */ + if (_network_reconnect_attempts == NETWORK_RECONNECT_ATTEMPTS - 1) { + ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT, INVALID_STRING_ID, WL_ERROR); + } + + /* If the AskRelay window is open, don't start a new connect. */ + if (FindWindowByClass(WC_NETWORK_ASK_RELAY) == nullptr) { + NetworkClientConnectGame(_settings_client.network.last_joined, COMPANY_SPECTATOR); + } + } } + /* Singleplayer */ StateGameLoop(); }