diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 33ce4a074..081b021ab 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -182,19 +182,15 @@ void ConnectToServer::asynchronousUpdate() { if (!m_server) { - while (!ServersManager::get()->refresh(false)) + auto server_list = + ServersManager::get()->getWANRefreshRequest(); + while (!server_list->m_list_updated) { if (ProtocolManager::lock()->isExiting()) return; StkTime::sleep(1); } - while (!ServersManager::get()->listUpdated()) - { - if (ProtocolManager::lock()->isExiting()) - return; - StkTime::sleep(1); - } - auto servers = std::move(ServersManager::get()->getServers()); + auto& servers = server_list->m_servers; // Remove password protected servers servers.erase(std::remove_if(servers.begin(), servers.end(), [] diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index 88acdcea9..d699a258e 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -48,8 +48,6 @@ # include #endif -const int64_t SERVER_REFRESH_INTERVAL = 5000; - static ServersManager* g_manager_singleton(NULL); // ============================================================================ @@ -71,7 +69,6 @@ void ServersManager::deallocate() // ---------------------------------------------------------------------------- ServersManager::ServersManager() { - reset(); } // ServersManager // ---------------------------------------------------------------------------- @@ -83,7 +80,7 @@ ServersManager::~ServersManager() /** Returns a WAN update-list-of-servers request. It queries the * STK server for an up-to-date list of servers. */ -std::shared_ptr ServersManager::getWANRefreshRequest() const +std::shared_ptr ServersManager::getWANRefreshRequest() const { // ======================================================================== /** A small local class that triggers an update of the ServersManager @@ -91,14 +88,17 @@ std::shared_ptr ServersManager::getWANRefreshRequest() const class WANRefreshRequest : public Online::XMLRequest { private: + std::weak_ptr m_server_list; // Run the ip detect in separate thread, so it can be done parallel // with the wan server request (which takes few seconds too) std::thread m_ip_detect_thread; public: - WANRefreshRequest() : Online::XMLRequest(/*priority*/100) + WANRefreshRequest(std::shared_ptr server_list) + : Online::XMLRequest(/*priority*/100) { m_ip_detect_thread = std::thread(std::bind( &NetworkConfig::detectIPType, NetworkConfig::get())); + m_server_list = server_list; } ~WANRefreshRequest() { @@ -111,16 +111,55 @@ std::shared_ptr ServersManager::getWANRefreshRequest() const Online::XMLRequest::afterOperation(); if (m_ip_detect_thread.joinable()) m_ip_detect_thread.join(); - ServersManager::get()->setWanServers(isSuccess(), getXMLData()); + + auto server_list = m_server_list.lock(); + if (!server_list) + return; + + if (!isSuccess()) + { + Log::error("ServersManager", "Could not refresh server list"); + server_list->m_list_updated = true; + return; + } + + const XMLNode *servers_xml = getXMLData()->getNode("servers"); + for (unsigned int i = 0; i < servers_xml->getNumNodes(); i++) + { + const XMLNode* s = servers_xml->getNode(i); + assert(s); + const XMLNode* si = s->getNode("server-info"); + assert(si); + int version = 0; + si->get("version", &version); + assert(version != 0); + if (version < stk_config->m_max_server_version || + version > stk_config->m_max_server_version) + { + Log::verbose("ServersManager", "Skipping a server"); + continue; + } + std::shared_ptr ser = std::make_shared(*s); + if (ser->getAddress().isUnset() && + NetworkConfig::get()->getIPType() == NetworkConfig::IP_V4) + { + Log::verbose("ServersManager", + "Skipping an IPv6 only server"); + continue; + } + server_list->m_servers.emplace_back(ser); + } + server_list->m_list_updated = true; } // afterOperation // -------------------------------------------------------------------- }; // RefreshRequest // ======================================================================== - auto request = std::make_shared(); + auto server_list = std::make_shared(); + auto request = std::make_shared(server_list); request->setApiURL(Online::API::SERVER_PATH, "get-all"); - - return request; + Online::RequestManager::get()->addRequest(request); + return server_list; } // getWANRefreshRequest // ---------------------------------------------------------------------------- @@ -128,7 +167,7 @@ std::shared_ptr ServersManager::getWANRefreshRequest() const * to find LAN servers, and waits for a certain amount of time fr * answers. */ -std::shared_ptr ServersManager::getLANRefreshRequest() const +std::shared_ptr ServersManager::getLANRefreshRequest() const { /** A simple class that uses LAN broadcasts to find local servers. * It is based on XML request, but actually does not use any of the @@ -137,10 +176,17 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const */ class LANRefreshRequest : public Online::XMLRequest { + private: + std::weak_ptr m_server_list; public: /** High priority for this request. */ - LANRefreshRequest() : XMLRequest(/*priority*/100) {m_success = false;} + LANRefreshRequest(std::shared_ptr server_list) + : XMLRequest(/*priority*/100) + { + m_success = false; + m_server_list = server_list; + } // -------------------------------------------------------------------- virtual ~LANRefreshRequest() {} // -------------------------------------------------------------------- @@ -159,6 +205,10 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const // -------------------------------------------------------------------- virtual void operation() OVERRIDE { + auto server_list = m_server_list.lock(); + if (!server_list) + return; + ENetAddress addr = {}; setIPv6Socket(UserConfigParams::m_ipv6_lan ? 1 : 0); NetworkConfig::get()->setIPType(UserConfigParams::m_ipv6_lan ? @@ -169,6 +219,7 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const setIPv6Socket(0); m_success = true; delete broadcast; + server_list->m_list_updated = true; return; } const std::vector &all_bcast = @@ -189,9 +240,7 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const // any local servers. uint64_t start_time = StkTime::getMonoTimeMs(); const uint64_t DURATION = 1000; - const auto& servers = ServersManager::get()->getServers(); - int cur_server_id = (int)servers.size(); - assert(cur_server_id == 0); + int cur_server_id = 0; // Use a map with the server name as key to automatically remove // duplicated answers from a server (since we potentially do // multiple broadcasts). We can not use the sender ip address, @@ -246,9 +295,11 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const } // if received_data } // while still waiting setIPv6Socket(0); - m_success = true; - ServersManager::get()->setLanServers(servers_now); delete broadcast; + m_success = true; + for (auto& i : servers_now) + server_list->m_servers.emplace_back(i.second); + server_list->m_list_updated = true; } // operation // -------------------------------------------------------------------- /** This function is necessary, otherwise the XML- and HTTP-Request @@ -258,95 +309,13 @@ std::shared_ptr ServersManager::getLANRefreshRequest() const }; // LANRefreshRequest // ======================================================================== - return std::make_shared(); + auto server_list = std::make_shared(); + auto request = std::make_shared(server_list); + Online::RequestManager::get()->addRequest(request); + return server_list; } // getLANRefreshRequest -// ---------------------------------------------------------------------------- -/** Takes a mapping of server name to server data (to avoid having the same - * server listed more than once since the client will be doing multiple - * broadcasts to find a server), and converts this into a list of servers. - * \param servers Mapping of server name to Server object. - */ -void ServersManager::setLanServers(const std::map >& servers) -{ - m_servers.clear(); - for (auto i : servers) m_servers.emplace_back(i.second); - m_last_load_time.store(StkTime::getMonoTimeMs()); - m_list_updated = true; - -} -// ---------------------------------------------------------------------------- -/** Factory function to create either a LAN or a WAN update-of-server - * requests. The current list of servers is also cleared. - */ -bool ServersManager::refresh(bool full_refresh) -{ - if ((int64_t)StkTime::getMonoTimeMs() - m_last_load_time.load() - < SERVER_REFRESH_INTERVAL) - { - // Avoid too frequent refreshing - return false; - } - - cleanUpServers(); - m_list_updated = false; - - if (NetworkConfig::get()->isWAN()) - { - Online::RequestManager::get()->addRequest(getWANRefreshRequest()); - } - else - { - Online::RequestManager::get()->addRequest(getLANRefreshRequest()); - } - return true; -} // refresh - -// ---------------------------------------------------------------------------- -/** Callback from the refresh request for wan servers. - * \param success If the refresh was successful. - * \param input The XML data describing the server. - */ -void ServersManager::setWanServers(bool success, const XMLNode* input) -{ - if (!success) - { - Log::error("Server Manager", "Could not refresh server list"); - m_list_updated = true; - return; - } - - const XMLNode *servers_xml = input->getNode("servers"); - for (unsigned int i = 0; i < servers_xml->getNumNodes(); i++) - { - const XMLNode* s = servers_xml->getNode(i); - assert(s); - const XMLNode* si = s->getNode("server-info"); - assert(si); - int version = 0; - si->get("version", &version); - assert(version != 0); - if (version < stk_config->m_max_server_version || - version > stk_config->m_max_server_version) - { - Log::verbose("ServersManager", "Skipping a server"); - continue; - } - std::shared_ptr ser = std::make_shared(*s); - if (ser->getAddress().isUnset() && - NetworkConfig::get()->getIPType() == NetworkConfig::IP_V4) - { - Log::verbose("ServersManager", "Skipping an IPv6 only server"); - continue; - } - m_servers.emplace_back(ser); - } - m_last_load_time.store(StkTime::getMonoTimeMs()); - m_list_updated = true; -} // refresh - // ---------------------------------------------------------------------------- /** Sets a list of default broadcast addresses which is used in case no valid * broadcast address is found. This list includes default private network diff --git a/src/network/servers_manager.hpp b/src/network/servers_manager.hpp index 495ffc553..d77181b45 100644 --- a/src/network/servers_manager.hpp +++ b/src/network/servers_manager.hpp @@ -36,18 +36,20 @@ class XMLNode; * \brief * \ingroup online */ +struct ServerList +{ + /** List of servers */ + std::vector > m_servers; + std::atomic_bool m_list_updated; + ServerList() { m_list_updated.store(false); } +}; + class ServersManager { private: - /** List of servers */ - std::vector > m_servers; - /** List of broadcast addresses to use. */ std::vector m_broadcast_address; - std::atomic m_last_load_time; - - std::atomic_bool m_list_updated; // ------------------------------------------------------------------------ ServersManager(); // ------------------------------------------------------------------------ @@ -55,10 +57,6 @@ private: // ------------------------------------------------------------------------ void setWanServers(bool success, const XMLNode* input); // ------------------------------------------------------------------------ - std::shared_ptr getWANRefreshRequest() const; - // ------------------------------------------------------------------------ - std::shared_ptr getLANRefreshRequest() const; - // ------------------------------------------------------------------------ void setLanServers(const std::map >& servers); @@ -72,20 +70,11 @@ public: // ------------------------------------------------------------------------ static void deallocate(); // ------------------------------------------------------------------------ - void cleanUpServers() { m_servers.clear(); } - // ------------------------------------------------------------------------ - bool refresh(bool full_refresh); - // ------------------------------------------------------------------------ - std::vector >& getServers() { return m_servers; } - // ------------------------------------------------------------------------ - bool listUpdated() const { return m_list_updated; } - // ------------------------------------------------------------------------ std::vector getBroadcastAddresses(bool ipv6); // ------------------------------------------------------------------------ - void reset() - { - m_last_load_time.store(-5000); - m_list_updated = false; - } + std::shared_ptr getWANRefreshRequest() const; + // ------------------------------------------------------------------------ + std::shared_ptr getLANRefreshRequest() const; + }; // class ServersManager #endif // HEADER_SERVERS_MANAGER_HPP diff --git a/src/states_screens/online/server_selection.cpp b/src/states_screens/online/server_selection.cpp index c6ba7b46a..218a50918 100644 --- a/src/states_screens/online/server_selection.cpp +++ b/src/states_screens/online/server_selection.cpp @@ -67,26 +67,30 @@ ServerSelection::~ServerSelection() void ServerSelection::tearDown() { m_servers.clear(); - ServersManager::get()->cleanUpServers(); m_server_list_widget->clear(); + m_server_list = nullptr; } // tearDown // ---------------------------------------------------------------------------- /** Requests the servers manager to update its list of servers, and disables * the 'refresh' button (till the refresh was finished). */ -void ServerSelection::refresh(bool full_refresh) +void ServerSelection::refresh() { // If the request was created (i.e. no error, and not re-requested within // 5 seconds), clear the list and display the waiting message: - if (ServersManager::get()->refresh(full_refresh)) - { - m_ip_warning_shown = false; - m_server_list_widget->clear(); - m_reload_widget->setActive(false); - m_refreshing_server = true; - m_refresh_timer = 0.0f; - } + if ((int64_t)StkTime::getMonoTimeMs() - m_last_load_time < 5000) + return; + + m_ip_warning_shown = false; + m_server_list_widget->clear(); + m_reload_widget->setActive(false); + m_refreshing_server = true; + m_refresh_timer = 0.0f; + m_last_load_time = StkTime::getMonoTimeMs(); + m_server_list = NetworkConfig::get()->isWAN() ? + ServersManager::get()->getWANRefreshRequest() : + ServersManager::get()->getLANRefreshRequest(); } // refresh // ---------------------------------------------------------------------------- @@ -137,6 +141,7 @@ void ServerSelection::beforeAddingWidget() void ServerSelection::init() { Screen::init(); + m_last_load_time = -5000; #ifndef ENABLE_IPV6 m_ipv6->setState(false); @@ -190,9 +195,7 @@ void ServerSelection::init() m_server_list_widget->setIcons(m_icon_bank, row_height); m_sort_desc = false; - /** Triggers the loading of the server list in the servers manager. */ - ServersManager::get()->reset(); - refresh(true); + refresh(); m_ipv6_only_without_nat64 = false; m_ip_warning_shown = false; } // init @@ -318,7 +321,7 @@ void ServerSelection::eventCallback(GUIEngine::Widget* widget, } else if (name == "reload") { - refresh(true); + refresh(); } else if (name == "private_server" || name == "ipv6") { @@ -334,7 +337,7 @@ void ServerSelection::eventCallback(GUIEngine::Widget* widget, m_ip_warning_shown = true; MessageQueue::add(MessageQueue::MT_ERROR, v4); } - copyFromServersManager(); + copyFromServerList(); } else if (name == m_server_list_widget->m_properties[GUIEngine::PROP_ID]) { @@ -372,14 +375,14 @@ void ServerSelection::onUpdate(float dt) sid->requestJoin(); } - if (ServersManager::get()->getServers().empty() && !m_refreshing_server && + if (m_server_list->m_list_updated && !m_refreshing_server && !NetworkConfig::get()->isWAN()) { m_refresh_timer += dt; if (m_refresh_timer > 10.0f) { - refresh(false); + refresh(); } } @@ -393,15 +396,15 @@ void ServerSelection::onUpdate(float dt) if (!m_refreshing_server) return; - if (ServersManager::get()->listUpdated()) + if (m_server_list->m_list_updated) { m_refreshing_server = false; - if (!ServersManager::get()->getServers().empty()) + if (!m_server_list->m_servers.empty()) { int selection = m_server_list_widget->getSelectionID(); std::string selection_str = m_server_list_widget ->getSelectionInternalName(); - copyFromServersManager(); + copyFromServerList(); // restore previous selection if (selection != -1 && selection_str != "loading") m_server_list_widget->setSelectionID(selection); @@ -425,9 +428,9 @@ void ServerSelection::onUpdate(float dt) } // onUpdate // ---------------------------------------------------------------------------- -void ServerSelection::copyFromServersManager() +void ServerSelection::copyFromServerList() { - m_servers = ServersManager::get()->getServers(); + m_servers = m_server_list->m_servers; if (m_servers.empty()) return; m_servers.erase(std::remove_if(m_servers.begin(), m_servers.end(), @@ -456,7 +459,7 @@ void ServerSelection::copyFromServersManager() }), m_servers.end()); } loadList(); -} // copyFromServersManager +} // copyFromServerList // ---------------------------------------------------------------------------- void ServerSelection::unloaded() diff --git a/src/states_screens/online/server_selection.hpp b/src/states_screens/online/server_selection.hpp index 514aa3083..5c96b2b5c 100644 --- a/src/states_screens/online/server_selection.hpp +++ b/src/states_screens/online/server_selection.hpp @@ -42,6 +42,7 @@ namespace irr } class Server; +struct ServerList; /** * \brief ServerSelection @@ -80,12 +81,14 @@ private: /** Load the servers into the main list.*/ void loadList(); - void copyFromServersManager(); + void copyFromServerList(); - void refresh(bool full_refresh); + void refresh(); bool m_ipv6_only_without_nat64; bool m_ip_warning_shown; + int64_t m_last_load_time; + std::shared_ptr m_server_list; public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void loadedFromFile() OVERRIDE; @@ -110,7 +113,7 @@ public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void onUpdate(float dt) OVERRIDE; - virtual void onTextUpdated() OVERRIDE { copyFromServersManager(); } + virtual void onTextUpdated() OVERRIDE { copyFromServerList(); } virtual bool onEnterPressed(const irr::core::stringw& text) OVERRIDE { return false; }