From 623214068986416f4d44458861f4f27c353856c0 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 20 Sep 2018 16:43:34 +0800 Subject: [PATCH] Implement auto server recovery if connection to stk-addons is lost --- src/network/protocols/server_lobby.cpp | 106 ++++++++++++++++++------- src/network/protocols/server_lobby.hpp | 8 +- 2 files changed, 82 insertions(+), 32 deletions(-) diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 3df6b853b..aa8c3ece6 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -93,6 +93,7 @@ */ ServerLobby::ServerLobby() : LobbyProtocol(NULL) { + m_last_success_poll_time.store(StkTime::getRealTimeMs() + 30000); m_waiting_players_counts.store(0); m_server_owner_id.store(-1); m_registered_for_once_only = false; @@ -110,7 +111,7 @@ ServerLobby::ServerLobby() : LobbyProtocol(NULL) m_result_ns = getNetworkString(); m_result_ns->setSynchronous(true); m_waiting_for_reset = false; - m_server_id_online = 0; + m_server_id_online.store(0); } // ServerLobby //----------------------------------------------------------------------------- @@ -356,7 +357,7 @@ void ServerLobby::createServerIdFile() if (!sid.empty() && !m_has_created_server_id_file) { std::fstream fs; - sid += StringUtils::toString(m_server_id_online) + "_" + + sid += StringUtils::toString(m_server_id_online.load()) + "_" + StringUtils::toString(STKHost::get()->getPrivatePort()); fs.open(sid, std::ios::out); fs.close(); @@ -384,6 +385,13 @@ void ServerLobby::asynchronousUpdate() handlePendingConnection(); } + if (allowJoinedPlayersWaiting() && m_server_recovering.expired() && + StkTime::getRealTimeMs() > m_last_success_poll_time.load() + 30000) + { + Log::warn("ServerLobby", "Trying auto server recovery."); + registerServer(false/*now*/); + } + switch (m_state.load()) { case SET_PUBLIC_ADDRESS: @@ -420,7 +428,7 @@ void ServerLobby::asynchronousUpdate() // Register this server with the STK server. This will block // this thread, because there is no need for the protocol manager // to react to any requests before the server is registered. - if (registerServer()) + if (registerServer(true/*now*/)) { if (allowJoinedPlayersWaiting()) m_registered_for_once_only = true; @@ -690,12 +698,56 @@ void ServerLobby::update(int ticks) * ProtocolManager thread). The information about this client is added * to the table 'server'. */ -bool ServerLobby::registerServer() +bool ServerLobby::registerServer(bool now) { - while (!m_server_unregistered.expired()) + while (now && !m_server_unregistered.expired()) StkTime::sleep(1); - Online::XMLRequest *request = new Online::XMLRequest(); + // ======================================================================== + class RegisterServerRequest : public Online::XMLRequest + { + private: + std::weak_ptr m_server_lobby; + protected: + virtual void afterOperation() + { + Online::XMLRequest::afterOperation(); + const XMLNode* result = getXMLData(); + std::string rec_success; + auto sl = m_server_lobby.lock(); + if (!sl) + return; + + if (result->get("success", &rec_success) && + rec_success == "yes") + { + const XMLNode* server = result->getNode("server"); + assert(server); + const XMLNode* server_info = server->getNode("server-info"); + assert(server_info); + unsigned server_id_online = 0; + server_info->get("id", &server_id_online); + assert(server_id_online != 0); + Log::info("ServerLobby", + "Server %d is now online.", server_id_online); + sl->m_server_id_online.store(server_id_online); + sl->m_last_success_poll_time.store(StkTime::getRealTimeMs()); + return; + } + Log::error("ServerLobby", "%s", + StringUtils::wideToUtf8(getInfo()).c_str()); + // For auto server recovery wait 3 seconds for next try + // This sleep only the request manager thread + if (manageMemory()) + StkTime::sleep(3000); + } + public: + RegisterServerRequest(bool now, std::shared_ptr sl) + : XMLRequest(!now/*manage memory*/), m_server_lobby(sl) {} + }; // RegisterServerRequest + + RegisterServerRequest *request = new RegisterServerRequest(now, + std::dynamic_pointer_cast(shared_from_this())); NetworkConfig::get()->setServerDetails(request, "create"); request->addParameter("address", m_server_address.getIP() ); request->addParameter("port", m_server_address.getPort() ); @@ -715,29 +767,19 @@ bool ServerLobby::registerServer() Log::info("ServerLobby", "Public server address %s", m_server_address.toString().c_str()); - request->executeNow(); - - const XMLNode* result = request->getXMLData(); - std::string rec_success; - - if (result->get("success", &rec_success) && rec_success == "yes") + if (now) { - const XMLNode* server = result->getNode("server"); - assert(server); - const XMLNode* server_info = server->getNode("server-info"); - assert(server_info); - server_info->get("id", &m_server_id_online); - assert(m_server_id_online != 0); - Log::info("ServerLobby", - "Server %d is now online.", m_server_id_online); + request->executeNow(); delete request; - return true; + if (m_server_id_online.load() == 0) + return false; } - - irr::core::stringc error(request->getInfo().c_str()); - Log::error("ServerLobby", "%s", error.c_str()); - delete request; - return false; + else + { + request->queue(); + m_server_recovering = request->observeExistence(); + } + return true; } // registerServer //----------------------------------------------------------------------------- @@ -811,7 +853,7 @@ void ServerLobby::startSelection(const Event *event) if (!allowJoinedPlayersWaiting()) { ProtocolManager::lock()->findAndTerminate(PROTOCOL_CONNECTION); - if (NetworkConfig::get()->isWAN() ) + if (NetworkConfig::get()->isWAN()) { unregisterServer(false/*now*/); } @@ -903,7 +945,9 @@ void ServerLobby::checkIncomingConnectionRequests() // First poll every 5 seconds. Return if no polling needs to be done. const uint64_t POLL_INTERVAL = 5000; static uint64_t last_poll_time = 0; - if (StkTime::getRealTimeMs() < last_poll_time + POLL_INTERVAL) + if (StkTime::getRealTimeMs() < last_poll_time + POLL_INTERVAL || + StkTime::getRealTimeMs() > m_last_success_poll_time.load() + 30000 || + m_server_id_online.load() == 0) return; // Keep the port open, it can be sent to anywhere as we will send to the @@ -927,12 +971,13 @@ void ServerLobby::checkIncomingConnectionRequests() virtual void afterOperation() { Online::XMLRequest::afterOperation(); - const XMLNode *result = getXMLData(); + const XMLNode* result = getXMLData(); std::string success; if (!result->get("success", &success) || success != "yes") { - Log::error("ServerLobby", "Cannot retrieve the list."); + Log::error("ServerLobby", "Poll server request failed: %s", + StringUtils::wideToUtf8(getInfo()).c_str()); return; } @@ -942,6 +987,7 @@ void ServerLobby::checkIncomingConnectionRequests() auto sl = m_server_lobby.lock(); if (!sl) return; + sl->m_last_success_poll_time.store(StkTime::getRealTimeMs()); if (sl->m_state.load() != WAITING_FOR_START_GAME && !sl->allowJoinedPlayersWaiting()) { diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 0ed797fbe..7ce641948 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -95,6 +95,8 @@ private: /** It indicates if this server is unregistered with the stk server. */ std::weak_ptr m_server_unregistered; + std::weak_ptr m_server_recovering; + /** Timeout counter for various state. */ std::atomic m_timeout; @@ -148,9 +150,11 @@ private: std::atomic m_waiting_players_counts; + std::atomic m_last_success_poll_time; + uint64_t m_server_started_at, m_server_delay; - unsigned m_server_id_online; + std::atomic m_server_id_online; bool m_registered_for_once_only; @@ -164,7 +168,7 @@ private: // Track(s) votes void playerVote(Event *event); void playerFinishedResult(Event *event); - bool registerServer(); + bool registerServer(bool now); void finishedLoadingWorldClient(Event *event); void kickHost(Event* event); void changeTeam(Event* event);