From c3d134793f01b54244435d59afc3c4409a9d7aa8 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 14:41:25 +0200 Subject: [PATCH] Fix #6598: Prevent invalid memory accesses when abandoning a join from within a network game One could join a network game from within an already running network game. This would call a NetworkDisconnect, but keeps the UI alive. If, during that process the join is aborted, e.g. by cancelling on a password dialog, you would still be in your network game but also get shown the server list. Solve all the underlying problems by falling back to the main UI when (re)connecting to a(nother) server. --- src/network/network.cpp | 22 +++++++++++++++++++++- src/network/network_func.h | 1 + src/openttd.cpp | 5 +++++ src/openttd.h | 1 + 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index a55f73b79b..cf07ba7e4f 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -670,13 +670,33 @@ void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_ _network_join_server_password = join_server_password; _network_join_company_password = join_company_password; + if (_game_mode == GM_MENU) { + /* From the menu we can immediately continue with the actual join. */ + NetworkClientJoinGame(); + } else { + /* When already playing a game, first go back to the main menu. This + * disconnects the user from the current game, meaning we can safely + * load in the new. After all, there is little point in continueing to + * play on a server if we are connecting to another one. + */ + _switch_mode = SM_JOIN_GAME; + } +} + +/** + * Actually perform the joining to the server. Use #NetworkClientConnectGame + * when you want to connect to a specific server/company. This function + * assumes _network_join is already fully set up. + */ +void NetworkClientJoinGame() +{ NetworkDisconnect(); NetworkInitialize(); _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(NetworkAddress(hostname, port)); + new TCPClientConnecter(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); } static void NetworkInitGameInfo() diff --git a/src/network/network_func.h b/src/network/network_func.h index ed9b8e04b3..26a919ed4c 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -49,6 +49,7 @@ void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientJoinGame(); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/openttd.cpp b/src/openttd.cpp index f3602812ea..c7285a2e69 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1109,6 +1109,11 @@ void SwitchToMode(SwitchMode new_mode) break; } + case SM_JOIN_GAME: // Join a multiplayer game + LoadIntroGame(); + NetworkClientJoinGame(); + break; + case SM_MENU: // Switch to game intro menu LoadIntroGame(); if (BaseSounds::ini_set.empty() && BaseSounds::GetUsedSet()->fallback && SoundDriver::GetInstance()->HasOutput()) { diff --git a/src/openttd.h b/src/openttd.h index 77fafab1d1..2cd9cc1f09 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -36,6 +36,7 @@ enum SwitchMode { SM_START_HEIGHTMAP, ///< Load a heightmap and start a new game from it. SM_LOAD_HEIGHTMAP, ///< Load heightmap from scenario editor. SM_RESTART_HEIGHTMAP, ///< Load a heightmap and start a new game from it with current settings. + SM_JOIN_GAME, ///< Join a network game. }; /** Display Options */